xpdf-3.04/0000755000076400007640000000000012341430012011701 5ustar derekndereknxpdf-3.04/doc/0000755000076400007640000000000012341430012012446 5ustar derekndereknxpdf-3.04/doc/pdfdetach.cat0000644000076400007640000000636212341430012015070 0ustar derekndereknpdfdetach(1) pdfdetach(1) NAME pdfdetach - Portable Document Format (PDF) document embedded file extractor (version 3.04) SYNOPSIS pdfdetach [options] [PDF-file] DESCRIPTION Pdfdetach lists or extracts embedded files (attachments) from a Porta- ble Document Format (PDF) file. CONFIGURATION FILE 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. 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.) EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The pdfinfo software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO 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/ 28 May 2014 pdfdetach(1) xpdf-3.04/doc/pdfimages.cat0000644000076400007640000000561712341430012015107 0ustar derekndereknpdfimages(1) pdfimages(1) NAME pdfimages - Portable Document Format (PDF) image extractor (version 3.04) SYNOPSIS pdfimages [options] PDF-file image-root DESCRIPTION Pdfimages saves images from a Portable Document Format (PDF) file as Portable Pixmap (PPM), Portable Bitmap (PBM), or JPEG files. Pdfimages reads the PDF file, scans one or more pages, PDF-file, and writes one PPM, PBM, or JPEG file for each image, image-root-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 performing any additional transforms. Any rotation, clipping, color inversion, etc. done by the PDF content stream is ignored. CONFIGURATION FILE Pdfimages reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdfimages is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with 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.) EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The pdfimages software and documentation are copyright 1998-2014 Glyph & Cog, LLC. SEE ALSO 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/ 28 May 2014 pdfimages(1) xpdf-3.04/doc/pdfinfo.10000644000076400007640000000634312341430012014163 0ustar dereknderekn.\" Copyright 1999-2014 Glyph & Cog, LLC .TH pdfinfo 1 "28 May 2014" .SH NAME pdfinfo \- Portable Document Format (PDF) document information extractor (version 3.04) .SH SYNOPSIS .B pdfinfo [options] .RI [ PDF-file ] .SH DESCRIPTION .B Pdfinfo prints the contents of the \'Info' dictionary (plus some other useful information) from a Portable Document Format (PDF) file. .PP The \'Info' dictionary contains the following values: .PP .RS title .RE .RS subject .RE .RS keywords .RE .RS author .RE .RS creator .RE .RS producer .RE .RS creation date .RE .RS modification date .RE .PP In addition, the following information is printed: .PP .RS tagged (yes/no) .RE .RS form (AcroForm / XFA / none) .RE .RS page count .RE .RS encrypted flag (yes/no) .RE .RS print and copy permissions (if encrypted) .RE .RS page size and rotation .RE .RS file size .RE .RS linearized (yes/no) .RE .RS PDF version .RE .RS metadata (only if requested) .RE .SH CONFIGURATION FILE Pdfinfo reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdfinfo is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-f " number" Specifies the first page to examine. If multiple pages are requested using the "\-f" and "\-l" options, the size of each requested page (and, optionally, the bounding boxes for each requested page) are printed. Otherwise, only page one is examined. .TP .BI \-l " number" Specifies the last page to examine. .TP .B \-box Prints the page box bounding boxes: MediaBox, CropBox, BleedBox, TrimBox, and ArtBox. .TP .B \-meta Prints document-level metadata. (This is the "Metadata" stream from the PDF file's Catalog object.) .TP .B \-rawdates Prints the raw (undecoded) date strings, directly from the PDF file. .TP .BI \-enc " encoding-name" Sets the encoding to use for text output. The .I encoding\-name must be defined with the unicodeMap command (see .BR xpdfrc (5)). This defaults to "Latin1" (which is a built-in encoding). .RB "[config file: " textEncoding ] .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .BI \-cfg " config-file" Read .I config-file in place of ~/.xpdfrc or the system-wide config file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdfinfo software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/pdffonts.10000644000076400007640000000651112341430012014356 0ustar dereknderekn.\" Copyright 1999-2014 Glyph & Cog, LLC .TH pdffonts 1 "28 May 2014" .SH NAME pdffonts \- Portable Document Format (PDF) font analyzer (version 3.04) .SH SYNOPSIS .B pdffonts [options] .RI [ PDF-file ] .SH DESCRIPTION .B Pdffonts lists the fonts used in a Portable Document Format (PDF) file along with various information for each font. .PP The following information is listed for each font: .TP .B name the font name, exactly as given in the PDF file (potentially including a subset prefix) .TP .B type the font type -- see below for details .TP .B emb "yes" if the font is embedded in the PDF file .TP .B sub "yes" if the font is a subset .TP .B uni "yes" if there is an explicit "ToUnicode" map in the PDF file (the absence of a ToUnicode map doesn't necessarily mean that the text can't be converted to Unicode) .TP .B object ID the font dictionary object ID (number and generation) .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 .RS Type 1 .RE .RS Type 1C -- aka Compact Font Format (CFF) .RE .RS Type 1C (OT) -- OpenType with 8-bit CFF data .RE .RS Type 3 .RE .RS TrueType .RE .RS TrueType (OT) -- OpenType with 8-bit TrueType data .RE .RS CID Type 0 -- 16-bit font with no specified type .RE .RS CID Type 0C -- 16-bit PostScript CFF font .RE .RS CID Type 0C (OT) -- OpenType with CID CFF data .RE .RS CID TrueType -- 16-bit TrueType font .RE .RS CID TrueType (OT) -- OpenType with CID TrueType data .RE .SH CONFIGURATION FILE Pdffonts reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdffonts is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-f " number" Specifies the first page to analyze. .TP .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 .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .BI \-cfg " config-file" Read .I config-file in place of ~/.xpdfrc or the system-wide config file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdffonts software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/pdftotext.cat0000644000076400007640000001253312341430012015164 0ustar derekndereknpdftotext(1) pdftotext(1) NAME pdftotext - Portable Document Format (PDF) to text converter (version 3.04) SYNOPSIS pdftotext [options] [PDF-file [text-file]] DESCRIPTION Pdftotext converts Portable Document Format (PDF) files to plain text. Pdftotext reads the PDF file, PDF-file, and writes a text file, text- file. If text-file is not specified, pdftotext converts file.pdf to file.txt. If text-file is '-', the text is sent to stdout. CONFIGURATION FILE Pdftotext reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdftotext is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with 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. If the -fixed option is given, character spacing within each line will be determined by the specified character pitch. -table Table mode is similar to physical layout mode, but optimized for tabular data, with the goal of keeping rows and columns aligned (at the expense of inserting extra whitespace). If the -fixed option is given, character spacing within each line will be determined by the specified character pitch. -lineprinter Line printer mode uses a strict fixed-character-pitch and -height layout. That is, the page is broken into a grid, and characters are placed into that grid. If the grid spacing is too small for the actual characters, the result is extra white- space. If the grid spacing is too large, the result is missing whitespace. The grid spacing can be specified using the -fixed and -linespacing options. If one or both are not given on the command line, pdftotext will attempt to compute appropriate value(s). -raw Keep the text in content stream order. Depending on how the PDF file was generated, this may or may not be useful. -fixed number 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 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.) BUGS Some PDF files contain fonts whose encodings have been mangled beyond recognition. There is no way (short of OCR) to extract text from these files. EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The pdftotext software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO 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/ 28 May 2014 pdftotext(1) xpdf-3.04/doc/pdftohtml.10000644000076400007640000000464712341430012014544 0ustar dereknderekn.\" 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/ xpdf-3.04/doc/pdftoppm.10000644000076400007640000000561612341430012014371 0ustar dereknderekn.\" 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.04) .SH SYNOPSIS .B pdftoppm [options] .I PDF-file PPM-root .SH DESCRIPTION .B Pdftoppm converts Portable Document Format (PDF) files to color image files in Portable Pixmap (PPM) format, grayscale image files in Portable Graymap (PGM) format, or monochrome image files in Portable Bitmap (PBM) format. .PP Pdftoppm reads the PDF file, .IR PDF-file , and writes one PPM file for each page, .IR PPM-root - nnnnnn .ppm, where .I nnnnnn is the page number. If .I PPM-root is \'-', the image is sent to stdout (this is probably only useful when converting a single page). .SH CONFIGURATION FILE Pdftoppm reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdftoppm is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-f " number" Specifies the first page to convert. .TP .BI \-l " number" Specifies the last page to convert. .TP .BI \-r " number" Specifies the resolution, in DPI. The default is 150 DPI. .TP .B \-mono Generate a monochrome PBM file (instead of a color PPM file). .TP .B \-gray Generate a grayscale PGM file (instead of a color PPM file). .TP .BI \-freetype " yes | no" Enable or disable FreeType (a TrueType / Type 1 font rasterizer). This defaults to "yes". .RB "[config file: " enableFreeType ] .TP .BI \-aa " yes | no" Enable or disable font anti-aliasing. This defaults to "yes". .RB "[config file: " antialias ] .TP .BI \-aaVector " yes | no" Enable or disable vector anti-aliasing. This defaults to "yes". .RB "[config file: " vectorAntialias ] .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .RB "[config file: " errQuiet ] .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdftoppm software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/pdftoppm.cat0000644000076400007640000000635412341430012015000 0ustar derekndereknpdftoppm(1) pdftoppm(1) NAME pdftoppm - Portable Document Format (PDF) to Portable Pixmap (PPM) con- verter (version 3.04) SYNOPSIS pdftoppm [options] PDF-file PPM-root DESCRIPTION Pdftoppm converts Portable Document Format (PDF) files to color image files in Portable Pixmap (PPM) format, grayscale image files in 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). CONFIGURATION FILE Pdftoppm reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdftoppm is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with 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). -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 pdftoppm 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), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ 28 May 2014 pdftoppm(1) xpdf-3.04/doc/xpdfrc.50000644000076400007640000006216712341430012014036 0ustar dereknderekn.\" Copyright 2002-2014 Glyph & Cog, LLC .TH xpdfrc 5 "28 May 2014" .SH NAME xpdfrc \- configuration file for Xpdf tools (version 3.04) .SH DESCRIPTION All of the Xpdf tools read a single configuration file. If you have a .I .xpdfrc file in your home directory, it will be read. Otherwise, a system-wide configuration file will be read from .IR /usr/local/etc/xpdfrc , if it exists. (This is its default location; depending on build options, it may be placed elsewhere.) On Win32 systems, the .I xpdfrc file should be placed in the same directory as the executables. .PP The xpdfrc file consists of a series of configuration options, one per line. Blank lines and lines starting with a \'#' (comments) are ignored. .PP Arguments may be quoted, using "double-quote" characters, e.g., for file names that contain spaces. .PP The following sections list all of the configuration options, sorted into functional groups. There is an examples section at the end. .SH INCLUDE FILES .TP .BI include " config\-file" Includes the specified config file. The effect of this is equivalent to inserting the contents of .I config\-file directly into the parent config file in place of the .I include command. Config files can be nested arbitrarily deeply. .SH CHARACTER MAPPING .TP .BI nameToUnicode " map\-file" Specifies a file with the mapping from character names to Unicode. This is used to handle PDF fonts that have valid encodings but no ToUnicode entry. Each line of a nameToUnicode file looks like this: .I " " hex\-string name The .I hex\-string is the Unicode (UCS-2) character index, and .I name is the corresponding character name. Multiple nameToUnicode files can be used; if a character name is given more than once, the code in the last specified file is used. There is a built-in default nameToUnicode table with all of Adobe's standard character names. .TP .BI cidToUnicode " registry\-ordering map\-file" Specifies the file with the mapping from character collection to Unicode. Each line of a cidToUnicode file represents one character: .I " " hex\-string The .I hex\-string is the Unicode (UCS-2) index for that character. The first line maps CID 0, the second line CID 1, etc. File size is determined by size of the character collection. Only one file is allowed per character collection; the last specified file is used. There are no built-in cidToUnicode mappings. .TP .BI unicodeToUnicode " font\-name\-substring map\-file" This is used to work around PDF fonts which have incorrect Unicode information. It specifies a file which maps from the given (incorrect) Unicode indexes to the correct ones. The mapping will be used for any font whose name contains .IR font\-name\-substring . Each line of a unicodeToUnicode file represents one Unicode character: .RI " " in\-hex " " out\-hex1 " " out\-hex2 " ..." The .I in\-hex field is an input (incorrect) Unicode index, and the rest of the fields are one or more output (correct) Unicode indexes. Each occurrence of .I in\-hex will be converted to the specified output sequence. .TP .BI unicodeMap " encoding\-name map\-file" Specifies the file with mapping from Unicode to .IR encoding\-name . These encodings are used for text output (see below). Each line of a unicodeMap file represents a range of one or more Unicode characters which maps linearly to a range in the output encoding: .nf .I " " in\-start\-hex in\-end\-hex out\-start\-hex .fi Entries for single characters can be abbreviated to: .nf .I " " in\-hex out\-hex .fi The .I in\-start\-hex and .I in\-end\-hex fields (or the single .I in\-hex field) specify the Unicode range. The .I out\-start\-hex field (or the .I out\-hex field) specifies the start of the output encoding range. The length of the .I out\-start\-hex (or .IR out\-hex ) string determines the length of the output characters (e.g., UTF-8 uses different numbers of bytes to represent characters in different ranges). Entries must be given in increasing Unicode order. Only one file is allowed per encoding; the last specified file is used. The .IR Latin1 , .IR ASCII7 , .IR Symbol , .IR ZapfDingbats , .IR UTF-8 , and .I UCS-2 encodings are predefined. .TP .BI cMapDir " registry\-ordering dir" Specifies a search directory, .IR dir , for CMaps for the .I registry\-ordering character collection. There can be multiple directories for a particular collection. There are no default CMap directories. .TP .BI toUnicodeDir " dir" Specifies a search directory, .IR dir , for ToUnicode CMaps. There can be multiple ToUnicode directories. There are no default ToUnicode directories. .SH GENERAL FONT CONFIGURATION .TP .BI fontFile " PDF\-font\-name font\-file" Maps a PDF font, .IR PDF\-font\-name , to a font for display or PostScript output. The font file, .IR font\-file , can be any type allowed in a PDF file. This command can be used for 8-bit or 16-bit (CID) fonts. .TP .BI fontDir " dir" Specifies a search directory for font files. There can be multiple fontDir commands; all of the specified directories will be searched in order. The font files can be Type 1 (.pfa or .pfb) or TrueType (.ttf or .ttc); other files in the directory will be ignored. The font file name (not including the extension) must exactly match the PDF font name. This search is performed if the font name doesn't match any of the fonts declared with the fontFile command. There are no default fontDir directories. .TP .BI fontFileCC " registry\-ordering font\-file" Maps the .I registry\-ordering character collection to a font for display or PostScript output. This mapping is used if the font name doesn't match any of the fonts declared with the fontFile, fontDir, psResidentFont16, or psResidentFontCC commands. .SH POSTSCRIPT FONT CONFIGURATION .TP .BR psFontPassthrough " yes | no" If set to "yes", pass 8-bit font names through to the PostScript output without substitution. Fonts which are not embedded in the PDF file are expected to be available on the printer. This defaults to "no". .TP .BI psResidentFont " PDF\-font\-name PS\-font\-name" When the 8-bit font .I PDF\-font\-name is used (without embedding) in a PDF file, it will be translated to the PostScript font .IR PS\-font\-name , which is assumed to be resident in the printer. Typically, .I PDF\-font\-name and .I PS\-font\-name are the same. By default, only the Base-14 fonts are assumed to be resident. .TP .BI psResidentFont16 " PDF\-font\-name wMode PS\-font\-name encoding" When the 16-bit (CID) font .I PDF\-font\-name with writing mode .I wMode is used (without embedding) in a PDF file, it will be translated to the PostScript font .IR PS\-font\-name , which is assumbed to be resident in the printer. The writing mode must be either \'H' for horizontal or \'V' for vertical. The resident font is assumed to use the specified encoding (which must have been defined with the unicodeMap command). .TP .BI psResidentFontCC " registry\-ordering wMode PS\-font\-name encoding" When a 16-bit (CID) font using the .I registry\-ordering character collection and .I wMode writing mode is used (without embedding) in a PDF file, the PostScript font, .IR PS\-font\-name , is substituted for it. The substituted font is assumbed to be resident in the printer. The writing mode must be either \'H' for horizontal or \'V' for vertical. The resident font is assumed to use the specified encoding (which must have been defined with the unicodeMap command). .TP .BR psEmbedType1Fonts " yes | no" If set to "no", prevents embedding of Type 1 fonts in generated PostScript. This defaults to "yes". .TP .BR psEmbedTrueTypeFonts " yes | no" If set to "no", prevents embedding of TrueType fonts in generated PostScript. This defaults to "yes". .TP .BR psEmbedCIDTrueTypeFonts " yes | no" If set to "no", prevents embedding of CID TrueType fonts in generated PostScript. For Level 3 PostScript, this generates a CID font, for lower levels it generates a non-CID composite font. This defaults to "yes". .TP .BR psEmbedCIDPostScriptFonts " yes | no" If set to "no", prevents embedding of CID PostScript fonts in generated PostScript. For Level 3 PostScript, this generates a CID font, for lower levels it generates a non-CID composite font. This defaults to "yes". .SH POSTSCRIPT CONTROL .TP .BI psPaperSize " width(pts) height(pts)" Sets the paper size for PostScript output. The .I width and .I height parameters give the paper size in PostScript points (1 point = 1/72 inch). .TP .BR psPaperSize " letter | legal | A4 | A3 | match" Sets the paper size for PostScript output to a standard size. The default paper size is set when xpdf and pdftops are built, typically to "letter" or "A4". This can also be set to "match", which will set the paper size to match the size specified in the PDF file. .TP .BR psImageableArea " llx lly urx ury" Sets the imageable area for PostScript output. The four integers are the coordinates of the lower-left and upper-right corners of the imageable region, specified in points (with the origin being the lower-left corner of the paper). This defaults to the full paper size; the psPaperSize option will reset the imageable area coordinates. .TP .BR psCrop " yes | no" If set to "yes", PostScript output is cropped to the CropBox specified in the PDF file; otherwise no cropping is done. This defaults to "yes". .TP .BR psUseCropBoxAsPage " yes | no" If set to "yes", PostScript output treats the CropBox as the page size. By default, this is "no", and the MediaBox is used as the page size. .TP .BR psExpandSmaller " yes | no" If set to "yes", PDF pages smaller than the PostScript imageable area are expanded to fill the imageable area. Otherwise, no scalling is done on smaller pages. This defaults to "no". .TP .BR psShrinkLarger " yes | no" If set to yes, PDF pages larger than the PostScript imageable area are shrunk to fit the imageable area. Otherwise, no scaling is done on larger pages. This defaults to "yes". .TP .BR psCenter " yes | no" If set to yes, PDF pages smaller than the PostScript imageable area (after any scaling) are centered in the imageable area. Otherwise, they are aligned at the lower-left corner of the imageable area. This defaults to "yes". .TP .BR psDuplex " yes | no" If set to "yes", the generated PostScript will set the "Duplex" pagedevice entry. This tells duplex-capable printers to enable duplexing. This defaults to "no". .TP .BR psLevel " level1 | level1sep | level2 | level2sep | level3 | level3Sep" Sets the PostScript level to generate. This defaults to "level2". .TP .BR psPreload " yes | no" If set to "yes", PDF forms are converted to PS procedures, and image data is preloaded. This uses more memory in the PostScript interpreter, but generates significantly smaller PS files in situations where, e.g., the same image is drawn on every page of a long document. This defaults to "no". .TP .BR psOPI " yes | no" If set to "yes", generates PostScript OPI comments for all images and forms which have OPI information. This option is only available if the Xpdf tools were compiled with OPI support. This defaults to "no". .TP .BR psASCIIHex " yes | no" If set to "yes", the ASCIIHexEncode filter will be used instead of ASCII85Encode for binary data. This defaults to "no". .TP .BR psLZW " yes | no" If set to "yes", the LZWEncode filter will be used for lossless compression in PostScript output; if set to "no", the RunLengthEncode filter will be used instead. LZW generates better compression (smaller PS files), but may not be supported by some printers. This defaults to "yes". .TP .BR psUncompressPreloadedImages " yes | no" If set to "yes", all preloaded images in PS files will uncompressed. If set to "no", the original compressed images will be used when possible. The "yes" setting is useful to work around certain buggy PostScript interpreters. This defaults to "no". .TP .BR psMinLineWidth " float" Set the minimum line width, in points, for PostScript output. The default value is 0 (no minimum). .TP .BR psRasterResolution " float" Set the resolution (in dpi) for rasterized pages in PostScript output. (Pdftops will rasterize pages which use transparency.) This defaults to 300. .TP .BR psRasterMono " yes | no" If set to "yes", rasterized pages in PS files will be monochrome (8-bit gray) instead of color. This defaults to "no". .TP .BR psRasterSliceSize " pixels" When rasterizing pages, pdftops splits the page into horizontal "slices", to limit memory usage. This option sets the maximum slice size, in pixels. This defaults to 20000000 (20 million). .TP .BR psAlwaysRasterize " yes | no" If set to "yes", all PostScript output will be rasterized. This defaults to "no". .TP .BI psFile " file\-or\-command" Sets the default PostScript file or print command for xpdf. Commands start with a \'|' character; anything else is a file. If the file name or command contains spaces it must be quoted. This defaults to unset, which tells xpdf to generate a name of the form .ps for a PDF file .pdf. .TP .BI fontDir " dir" See the description above, in the DISPLAY FONTS section. .SH TEXT CONTROL .TP .BI textEncoding " encoding\-name" Sets the encoding to use for text output. (This can be overridden with the "\-enc" switch on the command line.) The .I encoding\-name must be defined with the unicodeMap command (see above). This defaults to "Latin1". .TP .BR textEOL " unix | dos | mac" Sets the end-of-line convention to use for text output. The options are: .nf unix = LF dos = CR+LF mac = CR .fi (This can be overridden with the "\-eol" switch on the command line.) The default value is based on the OS where xpdf and pdftotext were built. .TP .BR textPageBreaks " yes | no" If set to "yes", text extraction will insert page breaks (form feed characters) between pages. This defaults to "yes". .TP .BR textKeepTinyChars " yes | no" If set to "yes", text extraction will keep all characters. If set to "no", text extraction will discard tiny (smaller than 3 point) characters after the first 50000 per page, avoiding extremely slow run times for PDF files that use special fonts to do shading or cross-hatching. This defaults to "yes". .SH MISCELLANEOUS SETTINGS .TP .BR initialZoom " \fIpercentage\fR | page | width" Sets the initial zoom factor. A number specifies a zoom percentage, where 100 means 72 dpi. You may also specify \'page', to fit the page to the window size, or \'width', to fit the page width to the window width. .TP .BR continuousView " yes | no" If set to "yes", xpdf will start in continuous view mode, i.e., with one vertical screoll bar for the whole document. This defaults to "no". .TP .BR enableFreeType " yes | no" Enables or disables use of FreeType (a TrueType / Type 1 font rasterizer). This is only relevant if the Xpdf tools were built with FreeType support. ("enableFreeType" replaces the old "freetypeControl" option.) This option defaults to "yes". .TP .BR enableFreeType " yes | no" Enables or disables use of FreeType (a TrueType / Type 1 font rasterizer). This is only relevant if the Xpdf tools were built with FreeType support. ("enableFreeType" replaces the old "freetypeControl" option.) This option defaults to "yes". .TP .BR disableFreeTypeHinting " yes | no" If this is set to "yes", FreeType hinting will be forced off. This option defaults to "no". .TP .BR antialias " yes | no" Enables or disables font anti-aliasing in the PDF rasterizer. This option affects all font rasterizers. ("antialias" replaces the anti-aliasing control provided by the old "t1libControl" and "freetypeControl" options.) This default to "yes". .TP .BR vectorAntialias " yes | no" Enables or disables anti-aliasing of vector graphics in the PDF rasterizer. This defaults to "yes". .TP .BR antialiasPrinting " yes | no" If this is "yes", bitmaps sent to the printer will be antialiased (according to the "antialias" and "vectorAntialias" settings). If this is "no", printed bitmaps will not be antialiased. This defaults to "no". .TP .BR strokeAdjust " yes | no" Enables or disables stroke adjustment. Stroke adjustment moves horizontal and vertical lines by up to half a pixel to make them look "cleaner" when vector anti-aliasing is enabled. This defaults to "yes". .TP .BR screenType " dispersed | clustered | stochasticClustered" Sets the halftone screen type, which will be used when generating a monochrome (1-bit) bitmap. The three options are dispersed-dot dithering, clustered-dot dithering (with a round dot and 45-degree screen angle), and stochastic clustered-dot dithering. By default, "stochasticClustered" is used for resolutions of 300 dpi and higher, and "dispersed" is used for resolutions lower then 300 dpi. .TP .BI screenSize " integer" Sets the size of the (square) halftone screen threshold matrix. By default, this is 4 for dispersed-dot dithering, 10 for clustered-dot dithering, and 100 for stochastic clustered-dot dithering. .TP .BI screenDotRadius " integer" Sets the halftone screen dot radius. This is only used when screenType is set to stochasticClustered, and it defaults to 2. In clustered-dot mode, the dot radius is half of the screen size. Dispersed-dot dithering doesn't have a dot radius. .TP .BI screenGamma " float" Sets the halftone screen gamma correction parameter. Gamma values greater than 1 make the output brighter; gamma values less than 1 make it darker. The default value is 1. .TP .BI screenBlackThreshold " float" When halftoning, all values below this threshold are forced to solid black. This parameter is a floating point value between 0 (black) and 1 (white). The default value is 0. .TP .BI screenWhiteThreshold " float" When halftoning, all values above this threshold are forced to solid white. This parameter is a floating point value between 0 (black) and 1 (white). The default value is 1. .TP .BI minLineWidth " float" Set the minimum line width, in device pixels. This affects the rasterizer only, not the PostScript converter (except when it uses rasterization to handle transparency). The default value is 0 (no minimum). .TP .BI drawAnnotations " yes | no" If set to "no", annotations will not be drawn or printed. The default value is "yes". .TP .BI overprintPreview " yes | no" If set to "yes", generate overprint preview output, honoring the OP/op/OPM settings in the PDF file. Ignored for non-CMYK output. The default value is "no". .TP .BI launchCommand " command" Sets the command executed when you click on a "launch"-type link. The intent is for the command to be a program/script which determines the file type and runs the appropriate viewer. The command line will consist of the file to be launched, followed by any parameters specified with the link. Do not use "%s" in "command". By default, this is unset, and Xpdf will simply try to execute the file (after prompting the user). .TP .BI urlCommand " command" Sets the command executed when you click on a URL link. The string "%s" will be replaced with the URL. (See the example below.) This has no default value. .TP .BI movieCommand " command" Sets the command executed when you click on a movie annotation. The string "%s" will be replaced with the movie file name. This has no default value. .TP .BI mapNumericCharNames " yes | no" If set to "yes", the Xpdf tools will attempt to map various numeric character names sometimes used in font subsets. In some cases this leads to usable text, and in other cases it leads to gibberish -- there is no way for Xpdf to tell. This defaults to "yes". .TP .BI mapUnknownCharNames " yes | no" If set to "yes", and mapNumericCharNames is set to "no", the Xpdf tools will apply a simple pass-through mapping (Unicode index = character code) for all unrecognized glyph names. (For CID fonts, setting mapNumericCharNames to "no" is unnecessary.) In some cases, this leads to usable text, and in other cases it leads to gibberish -- there is no way for Xpdf to tell. This defaults to "no". .TP .BI mapExtTrueTypeFontsViaUnicode " yes | no" When rasterizing text using an external TrueType font, there are two options for handling character codes. If mapExtTrueTypeFontsViaUnicode is set to "yes", Xpdf will use the font encoding/ToUnicode info to map character codes to Unicode, and then use the font's Unicode cmap to map Unicode to GIDs. If mapExtTrueTypeFontsViaUnicode is set to "no", Xpdf will assume the character codes are GIDs (i.e., use an identity mapping). This defaults to "yes". .TP .BI enableXFA " yes | no" If set to "yes", an XFA form (if present) will be rendered in place of an AcroForm. If "no", an XFA form will never be rendered. This defaults to "yes". .TP .BI bind " modifiers-key context command ..." Add a key or mouse button binding. .I Modifiers can be zero or more of: .nf shift- ctrl- alt- .fi .I Key can be a regular ASCII character, or any one of: .nf space tab return enter backspace insert delete home end pgup pgdn left / right / up / down (arrow keys) f1 .. f35 (function keys) mousePress1 .. mousePress7 (mouse buttons) mouseRelease1 .. mouseRelease7 (mouse buttons) .fi .I Context is either "any" or a comma-separated combination of: .nf fullScreen / window (full screen mode on/off) continuous / singlePage (continuous mode on/off) overLink / offLink (mouse over link or not) scrLockOn / scrLockOff (scroll lock on/off) .fi The context string can include only one of each pair in the above list. .I Command is an Xpdf command (see the COMMANDS section of the .BR xpdf (1) man page for details). Multiple commands are separated by whitespace. The bind command replaces any existing binding, but only if it was defined for the exact same modifiers, key, and context. All tokens (modifiers, key, context, commands) are case-sensitive. Example key bindings: .nf # bind ctrl-a in any context to the nextPage # command bind ctrl-a any nextPage # bind uppercase B, when in continuous mode # with scroll lock on, to the reload command # followed by the prevPage command bind B continuous,scrLockOn reload prevPage .fi See the .BR xpdf (1) man page for more examples. .TP .BI unbind " modifiers-key context" Removes a key binding established with the bind command. This is most useful to remove default key bindings before establishing new ones (e.g., if the default key binding is given for "any" context, and you want to create new key bindings for multiple contexts). .TP .BI printCommands " yes | no" If set to "yes", drawing commands are printed as they're executed (useful for debugging). This defaults to "no". .TP .BI errQuiet " yes | no" If set to "yes", this suppresses all error and warning messages from all of the Xpdf tools. This defaults to "no". .SH EXAMPLES The following is a sample xpdfrc file. .nf # from the Thai support package nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode # from the Japanese support package cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode unicodeMap JISX0208 /usr/local/share/xpdf/JISX0208.unicodeMap cMapDir Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1 # use the Base-14 Type 1 fonts from ghostscript fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb fontFile ZapfDingbats /usr/local/share/ghostscript/fonts/d050000l.pfb # use the Bakoma Type 1 fonts # (this assumes they happen to be installed in /usr/local/fonts/bakoma) fontDir /usr/local/fonts/bakoma # set some PostScript options psPaperSize letter psDuplex no psLevel level2 psEmbedType1Fonts yes psEmbedTrueTypeFonts yes psFile "| lpr \-Pprinter5" # assume that the PostScript printer has the Univers and # Univers-Bold fonts psResidentFont Univers Univers psResidentFont Univers-Bold Univers-Bold # set the text output options textEncoding UTF-8 textEOL unix # misc options enableFreeType yes launchCommand viewer-script urlCommand "netscape \-remote 'openURL(%s)'" .fi .SH FILES .TP .B /usr/local/etc/xpdfrc This is the default location for the system-wide configuration file. Depending on build options, it may be placed elsewhere. .TP .B $HOME/.xpdfrc This is the user's configuration file. If it exists, it will be read in place of the system-wide file. .SH AUTHOR The Xpdf software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), .BR pdftotext (1), .BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdfdetach (1), .BR pdftoppm (1), .BR pdftopng (1), .BR pdfimages (1) .br .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/pdfdetach.10000644000076400007640000000544212341430012014457 0ustar dereknderekn.\" 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.04) .SH SYNOPSIS .B pdfdetach [options] .RI [ PDF-file ] .SH DESCRIPTION .B Pdfdetach lists or extracts embedded files (attachments) from a Portable Document Format (PDF) file. .SH CONFIGURATION FILE 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 .BR xpdfrc (5) man page for details. .SH OPTIONS Some 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 .B \-list List all of the embedded files in the PDF file. File names are converted to the text encoding specified by the "\-enc" switch. .TP .BI \-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. .TP .BI \-saveall Save all of the embedded files. This uses the file names associated with the embedded files (as printed by the "\-list" switch). By default, the files are saved in the current directory; this can be changed with the "\-o" switch. .TP .BI \-o " path" Set the file name used when saving an embedded file with the "\-save" switch, or the directory used by "\-saveall". .TP .BI \-enc " encoding-name" Sets the encoding to use for text output (embedded file names). The .I encoding\-name must be defined with the unicodeMap command (see .BR xpdfrc (5)). This defaults to "Latin1" (which is a built-in encoding). .RB "[config file: " textEncoding ] .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .BI \-cfg " config-file" Read .I config-file in place of ~/.xpdfrc or the system-wide config file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdfinfo software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/xpdfrc.cat0000644000076400007640000007477212341430012014446 0ustar derekndereknxpdfrc(5) xpdfrc(5) NAME xpdfrc - configuration file for Xpdf tools (version 3.04) DESCRIPTION All of the Xpdf tools read a single configuration file. If you have a .xpdfrc file in your home directory, it will be read. Otherwise, a system-wide configuration file will be read from /usr/local/etc/xpdfrc, if it exists. (This is its default location; depending on build options, it may be placed elsewhere.) On Win32 systems, the xpdfrc file should be placed in the same directory as the executables. The xpdfrc file consists of a series of configuration options, one per line. Blank lines and lines starting with a '#' (comments) are ignored. Arguments may be quoted, using "double-quote" characters, e.g., for file names that contain spaces. The following sections list all of the configuration options, sorted into functional groups. There is an examples section at the end. INCLUDE FILES include config-file Includes the specified config file. The effect of this is equivalent to inserting the contents of config-file directly into the parent config file in place of the include command. Config files can be nested arbitrarily deeply. CHARACTER MAPPING nameToUnicode map-file Specifies a file with the mapping from character names to Uni- code. This is used to handle PDF fonts that have valid encod- ings but no ToUnicode entry. Each line of a nameToUnicode file looks like this: hex-string name The hex-string is the Unicode (UCS-2) character index, and name is the corresponding character name. Multiple nameToUnicode files can be used; if a character name is given more than once, the code in the last specified file is used. There is a built- in default nameToUnicode table with all of Adobe's standard character names. cidToUnicode registry-ordering map-file Specifies the file with the mapping from character collection to Unicode. Each line of a cidToUnicode file represents one char- acter: hex-string The hex-string is the Unicode (UCS-2) index for that character. The first line maps CID 0, the second line CID 1, etc. File size is determined by size of the character collection. Only one file is allowed per character collection; the last specified file is used. There are no built-in cidToUnicode mappings. unicodeToUnicode font-name-substring map-file This is used to work around PDF fonts which have incorrect Uni- code information. It specifies a file which maps from the given (incorrect) Unicode indexes to the correct ones. The mapping will be used for any font whose name contains font-name-sub- string. Each line of a unicodeToUnicode file represents one Unicode character: in-hex out-hex1 out-hex2 ... The in-hex field is an input (incorrect) Unicode index, and the rest of the fields are one or more output (correct) Unicode indexes. Each occurrence of in-hex will be converted to the specified output sequence. unicodeMap encoding-name map-file Specifies the file with mapping from Unicode to encoding-name. These encodings are used for text output (see below). Each line of a unicodeMap file represents a range of one or more Unicode characters which maps linearly to a range in the output encod- ing: in-start-hex in-end-hex out-start-hex Entries for single characters can be abbreviated to: in-hex out-hex The in-start-hex and in-end-hex fields (or the single in-hex field) specify the Unicode range. The out-start-hex field (or the out-hex field) specifies the start of the output encoding range. The length of the out-start-hex (or out-hex) string determines the length of the output characters (e.g., UTF-8 uses different numbers of bytes to represent characters in different ranges). Entries must be given in increasing Unicode order. Only one file is allowed per encoding; the last specified file is used. The Latin1, ASCII7, Symbol, ZapfDingbats, UTF-8, and UCS-2 encodings are predefined. cMapDir registry-ordering dir Specifies a search directory, dir, for CMaps for the reg- istry-ordering character collection. There can be multiple directories for a particular collection. There are no default CMap directories. toUnicodeDir dir Specifies a search directory, dir, for ToUnicode CMaps. There can be multiple ToUnicode directories. There are no default ToUnicode directories. GENERAL FONT CONFIGURATION fontFile PDF-font-name font-file Maps a PDF font, PDF-font-name, to a font for display or Post- Script output. The font file, font-file, can be any type allowed in a PDF file. This command can be used for 8-bit or 16-bit (CID) fonts. fontDir dir Specifies a search directory for font files. There can be mul- tiple fontDir commands; all of the specified directories will be searched in order. The font files can be Type 1 (.pfa or .pfb) or TrueType (.ttf or .ttc); other files in the directory will be ignored. The font file name (not including the extension) must exactly match the PDF font name. This search is performed if the font name doesn't match any of the fonts declared with the fontFile command. There are no default fontDir directories. fontFileCC registry-ordering font-file Maps the registry-ordering character collection to a font for display or PostScript output. This mapping is used if the font name doesn't match any of the fonts declared with the fontFile, fontDir, psResidentFont16, or psResidentFontCC commands. POSTSCRIPT FONT CONFIGURATION psFontPassthrough yes | no If set to "yes", pass 8-bit font names through to the PostScript output without substitution. Fonts which are not embedded in the PDF file are expected to be available on the printer. This defaults to "no". psResidentFont PDF-font-name PS-font-name When the 8-bit font PDF-font-name is used (without embedding) in a PDF file, it will be translated to the PostScript font PS-font-name, which is assumed to be resident in the printer. Typically, PDF-font-name and PS-font-name are the same. By default, only the Base-14 fonts are assumed to be resident. psResidentFont16 PDF-font-name wMode PS-font-name encoding When the 16-bit (CID) font PDF-font-name with writing mode wMode is used (without embedding) in a PDF file, it will be translated to the PostScript font PS-font-name, which is assumbed to be resident in the printer. The writing mode must be either 'H' for horizontal or 'V' for vertical. The resident font is assumed to use the specified encoding (which must have been defined with the unicodeMap command). psResidentFontCC registry-ordering wMode PS-font-name encoding When a 16-bit (CID) font using the registry-ordering character collection and wMode writing mode is used (without embedding) in a PDF file, the PostScript font, PS-font-name, is substituted for it. The substituted font is assumbed to be resident in the printer. The writing mode must be either 'H' for horizontal or 'V' for vertical. The resident font is assumed to use the spec- ified encoding (which must have been defined with the unicodeMap command). psEmbedType1Fonts yes | no If set to "no", prevents embedding of Type 1 fonts in generated PostScript. This defaults to "yes". psEmbedTrueTypeFonts yes | no If set to "no", prevents embedding of TrueType fonts in gener- ated PostScript. This defaults to "yes". psEmbedCIDTrueTypeFonts yes | no If set to "no", prevents embedding of CID TrueType fonts in gen- erated PostScript. For Level 3 PostScript, this generates a CID font, for lower levels it generates a non-CID composite font. This defaults to "yes". psEmbedCIDPostScriptFonts yes | no If set to "no", prevents embedding of CID PostScript fonts in generated PostScript. For Level 3 PostScript, this generates a CID font, for lower levels it generates a non-CID composite font. This defaults to "yes". POSTSCRIPT CONTROL psPaperSize width(pts) height(pts) Sets the paper size for PostScript output. The width and height parameters give the paper size in PostScript points (1 point = 1/72 inch). psPaperSize letter | legal | A4 | A3 | match Sets the paper size for PostScript output to a standard size. The default paper size is set when xpdf and pdftops are built, typically to "letter" or "A4". This can also be set to "match", which will set the paper size to match the size specified in the PDF file. psImageableArea llx lly urx ury Sets the imageable area for PostScript output. The four inte- gers are the coordinates of the lower-left and upper-right cor- ners of the imageable region, specified in points (with the ori- gin being the lower-left corner of the paper). This defaults to the full paper size; the psPaperSize option will reset the imageable area coordinates. psCrop yes | no If set to "yes", PostScript output is cropped to the CropBox specified in the PDF file; otherwise no cropping is done. This defaults to "yes". psUseCropBoxAsPage yes | no If set to "yes", PostScript output treats the CropBox as the page size. By default, this is "no", and the MediaBox is used as the page size. psExpandSmaller yes | no If set to "yes", PDF pages smaller than the PostScript imageable area are expanded to fill the imageable area. Otherwise, no scalling is done on smaller pages. This defaults to "no". psShrinkLarger yes | no If set to yes, PDF pages larger than the PostScript imageable area are shrunk to fit the imageable area. Otherwise, no scal- ing is done on larger pages. This defaults to "yes". psCenter yes | no If set to yes, PDF pages smaller than the PostScript imageable area (after any scaling) are centered in the imageable area. Otherwise, they are aligned at the lower-left corner of the imageable area. This defaults to "yes". psDuplex yes | no If set to "yes", the generated PostScript will set the "Duplex" pagedevice entry. This tells duplex-capable printers to enable duplexing. This defaults to "no". psLevel level1 | level1sep | level2 | level2sep | level3 | level3Sep Sets the PostScript level to generate. This defaults to "level2". psPreload yes | no If set to "yes", PDF forms are converted to PS procedures, and image data is preloaded. This uses more memory in the Post- Script interpreter, but generates significantly smaller PS files in situations where, e.g., the same image is drawn on every page of a long document. This defaults to "no". psOPI yes | no If set to "yes", generates PostScript OPI comments for all images and forms which have OPI information. This option is only available if the Xpdf tools were compiled with OPI support. This defaults to "no". psASCIIHex yes | no If set to "yes", the ASCIIHexEncode filter will be used instead of ASCII85Encode for binary data. This defaults to "no". psLZW yes | no If set to "yes", the LZWEncode filter will be used for lossless compression in PostScript output; if set to "no", the RunLength- Encode filter will be used instead. LZW generates better com- pression (smaller PS files), but may not be supported by some printers. This defaults to "yes". psUncompressPreloadedImages yes | no If set to "yes", all preloaded images in PS files will uncom- pressed. If set to "no", the original compressed images will be used when possible. The "yes" setting is useful to work around certain buggy PostScript interpreters. This defaults to "no". psMinLineWidth float Set the minimum line width, in points, for PostScript output. The default value is 0 (no minimum). psRasterResolution float Set the resolution (in dpi) for rasterized pages in PostScript output. (Pdftops will rasterize pages which use transparency.) This defaults to 300. psRasterMono yes | no If set to "yes", rasterized pages in PS files will be monochrome (8-bit gray) instead of color. This defaults to "no". psRasterSliceSize pixels When rasterizing pages, pdftops splits the page into horizontal "slices", to limit memory usage. This option sets the maximum slice size, in pixels. This defaults to 20000000 (20 million). psAlwaysRasterize yes | no If set to "yes", all PostScript output will be rasterized. This defaults to "no". psFile file-or-command Sets the default PostScript file or print command for xpdf. Commands start with a '|' character; anything else is a file. If the file name or command contains spaces it must be quoted. This defaults to unset, which tells xpdf to generate a name of the form .ps for a PDF file .pdf. fontDir dir See the description above, in the DISPLAY FONTS section. TEXT CONTROL textEncoding encoding-name Sets the encoding to use for text output. (This can be overrid- den with the "-enc" switch on the command line.) The encod- ing-name must be defined with the unicodeMap command (see above). This defaults to "Latin1". textEOL unix | dos | mac Sets the end-of-line convention to use for text output. The options are: unix = LF dos = CR+LF mac = CR (This can be overridden with the "-eol" switch on the command line.) The default value is based on the OS where xpdf and pdftotext were built. textPageBreaks yes | no If set to "yes", text extraction will insert page breaks (form feed characters) between pages. This defaults to "yes". textKeepTinyChars yes | no If set to "yes", text extraction will keep all characters. If set to "no", text extraction will discard tiny (smaller than 3 point) characters after the first 50000 per page, avoiding extremely slow run times for PDF files that use special fonts to do shading or cross-hatching. This defaults to "yes". MISCELLANEOUS SETTINGS initialZoom percentage | page | width Sets the initial zoom factor. A number specifies a zoom per- centage, where 100 means 72 dpi. You may also specify 'page', to fit the page to the window size, or 'width', to fit the page width to the window width. continuousView yes | no If set to "yes", xpdf will start in continuous view mode, i.e., with one vertical screoll bar for the whole document. This defaults to "no". enableFreeType yes | no Enables or disables use of FreeType (a TrueType / Type 1 font rasterizer). This is only relevant if the Xpdf tools were built with FreeType support. ("enableFreeType" replaces the old "freetypeControl" option.) This option defaults to "yes". enableFreeType yes | no Enables or disables use of FreeType (a TrueType / Type 1 font rasterizer). This is only relevant if the Xpdf tools were built with FreeType support. ("enableFreeType" replaces the old "freetypeControl" option.) This option defaults to "yes". disableFreeTypeHinting yes | no If this is set to "yes", FreeType hinting will be forced off. This option defaults to "no". antialias yes | no Enables or disables font anti-aliasing in the PDF rasterizer. This option affects all font rasterizers. ("antialias" replaces the anti-aliasing control provided by the old "t1libControl" and "freetypeControl" options.) This default to "yes". vectorAntialias yes | no Enables or disables anti-aliasing of vector graphics in the PDF rasterizer. This defaults to "yes". antialiasPrinting yes | no If this is "yes", bitmaps sent to the printer will be antialiased (according to the "antialias" and "vectorAntialias" settings). If this is "no", printed bitmaps will not be antialiased. This defaults to "no". strokeAdjust yes | no Enables or disables stroke adjustment. Stroke adjustment moves horizontal and vertical lines by up to half a pixel to make them look "cleaner" when vector anti-aliasing is enabled. This defaults to "yes". screenType dispersed | clustered | stochasticClustered Sets the halftone screen type, which will be used when generat- ing a monochrome (1-bit) bitmap. The three options are dis- persed-dot dithering, clustered-dot dithering (with a round dot and 45-degree screen angle), and stochastic clustered-dot dithering. By default, "stochasticClustered" is used for reso- lutions of 300 dpi and higher, and "dispersed" is used for reso- lutions lower then 300 dpi. screenSize integer Sets the size of the (square) halftone screen threshold matrix. By default, this is 4 for dispersed-dot dithering, 10 for clus- tered-dot dithering, and 100 for stochastic clustered-dot dithering. screenDotRadius integer Sets the halftone screen dot radius. This is only used when screenType is set to stochasticClustered, and it defaults to 2. In clustered-dot mode, the dot radius is half of the screen size. Dispersed-dot dithering doesn't have a dot radius. screenGamma float Sets the halftone screen gamma correction parameter. Gamma val- ues greater than 1 make the output brighter; gamma values less than 1 make it darker. The default value is 1. screenBlackThreshold float When halftoning, all values below this threshold are forced to solid black. This parameter is a floating point value between 0 (black) and 1 (white). The default value is 0. screenWhiteThreshold float When halftoning, all values above this threshold are forced to solid white. This parameter is a floating point value between 0 (black) and 1 (white). The default value is 1. minLineWidth float Set the minimum line width, in device pixels. This affects the rasterizer only, not the PostScript converter (except when it uses rasterization to handle transparency). The default value is 0 (no minimum). drawAnnotations yes | no If set to "no", annotations will not be drawn or printed. The default value is "yes". overprintPreview yes | no If set to "yes", generate overprint preview output, honoring the OP/op/OPM settings in the PDF file. Ignored for non-CMYK out- put. The default value is "no". launchCommand command Sets the command executed when you click on a "launch"-type link. The intent is for the command to be a program/script which determines the file type and runs the appropriate viewer. The command line will consist of the file to be launched, fol- lowed by any parameters specified with the link. Do not use "%s" in "command". By default, this is unset, and Xpdf will simply try to execute the file (after prompting the user). urlCommand command Sets the command executed when you click on a URL link. The string "%s" will be replaced with the URL. (See the example below.) This has no default value. movieCommand command Sets the command executed when you click on a movie annotation. The string "%s" will be replaced with the movie file name. This has no default value. mapNumericCharNames yes | no If set to "yes", the Xpdf tools will attempt to map various numeric character names sometimes used in font subsets. In some cases this leads to usable text, and in other cases it leads to gibberish -- there is no way for Xpdf to tell. This defaults to "yes". mapUnknownCharNames yes | no If set to "yes", and mapNumericCharNames is set to "no", the Xpdf tools will apply a simple pass-through mapping (Unicode index = character code) for all unrecognized glyph names. (For CID fonts, setting mapNumericCharNames to "no" is unnecessary.) In some cases, this leads to usable text, and in other cases it leads to gibberish -- there is no way for Xpdf to tell. This defaults to "no". mapExtTrueTypeFontsViaUnicode yes | no When rasterizing text using an external TrueType font, there are two options for handling character codes. If mapExtTrueType- FontsViaUnicode is set to "yes", Xpdf will use the font encod- ing/ToUnicode info to map character codes to Unicode, and then use the font's Unicode cmap to map Unicode to GIDs. If mapExt- TrueTypeFontsViaUnicode is set to "no", Xpdf will assume the character codes are GIDs (i.e., use an identity mapping). This defaults to "yes". enableXFA yes | no If set to "yes", an XFA form (if present) will be rendered in place of an AcroForm. If "no", an XFA form will never be ren- dered. This defaults to "yes". bind modifiers-key context command ... Add a key or mouse button binding. Modifiers can be zero or more of: shift- ctrl- alt- Key can be a regular ASCII character, or any one of: space tab return enter backspace insert delete home end pgup pgdn left / right / up / down (arrow keys) f1 .. f35 (function keys) mousePress1 .. mousePress7 (mouse buttons) mouseRelease1 .. mouseRelease7 (mouse buttons) Context is either "any" or a comma-separated combination of: fullScreen / window (full screen mode on/off) continuous / singlePage (continuous mode on/off) overLink / offLink (mouse over link or not) scrLockOn / scrLockOff (scroll lock on/off) The context string can include only one of each pair in the above list. Command is an Xpdf command (see the COMMANDS section of the xpdf(1) man page for details). Multiple commands are separated by whitespace. The bind command replaces any existing binding, but only if it was defined for the exact same modifiers, key, and context. All tokens (modifiers, key, context, commands) are case-sensitive. Example key bindings: # bind ctrl-a in any context to the nextPage # command bind ctrl-a any nextPage # bind uppercase B, when in continuous mode # with scroll lock on, to the reload command # followed by the prevPage command bind B continuous,scrLockOn reload prevPage See the xpdf(1) man page for more examples. unbind modifiers-key context Removes a key binding established with the bind command. This is most useful to remove default key bindings before establish- ing new ones (e.g., if the default key binding is given for "any" context, and you want to create new key bindings for mul- tiple contexts). printCommands yes | no If set to "yes", drawing commands are printed as they're exe- cuted (useful for debugging). This defaults to "no". errQuiet yes | no If set to "yes", this suppresses all error and warning messages from all of the Xpdf tools. This defaults to "no". EXAMPLES The following is a sample xpdfrc file. # from the Thai support package nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode # from the Japanese support package cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode unicodeMap JISX0208 /usr/local/share/xpdf/JISX0208.unicodeMap cMapDir Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1 # use the Base-14 Type 1 fonts from ghostscript fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb fontFile ZapfDingbats /usr/local/share/ghostscript/fonts/d050000l.pfb # use the Bakoma Type 1 fonts # (this assumes they happen to be installed in /usr/local/fonts/bakoma) fontDir /usr/local/fonts/bakoma # set some PostScript options psPaperSize letter psDuplex no psLevel level2 psEmbedType1Fonts yes psEmbedTrueTypeFonts yes psFile "| lpr -Pprinter5" # assume that the PostScript printer has the Univers and # Univers-Bold fonts psResidentFont Univers Univers psResidentFont Univers-Bold Univers-Bold # set the text output options textEncoding UTF-8 textEOL unix # misc options enableFreeType yes launchCommand viewer-script urlCommand "netscape -remote 'openURL(%s)'" FILES /usr/local/etc/xpdfrc This is the default location for the system-wide configuration file. Depending on build options, it may be placed elsewhere. $HOME/.xpdfrc This is the user's configuration file. If it exists, it will be read in place of the system-wide file. AUTHOR The Xpdf software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf- fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), pdfimages(1) http://www.foolabs.com/xpdf/ 28 May 2014 xpdfrc(5) xpdf-3.04/doc/pdftopng.10000644000076400007640000000547012341430012014357 0ustar dereknderekn.\" 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/ xpdf-3.04/doc/xpdf.cat0000644000076400007640000006505712341430012014115 0ustar derekndereknxpdf(1) xpdf(1) NAME xpdf - Portable Document Format (PDF) file viewer for X (version 3.04) SYNOPSIS xpdf [options] [PDF-file [page | +dest]] DESCRIPTION Xpdf is a viewer for Portable Document Format (PDF) files. (These are also sometimes also called 'Acrobat' files, from the name of Adobe's PDF software.) Xpdf runs under the X Window System on UNIX, VMS, and OS/2. To run xpdf, simply type: xpdf file.pdf where file.pdf is your PDF file. The file name can be followed by a number specifying the page which should be displayed first, e.g.: xpdf file.pdf 18 You can also give a named destination, prefixed with '+' in place of the page number. (This is only useful with PDF files that provide named destination targets.) You can also start xpdf without opening any files: xpdf CONFIGURATION FILE Xpdf reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when xpdf is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with configuration file com- mands or X resources. These are listed in square brackets with the description of the corresponding command line option. -g geometry Set the initial window geometry. (-geometry is equivalent.) [X resource: xpdf.geometry] -title title Set the window title. By default, the title will be "xpdf: foo.pdf". [X resource: xpdf.title] -cmap Install a private colormap. This is ignored on TrueColor visu- als. [X resource: xpdf.installCmap] -rgb number Set the size of largest RGB cube xpdf will try to allocate. The default is 5 (for a 5x5x5 cube); set to a smaller number to con- serve color table entries. This is ignored with private col- ormaps and on TrueColor visuals. [X resource: xpdf.rgbCubeSize] -rv Set reverse video mode. This reverses the colors of everything except images. It may not always produce great results for PDF files which do weird things with color. This also causes the paper color to default to black. [X resource: xpdf.reverseV- ideo] -papercolor color Set the "paper color", i.e., the background of the page display. This will not work too well with PDF files that do things like filling in white behind the text. [X resource: xpdf.paperColor] -mattecolor color Set the matte color, i.e., the color used for background outside the actual page area. (There is a separate setting, xpdf.fullScreenMatteColor, for full-screen mode.) [X resource: xpdf.matteColor] -z zoom Set the initial zoom factor. A number specifies a zoom percent- age, where 100 means 72 dpi. You may also specify 'page', to fit the page to the window size, or 'width', to fit the page width to the window width. [config file: initialZoom; or X resource: xpdf.initialZoom] -cont Start in continuous view mode, i.e., with one vertical scroll bar for the whole document. [config file: continuousView] -freetype yes | no Enable or disable FreeType (a TrueType / Type 1 font raster- izer). This defaults to "yes". [config file: enableFreeType] -aa yes | no Enable or disable font anti-aliasing. This defaults to "yes". [config file: antialias] -aaVector yes | no Enable or disable vector anti-aliasing. This defaults to "yes". [config file: vectorAntialias] -ps PS-file Set the default file name for PostScript output (i.e., the name which will appear in the print dialog). This can also be of the form '|command' to pipe the PostScript through a command. [con- fig file: psFile] -paper size Set the paper size to one of "letter", "legal", "A4", or "A3". This can also be set to "match", which will set the paper size to match the size specified in the PDF file. [config file: psPaperSize] -paperw size Set the paper width, in points. [config file: psPaperSize] -paperh size Set the paper height, in points. [config file: psPaperSize] -level1 Generate Level 1 PostScript. The resulting PostScript files will be significantly larger (if they contain images), but will print on Level 1 printers. This also converts all images to black and white. [config file: psLevel] -enc encoding-name Sets the encoding to use for text output. The encoding-name must be defined with the unicodeMap command (see xpdfrc(5)). This defaults to "Latin1" (which is a built-in encoding). [con- fig file: textEncoding] -eol unix | dos | mac Sets the end-of-line convention to use for text output. [config file: textEOL] -opw password Specify the owner password for the PDF file. Providing this will bypass all security restrictions. -upw password Specify the user password for the PDF file. -fullscreen Open xpdf in full-screen mode, useful for presentations. -remote name Start/contact xpdf remote server with specified name (see the REMOTE SERVER MODE section below). -exec command Execute a command (see the COMMANDS section below) in an xpdf remote server window (with -remote only). -reload Reload xpdf remote server window (with -remote only). -raise Raise xpdf remote server window (with -remote only). -quit Kill xpdf remote server (with -remote only). -cmd Print commands as they're executed (useful for debugging). [config file: printCommands] -q Don't print any messages or errors. [config file: errQuiet] -cfg config-file Read config-file in place of ~/.xpdfrc or the system-wide config file. -v Print copyright and version information. -h Print usage information. (-help and --help are equivalent.) Several other standard X options and resources will work as expected: -display display [X resource: xpdf.display] -fg color (-foreground is equivalent.) [X resource: xpdf*Foreground] -bg color (-background is equivalent.) [X resource: xpdf*Background] -font font (-fn is equivalent.) [X resource: xpdf*fontList] The color and font options only affect the user interface elements, not the PDF display (the 'paper'). The following X resources do not have command line option equivalents: xpdf.toolTipEnable Enables (if set to true) or disables (if set to false) the tool- tips on the toolbar buttons. xpdf.fullScreenMatteColor Sets the matte color to be used in full-screen mode. The default setting is "black". CONTROLS On-screen controls, at the bottom of the xpdf window left/right arrow buttons Move to the previous/next page. double left/right arrow buttons Move backward or forward by ten pages. dashed left/right arrow buttons Move backward or forward along the history path. 'Page' entry box Move to a specific page number. Click in the box to activate it, type the page number, then hit return. zoom popup menu Change the zoom factor (see the description of the -z option above). binoculars button Find a text string. print button Bring up a dialog for generating a PostScript file. The dialog has options to set the pages to be printed and the PostScript file name. The file name can be '-' for stdout or '|command' to pipe the PostScript through a command, e.g., '|lpr'. '?' button Bring up the 'about xpdf' window. link info The space between the '?' and 'Quit' buttons is used to show the URL or external file name when the mouse is over a link. 'Quit' button Quit xpdf. Menu Pressing the right mouse button will post a popup menu with the follow- ing commands: Open... Open a new PDF file via a file requester. Open in new window... Create a new window and open a new PDF file via a file requester. Reload Reload the current PDF file. Note that Xpdf will reload the file automatically (on a page change or redraw) if it has changed since it was last loaded. Save as... Save the current file via a file requester. Continuous view Toggles between single page and continuous view modes. Rotate counterclockwise Rotate the page 90 degrees counterclockwise. Rotate clockwise Rotate the page 90 degrees clockwise. The two rotate commands are intended primarily for PDF files where the rotation isn't correctly specified in the file. Zoom to selection Zoom in to the currently selected rectangle. Close Close the current window. If this is the only open window, the document is closed, but the window is left open (i.e., this menu command won't quit xpdf). Quit Quit xpdf. Outline If the PDF contains an outline (a.k.a., bookmarks), there will be an outline pane on the left side of the window. The width of the outline pane is adjustable with a vertical split bar via the knob near its bot- tom end. Text selection Dragging the mouse with the left button held down will highlight an arbitrary rectangle. Any text inside this rectangle will be copied to the X selection buffer. Links Clicking on a hyperlink will jump to the link's destination. A link to another PDF document will make xpdf load that document. A 'launch' link to an executable program will display a dialog, and if you click 'ok', execute the program. URL links call an external command (see the WEB BROWSERS section below). Panning Dragging the mouse with the middle button held down pans the window. Key bindings o Open a new PDF file via a file requester. r Reload the current PDF file. Note that Xpdf will reload the file automatically (on a page change or redraw) if it has changed since it was last loaded. control-L Redraw the current page. control-W Close the current window. f or control-F Find a text string. control-G Find next occurrence. control-P Print. n Move to the next page. Scrolls to the top of the page, unless scroll lock is turned on. p Move to the previous page. Scrolls to the top of the page, unless scroll lock is turned on. or or Scroll down on the current page; if already at bottom, move to next page. or or or Scroll up on the current page; if already at top, move to previ- ous page. v Move forward along the history path. b Move backward along the history path. Scroll to top of current page. Scroll to bottom of current page. control- Scroll to first page of document. control- Scroll to last page of document. arrows Scroll the current page. g Activate the page number text field ("goto page"). 0 Set the zoom factor to 125%. + Zoom in (increment the zoom factor by 1). - Zoom out (decrement the zoom factor by 1). z Set the zoom factor to 'page' (fit page to window). w Set the zoom factor to 'width' (fit page width to window). alt-F Toggle full-screen mode. q Quit xpdf. WEB BROWSERS If you want to run xpdf automatically from netscape or mosaic (and probably other browsers) when you click on a link to a PDF file, you need to edit (or create) the files .mime.types and .mailcap in your home directory. In .mime.types add the line: application/pdf pdf In .mailcap add the lines: # Use xpdf to view PDF files. application/pdf; xpdf -q %s Make sure that xpdf is on your executable search path. When you click on a URL link in a PDF file, xpdf will execute the com- mand specified by the urlCommand config file option, replacing an occurrence of '%s' with the URL. For example, to call netscape with the URL, add this line to your config file: urlCommand "netscape -remote 'openURL(%s)'" COMMANDS Xpdf's key and mouse bindings are user-configurable, using the bind and unbind options in the config file (see xpdfrc(5)). The bind command allows you to bind a key or mouse button to a sequence of one or more commands. Available Commands The following commands are supported: gotoPage(page) Go to the specified page. gotoPageNoScroll(page) Go to the specified page, with the current relative scroll posi- tion. gotoDest(dest) Go to a named destination. gotoLastPage Go to the last page in the PDF file. gotoLastPageNoScroll Go to the last page in the PDF file, with the current relative scroll position. nextPage Go to the next page. nextPageNoScroll Go to the next page, with the current relative scroll position. prevPage Go to the previous page. prevPageNoScroll Go to the previous page, with the current relative scroll posi- tion. pageUp Scroll up by one screenful. pageDown Scroll down by one screenful. scrollLeft(n) Scroll left by n pixels. scrollRight(n) Scroll right by n pixels. scrollUp(n) Scroll up by n pixels. scrollDown(n) Scroll down by n pixels. scrollUpPrevPage(n) Scroll up by n pixels, moving to the previous page if appropri- ate. scrollDownPrevPage(n) Scroll down by n pixels, moving to the next page if appropriate. scrollToTopEdge Scroll to the top edge of the current page, with no horizontal movement. scrollToBottomEdge Scroll to the bottom edge of the current page, with no horizon- tal movement. scrollToLeftEdge Scroll to the left edge of the current page, with no vertical movement. scrollToRightEdge Scroll to the right edge of the current page, with no vertical movement. scrollToTopLeft Scroll to the top-left corner of the current page. scrollToBottomRight Scroll to the bottom-right corner of the current page. goForward Move forward along the history path. goBackward Move backward along the history path. zoomPercent(z) Set the zoom factor to z%. zoomFitPage Set the zoom factor to fit-page. zoomFitWidth Set the zoom factor to fit-width. zoomIn Zoom in - go to the next higher zoom factor. zoomOut Zoom out - go the next lower zoom factor. rotateCW Rotate the page 90 degrees clockwise. rotateCCW Rotate the page 90 degrees counterclockwise. setSelection(pg,ulx,uly,lrx,lry) Set the selection to the specified coordinates on the specified page. continuousMode Go to continuous view mode. singlePageMode Go to single-page view mode. toggleContinuousMode Toggle between continuous and single page view modes. fullScreenMode Go to full-screen mode. windowMode Go to window (non-full-screen) mode. toggleFullScreenMode Toggle between full-screen and window modes. open Open a PDF file in this window, using the open dialog. openInNewWin Open a PDF file in a new window, using the open dialog. openFile(file) Open a specified PDF file in this window. openFileInNewWin(file) Open a specified PDF file in a new window. openFileAtDest(file,dest) Open a specified PDF file in this window and go to a named des- tination. openFileAtDestInNewWin(file,dest) Open a specified PDF file in a new window and go to a named des- tination. reload Reload the current PDF file. redraw Redraw the window. raise Raise the window to the front. closeWindow Close the window. If this was the last open window, clear the window, but don't quit from Xpdf. closeWindowOrQuit Close the window. If this was the last open window, quit from Xpdf. run(external-command-string) Run an external command. The following escapes are allowed in the command string: %f => PDF file name (or an empty string if no file is open) %b => PDF file base name, i.e., file name minus the extension (or an empty string if no file is open) %u => link URL (or an empty string if not over a URL link) %p => current page number (or an empty string if no file is open) %x => selection upper-left x coordinate (or 0 if there is no selection) %y => selection upper-left y coordinate (or 0 if there is no selection) %X => selection lower-right x coordinate (or 0 if there is no selection) %Y => selection lower-right y coordinate (or 0 if there is no selection) %i => page containing the mouse pointer %j => x coordinate of the mouse pointer %k => y coordinate of the mouse pointer %% => % The external command string will often contain spaces, so the whole command must be quoted in the xpdfrc file: bind x "run(ls -l)" openOutline Open the outline pane. closeOutline Close the outline pane. toggleOutline Toggle the outline pane between open and closed. scrollOutlineDown(n) Scroll the outline down by n increments. scrollOutlineUp(n) Scroll the outline up by n increments. focusToDocWin Set the keyboard focus to the main document window. focusToPageNum Set the keyboard focus to the page number text box. find Open the 'find' dialog. findNext Finds the next occurrence of the search string (no dialog). print Open the 'print' dialog. about Open the 'about' dialog. quit Quit from xpdf. The following commands depend on the current mouse position: startSelection Start a selection, which will be extended as the mouse moves. endSelection End a selection. startPan Start a pan, which will scroll the document as the mouse moves endPan End a pan. postPopupMenu Display the popup menu. followLink Follow a hyperlink (does nothing if the mouse is not over a link). followLinkInNewWin Follow a hyperlink, opening PDF files in a new window (does nothing if the mouse is not over a link). For links to non-PDF files, this command is identical to followLink. followLinkNoSel Same as followLink, but does nothing if there is a non-empty selection. (This is useful as a mouse button binding.) followLinkInNewWinNoSel Same as followLinkInNewWin, but does nothing if there is a non- empty selection. (This is useful as a mouse button binding.) Default Bindings The default mouse bindings are as follows: bind mousePress1 any startSelection bind mouseRelease1 any endSelection followLinkNoSel bind mousePress2 any startPan bind mouseRelease2 any endPan bind mousePress3 any postPopupMenu bind mousePress4 any scrollUpPrevPage(16) bind mousePress5 any scrollDownNextPage(16) bind mousePress6 any scrollLeft(16) bind mousePress7 any scrollRight(16) The default key bindings are as follows: bind ctrl-home any gotoPage(1) bind home any scrollToTopLeft bind ctrl-end any gotoLastPage bind end any scrollToBottomRight bind pgup any pageUp bind backspace any pageUp bind delete any pageUp bind pgdn any pageDown bind space any pageDown bind left any scrollLeft(16) bind right any scrollRight(16) bind up any scrollUp(16) bind down any scrollDown(16) bind o any open bind O any open bind r any reload bind R any reload bind f any find bind F any find bind ctrl-f any find bind ctrl-g any findNext bind ctrl-p any print bind n scrLockOff nextPage bind N scrLockOff nextPage bind n scrLockOn nextPageNoScroll bind N scrLockOn nextPageNoScroll bind p scrLockOff prevPage bind P scrLockOff prevPage bind p scrLockOn prevPageNoScroll bind P scrLockOn prevPageNoScroll bind v any goForward bind b any goBackward bind g any focusToPageNum bind 0 any zoomPercent(125) bind + any zoomIn bind - any zoomOut bind z any zoomFitPage bind w any zoomFitWidth bind alt-f any toggleFullScreenMode bind ctrl-l any redraw bind ctrl-w any closeWindowOrQuit bind ? any about bind q any quit bind Q any quit Previous versions of xpdf included a "viKeys" X resource. It is no longer available, but the following bindings are equivalent: bind h any scrollLeft(16) bind l any scrollRight(16) bind k any scrollUp(16) bind j any scrollDown(16) REMOTE SERVER MODE Xpdf can be started in remote server mode by specifying a server name (in addition to the file name and page number). For example: xpdf -remote myServer file.pdf If there is currently no xpdf running in server mode with the name 'myServer', a new xpdf window will be opened. If another command: xpdf -remote myServer another.pdf 9 is issued, a new copy of xpdf will not be started. Instead, the first xpdf (the server) will load another.pdf and display page nine. If the file name is the same: xpdf -remote myServer another.pdf 4 the xpdf server will simply display the specified page. The -raise option tells the server to raise its window; it can be spec- ified with or without a file name and page number. The -quit option tells the server to close its window and exit. EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The xpdf software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde- tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ 28 May 2014 xpdf(1) xpdf-3.04/doc/pdfimages.10000644000076400007640000000507012341430012014471 0ustar dereknderekn.\" Copyright 1998-2014 Glyph & Cog, LLC .TH pdfimages 1 "28 May 2014" .SH NAME pdfimages \- Portable Document Format (PDF) image extractor (version 3.04) .SH SYNOPSIS .B pdfimages [options] .I PDF-file image-root .SH DESCRIPTION .B Pdfimages saves images from a Portable Document Format (PDF) file as Portable Pixmap (PPM), Portable Bitmap (PBM), or JPEG files. .PP Pdfimages reads the PDF file, scans one or more pages, .IR PDF-file , and writes one PPM, PBM, or JPEG file for each image, .IR image-root - nnnn . xxx , where .I nnnn is the image number and .I xxx is the image type (.ppm, .pbm, .jpg). .PP NB: pdfimages extracts the raw image data from the PDF file, without performing any additional transforms. Any rotation, clipping, color inversion, etc. done by the PDF content stream is ignored. .SH CONFIGURATION FILE Pdfimages reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdfimages is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-f " number" Specifies the first page to scan. .TP .BI \-l " number" Specifies the last page to scan. .TP .B \-j Normally, all images are written as PBM (for monochrome images) or PPM (for non-monochrome images) files. With this option, images in DCT format are saved as JPEG files. All non-DCT images are saved in PBM/PPM format as usual. .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .RB "[config file: " errQuiet ] .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdfimages software and documentation are copyright 1998-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/ xpdf-3.04/doc/pdftopng.cat0000644000076400007640000000622412341430012014764 0ustar derekndereknpdftopng(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) xpdf-3.04/doc/pdftops.cat0000644000076400007640000002056412341430012014625 0ustar derekndereknpdftops(1) pdftops(1) NAME pdftops - Portable Document Format (PDF) to PostScript converter (ver- sion 3.04) SYNOPSIS pdftops [options] [PDF-file [PS-file]] DESCRIPTION Pdftops converts Portable Document Format (PDF) files to PostScript so they can be printed. Pdftops reads the PDF file, PDF-file, and writes a PostScript file, PS- file. If PS-file is not specified, pdftops converts file.pdf to file.ps (or file.eps with the -eps option). If PS-file is '-', the PostScript is sent to stdout. CONFIGURATION FILE Pdftops reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdftops is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with 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. [config file: psUseCropBoxAsPage] -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.) EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The pdftops software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO 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/ 28 May 2014 pdftops(1) xpdf-3.04/doc/pdftops.10000644000076400007640000001611312341430012014211 0ustar dereknderekn.\" Copyright 1996-2014 Glyph & Cog, LLC .TH pdftops 1 "28 May 2014" .SH NAME pdftops \- Portable Document Format (PDF) to PostScript converter (version 3.04) .SH SYNOPSIS .B pdftops [options] .RI [ PDF-file .RI [ PS-file ]] .SH DESCRIPTION .B Pdftops converts Portable Document Format (PDF) files to PostScript so they can be printed. .PP Pdftops reads the PDF file, .IR PDF-file , and writes a PostScript file, .IR PS-file . If .I PS-file is not specified, pdftops converts .I file.pdf to .I file.ps (or .I file.eps with the \-eps option). If .I PS-file is \'-', the PostScript is sent to stdout. .SH CONFIGURATION FILE Pdftops reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdftops is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-f " number" Specifies the first page to print. .TP .BI \-l " number" Specifies the last page to print. .TP .B \-level1 Generate Level 1 PostScript. The resulting PostScript files will be significantly larger (if they contain images), but will print on Level 1 printers. This also converts all images to black and white. No more than one of the PostScript level options (\-level1, \-level1sep, \-level2, \-level2sep, \-level3, \-level3Sep) may be given. .RB "[config file: " psLevel ] .TP .B \-level1sep Generate Level 1 separable PostScript. All colors are converted to CMYK. Images are written with separate stream data for the four components. .RB "[config file: " psLevel ] .TP .B \-level2 Generate Level 2 PostScript. Level 2 supports color images and image compression. This is the default setting. .RB "[config file: " psLevel ] .TP .B \-level2sep Generate Level 2 separable PostScript. All colors are converted to CMYK. The PostScript separation convention operators are used to handle custom (spot) colors. .RB "[config file: " psLevel ] .TP .B \-level3 Generate Level 3 PostScript. This enables all Level 2 features plus CID font embedding and masked image generation. .RB "[config file: " psLevel ] .TP .B \-level3Sep Generate Level 3 separable PostScript. The separation handling is the same as for \-level2Sep. .RB "[config file: " psLevel ] .TP .B \-eps Generate an Encapsulated PostScript (EPS) file. An EPS file contains a single image, so if you use this option with a multi-page PDF file, you must use \-f and \-l to specify a single page. No more than one of the mode options (\-eps, \-form) may be given. .TP .B \-form Generate a PostScript form which can be imported by software that understands forms. A form contains a single page, so if you use this option with a multi-page PDF file, you must use \-f and \-l to specify a single page. The \-level1 option cannot be used with \-form. .TP .B \-opi Generate OPI comments for all images and forms which have OPI information. (This option is only available if pdftops was compiled with OPI support.) .RB "[config file: " psOPI ] .TP .B \-noembt1 By default, any Type 1 fonts which are embedded in the PDF file are copied into the PostScript file. This option causes pdftops to substitute base fonts instead. Embedded fonts make PostScript files larger, but may be necessary for readable output. .RB "[config file: " psEmbedType1Fonts ] .TP .B \-noembtt By default, any TrueType fonts which are embedded in the PDF file are copied into the PostScript file. This option causes pdftops to substitute base fonts instead. Embedded fonts make PostScript files larger, but may be necessary for readable output. Also, some PostScript interpreters do not have TrueType rasterizers. .RB "[config file: " psEmbedTrueTypeFonts ] .TP .B \-noembcidps By default, any CID PostScript fonts which are embedded in the PDF file are copied into the PostScript file. This option disables that embedding. No attempt is made to substitute for non-embedded CID PostScript fonts. .RB "[config file: " psEmbedCIDPostScriptFonts ] .TP .B \-noembcidtt By default, any CID TrueType fonts which are embedded in the PDF file are copied into the PostScript file. This option disables that embedding. No attempt is made to substitute for non-embedded CID TrueType fonts. .RB "[config file: " psEmbedCIDTrueTypeFonts ] .TP .B \-preload Convert PDF forms to PS procedures, and preload image data. This uses more memory in the PostScript interpreter, but generates significantly smaller PS files in situations where, e.g., the same image is drawn on every page of a long document. .TP .BI \-paper " size" Set the paper size to one of "letter", "legal", "A4", or "A3". This can also be set to "match", which will set the paper size to match the size specified in the PDF file. .RB "[config file: " psPaperSize ] .TP .BI \-paperw " size" Set the paper width, in points. .RB "[config file: " psPaperSize ] .TP .BI \-paperh " size" Set the paper height, in points. .RB "[config file: " psPaperSize ] .TP .B \-nocrop By default, output is cropped to the CropBox specified in the PDF file. This option disables cropping. .RB "[config file: " psCrop ] .TP .B \-expand Expand PDF pages smaller than the paper to fill the paper. By default, these pages are not scaled. .RB "[config file: " psExpandSmaller ] .TP .B \-noshrink Don't scale PDF pages which are larger than the paper. By default, pages larger than the paper are shrunk to fit. .RB "[config file: " psShrinkLarger ] .TP .B \-nocenter By default, PDF pages smaller than the paper (after any scaling) are centered on the paper. This option causes them to be aligned to the lower-left corner of the paper instead. .RB "[config file: " psCenter ] .TP .B \-pagecrop Treat the CropBox as the PDF page size. By default, the MediaBox is used as the page size. .RB "[config file: " psUseCropBoxAsPage ] .TP .B \-duplex Set the Duplex pagedevice entry in the PostScript file. This tells duplex-capable printers to enable duplexing. .RB "[config file: " psDuplex ] .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .RB "[config file: " errQuiet ] .TP .BI \-cfg " config-file" Read .I config-file in place of ~/.xpdfrc or the system-wide config file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdftops software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/pdftohtml.cat0000644000076400007640000000534712341430012015151 0ustar derekndereknpdftohtml(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) xpdf-3.04/doc/pdfinfo.cat0000644000076400007640000000742012341430012014567 0ustar derekndereknpdfinfo(1) pdfinfo(1) NAME pdfinfo - Portable Document Format (PDF) document information extractor (version 3.04) SYNOPSIS pdfinfo [options] [PDF-file] DESCRIPTION Pdfinfo prints the contents of the 'Info' dictionary (plus some other useful information) from a Portable Document Format (PDF) file. The 'Info' dictionary contains the following values: title subject keywords author creator producer creation date modification date In addition, the following information is printed: tagged (yes/no) form (AcroForm / XFA / none) page count encrypted flag (yes/no) print and copy permissions (if encrypted) page size and rotation file size linearized (yes/no) PDF version metadata (only if requested) CONFIGURATION FILE Pdfinfo reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdfinfo is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with 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.) EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The pdfinfo software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO 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/ 28 May 2014 pdfinfo(1) xpdf-3.04/doc/sample-xpdfrc0000644000076400007640000000666112341430012015147 0ustar dereknderekn#======================================================================== # # Sample xpdfrc file # # The Xpdf tools look for a config file in two places: # 1. ~/.xpdfrc # 2. in a system-wide directory, typically /usr/local/etc/xpdfrc # # This sample config file demonstrates some of the more common # configuration options. Everything here is commented out. You # should edit things (especially the file/directory paths, since # they'll likely be different on your system), and uncomment whichever # options you want to use. For complete details on config file syntax # and available options, please see the xpdfrc(5) man page. # # Also, the Xpdf language support packages each include a set of # options to be added to the xpdfrc file. # # http://www.foolabs.com/xpdf/ # #======================================================================== #----- display fonts # These map the Base-14 fonts to the Type 1 fonts that ship with # ghostscript. You'll almost certainly want to use something like # this, but you'll need to adjust this to point to wherever # ghostscript is installed on your system. (But if the fonts are # installed in a "standard" location, xpdf will find them # automatically.) #fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb #fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb #fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb #fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb #fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb #fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb #fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb #fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb #fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb #fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb #fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb #fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb #fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb #fontFile ZapfDingbats /usr/local/share/ghostscript/fonts/d050000l.pfb # If you need to display PDF files that refer to non-embedded fonts, # you should add one or more fontDir options to point to the # directories containing the font files. Xpdf will only look at .pfa, # .pfb, .ttf, and .ttc files in those directories (other files will # simply be ignored). #fontDir /usr/local/fonts/bakoma #----- PostScript output control # Set the default PostScript file or command. #psFile "|lpr -Pmyprinter" # Set the default PostScript paper size -- this can be letter, legal, # A4, or A3. You can also specify a paper size as width and height # (in points). #psPaperSize letter #----- text output control # Choose a text encoding for copy-and-paste and for pdftotext output. # The Latin1, ASCII7, and UTF-8 encodings are built into Xpdf. Other # encodings are available in the language support packages. #textEncoding UTF-8 # Choose the end-of-line convention for multi-line copy-and-past and # for pdftotext output. The available options are unix, mac, and dos. #textEOL unix #----- misc settings # Enable FreeType, and anti-aliased text. #enableFreeType yes #antialias yes # Set the command used to run a web browser when a URL hyperlink is # clicked. #launchCommand viewer-script #urlCommand "netscape -remote 'openURL(%s)'" xpdf-3.04/doc/pdftotext.10000644000076400007640000001116012341430012014550 0ustar dereknderekn.\" Copyright 1997-2014 Glyph & Cog, LLC .TH pdftotext 1 "28 May 2014" .SH NAME pdftotext \- Portable Document Format (PDF) to text converter (version 3.04) .SH SYNOPSIS .B pdftotext [options] .RI [ PDF-file .RI [ text-file ]] .SH DESCRIPTION .B Pdftotext converts Portable Document Format (PDF) files to plain text. .PP Pdftotext reads the PDF file, .IR PDF-file , and writes a text file, .IR text-file . If .I text-file is not specified, pdftotext converts .I file.pdf to .IR file.txt . If .I text-file is \'-', the text is sent to stdout. .SH CONFIGURATION FILE Pdftotext reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdftotext is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-f " number" Specifies the first page to convert. .TP .BI \-l " number" Specifies the last page to convert. .TP .B \-layout Maintain (as best as possible) the original physical layout of the text. The default is to \'undo' physical layout (columns, hyphenation, etc.) and output the text in reading order. If the .B \-fixed option is given, character spacing within each line will be determined by the specified character pitch. .TP .B \-table Table mode is similar to physical layout mode, but optimized for tabular data, with the goal of keeping rows and columns aligned (at the expense of inserting extra whitespace). If the .B \-fixed option is given, character spacing within each line will be determined by the specified character pitch. .TP .B \-lineprinter Line printer mode uses a strict fixed-character-pitch and -height layout. That is, the page is broken into a grid, and characters are placed into that grid. If the grid spacing is too small for the actual characters, the result is extra whitespace. If the grid spacing is too large, the result is missing whitespace. The grid spacing can be specified using the .B \-fixed and .B \-linespacing options. If one or both are not given on the command line, pdftotext will attempt to compute appropriate value(s). .TP .B \-raw Keep the text in content stream order. Depending on how the PDF file was generated, this may or may not be useful. .TP .BI \-fixed " number" 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 .I encoding\-name must be defined with the unicodeMap command (see .BR xpdfrc (5)). The encoding name is case-sensitive. This defaults to "Latin1" (which is a built-in encoding). .RB "[config file: " textEncoding ] .TP .BI \-eol " unix | dos | mac" Sets the end-of-line convention to use for text output. .RB "[config file: " textEOL ] .TP .B \-nopgbrk Don't insert page breaks (form feed characters) between pages. .RB "[config file: " textPageBreaks ] .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .RB "[config file: " errQuiet ] .TP .BI \-cfg " config-file" Read .I config-file in place of ~/.xpdfrc or the system-wide config file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH BUGS Some PDF files contain fonts whose encodings have been mangled beyond recognition. There is no way (short of OCR) to extract text from these files. .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdftotext software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/xpdf.10000644000076400007640000005525212341430012013502 0ustar dereknderekn.\" 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.04) .SH SYNOPSIS .B xpdf [options] .RI [ PDF-file .RI [ page " | +" dest ]] .SH DESCRIPTION .B Xpdf is a viewer for Portable Document Format (PDF) files. (These are also sometimes also called \'Acrobat' files, from the name of Adobe's PDF software.) Xpdf runs under the X Window System on UNIX, VMS, and OS/2. .PP To run xpdf, simply type: .PP .RS xpdf file.pdf .RE .PP where .I file.pdf is your PDF file. The file name can be followed by a number specifying the page which should be displayed first, e.g.: .PP .RS xpdf file.pdf 18 .RE .PP You can also give a named destination, prefixed with \'+' in place of the page number. (This is only useful with PDF files that provide named destination targets.) .PP You can also start xpdf without opening any files: .PP .RS xpdf .RE .SH CONFIGURATION FILE Xpdf reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when xpdf is built). See the .BR xpdfrc (5) man page for details. .SH OPTIONS Many of the following options can be set with configuration file commands or X resources. These are listed in square brackets with the description of the corresponding command line option. .TP .BI \-g " geometry" Set the initial window geometry. .RB ( \-geometry is equivalent.) .RB "[X resource: " xpdf.geometry ] .TP .BI \-title " title" Set the window title. By default, the title will be "xpdf: foo.pdf". .RB "[X resource: " xpdf.title ] .TP .B \-cmap Install a private colormap. This is ignored on TrueColor visuals. .RB "[X resource: " xpdf.installCmap ] .TP .BI \-rgb " number" Set the size of largest RGB cube xpdf will try to allocate. The default is 5 (for a 5x5x5 cube); set to a smaller number to conserve color table entries. This is ignored with private colormaps and on TrueColor visuals. .RB "[X resource: " xpdf.rgbCubeSize ] .TP .B \-rv Set reverse video mode. This reverses the colors of everything except images. It may not always produce great results for PDF files which do weird things with color. This also causes the paper color to default to black. .RB "[X resource: " xpdf.reverseVideo ] .TP .BI \-papercolor " color" Set the "paper color", i.e., the background of the page display. This will not work too well with PDF files that do things like filling in white behind the text. .RB "[X resource: " xpdf.paperColor ] .TP .BI \-mattecolor " color" Set the matte color, i.e., the color used for background outside the actual page area. (There is a separate setting, xpdf.fullScreenMatteColor, for full-screen mode.) .RB "[X resource: " xpdf.matteColor ] .TP .BI \-z " zoom" Set the initial zoom factor. A number specifies a zoom percentage, where 100 means 72 dpi. You may also specify \'page', to fit the page to the window size, or \'width', to fit the page width to the window width. .RB "[config file: " initialZoom "; or X resource: " xpdf.initialZoom ] .TP .B \-cont Start in continuous view mode, i.e., with one vertical scroll bar for the whole document. .RB "[config file: " continuousView ] .TP .BI \-freetype " yes | no" Enable or disable FreeType (a TrueType / Type 1 font rasterizer). This defaults to "yes". .RB "[config file: " enableFreeType ] .TP .BI \-aa " yes | no" Enable or disable font anti-aliasing. This defaults to "yes". .RB "[config file: " antialias ] .TP .BI \-aaVector " yes | no" Enable or disable vector anti-aliasing. This defaults to "yes". .RB "[config file: " vectorAntialias ] .TP .BI \-ps " PS-file" Set the default file name for PostScript output (i.e., the name which will appear in the print dialog). This can also be of the form \'|command' to pipe the PostScript through a command. .RB "[config file: " psFile ] .TP .BI \-paper " size" Set the paper size to one of "letter", "legal", "A4", or "A3". This can also be set to "match", which will set the paper size to match the size specified in the PDF file. .RB "[config file: " psPaperSize ] .TP .BI \-paperw " size" Set the paper width, in points. .RB "[config file: " psPaperSize ] .TP .BI \-paperh " size" Set the paper height, in points. .RB "[config file: " psPaperSize ] .TP .B \-level1 Generate Level 1 PostScript. The resulting PostScript files will be significantly larger (if they contain images), but will print on Level 1 printers. This also converts all images to black and white. .RB "[config file: " psLevel ] .TP .BI \-enc " encoding-name" Sets the encoding to use for text output. The .I encoding\-name must be defined with the unicodeMap command (see .BR xpdfrc (5)). This defaults to "Latin1" (which is a built-in encoding). .RB "[config file: " textEncoding ] .TP .BI \-eol " unix | dos | mac" Sets the end-of-line convention to use for text output. .RB "[config file: " textEOL ] .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-fullscreen Open xpdf in full-screen mode, useful for presentations. .TP .BI \-remote " name" Start/contact xpdf remote server with specified name (see the .B "REMOTE SERVER MODE" section below). .TP .BI \-exec " command" Execute a command (see the .B COMMANDS section below) in an xpdf remote server window (with \-remote only). .TP .B \-reload Reload xpdf remote server window (with \-remote only). .TP .B \-raise Raise xpdf remote server window (with \-remote only). .TP .B \-quit Kill xpdf remote server (with \-remote only). .TP .B \-cmd Print commands as they're executed (useful for debugging). .RB "[config file: " printCommands ] .TP .B \-q Don't print any messages or errors. .RB "[config file: " errQuiet ] .TP .BI \-cfg " config-file" Read .I config-file in place of ~/.xpdfrc or the system-wide config file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .PP Several other standard X options and resources will work as expected: .TP .BI \-display " display" .RB "[X resource: " xpdf.display ] .TP .BI \-fg " color" .RB ( \-foreground is equivalent.) .RB "[X resource: " xpdf*Foreground ] .TP .BI \-bg " color" .RB ( \-background is equivalent.) .RB "[X resource: " xpdf*Background ] .TP .BI \-font " font" .RB ( \-fn is equivalent.) .RB "[X resource: " xpdf*fontList ] .PP The color and font options only affect the user interface elements, not the PDF display (the \'paper'). .PP The following X resources do not have command line option equivalents: .TP .B xpdf.toolTipEnable Enables (if set to true) or disables (if set to false) the tool-tips on the toolbar buttons. .TP .B xpdf.fullScreenMatteColor Sets the matte color to be used in full-screen mode. The default setting is "black". .SH CONTROLS .SS On-screen controls, at the bottom of the xpdf window .TP .B "left/right arrow buttons" Move to the previous/next page. .TP .B "double left/right arrow buttons" Move backward or forward by ten pages. .TP .B "dashed left/right arrow buttons" Move backward or forward along the history path. .TP .B "\'Page' entry box" Move to a specific page number. Click in the box to activate it, type the page number, then hit return. .TP .B "zoom popup menu" Change the zoom factor (see the description of the \-z option above). .TP .B "binoculars button" Find a text string. .TP .B "print button" Bring up a dialog for generating a PostScript file. The dialog has options to set the pages to be printed and the PostScript file name. The file name can be \'-' for stdout or \'|command' to pipe the PostScript through a command, e.g., \'|lpr'. .TP .B "\'?' button" Bring up the \'about xpdf' window. .TP .B "link info" The space between the \'?' and \'Quit' buttons is used to show the URL or external file name when the mouse is over a link. .TP .B "\'Quit' button" Quit xpdf. .PP .SS Menu Pressing the right mouse button will post a popup menu with the following commands: .TP .B "Open..." Open a new PDF file via a file requester. .TP .B "Open in new window..." Create a new window and open a new PDF file via a file requester. .TP .B "Reload" Reload the current PDF file. Note that Xpdf will reload the file automatically (on a page change or redraw) if it has changed since it was last loaded. .TP .B "Save as..." Save the current file via a file requester. .TP .B "Continuous view" Toggles between single page and continuous view modes. .TP .B "Rotate counterclockwise" Rotate the page 90 degrees counterclockwise. .TP .B "Rotate clockwise" Rotate the page 90 degrees clockwise. The two rotate commands are intended primarily for PDF files where the rotation isn't correctly specified in the file. .TP .B "Zoom to selection" Zoom in to the currently selected rectangle. .TP .B "Close" Close the current window. If this is the only open window, the document is closed, but the window is left open (i.e., this menu command won't quit xpdf). .TP .B "Quit" Quit xpdf. .PP .SS Outline If the PDF contains an outline (a.k.a., bookmarks), there will be an outline pane on the left side of the window. The width of the outline pane is adjustable with a vertical split bar via the knob near its bottom end. .PP .SS Text selection Dragging the mouse with the left button held down will highlight an arbitrary rectangle. Any text inside this rectangle will be copied to the X selection buffer. .PP .SS Links Clicking on a hyperlink will jump to the link's destination. A link to another PDF document will make xpdf load that document. A \'launch' link to an executable program will display a dialog, and if you click \'ok', execute the program. URL links call an external command (see the .B WEB BROWSERS section below). .PP .SS Panning Dragging the mouse with the middle button held down pans the window. .PP .SS Key bindings .TP .B o Open a new PDF file via a file requester. .TP .B r Reload the current PDF file. Note that Xpdf will reload the file automatically (on a page change or redraw) if it has changed since it was last loaded. .TP .B control-L Redraw the current page. .TP .B control-W Close the current window. .TP .B f or control-F Find a text string. .TP .B control-G Find next occurrence. .TP .B control-P Print. .TP .B n Move to the next page. Scrolls to the top of the page, unless scroll lock is turned on. .TP .B p Move to the previous page. Scrolls to the top of the page, unless scroll lock is turned on. .TP .BR " or " " or " Scroll down on the current page; if already at bottom, move to next page. .TP .BR " or " " or " " or " Scroll up on the current page; if already at top, move to previous page. .TP .B v Move forward along the history path. .TP .B b Move backward along the history path. .TP .B Scroll to top of current page. .TP .B Scroll to bottom of current page. .TP .B control- Scroll to first page of document. .TP .B control- Scroll to last page of document. .TP .B arrows Scroll the current page. .TP .B g Activate the page number text field ("goto page"). .TP .B 0 Set the zoom factor to 125%. .TP .B + Zoom in (increment the zoom factor by 1). .TP .B - Zoom out (decrement the zoom factor by 1). .TP .B z Set the zoom factor to 'page' (fit page to window). .TP .B w Set the zoom factor to 'width' (fit page width to window). .TP .B alt-F Toggle full-screen mode. .TP .B q Quit xpdf. .SH "WEB BROWSERS" If you want to run xpdf automatically from netscape or mosaic (and probably other browsers) when you click on a link to a PDF file, you need to edit (or create) the files .I .mime.types and .I .mailcap in your home directory. In .I .mime.types add the line: .PP .RS application/pdf pdf .RE .PP In .I .mailcap add the lines: .PP .RS # Use xpdf to view PDF files. .RE .RS application/pdf; xpdf \-q %s .RE .PP Make sure that xpdf is on your executable search path. .PP When you click on a URL link in a PDF file, xpdf will execute the command specified by the urlCommand config file option, replacing an occurrence of \'%s' with the URL. For example, to call netscape with the URL, add this line to your config file: .PP .RS urlCommand "netscape \-remote 'openURL(%s)'" .RE .SH COMMANDS Xpdf's key and mouse bindings are user-configurable, using the bind and unbind options in the config file (see .BR xpdfrc (5)). The bind command allows you to bind a key or mouse button to a sequence of one or more commands. .SS Available Commands The following commands are supported: .TP .BI gotoPage( page ) Go to the specified page. .TP .BI gotoPageNoScroll( page ) Go to the specified page, with the current relative scroll position. .TP .BI gotoDest( dest ) Go to a named destination. .TP .B gotoLastPage Go to the last page in the PDF file. .TP .B gotoLastPageNoScroll Go to the last page in the PDF file, with the current relative scroll position. .TP .B nextPage Go to the next page. .TP .B nextPageNoScroll Go to the next page, with the current relative scroll position. .TP .B prevPage Go to the previous page. .TP .B prevPageNoScroll Go to the previous page, with the current relative scroll position. .TP .B pageUp Scroll up by one screenful. .TP .B pageDown Scroll down by one screenful. .TP .BI scrollLeft( n ) Scroll left by .I n pixels. .TP .BI scrollRight( n ) Scroll right by .I n pixels. .TP .BI scrollUp( n ) Scroll up by .I n pixels. .TP .BI scrollDown( n ) Scroll down by .I n pixels. .TP .BI scrollUpPrevPage( n ) Scroll up by .I n pixels, moving to the previous page if appropriate. .TP .BI scrollDownPrevPage( n ) Scroll down by .I n pixels, moving to the next page if appropriate. .TP .B scrollToTopEdge Scroll to the top edge of the current page, with no horizontal movement. .TP .B scrollToBottomEdge Scroll to the bottom edge of the current page, with no horizontal movement. .TP .B scrollToLeftEdge Scroll to the left edge of the current page, with no vertical movement. .TP .B scrollToRightEdge Scroll to the right edge of the current page, with no vertical movement. .TP .B scrollToTopLeft Scroll to the top-left corner of the current page. .TP .B scrollToBottomRight Scroll to the bottom-right corner of the current page. .TP .B goForward Move forward along the history path. .TP .B goBackward Move backward along the history path. .TP .BI zoomPercent( z ) Set the zoom factor to .IR z %. .TP .B zoomFitPage Set the zoom factor to fit-page. .TP .B zoomFitWidth Set the zoom factor to fit-width. .TP .B zoomIn Zoom in - go to the next higher zoom factor. .TP .B zoomOut Zoom out - go the next lower zoom factor. .TP .B rotateCW Rotate the page 90 degrees clockwise. .TP .B rotateCCW Rotate the page 90 degrees counterclockwise. .TP .BI setSelection( pg , ulx , uly , lrx , lry ) Set the selection to the specified coordinates on the specified page. .TP .B continuousMode Go to continuous view mode. .TP .B singlePageMode Go to single-page view mode. .TP .B toggleContinuousMode Toggle between continuous and single page view modes. .TP .B fullScreenMode Go to full-screen mode. .TP .B windowMode Go to window (non-full-screen) mode. .TP .B toggleFullScreenMode Toggle between full-screen and window modes. .TP .B open Open a PDF file in this window, using the open dialog. .TP .B openInNewWin Open a PDF file in a new window, using the open dialog. .TP .BI openFile( file ) Open a specified PDF file in this window. .TP .BI openFileInNewWin( file ) Open a specified PDF file in a new window. .TP .BI openFileAtDest( file , dest ) Open a specified PDF file in this window and go to a named destination. .TP .BI openFileAtDestInNewWin( file , dest ) Open a specified PDF file in a new window and go to a named destination. .TP .B reload Reload the current PDF file. .TP .B redraw Redraw the window. .TP .B raise Raise the window to the front. .TP .B closeWindow Close the window. 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 command string: .nf %f => PDF file name (or an empty string if no file is open) %b => PDF file base name, i.e., file name minus the extension (or an empty string if no file is open) %u => link URL (or an empty string if not over a URL link) %p => current page number (or an empty string if no file is open) %x => selection upper-left x coordinate (or 0 if there is no selection) %y => selection upper-left y coordinate (or 0 if there is no selection) %X => selection lower-right x coordinate (or 0 if there is no selection) %Y => selection lower-right y coordinate (or 0 if there is no selection) %i => page containing the mouse pointer %j => x coordinate of the mouse pointer %k => y coordinate of the mouse pointer %% => % .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. .TP .B closeOutline Close the outline pane. .TP .B toggleOutline Toggle the outline pane between open and closed. .TP .BI scrollOutlineDown( n ) Scroll the outline down by .I n increments. .TP .BI scrollOutlineUp( n ) Scroll the outline up by .I n increments. .TP .B focusToDocWin Set the keyboard focus to the main document window. .TP .B focusToPageNum Set the keyboard focus to the page number text box. .TP .B find Open the 'find' dialog. .TP .B findNext Finds the next occurrence of the search string (no dialog). .TP .B print Open the 'print' dialog. .TP .B about Open the 'about' dialog. .TP .B quit Quit from xpdf. .PP The following commands depend on the current mouse position: .TP .B startSelection Start a selection, which will be extended as the mouse moves. .TP .B endSelection End a selection. .TP .B startPan Start a pan, which will scroll the document as the mouse moves .TP .B endPan End a pan. .TP .B postPopupMenu Display the popup menu. .TP .B followLink Follow a hyperlink (does nothing if the mouse is not over a link). .TP .B followLinkInNewWin Follow a hyperlink, opening PDF files in a new window (does nothing if the mouse is not over a link). For links to non-PDF files, this command is identical to followLink. .TP .B followLinkNoSel Same as followLink, but does nothing if there is a non-empty selection. (This is useful as a mouse button binding.) .TP .B followLinkInNewWinNoSel Same as followLinkInNewWin, but does nothing if there is a non-empty selection. (This is useful as a mouse button binding.) .SS Default Bindings The default mouse bindings are as follows: .nf bind mousePress1 any startSelection bind mouseRelease1 any endSelection followLinkNoSel bind mousePress2 any startPan bind mouseRelease2 any endPan bind mousePress3 any postPopupMenu bind mousePress4 any scrollUpPrevPage(16) bind mousePress5 any scrollDownNextPage(16) bind mousePress6 any scrollLeft(16) bind mousePress7 any scrollRight(16) .fi The default key bindings are as follows: .nf bind ctrl-home any gotoPage(1) bind home any scrollToTopLeft bind ctrl-end any gotoLastPage bind end any scrollToBottomRight bind pgup any pageUp bind backspace any pageUp bind delete any pageUp bind pgdn any pageDown bind space any pageDown bind left any scrollLeft(16) bind right any scrollRight(16) bind up any scrollUp(16) bind down any scrollDown(16) bind o any open bind O any open bind r any reload bind R any reload bind f any find bind F any find bind ctrl-f any find bind ctrl-g any findNext bind ctrl-p any print bind n scrLockOff nextPage bind N scrLockOff nextPage bind n scrLockOn nextPageNoScroll bind N scrLockOn nextPageNoScroll bind p scrLockOff prevPage bind P scrLockOff prevPage bind p scrLockOn prevPageNoScroll bind P scrLockOn prevPageNoScroll bind v any goForward bind b any goBackward bind g any focusToPageNum bind 0 any zoomPercent(125) bind + any zoomIn bind - any zoomOut bind z any zoomFitPage bind w any zoomFitWidth bind alt-f any toggleFullScreenMode bind ctrl-l any redraw bind ctrl-w any closeWindowOrQuit bind ? any about bind q any quit bind Q any quit .fi Previous versions of xpdf included a "viKeys" X resource. It is no longer available, but the following bindings are equivalent: .nf bind h any scrollLeft(16) bind l any scrollRight(16) bind k any scrollUp(16) bind j any scrollDown(16) .fi .SH "REMOTE SERVER MODE" Xpdf can be started in remote server mode by specifying a server name (in addition to the file name and page number). For example: .PP .RS xpdf \-remote myServer file.pdf .RE .PP If there is currently no xpdf running in server mode with the name \'myServer', a new xpdf window will be opened. If another command: .PP .RS xpdf \-remote myServer another.pdf 9 .RE .PP is issued, a new copy of xpdf will not be started. Instead, the first xpdf (the server) will load .I another.pdf and display page nine. If the file name is the same: .PP .RS xpdf \-remote myServer another.pdf 4 .RE .PP the xpdf server will simply display the specified page. .PP The \-raise option tells the server to raise its window; it can be specified with or without a file name and page number. .PP The \-quit option tells the server to close its window and exit. .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The xpdf software and documentation are copyright 1996-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 .B http://www.foolabs.com/xpdf/ xpdf-3.04/doc/pdffonts.cat0000644000076400007640000000747012341430012014772 0ustar derekndereknpdffonts(1) pdffonts(1) NAME pdffonts - Portable Document Format (PDF) font analyzer (version 3.04) SYNOPSIS pdffonts [options] [PDF-file] DESCRIPTION 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) location the font location (see the -loc and -locPS options). PDF files can contain the following types of fonts: Type 1 Type 1C -- aka Compact Font Format (CFF) Type 1C (OT) -- OpenType with 8-bit CFF data Type 3 TrueType TrueType (OT) -- OpenType with 8-bit TrueType data CID Type 0 -- 16-bit font with no specified type CID Type 0C -- 16-bit PostScript CFF font CID Type 0C (OT) -- OpenType with CID CFF data CID TrueType -- 16-bit TrueType font CID TrueType (OT) -- OpenType with CID TrueType data CONFIGURATION FILE Pdffonts reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc (but this location can be changed when pdffonts is built). See the xpdfrc(5) man page for details. OPTIONS Many of the following options can be set with 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. -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. -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.) EXIT CODES The Xpdf tools use the following exit codes: 0 No error. 1 Error opening a PDF file. 2 Error opening an output file. 3 Error related to PDF permissions. 99 Other error. AUTHOR The pdffonts software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO 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/ 28 May 2014 pdffonts(1) xpdf-3.04/aclocal.m40000644000076400007640000002424312341430012013546 0ustar dereknderekn# <<< smr.m4 from smr_macros 0.2.4 >>> dnl ####################### -*- Mode: M4 -*- ########################### dnl smr.m4 -- dnl dnl Copyright (C) 1999 Matthew D. Langston dnl Copyright (C) 1998 Steve Robbins dnl dnl This file is free software; you can redistribute it and/or modify it dnl under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This file is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this file; if not, write to: dnl dnl Free Software Foundation, Inc. dnl Suite 330 dnl 59 Temple Place dnl Boston, MA 02111-1307, USA. dnl #################################################################### dnl NOTE: The macros in this file are extensively documented in the dnl accompanying `smr_macros.texi' Texinfo file. Please see the dnl Texinfo documentation for the definitive specification of how dnl these macros are supposed to work. If the macros work dnl differently than the Texinfo documentation says they should, dnl then the macros (and not the Texinfo documentation) has the dnl bug(s). dnl This is a convenient macro which translates illegal characters for dnl bourne shell variables into legal characters. It has the same dnl functionality as sed 'y%./+-:%__p__%'. AC_DEFUN([smr_safe_translation], [patsubst(patsubst([$1], [+], [p]), [./-:], [_])]) AC_DEFUN(smr_SWITCH, [ dnl Define convenient aliases for the arguments since there are so dnl many of them and I keep confusing myself whenever I have to edit dnl this macro. pushdef([smr_name], $1) pushdef([smr_help_string], $2) pushdef([smr_default], $3) pushdef([smr_yes_define], $4) pushdef([smr_no_define], $5) dnl Do some sanity checking of the arguments. ifelse([regexp(smr_default, [^\(yes\|no\)$])], -1, [AC_MSG_ERROR($0: third arg must be either yes or no)]) dnl Create the help string pushdef([smr_lhs], [--ifelse(smr_default, yes, disable, enable)-smr_name])dnl pushdef([smr_rhs], [ifelse(smr_default, yes, disable, enable) smr_help_string (default is smr_default)])dnl dnl Add the option to `configure --help'. We don't need to supply the dnl 4th argument to AC_ARG_ENABLE (i.e. the code to set the default dnl value) because that is done below by AC_CACHE_CHECK. AC_ARG_ENABLE([smr_name], AC_HELP_STRING([smr_lhs], [smr_rhs]), smr_cv_enable_[]smr_name=$enableval) dnl We cache the result so that the user doesn't have to remember dnl which flags they passed to `configure'. AC_CACHE_CHECK([whether to enable smr_help_string], smr_cv_enable_[]smr_name, smr_cv_enable_[]smr_name=smr_default) ifelse(smr_yes_define, , , test x"[$]smr_cv_enable_[]smr_name" = xyes && AC_DEFINE(smr_yes_define)) ifelse(smr_no_define, , , test x"[$]smr_cv_enable_[]smr_name" = xno && AC_DEFINE(smr_no_define)) dnl Sanity check the value assigned to smr_cv_enable_$1 to force it to dnl be either `yes' or `no'. if test ! x"[$]smr_cv_enable_[]smr_name" = xyes; then if test ! x"[$]smr_cv_enable_[]smr_name" = xno; then AC_MSG_ERROR([smr_lhs must be either yes or no]) fi fi popdef([smr_name]) popdef([smr_help_string]) popdef([smr_default]) popdef([smr_yes_define]) popdef([smr_no_define]) popdef([smr_lhs]) popdef([smr_rhs]) ]) AC_DEFUN(smr_ARG_WITHLIB, [ dnl Define convenient aliases for the arguments since there are so dnl many of them and I keep confusing myself whenever I have to edit dnl this macro. pushdef([smr_name], $1) pushdef([smr_libname], ifelse($2, , smr_name, $2)) pushdef([smr_help_string], $3) pushdef([smr_safe_name], smr_safe_translation(smr_name)) dnl Add the option to `configure --help'. We don't need to supply the dnl 4th argument to AC_ARG_WITH (i.e. the code to set the default dnl value) because that is done below by AC_CACHE_CHECK. AC_ARG_WITH(smr_safe_name-library, AC_HELP_STRING([--with-smr_safe_name-library[[=PATH]]], [use smr_name library ifelse(smr_help_string, , , (smr_help_string))]), smr_cv_with_[]smr_safe_name[]_library=$withval) dnl We cache the result so that the user doesn't have to remember dnl which flags they passed to `configure'. AC_CACHE_CHECK([whether to use smr_name library], smr_cv_with_[]smr_safe_name[]_library, smr_cv_with_[]smr_safe_name[]_library=maybe) case x"[$]smr_cv_with_[]smr_safe_name[]_library" in xyes | xmaybe) smr_safe_name[]_LIBS="-l[]smr_libname" with_[]smr_safe_name=[$]smr_cv_with_[]smr_safe_name[]_library ;; xno) smr_safe_name[]_LIBS= with_[]smr_safe_name=no ;; *) if test -f "[$]smr_cv_with_[]smr_safe_name[]_library"; then smr_safe_name[]_LIBS=[$]smr_cv_with_[]smr_safe_name[]_library elif test -d "[$]smr_cv_with_[]smr_safe_name[]_library"; then smr_safe_name[]_LIBS="-L[$]smr_cv_with_[]smr_safe_name[]_library -l[]smr_libname" else AC_MSG_ERROR([argument must be boolean, file, or directory]) fi with_[]smr_safe_name=yes ;; esac popdef([smr_name]) popdef([smr_libname]) popdef([smr_help_string]) popdef([smr_safe_name]) ]) AC_DEFUN(smr_ARG_WITHINCLUDES, [ dnl Define convenient aliases for the arguments since there are so dnl many of them and I keep confusing myself whenever I have to edit dnl this macro. pushdef([smr_name], $1) pushdef([smr_header], $2) pushdef([smr_extra_flags], $3) pushdef([smr_safe_name], smr_safe_translation(smr_name)) dnl Add the option to `configure --help'. We don't need to supply the dnl 4th argument to AC_ARG_WITH (i.e. the code to set the default dnl value) because that is done below by AC_CACHE_CHECK. AC_ARG_WITH(smr_safe_name-includes, AC_HELP_STRING([--with-smr_safe_name-includes[[=DIR]]], [set directory for smr_name headers]), smr_cv_with_[]smr_safe_name[]_includes=$withval) dnl We cache the result so that the user doesn't have to remember dnl which flags they passed to `configure'. AC_CACHE_CHECK([where to find the smr_name header files], smr_cv_with_[]smr_safe_name[]_includes, smr_cv_with_[]smr_safe_name[]_includes=) if test ! x"[$]smr_cv_with_[]smr_safe_name[]_includes" = x; then if test -d "[$]smr_cv_with_[]smr_safe_name[]_includes"; then smr_safe_name[]_CFLAGS="-I[$]smr_cv_with_[]smr_safe_name[]_includes" else AC_MSG_ERROR([argument must be a directory]) fi else smr_safe_name[]_CFLAGS= fi dnl This bit of logic comes from the autoconf AC_PROG_CC macro. We dnl need to put the given include directory into CPPFLAGS temporarily, dnl but then restore CPPFLAGS to its old value. smr_test_CPPFLAGS="${CPPFLAGS+set}" smr_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS [$]smr_safe_name[]_CFLAGS smr_extra_flags" dnl If the header file smr_header exists, then define dnl HAVE_[]smr_header (in all capitals). AC_CHECK_HEADERS([smr_header], smr_have_[]smr_safe_name[]_header=yes, smr_have_[]smr_safe_name[]_header=no) if test x"$smr_test_CPPFLAGS" = xset; then CPPFLAGS=$smr_save_CPPFLAGS else unset CPPFLAGS fi popdef([smr_name]) popdef([smr_header]) popdef([smr_extra_flags]) popdef([smr_safe_name]) ]) AC_DEFUN(smr_CHECK_LIB, [ dnl Define convenient aliases for the arguments since there are so dnl many of them and I keep confusing myself whenever I have to edit dnl this macro. pushdef([smr_name], $1) pushdef([smr_libname], ifelse($2, , smr_name, $2)) pushdef([smr_help_string], $3) pushdef([smr_function], $4) pushdef([smr_header], $5) pushdef([smr_extra_libs], $6) pushdef([smr_extra_flags], $7) pushdef([smr_prototype], $8) pushdef([smr_safe_name], smr_safe_translation(smr_name)) dnl Give the user (via "configure --help") an interface to specify dnl whether we should use the library or not, and possibly where we dnl should find it. smr_ARG_WITHLIB([smr_name], [smr_libname], [smr_help_string]) if test ! x"$with_[]smr_safe_name" = xno; then # If we got this far, then the user didn't explicitly ask not to use # the library. dnl If the caller of smr_CHECK_LIB specified a header file for this dnl library, then give the user (via "configure --help") an dnl interface to specify where this header file can be found (if it dnl isn't found by the compiler by default). ifelse(smr_header, , , [smr_ARG_WITHINCLUDES(smr_name, smr_header, smr_extra_flags)]) # We need only look for the library if the header has been found # (or no header is needed). if test [$]smr_have_[]smr_safe_name[]_header != no; then AC_CHECK_LIB(smr_libname, smr_function, smr_have_[]smr_safe_name[]_library=yes, smr_have_[]smr_safe_name[]_library=no, [$]smr_safe_name[]_CFLAGS [smr_extra_flags] [$]smr_safe_name[]_LIBS [smr_extra_libs], [ifelse(smr_prototype, , , [[#]include ])], smr_prototype) fi if test x"[$]smr_have_[]smr_safe_name[]_library" = xyes; then AC_MSG_RESULT([using smr_name library]) else smr_safe_name[]_LIBS= smr_safe_name[]_CFLAGS= if test x"$with_[]smr_safe_name" = xmaybe; then AC_MSG_RESULT([not using smr_name library]) else AC_MSG_WARN([requested smr_name library not found!]) fi fi fi popdef([smr_name]) popdef([smr_libname]) popdef([smr_help_string]) popdef([smr_function]) popdef([smr_header]) popdef([smr_extra_libs]) popdef([smr_extra_flags]) popdef([smr_prototype]) popdef([smr_safe_name]) ]) xpdf-3.04/ANNOUNCE0000644000076400007640000000210012341430012013023 0ustar derekndereknSubject: ANNOUNCE: Xpdf 3.04 - a PDF viewer for X Glyph & Cog, LLC is pleased to announce a new version of Xpdf, the open source Portable Document Format (PDF) viewer for X. The Xpdf project also includes a PDF text extractor, PDF-to-PostScript converter, and various other utilities. Xpdf runs under the X Window System on Unix, and OS/2. The non-X components (pdftops, pdftotext, etc.) also run on Win32 systems and should run on pretty much any system with a decent C++ compiler. Major changes: * New text extractor. * Added the pdftohtml tool. * Added the pdftopng tool. * New trapezoid-based rasterizer core (for performance). See the `CHANGES' file for a complete list of changes. Source (C++ and C) is available, and it should be fairly easy to compile for UNIX, OS/2, and Win32. More information, source code, and precompiled binaries are on the xpdf web page and ftp site: http://www.foolabs.com/xpdf/ ftp://ftp.foolabs.com/pub/xpdf/ For information on commercial licensing and consulting, please see the Glyph & Cog web site: http://www.glyphandcog.com/ xpdf-3.04/goo/0000755000076400007640000000000012341430012012465 5ustar derekndereknxpdf-3.04/goo/gmem.h0000644000076400007640000000303312341430012013562 0ustar dereknderekn/* * gmem.h * * Memory routines with out-of-memory checking. * * Copyright 1996-2003 Glyph & Cog, LLC */ #ifndef GMEM_H #define GMEM_H #include #include #if USE_EXCEPTIONS class GMemException { public: GMemException() {} ~GMemException() {} }; #define GMEM_EXCEP throw(GMemException) #else // USE_EXCEPTIONS #define GMEM_EXCEP #endif // USE_EXCEPTIONS #ifdef __cplusplus extern "C" { #endif /* * Same as malloc, but prints error message and exits if malloc() * returns NULL. */ extern void *gmalloc(int size) GMEM_EXCEP; /* * Same as realloc, but prints error message and exits if realloc() * returns NULL. If

is NULL, calls malloc instead of realloc(). */ extern void *grealloc(void *p, int size) GMEM_EXCEP; /* * These are similar to gmalloc and grealloc, but take an object count * and size. The result is similar to allocating nObjs * objSize * bytes, but there is an additional error check that the total size * doesn't overflow an int. */ extern void *gmallocn(int nObjs, int objSize) GMEM_EXCEP; extern void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP; /* * Same as free, but checks for and ignores NULL pointers. */ extern void gfree(void *p); /* * Report a memory error. */ extern void gMemError(const char *msg) GMEM_EXCEP; #ifdef DEBUG_MEM /* * Report on unfreed memory. */ extern void gMemReport(FILE *f); #else #define gMemReport(f) #endif /* * Allocate memory and copy a string into it. */ extern char *copyString(const char *s); #ifdef __cplusplus } #endif #endif xpdf-3.04/goo/GString.h0000644000076400007640000001025512341430012014216 0ustar dereknderekn//======================================================================== // // GString.h // // Simple variable-length string type. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GSTRING_H #define GSTRING_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include // for LLONG_MAX and ULLONG_MAX #include #include "gtypes.h" class GString { public: // Create an empty string. GString(); // Create a string from a C string. GString(const char *sA); // Create a string from chars at . This string // can contain null characters. GString(const char *sA, int lengthA); // Create a string from chars at in . GString(GString *str, int idx, int lengthA); // Copy a string. GString(GString *str); GString *copy() { return new GString(this); } // Concatenate two strings. GString(GString *str1, GString *str2); // Convert an integer to a string. static GString *fromInt(int x); // Create a formatted string. Similar to printf, but without the // string overflow issues. Formatting elements consist of: // {:[][.]} // where: // - is the argument number (arg 0 is the first argument // following the format string) -- NB: args must be first used in // order; they can be reused in any order // - is the field width -- negative to reverse the alignment; // starting with a leading zero to zero-fill (for integers) // - is the number of digits to the right of the decimal // point (for floating point numbers) // - is one of: // d, x, o, b -- int in decimal, hex, octal, binary // ud, ux, uo, ub -- unsigned int // ld, lx, lo, lb, uld, ulx, ulo, ulb -- long, unsigned long // lld, llx, llo, llb, ulld, ullx, ullo, ullb // -- long long, unsigned long long // f, g -- double // c -- char // s -- string (char *) // t -- GString * // w -- blank space; arg determines width // To get literal curly braces, use {{ or }}. static GString *format(const char *fmt, ...); static GString *formatv(const char *fmt, va_list argList); // Destructor. ~GString(); // Get length. int getLength() { return length; } // Get C string. char *getCString() { return s; } // Get th character. char getChar(int i) { return s[i]; } // Change th character. void setChar(int i, char c) { s[i] = c; } // Clear string to zero length. GString *clear(); // Append a character or string. GString *append(char c); GString *append(GString *str); GString *append(const char *str); GString *append(const char *str, int lengthA); // Append a formatted string. GString *appendf(const char *fmt, ...); GString *appendfv(const char *fmt, va_list argList); // Insert a character or string. GString *insert(int i, char c); GString *insert(int i, GString *str); GString *insert(int i, const char *str); GString *insert(int i, const char *str, int lengthA); // Delete a character or range of characters. GString *del(int i, int n = 1); // Convert string to all-upper/all-lower case. GString *upperCase(); GString *lowerCase(); // Compare two strings: -1:< 0:= +1:> int cmp(GString *str); int cmpN(GString *str, int n); int cmp(const char *sA); int cmpN(const char *sA, int n); private: int length; char *s; void resize(int length1); #ifdef LLONG_MAX static void formatInt(long long x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len); #else static void formatInt(long x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len); #endif #ifdef ULLONG_MAX static void formatUInt(unsigned long long x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len); #else static void formatUInt(Gulong x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len); #endif static void formatDouble(double x, char *buf, int bufSize, int prec, GBool trim, const char **p, int *len); }; #endif xpdf-3.04/goo/parseargs.h0000644000076400007640000000305712341430012014632 0ustar dereknderekn/* * parseargs.h * * Command line argument parser. * * Copyright 1996-2003 Glyph & Cog, LLC */ #ifndef PARSEARGS_H #define PARSEARGS_H #ifdef __cplusplus extern "C" { #endif #include "gtypes.h" /* * Argument kinds. */ typedef enum { argFlag, /* flag (present / not-present) */ /* [val: GBool *] */ argInt, /* integer arg */ /* [val: int *] */ argFP, /* floating point arg */ /* [val: double *] */ argString, /* string arg */ /* [val: char *] */ /* dummy entries -- these show up in the usage listing only; */ /* useful for X args, for example */ argFlagDummy, argIntDummy, argFPDummy, argStringDummy } ArgKind; /* * Argument descriptor. */ typedef struct { const char *arg; /* the command line switch */ ArgKind kind; /* kind of arg */ void *val; /* place to store value */ int size; /* for argString: size of string */ const char *usage; /* usage string */ } ArgDesc; /* * Parse command line. Removes all args which are found in the arg * descriptor list . Stops parsing if "--" is found (and removes * it). Returns gFalse if there was an error. */ extern GBool parseArgs(ArgDesc *args, int *argc, char *argv[]); /* * Print usage message, based on arg descriptor list. */ extern void printUsage(const char *program, const char *otherArgs, ArgDesc *args); /* * Check if a string is a valid integer or floating point number. */ extern GBool isInt(char *s); extern GBool isFP(char *s); #ifdef __cplusplus } #endif #endif xpdf-3.04/goo/GHash.cc0000644000076400007640000001404512341430012013772 0ustar dereknderekn//======================================================================== // // GHash.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "gmem.h" #include "GString.h" #include "GHash.h" //------------------------------------------------------------------------ struct GHashBucket { GString *key; union { void *p; int i; } val; GHashBucket *next; }; struct GHashIter { int h; GHashBucket *p; }; //------------------------------------------------------------------------ GHash::GHash(GBool deleteKeysA) { int h; deleteKeys = deleteKeysA; size = 7; tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *)); for (h = 0; h < size; ++h) { tab[h] = NULL; } len = 0; } GHash::~GHash() { GHashBucket *p; int h; for (h = 0; h < size; ++h) { while (tab[h]) { p = tab[h]; tab[h] = p->next; if (deleteKeys) { delete p->key; } delete p; } } gfree(tab); } void GHash::add(GString *key, void *val) { GHashBucket *p; int h; // expand the table if necessary if (len >= size) { expand(); } // add the new symbol p = new GHashBucket; p->key = key; p->val.p = val; h = hash(key); p->next = tab[h]; tab[h] = p; ++len; } void GHash::add(GString *key, int val) { GHashBucket *p; int h; // expand the table if necessary if (len >= size) { expand(); } // add the new symbol p = new GHashBucket; p->key = key; p->val.i = val; h = hash(key); p->next = tab[h]; tab[h] = p; ++len; } void GHash::replace(GString *key, void *val) { GHashBucket *p; int h; if ((p = find(key, &h))) { p->val.p = val; if (deleteKeys) { delete key; } } else { add(key, val); } } void GHash::replace(GString *key, int val) { GHashBucket *p; int h; if ((p = find(key, &h))) { p->val.i = val; if (deleteKeys) { delete key; } } else { add(key, val); } } void *GHash::lookup(GString *key) { GHashBucket *p; int h; if (!(p = find(key, &h))) { return NULL; } return p->val.p; } int GHash::lookupInt(GString *key) { GHashBucket *p; int h; if (!(p = find(key, &h))) { return 0; } return p->val.i; } void *GHash::lookup(const char *key) { GHashBucket *p; int h; if (!(p = find(key, &h))) { return NULL; } return p->val.p; } int GHash::lookupInt(const char *key) { GHashBucket *p; int h; if (!(p = find(key, &h))) { return 0; } return p->val.i; } void *GHash::remove(GString *key) { GHashBucket *p; GHashBucket **q; void *val; int h; if (!(p = find(key, &h))) { return NULL; } q = &tab[h]; while (*q != p) { q = &((*q)->next); } *q = p->next; if (deleteKeys) { delete p->key; } val = p->val.p; delete p; --len; return val; } int GHash::removeInt(GString *key) { GHashBucket *p; GHashBucket **q; int val; int h; if (!(p = find(key, &h))) { return 0; } q = &tab[h]; while (*q != p) { q = &((*q)->next); } *q = p->next; if (deleteKeys) { delete p->key; } val = p->val.i; delete p; --len; return val; } void *GHash::remove(const char *key) { GHashBucket *p; GHashBucket **q; void *val; int h; if (!(p = find(key, &h))) { return NULL; } q = &tab[h]; while (*q != p) { q = &((*q)->next); } *q = p->next; if (deleteKeys) { delete p->key; } val = p->val.p; delete p; --len; return val; } int GHash::removeInt(const char *key) { GHashBucket *p; GHashBucket **q; int val; int h; if (!(p = find(key, &h))) { return 0; } q = &tab[h]; while (*q != p) { q = &((*q)->next); } *q = p->next; if (deleteKeys) { delete p->key; } val = p->val.i; delete p; --len; return val; } void GHash::startIter(GHashIter **iter) { *iter = new GHashIter; (*iter)->h = -1; (*iter)->p = NULL; } GBool GHash::getNext(GHashIter **iter, GString **key, void **val) { if (!*iter) { return gFalse; } if ((*iter)->p) { (*iter)->p = (*iter)->p->next; } while (!(*iter)->p) { if (++(*iter)->h == size) { delete *iter; *iter = NULL; return gFalse; } (*iter)->p = tab[(*iter)->h]; } *key = (*iter)->p->key; *val = (*iter)->p->val.p; return gTrue; } GBool GHash::getNext(GHashIter **iter, GString **key, int *val) { if (!*iter) { return gFalse; } if ((*iter)->p) { (*iter)->p = (*iter)->p->next; } while (!(*iter)->p) { if (++(*iter)->h == size) { delete *iter; *iter = NULL; return gFalse; } (*iter)->p = tab[(*iter)->h]; } *key = (*iter)->p->key; *val = (*iter)->p->val.i; return gTrue; } void GHash::killIter(GHashIter **iter) { delete *iter; *iter = NULL; } void GHash::expand() { GHashBucket **oldTab; GHashBucket *p; int oldSize, h, i; oldSize = size; oldTab = tab; size = 2*size + 1; tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *)); for (h = 0; h < size; ++h) { tab[h] = NULL; } for (i = 0; i < oldSize; ++i) { while (oldTab[i]) { p = oldTab[i]; oldTab[i] = oldTab[i]->next; h = hash(p->key); p->next = tab[h]; tab[h] = p; } } gfree(oldTab); } GHashBucket *GHash::find(GString *key, int *h) { GHashBucket *p; *h = hash(key); for (p = tab[*h]; p; p = p->next) { if (!p->key->cmp(key)) { return p; } } return NULL; } GHashBucket *GHash::find(const char *key, int *h) { GHashBucket *p; *h = hash(key); for (p = tab[*h]; p; p = p->next) { if (!p->key->cmp(key)) { return p; } } return NULL; } int GHash::hash(GString *key) { const char *p; unsigned int h; int i; h = 0; for (p = key->getCString(), i = 0; i < key->getLength(); ++p, ++i) { h = 17 * h + (int)(*p & 0xff); } return (int)(h % size); } int GHash::hash(const char *key) { const char *p; unsigned int h; h = 0; for (p = key; *p; ++p) { h = 17 * h + (int)(*p & 0xff); } return (int)(h % size); } xpdf-3.04/goo/gmem.cc0000644000076400007640000001221412341430012013721 0ustar dereknderekn/* * gmem.c * * Memory routines with out-of-memory checking. * * Copyright 1996-2003 Glyph & Cog, LLC */ #include #include #include #include #include #include #include "gmem.h" #ifdef DEBUG_MEM typedef struct _GMemHdr { unsigned int magic; int size; int index; struct _GMemHdr *next, *prev; } GMemHdr; #define gMemHdrSize ((sizeof(GMemHdr) + 7) & ~7) #define gMemTrlSize (sizeof(long)) #define gMemMagic 0xabcd9999 #if gmemTrlSize==8 #define gMemDeadVal 0xdeadbeefdeadbeefUL #else #define gMemDeadVal 0xdeadbeefUL #endif /* round data size so trailer will be aligned */ #define gMemDataSize(size) \ ((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize) static GMemHdr *gMemHead = NULL; static GMemHdr *gMemTail = NULL; static int gMemIndex = 0; static int gMemAlloc = 0; static int gMemInUse = 0; static int gMaxMemInUse = 0; #endif /* DEBUG_MEM */ void *gmalloc(int size) GMEM_EXCEP { #ifdef DEBUG_MEM int size1; char *mem; GMemHdr *hdr; void *data; unsigned long *trl, *p; if (size < 0) { gMemError("Invalid memory allocation size"); } if (size == 0) { return NULL; } size1 = gMemDataSize(size); if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) { gMemError("Out of memory"); } hdr = (GMemHdr *)mem; data = (void *)(mem + gMemHdrSize); trl = (unsigned long *)(mem + gMemHdrSize + size1); hdr->magic = gMemMagic; hdr->size = size; hdr->index = gMemIndex++; if (gMemTail) { gMemTail->next = hdr; hdr->prev = gMemTail; gMemTail = hdr; } else { hdr->prev = NULL; gMemHead = gMemTail = hdr; } hdr->next = NULL; ++gMemAlloc; gMemInUse += size; if (gMemInUse > gMaxMemInUse) { gMaxMemInUse = gMemInUse; } for (p = (unsigned long *)data; p <= trl; ++p) { *p = gMemDeadVal; } return data; #else void *p; if (size < 0) { gMemError("Invalid memory allocation size"); } if (size == 0) { return NULL; } if (!(p = malloc(size))) { gMemError("Out of memory"); } return p; #endif } void *grealloc(void *p, int size) GMEM_EXCEP { #ifdef DEBUG_MEM GMemHdr *hdr; void *q; int oldSize; if (size < 0) { gMemError("Invalid memory allocation size"); } if (size == 0) { if (p) { gfree(p); } return NULL; } if (p) { hdr = (GMemHdr *)((char *)p - gMemHdrSize); oldSize = hdr->size; q = gmalloc(size); memcpy(q, p, size < oldSize ? size : oldSize); gfree(p); } else { q = gmalloc(size); } return q; #else void *q; if (size < 0) { gMemError("Invalid memory allocation size"); } if (size == 0) { if (p) { free(p); } return NULL; } if (p) { q = realloc(p, size); } else { q = malloc(size); } if (!q) { gMemError("Out of memory"); } return q; #endif } void *gmallocn(int nObjs, int objSize) GMEM_EXCEP { int n; if (nObjs == 0) { return NULL; } n = nObjs * objSize; if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) { gMemError("Bogus memory allocation size"); } return gmalloc(n); } void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP { int n; if (nObjs == 0) { if (p) { gfree(p); } return NULL; } n = nObjs * objSize; if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) { gMemError("Bogus memory allocation size"); } return grealloc(p, n); } void gfree(void *p) { #ifdef DEBUG_MEM int size; GMemHdr *hdr; unsigned long *trl, *clr; if (p) { hdr = (GMemHdr *)((char *)p - gMemHdrSize); if (hdr->magic == gMemMagic && ((hdr->prev == NULL) == (hdr == gMemHead)) && ((hdr->next == NULL) == (hdr == gMemTail))) { if (hdr->prev) { hdr->prev->next = hdr->next; } else { gMemHead = hdr->next; } if (hdr->next) { hdr->next->prev = hdr->prev; } else { gMemTail = hdr->prev; } --gMemAlloc; gMemInUse -= hdr->size; size = gMemDataSize(hdr->size); trl = (unsigned long *)((char *)hdr + gMemHdrSize + size); if (*trl != gMemDeadVal) { fprintf(stderr, "Overwrite past end of block %d at address %p\n", hdr->index, p); } for (clr = (unsigned long *)hdr; clr <= trl; ++clr) { *clr = gMemDeadVal; } free(hdr); } else { fprintf(stderr, "Attempted to free bad address %p\n", p); } } #else if (p) { free(p); } #endif } void gMemError(const char *msg) GMEM_EXCEP { #if USE_EXCEPTIONS throw GMemException(); #else fprintf(stderr, "%s\n", msg); exit(1); #endif } #ifdef DEBUG_MEM void gMemReport(FILE *f) { GMemHdr *p; fprintf(f, "%d memory allocations in all\n", gMemIndex); fprintf(f, "maximum memory in use: %d bytes\n", gMaxMemInUse); if (gMemAlloc > 0) { fprintf(f, "%d memory blocks left allocated:\n", gMemAlloc); fprintf(f, " index size\n"); fprintf(f, "-------- --------\n"); for (p = gMemHead; p; p = p->next) { fprintf(f, "%8d %8d\n", p->index, p->size); } } else { fprintf(f, "No memory blocks left allocated\n"); } } #endif char *copyString(const char *s) { char *s1; s1 = (char *)gmalloc((int)strlen(s) + 1); strcpy(s1, s); return s1; } xpdf-3.04/goo/Makefile.in0000644000076400007640000000314112341430012014531 0ustar dereknderekn#======================================================================== # # Goo library Makefile # # Copyright 1996-2003 Glyph & Cog, LLC # #======================================================================== SHELL = /bin/sh srcdir = @srcdir@ VPATH = @srcdir@ CFLAGS = @CFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(srcdir) CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(srcdir) CC = @CC@ CXX = @CXX@ AR = @AR@ RANLIB = @RANLIB@ LIBPREFIX = @LIBPREFIX@ #------------------------------------------------------------------------ .SUFFIXES: .cc .cc.o: $(CXX) $(CXXFLAGS) -c $< #------------------------------------------------------------------------ CXX_SRC = \ $(srcdir)/GHash.cc \ $(srcdir)/GList.cc \ $(srcdir)/GString.cc \ $(srcdir)/gmem.cc \ $(srcdir)/gmempp.cc \ $(srcdir)/gfile.cc \ $(srcdir)/FixedPoint.cc C_SRC = \ $(srcdir)/parseargs.c #------------------------------------------------------------------------ all: $(LIBPREFIX)Goo.a #------------------------------------------------------------------------ GOO_CXX_OBJS = GHash.o GList.o GString.o gmem.o gmempp.o gfile.o FixedPoint.o GOO_C_OBJS = parseargs.o GOO_OBJS = $(GOO_CXX_OBJS) $(GOO_C_OBJS) $(LIBPREFIX)Goo.a: $(GOO_OBJS) rm -f $(LIBPREFIX)Goo.a $(AR) $(LIBPREFIX)Goo.a $(GOO_OBJS) $(RANLIB) $(LIBPREFIX)Goo.a #------------------------------------------------------------------------ clean: rm -f $(GOO_OBJS) $(LIBPREFIX)Goo.a #------------------------------------------------------------------------ depend: $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep $(CC) $(CFLAGS) -MM $(C_SRC) >>Makefile.dep -include Makefile.dep xpdf-3.04/goo/GString.cc0000644000076400007640000004222612341430012014357 0ustar dereknderekn//======================================================================== // // GString.cc // // Simple variable-length string type. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include #include "gmem.h" #include "GString.h" //------------------------------------------------------------------------ union GStringFormatArg { int i; Guint ui; long l; Gulong ul; #ifdef LLONG_MAX long long ll; #endif #ifdef ULLONG_MAX unsigned long long ull; #endif double f; char c; char *s; GString *gs; }; enum GStringFormatType { fmtIntDecimal, fmtIntHex, fmtIntOctal, fmtIntBinary, fmtUIntDecimal, fmtUIntHex, fmtUIntOctal, fmtUIntBinary, fmtLongDecimal, fmtLongHex, fmtLongOctal, fmtLongBinary, fmtULongDecimal, fmtULongHex, fmtULongOctal, fmtULongBinary, #ifdef LLONG_MAX fmtLongLongDecimal, fmtLongLongHex, fmtLongLongOctal, fmtLongLongBinary, #endif #ifdef ULLONG_MAX fmtULongLongDecimal, fmtULongLongHex, fmtULongLongOctal, fmtULongLongBinary, #endif fmtDouble, fmtDoubleTrim, fmtChar, fmtString, fmtGString, fmtSpace }; static const char *formatStrings[] = { "d", "x", "o", "b", "ud", "ux", "uo", "ub", "ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb", #ifdef LLONG_MAX "lld", "llx", "llo", "llb", #endif #ifdef ULLONG_MAX "ulld", "ullx", "ullo", "ullb", #endif "f", "g", "c", "s", "t", "w", NULL }; //------------------------------------------------------------------------ static inline int size(int len) { int delta; for (delta = 8; delta < len && delta < 0x100000; delta <<= 1) ; if (len > INT_MAX - delta) { gMemError("Integer overflow in GString::size()"); } // this is ((len + 1) + (delta - 1)) & ~(delta - 1) return (len + delta) & ~(delta - 1); } inline void GString::resize(int length1) { char *s1; if (length1 < 0) { gMemError("GString::resize() with negative length"); } if (!s) { s = new char[size(length1)]; } else if (size(length1) != size(length)) { s1 = new char[size(length1)]; if (length1 < length) { memcpy(s1, s, length1); s1[length1] = '\0'; } else { memcpy(s1, s, length + 1); } delete[] s; s = s1; } } GString::GString() { s = NULL; resize(length = 0); s[0] = '\0'; } GString::GString(const char *sA) { int n = (int)strlen(sA); s = NULL; resize(length = n); memcpy(s, sA, n + 1); } GString::GString(const char *sA, int lengthA) { s = NULL; resize(length = lengthA); memcpy(s, sA, length * sizeof(char)); s[length] = '\0'; } GString::GString(GString *str, int idx, int lengthA) { s = NULL; resize(length = lengthA); memcpy(s, str->getCString() + idx, length); s[length] = '\0'; } GString::GString(GString *str) { s = NULL; resize(length = str->getLength()); memcpy(s, str->getCString(), length + 1); } GString::GString(GString *str1, GString *str2) { int n1 = str1->getLength(); int n2 = str2->getLength(); s = NULL; if (n1 > INT_MAX - n2) { gMemError("Integer overflow in GString::GString()"); } resize(length = n1 + n2); memcpy(s, str1->getCString(), n1); memcpy(s + n1, str2->getCString(), n2 + 1); } GString *GString::fromInt(int x) { char buf[24]; // enough space for 64-bit ints plus a little extra const char *p; int len; formatInt(x, buf, sizeof(buf), gFalse, 0, 10, &p, &len); return new GString(p, len); } GString *GString::format(const char *fmt, ...) { va_list argList; GString *s; s = new GString(); va_start(argList, fmt); s->appendfv(fmt, argList); va_end(argList); return s; } GString *GString::formatv(const char *fmt, va_list argList) { GString *s; s = new GString(); s->appendfv(fmt, argList); return s; } GString::~GString() { delete[] s; } GString *GString::clear() { s[length = 0] = '\0'; resize(0); return this; } GString *GString::append(char c) { if (length > INT_MAX - 1) { gMemError("Integer overflow in GString::append()"); } resize(length + 1); s[length++] = c; s[length] = '\0'; return this; } GString *GString::append(GString *str) { int n = str->getLength(); if (length > INT_MAX - n) { gMemError("Integer overflow in GString::append()"); } resize(length + n); memcpy(s + length, str->getCString(), n + 1); length += n; return this; } GString *GString::append(const char *str) { int n = (int)strlen(str); if (length > INT_MAX - n) { gMemError("Integer overflow in GString::append()"); } resize(length + n); memcpy(s + length, str, n + 1); length += n; return this; } GString *GString::append(const char *str, int lengthA) { if (lengthA < 0 || length > INT_MAX - lengthA) { gMemError("Integer overflow in GString::append()"); } resize(length + lengthA); memcpy(s + length, str, lengthA); length += lengthA; s[length] = '\0'; return this; } GString *GString::appendf(const char *fmt, ...) { va_list argList; va_start(argList, fmt); appendfv(fmt, argList); va_end(argList); return this; } GString *GString::appendfv(const char *fmt, va_list argList) { GStringFormatArg *args; int argsLen, argsSize; GStringFormatArg arg; int idx, width, prec; GBool reverseAlign, zeroFill; GStringFormatType ft; char buf[65]; int len, i; const char *p0, *p1; const char *str; argsLen = 0; argsSize = 8; args = (GStringFormatArg *)gmallocn(argsSize, sizeof(GStringFormatArg)); p0 = fmt; while (*p0) { if (*p0 == '{') { ++p0; if (*p0 == '{') { ++p0; append('{'); } else { // parse the format string if (!(*p0 >= '0' && *p0 <= '9')) { break; } idx = *p0 - '0'; for (++p0; *p0 >= '0' && *p0 <= '9'; ++p0) { idx = 10 * idx + (*p0 - '0'); } if (*p0 != ':') { break; } ++p0; if (*p0 == '-') { reverseAlign = gTrue; ++p0; } else { reverseAlign = gFalse; } width = 0; zeroFill = *p0 == '0'; for (; *p0 >= '0' && *p0 <= '9'; ++p0) { width = 10 * width + (*p0 - '0'); } if (width < 0) { width = 0; } if (*p0 == '.') { ++p0; prec = 0; for (; *p0 >= '0' && *p0 <= '9'; ++p0) { prec = 10 * prec + (*p0 - '0'); } } else { prec = 0; } for (ft = (GStringFormatType)0; formatStrings[ft]; ft = (GStringFormatType)(ft + 1)) { if (!strncmp(p0, formatStrings[ft], strlen(formatStrings[ft]))) { break; } } if (!formatStrings[ft]) { break; } p0 += strlen(formatStrings[ft]); if (*p0 != '}') { break; } ++p0; // fetch the argument if (idx > argsLen) { break; } if (idx == argsLen) { if (argsLen == argsSize) { argsSize *= 2; args = (GStringFormatArg *)greallocn(args, argsSize, sizeof(GStringFormatArg)); } switch (ft) { case fmtIntDecimal: case fmtIntHex: case fmtIntOctal: case fmtIntBinary: case fmtSpace: args[argsLen].i = va_arg(argList, int); break; case fmtUIntDecimal: case fmtUIntHex: case fmtUIntOctal: case fmtUIntBinary: args[argsLen].ui = va_arg(argList, Guint); break; case fmtLongDecimal: case fmtLongHex: case fmtLongOctal: case fmtLongBinary: args[argsLen].l = va_arg(argList, long); break; case fmtULongDecimal: case fmtULongHex: case fmtULongOctal: case fmtULongBinary: args[argsLen].ul = va_arg(argList, Gulong); break; #ifdef LLONG_MAX case fmtLongLongDecimal: case fmtLongLongHex: case fmtLongLongOctal: case fmtLongLongBinary: args[argsLen].ll = va_arg(argList, long long); break; #endif #ifdef ULLONG_MAX case fmtULongLongDecimal: case fmtULongLongHex: case fmtULongLongOctal: case fmtULongLongBinary: args[argsLen].ull = va_arg(argList, unsigned long long); break; #endif case fmtDouble: case fmtDoubleTrim: args[argsLen].f = va_arg(argList, double); break; case fmtChar: args[argsLen].c = (char)va_arg(argList, int); break; case fmtString: args[argsLen].s = va_arg(argList, char *); break; case fmtGString: args[argsLen].gs = va_arg(argList, GString *); break; } ++argsLen; } // format the argument arg = args[idx]; switch (ft) { case fmtIntDecimal: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtIntHex: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtIntOctal: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtIntBinary: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtUIntDecimal: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtUIntHex: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtUIntOctal: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtUIntBinary: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtLongDecimal: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtLongHex: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtLongOctal: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtLongBinary: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtULongDecimal: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtULongHex: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtULongOctal: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtULongBinary: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; #ifdef LLONG_MAX case fmtLongLongDecimal: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtLongLongHex: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtLongLongOctal: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtLongLongBinary: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; #endif #ifdef ULLONG_MAX case fmtULongLongDecimal: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtULongLongHex: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtULongLongOctal: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtULongLongBinary: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; #endif case fmtDouble: formatDouble(arg.f, buf, sizeof(buf), prec, gFalse, &str, &len); break; case fmtDoubleTrim: formatDouble(arg.f, buf, sizeof(buf), prec, gTrue, &str, &len); break; case fmtChar: buf[0] = arg.c; str = buf; len = 1; reverseAlign = !reverseAlign; break; case fmtString: if (arg.s) { str = arg.s; len = (int)strlen(str); } else { str = "(null)"; len = 6; } reverseAlign = !reverseAlign; break; case fmtGString: if (arg.gs) { str = arg.gs->getCString(); len = arg.gs->getLength(); } else { str = "(null)"; len = 6; } reverseAlign = !reverseAlign; break; case fmtSpace: str = buf; len = 0; width = arg.i; break; } // append the formatted arg, handling width and alignment if (!reverseAlign && len < width) { for (i = len; i < width; ++i) { append(' '); } } append(str, len); if (reverseAlign && len < width) { for (i = len; i < width; ++i) { append(' '); } } } } else if (*p0 == '}') { ++p0; if (*p0 == '}') { ++p0; } append('}'); } else { for (p1 = p0 + 1; *p1 && *p1 != '{' && *p1 != '}'; ++p1) ; append(p0, (int)(p1 - p0)); p0 = p1; } } gfree(args); return this; } #ifdef LLONG_MAX void GString::formatInt(long long x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len) { #else void GString::formatInt(long x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len) { #endif static char vals[17] = "0123456789abcdef"; GBool neg; int start, i, j; i = bufSize; if ((neg = x < 0)) { x = -x; } start = neg ? 1 : 0; if (x == 0) { buf[--i] = '0'; } else { while (i > start && x) { buf[--i] = vals[x % base]; x /= base; } } if (zeroFill) { for (j = bufSize - i; i > start && j < width - start; ++j) { buf[--i] = '0'; } } if (neg) { buf[--i] = '-'; } *p = buf + i; *len = bufSize - i; } #ifdef ULLONG_MAX void GString::formatUInt(unsigned long long x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len) { #else void GString::formatUInt(Gulong x, char *buf, int bufSize, GBool zeroFill, int width, int base, const char **p, int *len) { #endif static char vals[17] = "0123456789abcdef"; int i, j; i = bufSize; if (x == 0) { buf[--i] = '0'; } else { while (i > 0 && x) { buf[--i] = vals[x % base]; x /= base; } } if (zeroFill) { for (j = bufSize - i; i > 0 && j < width; ++j) { buf[--i] = '0'; } } *p = buf + i; *len = bufSize - i; } void GString::formatDouble(double x, char *buf, int bufSize, int prec, GBool trim, const char **p, int *len) { GBool neg, started; double x2; int d, i, j; if ((neg = x < 0)) { x = -x; } x = floor(x * pow(10.0, prec) + 0.5); i = bufSize; started = !trim; for (j = 0; j < prec && i > 1; ++j) { x2 = floor(0.1 * (x + 0.5)); d = (int)floor(x - 10 * x2 + 0.5); if (started || d != 0) { buf[--i] = '0' + d; started = gTrue; } x = x2; } if (i > 1 && started) { buf[--i] = '.'; } if (i > 1) { do { x2 = floor(0.1 * (x + 0.5)); d = (int)floor(x - 10 * x2 + 0.5); buf[--i] = '0' + d; x = x2; } while (i > 1 && x); } if (neg) { buf[--i] = '-'; } *p = buf + i; *len = bufSize - i; } GString *GString::insert(int i, char c) { int j; if (length > INT_MAX - 1) { gMemError("Integer overflow in GString::insert()"); } resize(length + 1); for (j = length + 1; j > i; --j) s[j] = s[j-1]; s[i] = c; ++length; return this; } GString *GString::insert(int i, GString *str) { int n = str->getLength(); int j; if (length > INT_MAX - n) { gMemError("Integer overflow in GString::insert()"); } resize(length + n); for (j = length; j >= i; --j) s[j+n] = s[j]; memcpy(s+i, str->getCString(), n); length += n; return this; } GString *GString::insert(int i, const char *str) { int n = (int)strlen(str); int j; if (length > INT_MAX - n) { gMemError("Integer overflow in GString::insert()"); } resize(length + n); for (j = length; j >= i; --j) s[j+n] = s[j]; memcpy(s+i, str, n); length += n; return this; } GString *GString::insert(int i, const char *str, int lengthA) { int j; if (lengthA < 0 || length > INT_MAX - lengthA) { gMemError("Integer overflow in GString::insert()"); } resize(length + lengthA); for (j = length; j >= i; --j) s[j+lengthA] = s[j]; memcpy(s+i, str, lengthA); length += lengthA; return this; } GString *GString::del(int i, int n) { int j; if (i >= 0 && n > 0 && i <= INT_MAX - n) { if (i + n > length) { n = length - i; } for (j = i; j <= length - n; ++j) { s[j] = s[j + n]; } resize(length -= n); } return this; } GString *GString::upperCase() { int i; for (i = 0; i < length; ++i) { if (islower(s[i])) s[i] = toupper(s[i]); } return this; } GString *GString::lowerCase() { int i; for (i = 0; i < length; ++i) { if (isupper(s[i])) s[i] = tolower(s[i]); } return this; } int GString::cmp(GString *str) { int n1, n2, i, x; char *p1, *p2; n1 = length; n2 = str->length; for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) { x = *p1 - *p2; if (x != 0) { return x; } } return n1 - n2; } int GString::cmpN(GString *str, int n) { int n1, n2, i, x; char *p1, *p2; n1 = length; n2 = str->length; for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2 && i < n; ++i, ++p1, ++p2) { x = *p1 - *p2; if (x != 0) { return x; } } if (i == n) { return 0; } return n1 - n2; } int GString::cmp(const char *sA) { int n1, i, x; const char *p1, *p2; n1 = length; for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) { x = *p1 - *p2; if (x != 0) { return x; } } if (i < n1) { return 1; } if (*p2) { return -1; } return 0; } int GString::cmpN(const char *sA, int n) { int n1, i, x; const char *p1, *p2; n1 = length; for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) { x = *p1 - *p2; if (x != 0) { return x; } } if (i == n) { return 0; } if (i < n1) { return 1; } if (*p2) { return -1; } return 0; } xpdf-3.04/goo/parseargs.c0000644000076400007640000000670512341430012014630 0ustar dereknderekn/* * parseargs.h * * Command line argument parser. * * Copyright 1996-2003 Glyph & Cog, LLC */ #include #include #include #include #include #include "parseargs.h" static ArgDesc *findArg(ArgDesc *args, char *arg); static GBool grabArg(ArgDesc *arg, int i, int *argc, char *argv[]); GBool parseArgs(ArgDesc *args, int *argc, char *argv[]) { ArgDesc *arg; int i, j; GBool ok; ok = gTrue; i = 1; while (i < *argc) { if (!strcmp(argv[i], "--")) { --*argc; for (j = i; j < *argc; ++j) argv[j] = argv[j+1]; break; } else if ((arg = findArg(args, argv[i]))) { if (!grabArg(arg, i, argc, argv)) ok = gFalse; } else { ++i; } } return ok; } void printUsage(const char *program, const char *otherArgs, ArgDesc *args) { ArgDesc *arg; char *typ; int w, w1; w = 0; for (arg = args; arg->arg; ++arg) { if ((w1 = (int)strlen(arg->arg)) > w) w = w1; } fprintf(stderr, "Usage: %s [options]", program); if (otherArgs) fprintf(stderr, " %s", otherArgs); fprintf(stderr, "\n"); for (arg = args; arg->arg; ++arg) { fprintf(stderr, " %s", arg->arg); w1 = 9 + w - (int)strlen(arg->arg); switch (arg->kind) { case argInt: case argIntDummy: typ = " "; break; case argFP: case argFPDummy: typ = " "; break; case argString: case argStringDummy: typ = " "; break; case argFlag: case argFlagDummy: default: typ = ""; break; } fprintf(stderr, "%-*s", w1, typ); if (arg->usage) fprintf(stderr, ": %s", arg->usage); fprintf(stderr, "\n"); } } static ArgDesc *findArg(ArgDesc *args, char *arg) { ArgDesc *p; for (p = args; p->arg; ++p) { if (p->kind < argFlagDummy && !strcmp(p->arg, arg)) return p; } return NULL; } static GBool grabArg(ArgDesc *arg, int i, int *argc, char *argv[]) { int n; int j; GBool ok; ok = gTrue; n = 0; switch (arg->kind) { case argFlag: *(GBool *)arg->val = gTrue; n = 1; break; case argInt: if (i + 1 < *argc && isInt(argv[i+1])) { *(int *)arg->val = atoi(argv[i+1]); n = 2; } else { ok = gFalse; n = 1; } break; case argFP: if (i + 1 < *argc && isFP(argv[i+1])) { *(double *)arg->val = atof(argv[i+1]); n = 2; } else { ok = gFalse; n = 1; } break; case argString: if (i + 1 < *argc) { strncpy((char *)arg->val, argv[i+1], arg->size - 1); ((char *)arg->val)[arg->size - 1] = '\0'; n = 2; } else { ok = gFalse; n = 1; } break; default: fprintf(stderr, "Internal error in arg table\n"); n = 1; break; } if (n > 0) { *argc -= n; for (j = i; j < *argc; ++j) argv[j] = argv[j+n]; } return ok; } GBool isInt(char *s) { if (*s == '-' || *s == '+') ++s; while (isdigit(*s & 0xff)) ++s; if (*s) return gFalse; return gTrue; } GBool isFP(char *s) { int n; if (*s == '-' || *s == '+') ++s; n = 0; while (isdigit(*s & 0xff)) { ++s; ++n; } if (*s == '.') ++s; while (isdigit(*s & 0xff)) { ++s; ++n; } if (n > 0 && (*s == 'e' || *s == 'E')) { ++s; if (*s == '-' || *s == '+') ++s; n = 0; if (!isdigit(*s & 0xff)) return gFalse; do { ++s; } while (isdigit(*s & 0xff)); } if (*s) return gFalse; return gTrue; } xpdf-3.04/goo/GMutex.h0000644000076400007640000000173012341430012014050 0ustar dereknderekn//======================================================================== // // GMutex.h // // Portable mutex macros. // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GMUTEX_H #define GMUTEX_H // Usage: // // GMutex m; // gInitMutex(&m); // ... // gLockMutex(&m); // ... critical section ... // gUnlockMutex(&m); // ... // gDestroyMutex(&m); #ifdef _WIN32 #include typedef CRITICAL_SECTION GMutex; #define gInitMutex(m) InitializeCriticalSection(m) #define gDestroyMutex(m) DeleteCriticalSection(m) #define gLockMutex(m) EnterCriticalSection(m) #define gUnlockMutex(m) LeaveCriticalSection(m) #else // assume pthreads #include typedef pthread_mutex_t GMutex; #define gInitMutex(m) pthread_mutex_init(m, NULL) #define gDestroyMutex(m) pthread_mutex_destroy(m) #define gLockMutex(m) pthread_mutex_lock(m) #define gUnlockMutex(m) pthread_mutex_unlock(m) #endif #endif xpdf-3.04/goo/gfile.cc0000644000076400007640000004454312341430012014074 0ustar dereknderekn//======================================================================== // // gfile.cc // // Miscellaneous file and directory name manipulation. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef _WIN32 # include # include #else # if defined(MACOS) # include # elif !defined(ACORN) # include # include # include # endif # include # include # include # if !defined(VMS) && !defined(ACORN) && !defined(MACOS) # include # endif # if defined(VMS) && (__DECCXX_VER < 50200000) # include # endif #endif // _WIN32 #include "GString.h" #include "gfile.h" // Some systems don't define this, so just make it something reasonably // large. #ifndef PATH_MAX #define PATH_MAX 1024 #endif //------------------------------------------------------------------------ GString *getHomeDir() { #ifdef VMS //---------- VMS ---------- return new GString("SYS$LOGIN:"); #elif defined(__EMX__) || defined(_WIN32) //---------- OS/2+EMX and Win32 ---------- char *s; GString *ret; if ((s = getenv("HOME"))) ret = new GString(s); else ret = new GString("."); return ret; #elif defined(ACORN) //---------- RISCOS ---------- return new GString("@"); #elif defined(MACOS) //---------- MacOS ---------- return new GString(":"); #else //---------- Unix ---------- char *s; struct passwd *pw; GString *ret; if ((s = getenv("HOME"))) { ret = new GString(s); } else { if ((s = getenv("USER"))) pw = getpwnam(s); else pw = getpwuid(getuid()); if (pw) ret = new GString(pw->pw_dir); else ret = new GString("."); } return ret; #endif } GString *getCurrentDir() { char buf[PATH_MAX+1]; #if defined(__EMX__) if (_getcwd2(buf, sizeof(buf))) #elif defined(_WIN32) if (GetCurrentDirectoryA(sizeof(buf), buf)) #elif defined(ACORN) if (strcpy(buf, "@")) #elif defined(MACOS) if (strcpy(buf, ":")) #else if (getcwd(buf, sizeof(buf))) #endif return new GString(buf); return new GString(); } GString *appendToPath(GString *path, const char *fileName) { #if defined(VMS) //---------- VMS ---------- //~ this should handle everything necessary for file //~ requesters, but it's certainly not complete char *p0, *p1, *p2; char *q1; p0 = path->getCString(); p1 = p0 + path->getLength() - 1; if (!strcmp(fileName, "-")) { if (*p1 == ']') { for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ; if (*p2 == '[') ++p2; path->del(p2 - p0, p1 - p2); } else if (*p1 == ':') { path->append("[-]"); } else { path->clear(); path->append("[-]"); } } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) { if (*p1 == ']') { path->insert(p1 - p0, '.'); path->insert(p1 - p0 + 1, fileName, q1 - fileName); } else if (*p1 == ':') { path->append('['); path->append(']'); path->append(fileName, q1 - fileName); } else { path->clear(); path->append(fileName, q1 - fileName); } } else { if (*p1 != ']' && *p1 != ':') path->clear(); path->append(fileName); } return path; #elif defined(_WIN32) //---------- Win32 ---------- GString *tmp; char buf[256]; char *fp; tmp = new GString(path); tmp->append('/'); tmp->append(fileName); GetFullPathNameA(tmp->getCString(), sizeof(buf), buf, &fp); delete tmp; path->clear(); path->append(buf); return path; #elif defined(ACORN) //---------- RISCOS ---------- char *p; int i; path->append("."); i = path->getLength(); path->append(fileName); for (p = path->getCString() + i; *p; ++p) { if (*p == '/') { *p = '.'; } else if (*p == '.') { *p = '/'; } } return path; #elif defined(MACOS) //---------- MacOS ---------- char *p; int i; path->append(":"); i = path->getLength(); path->append(fileName); for (p = path->getCString() + i; *p; ++p) { if (*p == '/') { *p = ':'; } else if (*p == '.') { *p = ':'; } } return path; #elif defined(__EMX__) //---------- OS/2+EMX ---------- int i; // appending "." does nothing if (!strcmp(fileName, ".")) return path; // appending ".." goes up one directory if (!strcmp(fileName, "..")) { for (i = path->getLength() - 2; i >= 0; --i) { if (path->getChar(i) == '/' || path->getChar(i) == '\\' || path->getChar(i) == ':') break; } if (i <= 0) { if (path->getChar(0) == '/' || path->getChar(0) == '\\') { path->del(1, path->getLength() - 1); } else if (path->getLength() >= 2 && path->getChar(1) == ':') { path->del(2, path->getLength() - 2); } else { path->clear(); path->append(".."); } } else { if (path->getChar(i-1) == ':') ++i; path->del(i, path->getLength() - i); } return path; } // otherwise, append "/" and new path component if (path->getLength() > 0 && path->getChar(path->getLength() - 1) != '/' && path->getChar(path->getLength() - 1) != '\\') path->append('/'); path->append(fileName); return path; #else //---------- Unix ---------- int i; // appending "." does nothing if (!strcmp(fileName, ".")) return path; // appending ".." goes up one directory if (!strcmp(fileName, "..")) { for (i = path->getLength() - 2; i >= 0; --i) { if (path->getChar(i) == '/') break; } if (i <= 0) { if (path->getChar(0) == '/') { path->del(1, path->getLength() - 1); } else { path->clear(); path->append(".."); } } else { path->del(i, path->getLength() - i); } return path; } // otherwise, append "/" and new path component if (path->getLength() > 0 && path->getChar(path->getLength() - 1) != '/') path->append('/'); path->append(fileName); return path; #endif } GString *grabPath(char *fileName) { #ifdef VMS //---------- VMS ---------- char *p; if ((p = strrchr(fileName, ']'))) return new GString(fileName, p + 1 - fileName); if ((p = strrchr(fileName, ':'))) return new GString(fileName, p + 1 - fileName); return new GString(); #elif defined(__EMX__) || defined(_WIN32) //---------- OS/2+EMX and Win32 ---------- char *p; if ((p = strrchr(fileName, '/'))) return new GString(fileName, (int)(p - fileName)); if ((p = strrchr(fileName, '\\'))) return new GString(fileName, (int)(p - fileName)); if ((p = strrchr(fileName, ':'))) return new GString(fileName, (int)(p + 1 - fileName)); return new GString(); #elif defined(ACORN) //---------- RISCOS ---------- char *p; if ((p = strrchr(fileName, '.'))) return new GString(fileName, p - fileName); return new GString(); #elif defined(MACOS) //---------- MacOS ---------- char *p; if ((p = strrchr(fileName, ':'))) return new GString(fileName, p - fileName); return new GString(); #else //---------- Unix ---------- char *p; if ((p = strrchr(fileName, '/'))) return new GString(fileName, p - fileName); return new GString(); #endif } GBool isAbsolutePath(char *path) { #ifdef VMS //---------- VMS ---------- return strchr(path, ':') || (path[0] == '[' && path[1] != '.' && path[1] != '-'); #elif defined(__EMX__) || defined(_WIN32) //---------- OS/2+EMX and Win32 ---------- return path[0] == '/' || path[0] == '\\' || path[1] == ':'; #elif defined(ACORN) //---------- RISCOS ---------- return path[0] == '$'; #elif defined(MACOS) //---------- MacOS ---------- return path[0] != ':'; #else //---------- Unix ---------- return path[0] == '/'; #endif } GString *makePathAbsolute(GString *path) { #ifdef VMS //---------- VMS ---------- char buf[PATH_MAX+1]; if (!isAbsolutePath(path->getCString())) { if (getcwd(buf, sizeof(buf))) { path->insert(0, buf); } } return path; #elif defined(_WIN32) //---------- Win32 ---------- char buf[_MAX_PATH]; char *fp; buf[0] = '\0'; if (!GetFullPathNameA(path->getCString(), _MAX_PATH, buf, &fp)) { path->clear(); return path; } path->clear(); path->append(buf); return path; #elif defined(ACORN) //---------- RISCOS ---------- path->insert(0, '@'); return path; #elif defined(MACOS) //---------- MacOS ---------- path->del(0, 1); return path; #else //---------- Unix and OS/2+EMX ---------- struct passwd *pw; char buf[PATH_MAX+1]; GString *s; char *p1, *p2; int n; if (path->getChar(0) == '~') { if (path->getChar(1) == '/' || #ifdef __EMX__ path->getChar(1) == '\\' || #endif path->getLength() == 1) { path->del(0, 1); s = getHomeDir(); path->insert(0, s); delete s; } else { p1 = path->getCString() + 1; #ifdef __EMX__ for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ; #else for (p2 = p1; *p2 && *p2 != '/'; ++p2) ; #endif if ((n = p2 - p1) > PATH_MAX) n = PATH_MAX; strncpy(buf, p1, n); buf[n] = '\0'; if ((pw = getpwnam(buf))) { path->del(0, p2 - p1 + 1); path->insert(0, pw->pw_dir); } } } else if (!isAbsolutePath(path->getCString())) { if (getcwd(buf, sizeof(buf))) { #ifndef __EMX__ path->insert(0, '/'); #endif path->insert(0, buf); } } return path; #endif } time_t getModTime(char *fileName) { #ifdef _WIN32 //~ should implement this, but it's (currently) only used in xpdf return 0; #else struct stat statBuf; if (stat(fileName, &statBuf)) { return 0; } return statBuf.st_mtime; #endif } GBool openTempFile(GString **name, FILE **f, const char *mode, const char *ext) { #if defined(_WIN32) //---------- Win32 ---------- char *tempDir; GString *s, *s2; FILE *f2; int t, i; // this has the standard race condition problem, but I haven't found // a better way to generate temp file names with extensions on // Windows if ((tempDir = getenv("TEMP"))) { s = new GString(tempDir); s->append('\\'); } else { s = new GString(); } s->appendf("x_{0:d}_{1:d}_", (int)GetCurrentProcessId(), (int)GetCurrentThreadId()); t = (int)time(NULL); for (i = 0; i < 1000; ++i) { s2 = s->copy()->appendf("{0:d}", t + i); if (ext) { s2->append(ext); } if (!(f2 = fopen(s2->getCString(), "r"))) { if (!(f2 = fopen(s2->getCString(), mode))) { delete s2; delete s; return gFalse; } *name = s2; *f = f2; delete s; return gTrue; } fclose(f2); delete s2; } delete s; return gFalse; #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS) //---------- non-Unix ---------- char *s; // There is a security hole here: an attacker can create a symlink // with this file name after the tmpnam call and before the fopen // call. I will happily accept fixes to this function for non-Unix // OSs. if (!(s = tmpnam(NULL))) { return gFalse; } *name = new GString(s); if (ext) { (*name)->append(ext); } if (!(*f = fopen((*name)->getCString(), mode))) { delete (*name); *name = NULL; return gFalse; } return gTrue; #else //---------- Unix ---------- char *s; int fd; if (ext) { #if HAVE_MKSTEMPS if ((s = getenv("TMPDIR"))) { *name = new GString(s); } else { *name = new GString("/tmp"); } (*name)->append("/XXXXXX")->append(ext); fd = mkstemps((*name)->getCString(), strlen(ext)); #else if (!(s = tmpnam(NULL))) { return gFalse; } *name = new GString(s); (*name)->append(ext); fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600); #endif } else { #if HAVE_MKSTEMP if ((s = getenv("TMPDIR"))) { *name = new GString(s); } else { *name = new GString("/tmp"); } (*name)->append("/XXXXXX"); fd = mkstemp((*name)->getCString()); #else // HAVE_MKSTEMP if (!(s = tmpnam(NULL))) { return gFalse; } *name = new GString(s); fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600); #endif // HAVE_MKSTEMP } if (fd < 0 || !(*f = fdopen(fd, mode))) { delete *name; *name = NULL; return gFalse; } return gTrue; #endif } GBool createDir(char *path, int mode) { #ifdef _WIN32 return !mkdir(path); #else return !mkdir(path, mode); #endif } GBool executeCommand(char *cmd) { #ifdef VMS return system(cmd) ? gTrue : gFalse; #else return system(cmd) ? gFalse : gTrue; #endif } #ifdef _WIN32 GString *fileNameToUTF8(char *path) { GString *s; char *p; s = new GString(); for (p = path; *p; ++p) { if (*p & 0x80) { s->append((char)(0xc0 | ((*p >> 6) & 0x03))); s->append((char)(0x80 | (*p & 0x3f))); } else { s->append(*p); } } return s; } GString *fileNameToUTF8(wchar_t *path) { GString *s; wchar_t *p; s = new GString(); for (p = path; *p; ++p) { if (*p < 0x80) { s->append((char)*p); } else if (*p < 0x800) { s->append((char)(0xc0 | ((*p >> 6) & 0x1f))); s->append((char)(0x80 | (*p & 0x3f))); } else { s->append((char)(0xe0 | ((*p >> 12) & 0x0f))); s->append((char)(0x80 | ((*p >> 6) & 0x3f))); s->append((char)(0x80 | (*p & 0x3f))); } } return s; } #endif FILE *openFile(const char *path, const char *mode) { #ifdef _WIN32 OSVERSIONINFO version; wchar_t wPath[_MAX_PATH + 1]; char nPath[_MAX_PATH + 1]; wchar_t wMode[8]; const char *p; int i; // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { for (p = path, i = 0; *p && i < _MAX_PATH; ++i) { if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { wPath[i] = (wchar_t)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); p += 2; } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { wPath[i] = (wchar_t)(((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); p += 3; } else { wPath[i] = (wchar_t)(p[0] & 0xff); p += 1; } } wPath[i] = (wchar_t)0; for (i = 0; mode[i] && i < sizeof(mode) - 1; ++i) { wMode[i] = (wchar_t)(mode[i] & 0xff); } wMode[i] = (wchar_t)0; return _wfopen(wPath, wMode); } else { for (p = path, i = 0; *p && i < _MAX_PATH; ++i) { if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { nPath[i] = (char)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); p += 2; } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { nPath[i] = (char)(((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); p += 3; } else { nPath[i] = p[0]; p += 1; } } nPath[i] = '\0'; return fopen(nPath, mode); } #else return fopen(path, mode); #endif } char *getLine(char *buf, int size, FILE *f) { int c, i; i = 0; while (i < size - 1) { if ((c = fgetc(f)) == EOF) { break; } buf[i++] = (char)c; if (c == '\x0a') { break; } if (c == '\x0d') { c = fgetc(f); if (c == '\x0a' && i < size - 1) { buf[i++] = (char)c; } else if (c != EOF) { ungetc(c, f); } break; } } buf[i] = '\0'; if (i == 0) { return NULL; } return buf; } int gfseek(FILE *f, GFileOffset offset, int whence) { #if HAVE_FSEEKO return fseeko(f, offset, whence); #elif HAVE_FSEEK64 return fseek64(f, offset, whence); #elif HAVE_FSEEKI64 return _fseeki64(f, offset, whence); #else return fseek(f, offset, whence); #endif } GFileOffset gftell(FILE *f) { #if HAVE_FSEEKO return ftello(f); #elif HAVE_FSEEK64 return ftell64(f); #elif HAVE_FSEEKI64 return _ftelli64(f); #else return ftell(f); #endif } //------------------------------------------------------------------------ // GDir and GDirEntry //------------------------------------------------------------------------ GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) { #ifdef VMS char *p; #elif defined(_WIN32) int fa; GString *s; #elif defined(ACORN) #else struct stat st; GString *s; #endif name = new GString(nameA); dir = gFalse; if (doStat) { #ifdef VMS if (!strcmp(nameA, "-") || ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5))) dir = gTrue; #elif defined(ACORN) #else s = new GString(dirPath); appendToPath(s, nameA); #ifdef _WIN32 fa = GetFileAttributesA(s->getCString()); dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY)); #else if (stat(s->getCString(), &st) == 0) dir = S_ISDIR(st.st_mode); #endif delete s; #endif } } GDirEntry::~GDirEntry() { delete name; } GDir::GDir(char *name, GBool doStatA) { path = new GString(name); doStat = doStatA; #if defined(_WIN32) GString *tmp; tmp = path->copy(); tmp->append("/*.*"); hnd = FindFirstFileA(tmp->getCString(), &ffd); delete tmp; #elif defined(ACORN) #elif defined(MACOS) #elif defined(ANDROID) #else dir = opendir(name); #ifdef VMS needParent = strchr(name, '[') != NULL; #endif #endif } GDir::~GDir() { delete path; #if defined(_WIN32) if (hnd) { FindClose(hnd); hnd = NULL; } #elif defined(ACORN) #elif defined(MACOS) #elif defined(ANDROID) #else if (dir) closedir(dir); #endif } GDirEntry *GDir::getNextEntry() { GDirEntry *e; #if defined(_WIN32) if (hnd) { e = new GDirEntry(path->getCString(), ffd.cFileName, doStat); if (hnd && !FindNextFileA(hnd, &ffd)) { FindClose(hnd); hnd = NULL; } } else { e = NULL; } #elif defined(ACORN) #elif defined(MACOS) #elif defined(ANDROID) #elif defined(VMS) struct dirent *ent; e = NULL; if (dir) { if (needParent) { e = new GDirEntry(path->getCString(), "-", doStat); needParent = gFalse; return e; } ent = readdir(dir); if (ent) { e = new GDirEntry(path->getCString(), ent->d_name, doStat); } } #else struct dirent *ent; e = NULL; if (dir) { ent = (struct dirent *)readdir(dir); if (ent && !strcmp(ent->d_name, ".")) { ent = (struct dirent *)readdir(dir); } if (ent) { e = new GDirEntry(path->getCString(), ent->d_name, doStat); } } #endif return e; } void GDir::rewind() { #ifdef _WIN32 GString *tmp; if (hnd) FindClose(hnd); tmp = path->copy(); tmp->append("/*.*"); hnd = FindFirstFileA(tmp->getCString(), &ffd); delete tmp; #elif defined(ACORN) #elif defined(MACOS) #elif defined(ANDROID) #else if (dir) rewinddir(dir); #ifdef VMS needParent = strchr(path->getCString(), '[') != NULL; #endif #endif } xpdf-3.04/goo/FixedPoint.cc0000644000076400007640000000524312341430012015051 0ustar dereknderekn//======================================================================== // // FixedPoint.cc // // Fixed point type, with C++ operators. // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #include #if USE_FIXEDPOINT #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "FixedPoint.h" #define ln2 ((FixedPoint)0.69314718) FixedPoint FixedPoint::sqrt(FixedPoint x) { FixedPoint y0, y1, z; if (x.val <= 0) { y1.val = 0; } else { y1.val = x.val == 1 ? 2 : x.val >> 1; do { y0.val = y1.val; z = x / y0; y1.val = (y0.val + z.val) >> 1; } while (::abs(y0.val - y1.val) > 1); } return y1; } FixedPoint FixedPoint::pow(FixedPoint x, FixedPoint y) { FixedPoint t, t2, lnx0, lnx, z0, z; int d, n, i; if (y.val <= 0) { z.val = 0; } else { // y * ln(x) t = (x - 1) / (x + 1); t2 = t * t; d = 1; lnx = 0; do { lnx0 = lnx; lnx += t / d; t *= t2; d += 2; } while (::abs(lnx.val - lnx0.val) > 2); lnx.val <<= 1; t = y * lnx; // exp(y * ln(x)) n = floor(t / ln2); t -= ln2 * n; t2 = t; d = 1; i = 1; z = 1; do { z0 = z; z += t2 / d; t2 *= t; ++i; d *= i; } while (::abs(z.val - z0.val) > 2 && d < (1 << fixptShift)); if (n >= 0) { z.val <<= n; } else if (n < 0) { z.val >>= -n; } } return z; } int FixedPoint::mul(int x, int y) { FixPtInt64 z; z = ((FixPtInt64)x * y) >> fixptShift; if (z > 0x7fffffffLL) { return 0x7fffffff; } else if (z < -0x80000000LL) { return 0x80000000; } else { return (int)z; } } int FixedPoint::div(int x, int y) { FixPtInt64 z; z = ((FixPtInt64)x << fixptShift) / y; if (z > 0x7fffffffLL) { return 0x7fffffff; } else if (z < -0x80000000LL) { return 0x80000000; } else { return (int)z; } } GBool FixedPoint::divCheck(FixedPoint x, FixedPoint y, FixedPoint *result) { FixPtInt64 z; z = ((FixPtInt64)x.val << fixptShift) / y.val; if ((z == 0 && x != 0) || z >= ((FixPtInt64)1 << 31) || z < -((FixPtInt64)1 << 31)) { return gFalse; } result->val = z; return gTrue; } GBool FixedPoint::checkDet(FixedPoint m11, FixedPoint m12, FixedPoint m21, FixedPoint m22, FixedPoint epsilon) { FixPtInt64 det, e; det = (FixPtInt64)m11.val * (FixPtInt64)m22.val - (FixPtInt64)m12.val * (FixPtInt64)m21.val; e = (FixPtInt64)epsilon.val << fixptShift; // NB: this comparison has to be >= not > because epsilon can be // truncated to zero as a fixed point value. return det >= e || det <= -e; } #endif // USE_FIXEDPOINT xpdf-3.04/goo/Makefile.dep0000644000076400007640000000000012341430012014662 0ustar derekndereknxpdf-3.04/goo/GList.cc0000644000076400007640000000432012341430012014015 0ustar dereknderekn//======================================================================== // // GList.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "GList.h" //------------------------------------------------------------------------ // GList //------------------------------------------------------------------------ GList::GList() { size = 8; data = (void **)gmallocn(size, sizeof(void*)); length = 0; inc = 0; } GList::GList(int sizeA) { size = sizeA ? sizeA : 8; data = (void **)gmallocn(size, sizeof(void*)); length = 0; inc = 0; } GList::~GList() { gfree(data); } GList *GList::copy() { GList *ret; ret = new GList(length); ret->length = length; memcpy(ret->data, data, length * sizeof(void *)); ret->inc = inc; return ret; } void GList::append(void *p) { if (length >= size) { expand(); } data[length++] = p; } void GList::append(GList *list) { int i; while (length + list->length > size) { expand(); } for (i = 0; i < list->length; ++i) { data[length++] = list->data[i]; } } void GList::insert(int i, void *p) { if (length >= size) { expand(); } if (i < 0) { i = 0; } if (i < length) { memmove(data+i+1, data+i, (length - i) * sizeof(void *)); } data[i] = p; ++length; } void *GList::del(int i) { void *p; p = data[i]; if (i < length - 1) { memmove(data+i, data+i+1, (length - i - 1) * sizeof(void *)); } --length; if (size - length >= ((inc > 0) ? inc : size/2)) { shrink(); } return p; } void GList::sort(int (*cmp)(const void *obj1, const void *obj2)) { qsort(data, length, sizeof(void *), cmp); } void GList::reverse() { void *t; int n, i; n = length / 2; for (i = 0; i < n; ++i) { t = data[i]; data[i] = data[length - 1 - i]; data[length - 1 - i] = t; } } void GList::expand() { size += (inc > 0) ? inc : size; data = (void **)greallocn(data, size, sizeof(void*)); } void GList::shrink() { size -= (inc > 0) ? inc : size/2; data = (void **)greallocn(data, size, sizeof(void*)); } xpdf-3.04/goo/GList.h0000644000076400007640000000524412341430012013665 0ustar dereknderekn//======================================================================== // // GList.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GLIST_H #define GLIST_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" //------------------------------------------------------------------------ // GList //------------------------------------------------------------------------ class GList { public: // Create an empty list. GList(); // Create an empty list with space for elements. GList(int sizeA); // Destructor - does not free pointed-to objects. ~GList(); //----- general // Get the number of elements. int getLength() { return length; } // Returns a (shallow) copy of this list. GList *copy(); //----- ordered list support // Return the th element. // Assumes 0 <= i < length. void *get(int i) { return data[i]; } // Replace the th element. // Assumes 0 <= i < length. void put(int i, void *p) { data[i] = p; } // Append an element to the end of the list. void append(void *p); // Append another list to the end of this one. void append(GList *list); // Insert an element at index . // Assumes 0 <= i <= length. void insert(int i, void *p); // Deletes and returns the element at index . // Assumes 0 <= i < length. void *del(int i); // Sort the list accoring to the given comparison function. // NB: this sorts an array of pointers, so the pointer args need to // be double-dereferenced. void sort(int (*cmp)(const void *ptr1, const void *ptr2)); // Reverse the list. void reverse(); //----- control // Set allocation increment to . If inc > 0, that many // elements will be allocated every time the list is expanded. // If inc <= 0, the list will be doubled in size. void setAllocIncr(int incA) { inc = incA; } private: void expand(); void shrink(); void **data; // the list elements int size; // size of data array int length; // number of elements on list int inc; // allocation increment }; #define deleteGList(list, T) \ do { \ GList *_list = (list); \ { \ int _i; \ for (_i = 0; _i < _list->getLength(); ++_i) { \ delete (T*)_list->get(_i); \ } \ delete _list; \ } \ } while (0) #endif xpdf-3.04/goo/gfile.h0000644000076400007640000001133412341430012013726 0ustar dereknderekn//======================================================================== // // gfile.h // // Miscellaneous file and directory name manipulation. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GFILE_H #define GFILE_H #include #include #include #if defined(_WIN32) # include # ifdef FPTEX # include # else # include # endif #elif defined(ACORN) #elif defined(MACOS) # include #elif defined(ANDROID) #else # include # include # ifdef VMS # include "vms_dirent.h" # elif HAVE_DIRENT_H # include # define NAMLEN(d) strlen((d)->d_name) # else # define dirent direct # define NAMLEN(d) (d)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif # endif #endif #include "gtypes.h" class GString; //------------------------------------------------------------------------ // Get home directory path. extern GString *getHomeDir(); // Get current directory. extern GString *getCurrentDir(); // Append a file name to a path string. may be an empty // string, denoting the current directory). Returns . extern GString *appendToPath(GString *path, const char *fileName); // Grab the path from the front of the file name. If there is no // directory component in , returns an empty string. extern GString *grabPath(char *fileName); // Is this an absolute path or file name? extern GBool isAbsolutePath(char *path); // Make this path absolute by prepending current directory (if path is // relative) or prepending user's directory (if path starts with '~'). extern GString *makePathAbsolute(GString *path); // Get the modification time for . Returns 0 if there is an // error. extern time_t getModTime(char *fileName); // Create a temporary file and open it for writing. If is not // NULL, it will be used as the file name extension. Returns both the // name and the file pointer. For security reasons, all writing // should be done to the returned file pointer; the file may be // reopened later for reading, but not for writing. The string // should be "w" or "wb". Returns true on success. extern GBool openTempFile(GString **name, FILE **f, const char *mode, const char *ext); // Create a directory. Returns true on success. extern GBool createDir(char *path, int mode); // Execute . Returns true on success. extern GBool executeCommand(char *cmd); #ifdef _WIN32 // Convert a file name from Latin-1 to UTF-8. extern GString *fileNameToUTF8(char *path); // Convert a file name from UCS-2 to UTF-8. extern GString *fileNameToUTF8(wchar_t *path); #endif // Open a file. On Windows, this converts the path from UTF-8 to // UCS-2 and calls _wfopen (if available). On other OSes, this simply // calls fopen. extern FILE *openFile(const char *path, const char *mode); // Just like fgets, but handles Unix, Mac, and/or DOS end-of-line // conventions. extern char *getLine(char *buf, int size, FILE *f); // Type used by gfseek/gftell for file offsets. This will be 64 bits // on systems that support it. #if HAVE_FSEEKO typedef off_t GFileOffset; #define GFILEOFFSET_MAX 0x7fffffffffffffffLL #elif HAVE_FSEEK64 typedef long long GFileOffset; #define GFILEOFFSET_MAX 0x7fffffffffffffffLL #elif HAVE_FSEEKI64 typedef __int64 GFileOffset; #define GFILEOFFSET_MAX 0x7fffffffffffffffLL #else typedef long GFileOffset; #define GFILEOFFSET_MAX LONG_MAX #endif // Like fseek, but uses a 64-bit file offset if available. extern int gfseek(FILE *f, GFileOffset offset, int whence); // Like ftell, but returns a 64-bit file offset if available. extern GFileOffset gftell(FILE *f); //------------------------------------------------------------------------ // GDir and GDirEntry //------------------------------------------------------------------------ class GDirEntry { public: GDirEntry(char *dirPath, char *nameA, GBool doStat); ~GDirEntry(); GString *getName() { return name; } GBool isDir() { return dir; } private: GString *name; // dir/file name GBool dir; // is it a directory? }; class GDir { public: GDir(char *name, GBool doStatA = gTrue); ~GDir(); GDirEntry *getNextEntry(); void rewind(); private: GString *path; // directory path GBool doStat; // call stat() for each entry? #if defined(_WIN32) WIN32_FIND_DATAA ffd; HANDLE hnd; #elif defined(ACORN) #elif defined(MACOS) #elif defined(ANDROID) #else DIR *dir; // the DIR structure from opendir() #ifdef VMS GBool needParent; // need to return an entry for [-] #endif #endif }; #endif xpdf-3.04/goo/gtypes.h0000644000076400007640000000106212341430012014150 0ustar dereknderekn/* * gtypes.h * * Some useful simple types. * * Copyright 1996-2003 Glyph & Cog, LLC */ #ifndef GTYPES_H #define GTYPES_H /* * These have stupid names to avoid conflicts with some (but not all) * C++ compilers which define them. */ typedef int GBool; #define gTrue 1 #define gFalse 0 /* * These have stupid names to avoid conflicts with , * which on various systems defines some random subset of these. */ typedef unsigned char Guchar; typedef unsigned short Gushort; typedef unsigned int Guint; typedef unsigned long Gulong; #endif xpdf-3.04/goo/FixedPoint.h0000644000076400007640000001427212341430012014715 0ustar dereknderekn//======================================================================== // // FixedPoint.h // // Fixed point type, with C++ operators. // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #ifndef FIXEDPOINT_H #define FIXEDPOINT_H #include #if USE_FIXEDPOINT #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include #include "gtypes.h" #define fixptShift 16 #define fixptMaskL ((1 << fixptShift) - 1) #define fixptMaskH (~fixptMaskL) typedef long long FixPtInt64; class FixedPoint { public: FixedPoint() { val = 0; } FixedPoint(const FixedPoint &x) { val = x.val; } FixedPoint(double x) { val = (int)(x * (1 << fixptShift) + 0.5); } FixedPoint(int x) { val = x << fixptShift; } FixedPoint(long x) { val = x << fixptShift; } operator float() { return (float) val * ((float)1 / (float)(1 << fixptShift)); } operator double() { return (double) val * (1.0 / (double)(1 << fixptShift)); } operator int() { return val >> fixptShift; } int get16Dot16() { return val; } FixedPoint operator =(FixedPoint x) { val = x.val; return *this; } int operator ==(FixedPoint x) const { return val == x.val; } int operator ==(double x) const { return *this == (FixedPoint)x; } int operator ==(int x) const { return *this == (FixedPoint)x; } int operator ==(long x) const { return *this == (FixedPoint)x; } int operator !=(FixedPoint x) const { return val != x.val; } int operator !=(double x) const { return *this != (FixedPoint)x; } int operator !=(int x) const { return *this != (FixedPoint)x; } int operator !=(long x) const { return *this != (FixedPoint)x; } int operator <(FixedPoint x) const { return val < x.val; } int operator <(double x) const { return *this < (FixedPoint)x; } int operator <(int x) const { return *this < (FixedPoint)x; } int operator <(long x) const { return *this < (FixedPoint)x; } int operator <=(FixedPoint x) const { return val <= x.val; } int operator <=(double x) const { return *this <= (FixedPoint)x; } int operator <=(int x) const { return *this <= (FixedPoint)x; } int operator <=(long x) const { return *this <= (FixedPoint)x; } int operator >(FixedPoint x) const { return val > x.val; } int operator >(double x) const { return *this > (FixedPoint)x; } int operator >(int x) const { return *this > (FixedPoint)x; } int operator >(long x) const { return *this > (FixedPoint)x; } int operator >=(FixedPoint x) const { return val >= x.val; } int operator >=(double x) const { return *this >= (FixedPoint)x; } int operator >=(int x) const { return *this >= (FixedPoint)x; } int operator >=(long x) const { return *this >= (FixedPoint)x; } FixedPoint operator -() { return make(-val); } FixedPoint operator +(FixedPoint x) { return make(val + x.val); } FixedPoint operator +(double x) { return *this + (FixedPoint)x; } FixedPoint operator +(int x) { return *this + (FixedPoint)x; } FixedPoint operator +(long x) { return *this + (FixedPoint)x; } FixedPoint operator +=(FixedPoint x) { val = val + x.val; return *this; } FixedPoint operator +=(double x) { return *this += (FixedPoint)x; } FixedPoint operator +=(int x) { return *this += (FixedPoint)x; } FixedPoint operator +=(long x) { return *this += (FixedPoint)x; } FixedPoint operator -(FixedPoint x) { return make(val - x.val); } FixedPoint operator -(double x) { return *this - (FixedPoint)x; } FixedPoint operator -(int x) { return *this - (FixedPoint)x; } FixedPoint operator -(long x) { return *this - (FixedPoint)x; } FixedPoint operator -=(FixedPoint x) { val = val - x.val; return *this; } FixedPoint operator -=(double x) { return *this -= (FixedPoint)x; } FixedPoint operator -=(int x) { return *this -= (FixedPoint)x; } FixedPoint operator -=(long x) { return *this -= (FixedPoint)x; } FixedPoint operator *(FixedPoint x) { return make(mul(val, x.val)); } FixedPoint operator *(double x) { return *this * (FixedPoint)x; } FixedPoint operator *(int x) { return *this * (FixedPoint)x; } FixedPoint operator *(long x) { return *this * (FixedPoint)x; } FixedPoint operator *=(FixedPoint x) { val = mul(val, x.val); return *this; } FixedPoint operator *=(double x) { return *this *= (FixedPoint)x; } FixedPoint operator *=(int x) { return *this *= (FixedPoint)x; } FixedPoint operator *=(long x) { return *this *= (FixedPoint)x; } FixedPoint operator /(FixedPoint x) { return make(div(val, x.val)); } FixedPoint operator /(double x) { return *this / (FixedPoint)x; } FixedPoint operator /(int x) { return *this / (FixedPoint)x; } FixedPoint operator /(long x) { return *this / (FixedPoint)x; } FixedPoint operator /=(FixedPoint x) { val = div(val, x.val); return *this; } FixedPoint operator /=(double x) { return *this /= (FixedPoint)x; } FixedPoint operator /=(int x) { return *this /= (FixedPoint)x; } FixedPoint operator /=(long x) { return *this /= (FixedPoint)x; } static FixedPoint abs(FixedPoint x) { return make(::abs(x.val)); } static int floor(FixedPoint x) { return x.val >> fixptShift; } static int ceil(FixedPoint x) { return (x.val & fixptMaskL) ? ((x.val >> fixptShift) + 1) : (x.val >> fixptShift); } static int round(FixedPoint x) { return (x.val + (1 << (fixptShift - 1))) >> fixptShift; } // Computes (x+y)/2 avoiding overflow and LSbit accuracy issues. static FixedPoint avg(FixedPoint x, FixedPoint y) { return make((x.val >> 1) + (y.val >> 1) + ((x.val | y.val) & 1)); } static FixedPoint sqrt(FixedPoint x); static FixedPoint pow(FixedPoint x, FixedPoint y); // Compute *result = x/y; return false if there is an underflow or // overflow. static GBool divCheck(FixedPoint x, FixedPoint y, FixedPoint *result); // Compute abs(m11*m22 - m12*m21) >= epsilon, handling the case // where the multiplications overflow. static GBool checkDet(FixedPoint m11, FixedPoint m12, FixedPoint m21, FixedPoint m22, FixedPoint epsilon); private: static FixedPoint make(int valA) { FixedPoint x; x.val = valA; return x; } static int mul(int x, int y); static int div(int x, int y); int val; // fixed point: (n-fixptShift).(fixptShift) }; #endif // USE_FIXEDPOINT #endif xpdf-3.04/goo/GHash.h0000644000076400007640000000424112341430012013631 0ustar dereknderekn//======================================================================== // // GHash.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GHASH_H #define GHASH_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" class GString; struct GHashBucket; struct GHashIter; //------------------------------------------------------------------------ class GHash { public: GHash(GBool deleteKeysA = gFalse); ~GHash(); void add(GString *key, void *val); void add(GString *key, int val); void replace(GString *key, void *val); void replace(GString *key, int val); void *lookup(GString *key); int lookupInt(GString *key); void *lookup(const char *key); int lookupInt(const char *key); void *remove(GString *key); int removeInt(GString *key); void *remove(const char *key); int removeInt(const char *key); int getLength() { return len; } void startIter(GHashIter **iter); GBool getNext(GHashIter **iter, GString **key, void **val); GBool getNext(GHashIter **iter, GString **key, int *val); void killIter(GHashIter **iter); private: void expand(); GHashBucket *find(GString *key, int *h); GHashBucket *find(const char *key, int *h); int hash(GString *key); int hash(const char *key); GBool deleteKeys; // set if key strings should be deleted int size; // number of buckets int len; // number of entries GHashBucket **tab; }; #define deleteGHash(hash, T) \ do { \ GHash *_hash = (hash); \ { \ GHashIter *_iter; \ GString *_key; \ void *_p; \ _hash->startIter(&_iter); \ while (_hash->getNext(&_iter, &_key, &_p)) { \ delete (T*)_p; \ } \ delete _hash; \ } \ } while(0) #endif xpdf-3.04/goo/gmempp.cc0000644000076400007640000000106012341430012014256 0ustar dereknderekn//======================================================================== // // gmempp.cc // // Use gmalloc/gfree for C++ new/delete operators. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #include "gmem.h" #ifdef DEBUG_MEM void *operator new(size_t size) { return gmalloc((int)size); } void *operator new[](size_t size) { return gmalloc((int)size); } void operator delete(void *p) { gfree(p); } void operator delete[](void *p) { gfree(p); } #endif xpdf-3.04/Makefile.in0000644000076400007640000000771312341430012013756 0ustar dereknderekn#======================================================================== # # Main xpdf Makefile. # # Copyright 1996-2003 Glyph & Cog, LLC # #======================================================================== SHELL = /bin/sh DESTDIR = prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ EXE = @EXE@ all: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) @XPDF_TARGET@ all-no-x: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) all-no-x xpdf: dummy cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) xpdf$(EXE) pdftops: dummy cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdftops$(EXE) pdftotext: dummy cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdftotext$(EXE) pdfinfo: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdfinfo$(EXE) pdffonts: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdffonts$(EXE) pdfdetach: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdfdetach$(EXE) pdftoppm: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdftoppm$(EXE) pdfimages: cd goo; $(MAKE) cd @UP_DIR@fofi; $(MAKE) cd @UP_DIR@splash; $(MAKE) cd @UP_DIR@xpdf; $(MAKE) pdfimages$(EXE) dummy: install: dummy -mkdir -p $(DESTDIR)@bindir@ @X@ $(INSTALL_PROGRAM) xpdf/xpdf$(EXE) $(DESTDIR)@bindir@/xpdf$(EXE) $(INSTALL_PROGRAM) xpdf/pdftops$(EXE) $(DESTDIR)@bindir@/pdftops$(EXE) $(INSTALL_PROGRAM) xpdf/pdftotext$(EXE) $(DESTDIR)@bindir@/pdftotext$(EXE) $(INSTALL_PROGRAM) xpdf/pdfinfo$(EXE) $(DESTDIR)@bindir@/pdfinfo$(EXE) $(INSTALL_PROGRAM) xpdf/pdffonts$(EXE) $(DESTDIR)@bindir@/pdffonts$(EXE) $(INSTALL_PROGRAM) xpdf/pdfdetach$(EXE) $(DESTDIR)@bindir@/pdfdetach$(EXE) @X@ $(INSTALL_PROGRAM) xpdf/pdftoppm$(EXE) $(DESTDIR)@bindir@/pdftoppm$(EXE) $(INSTALL_PROGRAM) xpdf/pdfimages$(EXE) $(DESTDIR)@bindir@/pdfimages$(EXE) -mkdir -p $(DESTDIR)@mandir@/man1 @X@ $(INSTALL_DATA) $(srcdir)/doc/xpdf.1 $(DESTDIR)@mandir@/man1/xpdf.1 $(INSTALL_DATA) $(srcdir)/doc/pdftops.1 $(DESTDIR)@mandir@/man1/pdftops.1 $(INSTALL_DATA) $(srcdir)/doc/pdftotext.1 $(DESTDIR)@mandir@/man1/pdftotext.1 $(INSTALL_DATA) $(srcdir)/doc/pdfinfo.1 $(DESTDIR)@mandir@/man1/pdfinfo.1 $(INSTALL_DATA) $(srcdir)/doc/pdffonts.1 $(DESTDIR)@mandir@/man1/pdffonts.1 $(INSTALL_DATA) $(srcdir)/doc/pdfdetach.1 $(DESTDIR)@mandir@/man1/pdfdetach.1 @X@ $(INSTALL_DATA) $(srcdir)/doc/pdftoppm.1 $(DESTDIR)@mandir@/man1/pdftoppm.1 $(INSTALL_DATA) $(srcdir)/doc/pdfimages.1 $(DESTDIR)@mandir@/man1/pdfimages.1 -mkdir -p $(DESTDIR)@mandir@/man5 $(INSTALL_DATA) $(srcdir)/doc/xpdfrc.5 $(DESTDIR)@mandir@/man5/xpdfrc.5 -mkdir -p $(DESTDIR)@sysconfdir@ @if test ! -f $(DESTDIR)@sysconfdir@/xpdfrc; then \ echo "$(INSTALL_DATA) $(srcdir)/doc/sample-xpdfrc $(DESTDIR)@sysconfdir@/xpdfrc"; \ $(INSTALL_DATA) $(srcdir)/doc/sample-xpdfrc $(DESTDIR)@sysconfdir@/xpdfrc; \ else \ echo "# not overwriting the existing $(DESTDIR)@sysconfdir@/xpdfrc"; \ fi clean: -cd goo; $(MAKE) clean -cd @UP_DIR@fofi; $(MAKE) clean -cd @UP_DIR@splash; $(MAKE) clean -cd @UP_DIR@xpdf; $(MAKE) clean distclean: clean rm -f config.log config.status config.cache rm -f aconf.h rm -f Makefile goo/Makefile fofi/Makefile splash/Makefile xpdf/Makefile rm -f goo/Makefile.dep fofi/Makefile.dep splash/Makefile.dep xpdf/Makefile.dep rm -f goo/Makefile.in.bak fofi/Makefile.in.bak splash/Makefile.in.bak xpdf/Makefile.in.bak touch goo/Makefile.dep touch fofi/Makefile.dep touch splash/Makefile.dep touch xpdf/Makefile.dep depend: cd goo; $(MAKE) depend cd @UP_DIR@fofi; $(MAKE) depend cd @UP_DIR@splash; $(MAKE) depend cd @UP_DIR@xpdf; $(MAKE) depend xpdf-3.04/COPYING0000644000076400007640000004307612341430012012746 0ustar dereknderekn GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. xpdf-3.04/xpdf/0000755000076400007640000000000012341430012012642 5ustar derekndereknxpdf-3.04/xpdf/TextOutputDev.h0000644000076400007640000004456312341430012015633 0ustar dereknderekn//======================================================================== // // TextOutputDev.h // // Copyright 1997-2012 Glyph & Cog, LLC // //======================================================================== #ifndef TEXTOUTPUTDEV_H #define TEXTOUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "gtypes.h" #include "GfxFont.h" #include "OutputDev.h" class GList; class UnicodeMap; class TextBlock; class TextChar; class TextLink; 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 //------------------------------------------------------------------------ class TextFontInfo { public: TextFontInfo(GfxState *state); ~TextFontInfo(); GBool matches(GfxState *state); // Get the font name (which may be NULL). GString *getFontName() { return fontName; } // Get font descriptor flags. GBool isFixedWidth() { return flags & fontFixedWidth; } GBool isSerif() { return flags & fontSerif; } GBool isSymbolic() { return flags & fontSymbolic; } GBool isItalic() { return flags & fontItalic; } GBool isBold() { return flags & fontBold; } // Get the width of the 'm' character, if available. double getMWidth() { return mWidth; } private: Ref fontID; GString *fontName; int flags; double mWidth; double ascent, descent; friend class TextLine; friend class TextPage; friend class TextWord; }; //------------------------------------------------------------------------ // TextWord //------------------------------------------------------------------------ class TextWord { public: TextWord(GList *chars, int start, int lenA, int rotA, GBool spaceAfterA); ~TextWord(); TextWord *copy() { return new TextWord(this); } // Get the TextFontInfo object associated with this word. TextFontInfo *getFontInfo() { return font; } 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, double *xMaxA, double *yMaxA); double getFontSize() { return fontSize; } int getRotation() { return rot; } int getCharPos() { return charPos[0]; } int getCharLen() { return charPos[len] - charPos[0]; } GBool getSpaceAfter() { return spaceAfter; } double getBaseline(); GBool isUnderlined() { return underlined; } 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 Unicode *text; // the text int *charPos; // character position (within content stream) // of each char (plus one extra entry for // the last char) 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 GBool underlined; TextLink *link; double colorR, // word color colorG, colorB; GBool invisible; // set for invisible text (render mode 3) friend class TextBlock; friend class TextLine; friend class TextPage; }; //------------------------------------------------------------------------ // TextLine //------------------------------------------------------------------------ class TextLine { public: TextLine(GList *wordsA, double xMinA, double yMinA, double xMaxA, double yMaxA, double fontSizeA); ~TextLine(); double getXMin() { return xMin; } double getYMin() { return yMin; } double getBaseline(); int getRotation() { return rot; } GList *getWords() { return words; } private: 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 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 len; // number of Unicode chars GBool hyphenated; // set if last char is a hyphen 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 TextPage; friend class TextParagraph; }; //------------------------------------------------------------------------ // TextParagraph //------------------------------------------------------------------------ class TextParagraph { public: TextParagraph(GList *linesA); ~TextParagraph(); // Get the list of TextLine objects. GList *getLines() { return lines; } private: GList *lines; // [TextLine] double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates friend class TextPage; }; //------------------------------------------------------------------------ // TextColumn //------------------------------------------------------------------------ class TextColumn { public: TextColumn(GList *paragraphsA, double xMinA, double yMinA, double xMaxA, double yMaxA); ~TextColumn(); // Get the list of TextParagraph objects. GList *getParagraphs() { return paragraphs; } private: 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 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 TextPage; }; //------------------------------------------------------------------------ // TextWordList //------------------------------------------------------------------------ class TextWordList { public: TextWordList(GList *wordsA); ~TextWordList(); // Return the number of words on the list. int getLength(); // Return the th word from the list. TextWord *get(int idx); private: GList *words; // [TextWord] }; //------------------------------------------------------------------------ // TextPage //------------------------------------------------------------------------ class TextPage { public: TextPage(TextOutputControl *controlA); ~TextPage(); // 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 // immediately after the last find result; else starts looking at // ,. If is true, stops looking at the // bottom of the page; else if is true, stops looking // just before the last find result; else stops looking at // ,. GBool findText(Unicode *s, int len, GBool startAtTop, GBool stopAtBottom, GBool startAtLast, GBool stopAtLast, GBool caseSensitive, GBool backward, GBool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax); // Get the text which is inside the specified rectangle. GString *getText(double xMin, double yMin, double xMax, double yMax); // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns // false. GBool findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax); // Create and return a list of TextColumn objects. GList *makeColumns(); // Get the list of all TextFontInfo objects used on this page. GList *getFonts() { return fonts; } // Build a flat word list, in the specified ordering. TextWordList *makeWordList(); private: void startPage(GfxState *state); void clear(); void updateFont(GfxState *state); void addChar(GfxState *state, double x, double y, double dx, double dy, CharCode c, int nBytes, Unicode *u, int uLen); void incCharCount(int nChars); void beginActualText(GfxState *state, Unicode *u, int uLen); void endActualText(GfxState *state); void addUnderline(double x0, double y0, double x1, double y1); void addLink(double xMin, double yMin, double xMax, double yMax, Link *link); // output void writeReadingOrder(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen); void writePhysLayout(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen); void writeLinePrinter(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen); void writeRaw(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen); void encodeFragment(Unicode *text, int len, UnicodeMap *uMap, GBool primaryLR, GString *s); // analysis int rotateChars(GList *charsA); void rotateUnderlinesAndLinks(int rot); void unrotateChars(GList *charsA, int rot); void unrotateColumns(GList *columns, int rot); void unrotateWords(GList *words, int rot); GBool checkPrimaryLR(GList *charsA); void removeDuplicates(GList *charsA, int rot); TextBlock *splitChars(GList *charsA); TextBlock *split(GList *charsA, int rot); GList *getChars(GList *charsA, double xMin, double yMin, double xMax, double yMax); void tagBlock(TextBlock *blk); void insertLargeChars(GList *largeChars, TextBlock *blk); void insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk); void insertLargeCharInLeaf(TextChar *ch, TextBlock *blk); void insertIntoTree(TextBlock *subtree, TextBlock *primaryTree); void insertColumnIntoTree(TextBlock *column, TextBlock *tree); void insertClippedChars(GList *clippedChars, TextBlock *tree); TextBlock *findClippedCharLeaf(TextChar *ch, TextBlock *tree); GList *buildColumns(TextBlock *tree); void buildColumns2(TextBlock *blk, GList *columns); TextColumn *buildColumn(TextBlock *tree); double getLineIndent(TextLine *line, TextBlock *blk); double getAverageLineSpacing(GList *lines); double getLineSpacing(TextLine *line0, TextLine *line1); void buildLines(TextBlock *blk, GList *lines); TextLine *buildLine(TextBlock *blk); void getLineChars(TextBlock *blk, GList *charsA); double computeWordSpacingThreshold(GList *charsA, int rot); int assignPhysLayoutPositions(GList *columns); void assignLinePhysPositions(GList *columns); void computeLinePhysWidth(TextLine *line, UnicodeMap *uMap); int assignColumnPhysPositions(GList *columns); void generateUnderlinesAndLinks(GList *columns); // debug #if 0 //~debug void dumpChars(GList *charsA); void dumpTree(TextBlock *tree, int indent = 0); void dumpColumns(GList *columns); #endif TextOutputControl control; // formatting parameters double pageWidth, pageHeight; // width and height of current page int charPos; // next character position (within content // stream) TextFontInfo *curFont; // current font double curFontSize; // current font size int curRot; // current rotation int nTinyChars; // number of "tiny" chars seen so far Unicode *actualText; // current "ActualText" span int actualTextLen; double actualTextX0, actualTextY0, actualTextX1, actualTextY1; int actualTextNBytes; 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; friend class TextOutputDev; }; //------------------------------------------------------------------------ // TextOutputDev //------------------------------------------------------------------------ class TextOutputDev: public OutputDev { public: // Open a text output file. If is NULL, no file is // written (this is useful, e.g., for searching text). If // 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, TextOutputControl *controlA, GBool append); // Create a TextOutputDev which will write to a generic stream. If // is true, the original physical layout of the text // is maintained. If is true, the text is kept in // content stream order. TextOutputDev(TextOutputFunc func, void *stream, TextOutputControl *controlA); // Destructor. virtual ~TextOutputDev(); // Check if file was successfully created. virtual GBool isOk() { return ok; } //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return gTrue; } // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() { return gTrue; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gFalse; } // Does this device need non-text content? virtual GBool needNonText() { return gFalse; } // Does this device require incCharCount to be called for text on // non-shown layers? virtual GBool needCharCount() { return gTrue; } //----- initialization and control // Start a page. virtual void startPage(int pageNum, GfxState *state); // End a page. virtual void endPage(); //----- save/restore graphics state virtual void restoreState(GfxState *state); //----- update text state virtual void updateFont(GfxState *state); //----- text drawing virtual void beginString(GfxState *state, GString *s); virtual void endString(GfxState *state); virtual void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode c, int nBytes, Unicode *u, int uLen); virtual void incCharCount(int nChars); virtual void beginActualText(GfxState *state, Unicode *u, int uLen); virtual void endActualText(GfxState *state); //----- path painting virtual void stroke(GfxState *state); virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); //----- link borders virtual void processLink(Link *link); //----- special access // Find a string. If is true, starts looking at the // top of the page; else if is true, starts looking // immediately after the last find result; else starts looking at // ,. If is true, stops looking at the // bottom of the page; else if is true, stops looking // just before the last find result; else stops looking at // ,. GBool findText(Unicode *s, int len, GBool startAtTop, GBool stopAtBottom, GBool startAtLast, GBool stopAtLast, GBool caseSensitive, GBool backward, GBool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax); // Get the text which is inside the specified rectangle. GString *getText(double xMin, double yMin, double xMax, double yMax); // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns // false. GBool findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax); // 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(); // 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 html) { control.html = html; } private: TextOutputFunc outputFunc; // output function void *outputStream; // output stream GBool needClose; // need to close the output file? // (only if outputStream is a FILE*) TextPage *text; // text for the current page TextOutputControl control; // formatting parameters GBool ok; // set up ok? }; #endif xpdf-3.04/xpdf/TextString.h0000644000076400007640000000264012341430012015130 0ustar dereknderekn//======================================================================== // // 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 xpdf-3.04/xpdf/print.xbm0000644000076400007640000000042312341430012014505 0ustar dereknderekn#define print_width 15 #define print_height 15 static unsigned char print_bits[] = { 0xf0, 0x7f, 0x10, 0x40, 0x10, 0x40, 0xc8, 0x23, 0x08, 0x20, 0x68, 0x23, 0x04, 0x10, 0x34, 0x10, 0x04, 0x10, 0xff, 0x7f, 0x55, 0x55, 0xab, 0x6a, 0x55, 0x55, 0xab, 0x6a, 0xfe, 0x3f}; xpdf-3.04/xpdf/GfxState.cc0000644000076400007640000032665512341430012014717 0ustar dereknderekn//======================================================================== // // GfxState.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include "gmem.h" #include "Error.h" #include "Object.h" #include "Array.h" #include "Page.h" #include "XRef.h" #include "GfxState.h" //------------------------------------------------------------------------ // Max depth of nested color spaces. This is used to catch infinite // loops in the color space object structure. #define colorSpaceRecursionLimit 8 //------------------------------------------------------------------------ static inline GfxColorComp clip01(GfxColorComp x) { return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x; } static inline double clip01(double x) { return (x < 0) ? 0 : (x > 1) ? 1 : x; } //------------------------------------------------------------------------ struct GfxBlendModeInfo { const char *name; GfxBlendMode mode; }; static GfxBlendModeInfo gfxBlendModeNames[] = { { "Normal", gfxBlendNormal }, { "Compatible", gfxBlendNormal }, { "Multiply", gfxBlendMultiply }, { "Screen", gfxBlendScreen }, { "Overlay", gfxBlendOverlay }, { "Darken", gfxBlendDarken }, { "Lighten", gfxBlendLighten }, { "ColorDodge", gfxBlendColorDodge }, { "ColorBurn", gfxBlendColorBurn }, { "HardLight", gfxBlendHardLight }, { "SoftLight", gfxBlendSoftLight }, { "Difference", gfxBlendDifference }, { "Exclusion", gfxBlendExclusion }, { "Hue", gfxBlendHue }, { "Saturation", gfxBlendSaturation }, { "Color", gfxBlendColor }, { "Luminosity", gfxBlendLuminosity } }; #define nGfxBlendModeNames \ ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo)))) //------------------------------------------------------------------------ // NB: This must match the GfxColorSpaceMode enum defined in // GfxState.h static const char *gfxColorSpaceModeNames[] = { "DeviceGray", "CalGray", "DeviceRGB", "CalRGB", "DeviceCMYK", "Lab", "ICCBased", "Indexed", "Separation", "DeviceN", "Pattern" }; #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *))) //------------------------------------------------------------------------ // GfxColorSpace //------------------------------------------------------------------------ GfxColorSpace::GfxColorSpace() { overprintMask = 0x0f; } GfxColorSpace::~GfxColorSpace() { } GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) { GfxColorSpace *cs; Object obj1; if (recursion > colorSpaceRecursionLimit) { error(errSyntaxError, -1, "Loop detected in color space objects"); return NULL; } cs = NULL; if (csObj->isName()) { if (csObj->isName("DeviceGray") || csObj->isName("G")) { cs = GfxColorSpace::create(csDeviceGray); } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) { cs = GfxColorSpace::create(csDeviceRGB); } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) { cs = GfxColorSpace::create(csDeviceCMYK); } else if (csObj->isName("Pattern")) { cs = new GfxPatternColorSpace(NULL); } else { error(errSyntaxError, -1, "Bad color space '{0:s}'", csObj->getName()); } } else if (csObj->isArray() && csObj->arrayGetLength() > 0) { csObj->arrayGet(0, &obj1); if (obj1.isName("DeviceGray") || obj1.isName("G")) { cs = GfxColorSpace::create(csDeviceGray); } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) { cs = GfxColorSpace::create(csDeviceRGB); } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) { cs = GfxColorSpace::create(csDeviceCMYK); } else if (obj1.isName("CalGray")) { cs = GfxCalGrayColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("CalRGB")) { cs = GfxCalRGBColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("Lab")) { cs = GfxLabColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("ICCBased")) { cs = GfxICCBasedColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("Indexed") || obj1.isName("I")) { cs = GfxIndexedColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("Separation")) { cs = GfxSeparationColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("DeviceN")) { cs = GfxDeviceNColorSpace::parse(csObj->getArray(), recursion); } else if (obj1.isName("Pattern")) { cs = GfxPatternColorSpace::parse(csObj->getArray(), recursion); } else { error(errSyntaxError, -1, "Bad color space"); } obj1.free(); } else { error(errSyntaxError, -1, "Bad color space - expected name or array"); } 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; for (i = 0; i < getNComps(); ++i) { decodeLow[i] = 0; decodeRange[i] = 1; } } int GfxColorSpace::getNumColorSpaceModes() { return nGfxColorSpaceModes; } const char *GfxColorSpace::getColorSpaceModeName(int idx) { return gfxColorSpaceModeNames[idx]; } //------------------------------------------------------------------------ // GfxDeviceGrayColorSpace //------------------------------------------------------------------------ GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() { } GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() { } GfxColorSpace *GfxDeviceGrayColorSpace::copy() { GfxDeviceGrayColorSpace *cs; cs = new GfxDeviceGrayColorSpace(); return cs; } void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) { *gray = clip01(color->c[0]); } void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { rgb->r = rgb->g = rgb->b = clip01(color->c[0]); } void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { cmyk->c = cmyk->m = cmyk->y = 0; cmyk->k = clip01(gfxColorComp1 - color->c[0]); } void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; } //------------------------------------------------------------------------ // GfxCalGrayColorSpace //------------------------------------------------------------------------ GfxCalGrayColorSpace::GfxCalGrayColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; gamma = 1; } GfxCalGrayColorSpace::~GfxCalGrayColorSpace() { } GfxColorSpace *GfxCalGrayColorSpace::copy() { GfxCalGrayColorSpace *cs; cs = new GfxCalGrayColorSpace(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; cs->blackX = blackX; cs->blackY = blackY; cs->blackZ = blackZ; cs->gamma = gamma; return cs; } GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, int recursion) { GfxCalGrayColorSpace *cs; Object obj1, obj2, obj3; if (arr->getLength() < 2) { error(errSyntaxError, -1, "Bad CalGray color space"); return NULL; } arr->get(1, &obj1); if (!obj1.isDict()) { error(errSyntaxError, -1, "Bad CalGray color space"); obj1.free(); return NULL; } cs = new GfxCalGrayColorSpace(); if (obj1.dictLookup("WhitePoint", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->whiteX = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->whiteY = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->whiteZ = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("BlackPoint", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->blackX = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->blackY = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->blackZ = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("Gamma", &obj2)->isNum()) { cs->gamma = obj2.getNum(); } obj2.free(); obj1.free(); return cs; } void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) { *gray = clip01(color->c[0]); } void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { rgb->r = rgb->g = rgb->b = clip01(color->c[0]); } void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { cmyk->c = cmyk->m = cmyk->y = 0; cmyk->k = clip01(gfxColorComp1 - color->c[0]); } void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; } //------------------------------------------------------------------------ // GfxDeviceRGBColorSpace //------------------------------------------------------------------------ GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() { } GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() { } GfxColorSpace *GfxDeviceRGBColorSpace::copy() { 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] + 0.11 * color->c[2] + 0.5)); } void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { rgb->r = clip01(color->c[0]); rgb->g = clip01(color->c[1]); rgb->b = clip01(color->c[2]); } void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { GfxColorComp c, m, y, k; c = clip01(gfxColorComp1 - color->c[0]); m = clip01(gfxColorComp1 - color->c[1]); y = clip01(gfxColorComp1 - color->c[2]); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; color->c[1] = 0; color->c[2] = 0; } //------------------------------------------------------------------------ // GfxCalRGBColorSpace //------------------------------------------------------------------------ GfxCalRGBColorSpace::GfxCalRGBColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; gammaR = gammaG = gammaB = 1; mat[0] = 1; mat[1] = 0; mat[2] = 0; mat[3] = 0; mat[4] = 1; mat[5] = 0; mat[6] = 0; mat[7] = 0; mat[8] = 1; } GfxCalRGBColorSpace::~GfxCalRGBColorSpace() { } GfxColorSpace *GfxCalRGBColorSpace::copy() { GfxCalRGBColorSpace *cs; int i; cs = new GfxCalRGBColorSpace(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; cs->blackX = blackX; cs->blackY = blackY; cs->blackZ = blackZ; cs->gammaR = gammaR; cs->gammaG = gammaG; cs->gammaB = gammaB; for (i = 0; i < 9; ++i) { cs->mat[i] = mat[i]; } return cs; } GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, int recursion) { GfxCalRGBColorSpace *cs; Object obj1, obj2, obj3; int i; if (arr->getLength() < 2) { error(errSyntaxError, -1, "Bad CalRGB color space"); return NULL; } arr->get(1, &obj1); if (!obj1.isDict()) { error(errSyntaxError, -1, "Bad CalRGB color space"); obj1.free(); return NULL; } cs = new GfxCalRGBColorSpace(); if (obj1.dictLookup("WhitePoint", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->whiteX = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->whiteY = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->whiteZ = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("BlackPoint", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->blackX = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->blackY = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->blackZ = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("Gamma", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->gammaR = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->gammaG = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->gammaB = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("Matrix", &obj2)->isArray() && obj2.arrayGetLength() == 9) { for (i = 0; i < 9; ++i) { obj2.arrayGet(i, &obj3); cs->mat[i] = obj3.getNum(); obj3.free(); } } obj2.free(); obj1.free(); return cs; } void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) { *gray = clip01((GfxColorComp)(0.299 * color->c[0] + 0.587 * color->c[1] + 0.114 * color->c[2] + 0.5)); } void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { rgb->r = clip01(color->c[0]); rgb->g = clip01(color->c[1]); rgb->b = clip01(color->c[2]); } void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { GfxColorComp c, m, y, k; c = clip01(gfxColorComp1 - color->c[0]); m = clip01(gfxColorComp1 - color->c[1]); y = clip01(gfxColorComp1 - color->c[2]); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; color->c[1] = 0; color->c[2] = 0; } //------------------------------------------------------------------------ // GfxDeviceCMYKColorSpace //------------------------------------------------------------------------ GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() { } GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() { } GfxColorSpace *GfxDeviceCMYKColorSpace::copy() { 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] - 0.59 * color->c[1] - 0.11 * color->c[2] + 0.5)); } void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { double c, m, y, k, c1, m1, y1, k1, r, g, b, x; c = colToDbl(color->c[0]); m = colToDbl(color->c[1]); y = colToDbl(color->c[2]); k = colToDbl(color->c[3]); c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; // this is a matrix multiplication, unrolled for performance // C M Y K x = c1 * m1 * y1 * k1; // 0 0 0 0 r = g = b = x; x = c1 * m1 * y1 * k; // 0 0 0 1 r += 0.1373 * x; g += 0.1216 * x; b += 0.1255 * x; x = c1 * m1 * y * k1; // 0 0 1 0 r += x; g += 0.9490 * x; x = c1 * m1 * y * k; // 0 0 1 1 r += 0.1098 * x; g += 0.1020 * x; x = c1 * m * y1 * k1; // 0 1 0 0 r += 0.9255 * x; b += 0.5490 * x; x = c1 * m * y1 * k; // 0 1 0 1 r += 0.1412 * x; x = c1 * m * y * k1; // 0 1 1 0 r += 0.9294 * x; g += 0.1098 * x; b += 0.1412 * x; x = c1 * m * y * k; // 0 1 1 1 r += 0.1333 * x; x = c * m1 * y1 * k1; // 1 0 0 0 g += 0.6784 * x; b += 0.9373 * x; x = c * m1 * y1 * k; // 1 0 0 1 g += 0.0588 * x; b += 0.1412 * x; x = c * m1 * y * k1; // 1 0 1 0 g += 0.6510 * x; b += 0.3137 * x; x = c * m1 * y * k; // 1 0 1 1 g += 0.0745 * x; x = c * m * y1 * k1; // 1 1 0 0 r += 0.1804 * x; g += 0.1922 * x; b += 0.5725 * x; x = c * m * y1 * k; // 1 1 0 1 b += 0.0078 * x; x = c * m * y * k1; // 1 1 1 0 r += 0.2118 * x; g += 0.2119 * x; b += 0.2235 * x; rgb->r = clip01(dblToCol(r)); rgb->g = clip01(dblToCol(g)); rgb->b = clip01(dblToCol(b)); } void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { cmyk->c = clip01(color->c[0]); cmyk->m = clip01(color->c[1]); cmyk->y = clip01(color->c[2]); cmyk->k = clip01(color->c[3]); } void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; color->c[1] = 0; color->c[2] = 0; color->c[3] = gfxColorComp1; } //------------------------------------------------------------------------ // GfxLabColorSpace //------------------------------------------------------------------------ // This is the inverse of MatrixLMN in Example 4.10 from the PostScript // Language Reference, Third Edition. static double xyzrgb[3][3] = { { 3.240449, -1.537136, -0.498531 }, { -0.969265, 1.876011, 0.041556 }, { 0.055643, -0.204026, 1.057229 } }; GfxLabColorSpace::GfxLabColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; aMin = bMin = -100; aMax = bMax = 100; } GfxLabColorSpace::~GfxLabColorSpace() { } GfxColorSpace *GfxLabColorSpace::copy() { GfxLabColorSpace *cs; cs = new GfxLabColorSpace(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; cs->blackX = blackX; cs->blackY = blackY; cs->blackZ = blackZ; cs->aMin = aMin; cs->aMax = aMax; cs->bMin = bMin; cs->bMax = bMax; cs->kr = kr; cs->kg = kg; cs->kb = kb; return cs; } GfxColorSpace *GfxLabColorSpace::parse(Array *arr, int recursion) { GfxLabColorSpace *cs; Object obj1, obj2, obj3; if (arr->getLength() < 2) { error(errSyntaxError, -1, "Bad Lab color space"); return NULL; } arr->get(1, &obj1); if (!obj1.isDict()) { error(errSyntaxError, -1, "Bad Lab color space"); obj1.free(); return NULL; } cs = new GfxLabColorSpace(); if (obj1.dictLookup("WhitePoint", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->whiteX = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->whiteY = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->whiteZ = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("BlackPoint", &obj2)->isArray() && obj2.arrayGetLength() == 3) { obj2.arrayGet(0, &obj3); cs->blackX = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->blackY = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->blackZ = obj3.getNum(); obj3.free(); } obj2.free(); if (obj1.dictLookup("Range", &obj2)->isArray() && obj2.arrayGetLength() == 4) { obj2.arrayGet(0, &obj3); cs->aMin = obj3.getNum(); obj3.free(); obj2.arrayGet(1, &obj3); cs->aMax = obj3.getNum(); obj3.free(); obj2.arrayGet(2, &obj3); cs->bMin = obj3.getNum(); obj3.free(); obj2.arrayGet(3, &obj3); cs->bMax = obj3.getNum(); obj3.free(); } obj2.free(); obj1.free(); cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX + xyzrgb[0][1] * cs->whiteY + xyzrgb[0][2] * cs->whiteZ); cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX + xyzrgb[1][1] * cs->whiteY + xyzrgb[1][2] * cs->whiteZ); cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX + xyzrgb[2][1] * cs->whiteY + xyzrgb[2][2] * cs->whiteZ); return cs; } void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) { GfxRGB rgb; getRGB(color, &rgb); *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); } void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { double X, Y, Z; double t1, t2; double r, g, b; // convert L*a*b* to CIE 1931 XYZ color space t1 = (colToDbl(color->c[0]) + 16) / 116; t2 = t1 + colToDbl(color->c[1]) / 500; if (t2 >= (6.0 / 29.0)) { X = t2 * t2 * t2; } else { X = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); } X *= whiteX; if (t1 >= (6.0 / 29.0)) { Y = t1 * t1 * t1; } else { Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0)); } Y *= whiteY; t2 = t1 - colToDbl(color->c[2]) / 200; if (t2 >= (6.0 / 29.0)) { Z = t2 * t2 * t2; } else { Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); } Z *= whiteZ; // convert XYZ to RGB, including gamut mapping and gamma correction r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; rgb->r = dblToCol(pow(clip01(r * kr), 0.5)); rgb->g = dblToCol(pow(clip01(g * kg), 0.5)); rgb->b = dblToCol(pow(clip01(b * kb), 0.5)); } void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { GfxRGB rgb; GfxColorComp c, m, y, k; getRGB(color, &rgb); c = clip01(gfxColorComp1 - rgb.r); m = clip01(gfxColorComp1 - rgb.g); y = clip01(gfxColorComp1 - rgb.b); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxLabColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; if (aMin > 0) { color->c[1] = dblToCol(aMin); } else if (aMax < 0) { color->c[1] = dblToCol(aMax); } else { color->c[1] = 0; } if (bMin > 0) { color->c[2] = dblToCol(bMin); } else if (bMax < 0) { color->c[2] = dblToCol(bMax); } else { color->c[2] = 0; } } void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) { decodeLow[0] = 0; decodeRange[0] = 100; decodeLow[1] = aMin; decodeRange[1] = aMax - aMin; decodeLow[2] = bMin; decodeRange[2] = bMax - bMin; } //------------------------------------------------------------------------ // GfxICCBasedColorSpace //------------------------------------------------------------------------ GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, Ref *iccProfileStreamA) { nComps = nCompsA; alt = altA; iccProfileStream = *iccProfileStreamA; rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0; rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1; } GfxICCBasedColorSpace::~GfxICCBasedColorSpace() { delete alt; } GfxColorSpace *GfxICCBasedColorSpace::copy() { GfxICCBasedColorSpace *cs; int i; cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream); for (i = 0; i < 4; ++i) { cs->rangeMin[i] = rangeMin[i]; cs->rangeMax[i] = rangeMax[i]; } return cs; } GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) { GfxICCBasedColorSpace *cs; Ref iccProfileStreamA; int nCompsA; GfxColorSpace *altA; Dict *dict; Object obj1, obj2, obj3; int i; if (arr->getLength() < 2) { error(errSyntaxError, -1, "Bad ICCBased color space"); return NULL; } arr->getNF(1, &obj1); if (obj1.isRef()) { iccProfileStreamA = obj1.getRef(); } else { iccProfileStreamA.num = 0; iccProfileStreamA.gen = 0; } obj1.free(); arr->get(1, &obj1); if (!obj1.isStream()) { error(errSyntaxError, -1, "Bad ICCBased color space (stream)"); obj1.free(); return NULL; } dict = obj1.streamGetDict(); if (!dict->lookup("N", &obj2)->isInt()) { error(errSyntaxError, -1, "Bad ICCBased color space (N)"); obj2.free(); obj1.free(); return NULL; } nCompsA = obj2.getInt(); obj2.free(); if (nCompsA > 4) { error(errSyntaxError, -1, "ICCBased color space with too many ({0:d} > 4) components", nCompsA); nCompsA = 4; } if (dict->lookup("Alternate", &obj2)->isNull() || !(altA = GfxColorSpace::parse(&obj2, recursion + 1))) { switch (nCompsA) { case 1: altA = GfxColorSpace::create(csDeviceGray); break; case 3: altA = GfxColorSpace::create(csDeviceRGB); break; case 4: altA = GfxColorSpace::create(csDeviceCMYK); break; default: error(errSyntaxError, -1, "Bad ICCBased color space - invalid N"); obj2.free(); obj1.free(); return NULL; } } obj2.free(); cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA); if (dict->lookup("Range", &obj2)->isArray() && obj2.arrayGetLength() == 2 * nCompsA) { for (i = 0; i < nCompsA; ++i) { obj2.arrayGet(2*i, &obj3); cs->rangeMin[i] = obj3.getNum(); obj3.free(); obj2.arrayGet(2*i+1, &obj3); cs->rangeMax[i] = obj3.getNum(); obj3.free(); } } obj2.free(); obj1.free(); return cs; } void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) { alt->getGray(color, gray); } void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { alt->getRGB(color, rgb); } void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { alt->getCMYK(color, cmyk); } void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) { int i; for (i = 0; i < nComps; ++i) { if (rangeMin[i] > 0) { color->c[i] = dblToCol(rangeMin[i]); } else if (rangeMax[i] < 0) { color->c[i] = dblToCol(rangeMax[i]); } else { color->c[i] = 0; } } } void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) { alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel); #if 0 // this is nominally correct, but some PDF files don't set the // correct ranges in the ICCBased dict int i; for (i = 0; i < nComps; ++i) { decodeLow[i] = rangeMin[i]; decodeRange[i] = rangeMax[i] - rangeMin[i]; } #endif } //------------------------------------------------------------------------ // GfxIndexedColorSpace //------------------------------------------------------------------------ GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA) { base = baseA; indexHigh = indexHighA; lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(), sizeof(Guchar)); overprintMask = base->getOverprintMask(); } GfxIndexedColorSpace::~GfxIndexedColorSpace() { delete base; gfree(lookup); } GfxColorSpace *GfxIndexedColorSpace::copy() { GfxIndexedColorSpace *cs; cs = new GfxIndexedColorSpace(base->copy(), indexHigh); memcpy(cs->lookup, lookup, (indexHigh + 1) * base->getNComps() * sizeof(Guchar)); return cs; } GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) { GfxIndexedColorSpace *cs; GfxColorSpace *baseA; int indexHighA; Object obj1; int x; char *s; int n, i, j; if (arr->getLength() != 4) { error(errSyntaxError, -1, "Bad Indexed color space"); goto err1; } arr->get(1, &obj1); if (!(baseA = GfxColorSpace::parse(&obj1, recursion + 1))) { error(errSyntaxError, -1, "Bad Indexed color space (base color space)"); goto err2; } obj1.free(); if (!arr->get(2, &obj1)->isInt()) { error(errSyntaxError, -1, "Bad Indexed color space (hival)"); delete baseA; goto err2; } indexHighA = obj1.getInt(); if (indexHighA < 0 || indexHighA > 255) { // the PDF spec requires indexHigh to be in [0,255] -- allowing // values larger than 255 creates a security hole: if nComps * // indexHigh is greater than 2^31, the loop below may overwrite // past the end of the array error(errSyntaxError, -1, "Bad Indexed color space (invalid indexHigh value)"); delete baseA; goto err2; } obj1.free(); cs = new GfxIndexedColorSpace(baseA, indexHighA); arr->get(3, &obj1); n = baseA->getNComps(); if (obj1.isStream()) { obj1.streamReset(); for (i = 0; i <= indexHighA; ++i) { for (j = 0; j < n; ++j) { if ((x = obj1.streamGetChar()) == EOF) { error(errSyntaxError, -1, "Bad Indexed color space (lookup table stream too short)"); cs->indexHigh = indexHighA = i - 1; } cs->lookup[i*n + j] = (Guchar)x; } } obj1.streamClose(); } else if (obj1.isString()) { if (obj1.getString()->getLength() < (indexHighA + 1) * n) { error(errSyntaxError, -1, "Bad Indexed color space (lookup table string too short)"); cs->indexHigh = indexHighA = obj1.getString()->getLength() / n - 1; } s = obj1.getString()->getCString(); for (i = 0; i <= indexHighA; ++i) { for (j = 0; j < n; ++j) { cs->lookup[i*n + j] = (Guchar)*s++; } } } else { error(errSyntaxError, -1, "Bad Indexed color space (lookup table)"); goto err3; } obj1.free(); return cs; err3: delete cs; err2: obj1.free(); err1: return NULL; } GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color, GfxColor *baseColor) { Guchar *p; double low[gfxColorMaxComps], range[gfxColorMaxComps]; int n, i; n = base->getNComps(); base->getDefaultRanges(low, range, indexHigh); p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * n]; for (i = 0; i < n; ++i) { baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]); } return baseColor; } void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) { GfxColor color2; base->getGray(mapColorToBase(color, &color2), gray); } void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { GfxColor color2; base->getRGB(mapColorToBase(color, &color2), rgb); } void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { GfxColor color2; base->getCMYK(mapColorToBase(color, &color2), cmyk); } void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = 0; } void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) { decodeLow[0] = 0; decodeRange[0] = maxImgPixel; } //------------------------------------------------------------------------ // GfxSeparationColorSpace //------------------------------------------------------------------------ GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA, Function *funcA) { name = nameA; alt = altA; func = funcA; nonMarking = !name->cmp("None"); if (!name->cmp("Cyan")) { overprintMask = 0x01; } else if (!name->cmp("Magenta")) { overprintMask = 0x02; } else if (!name->cmp("Yellow")) { overprintMask = 0x04; } else if (!name->cmp("Black")) { overprintMask = 0x08; } } GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA, Function *funcA, GBool nonMarkingA, Guint overprintMaskA) { name = nameA; alt = altA; func = funcA; nonMarking = nonMarkingA; overprintMask = overprintMaskA; } GfxSeparationColorSpace::~GfxSeparationColorSpace() { delete name; delete alt; delete func; } GfxColorSpace *GfxSeparationColorSpace::copy() { 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) { GfxSeparationColorSpace *cs; GString *nameA; GfxColorSpace *altA; Function *funcA; Object obj1; if (arr->getLength() != 4) { error(errSyntaxError, -1, "Bad Separation color space"); goto err1; } if (!arr->get(1, &obj1)->isName()) { error(errSyntaxError, -1, "Bad Separation color space (name)"); goto err2; } nameA = new GString(obj1.getName()); obj1.free(); arr->get(2, &obj1); if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) { error(errSyntaxError, -1, "Bad Separation color space (alternate color space)"); goto err3; } obj1.free(); arr->get(3, &obj1); if (!(funcA = Function::parse(&obj1))) { goto err4; } obj1.free(); cs = new GfxSeparationColorSpace(nameA, altA, funcA); return cs; err4: delete altA; err3: delete nameA; err2: obj1.free(); err1: return NULL; } void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) { double x; double c[gfxColorMaxComps]; GfxColor color2; int i; x = colToDbl(color->c[0]); func->transform(&x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getGray(&color2, gray); } void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { double x; double c[gfxColorMaxComps]; GfxColor color2; int i; x = colToDbl(color->c[0]); func->transform(&x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getRGB(&color2, rgb); } void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { double x; double c[gfxColorMaxComps]; GfxColor color2; int i; x = colToDbl(color->c[0]); func->transform(&x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getCMYK(&color2, cmyk); } void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) { color->c[0] = gfxColorComp1; } //------------------------------------------------------------------------ // GfxDeviceNColorSpace //------------------------------------------------------------------------ GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, GString **namesA, GfxColorSpace *altA, Function *funcA) { int i; nComps = nCompsA; alt = altA; func = funcA; nonMarking = gTrue; overprintMask = 0; for (i = 0; i < nComps; ++i) { names[i] = namesA[i]; if (names[i]->cmp("None")) { nonMarking = gFalse; } if (!names[i]->cmp("Cyan")) { overprintMask |= 0x01; } else if (!names[i]->cmp("Magenta")) { overprintMask |= 0x02; } else if (!names[i]->cmp("Yellow")) { overprintMask |= 0x04; } else if (!names[i]->cmp("Black")) { overprintMask |= 0x08; } else { overprintMask = 0x0f; } } } GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, GString **namesA, GfxColorSpace *altA, Function *funcA, GBool nonMarkingA, Guint overprintMaskA) { int i; nComps = nCompsA; alt = altA; func = funcA; nonMarking = nonMarkingA; overprintMask = overprintMaskA; for (i = 0; i < nComps; ++i) { names[i] = namesA[i]->copy(); } } GfxDeviceNColorSpace::~GfxDeviceNColorSpace() { int i; for (i = 0; i < nComps; ++i) { delete names[i]; } delete alt; delete func; } GfxColorSpace *GfxDeviceNColorSpace::copy() { 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) { GfxDeviceNColorSpace *cs; int nCompsA; GString *namesA[gfxColorMaxComps]; GfxColorSpace *altA; Function *funcA; Object obj1, obj2; int i; if (arr->getLength() != 4 && arr->getLength() != 5) { error(errSyntaxError, -1, "Bad DeviceN color space"); goto err1; } if (!arr->get(1, &obj1)->isArray()) { error(errSyntaxError, -1, "Bad DeviceN color space (names)"); goto err2; } nCompsA = obj1.arrayGetLength(); if (nCompsA > gfxColorMaxComps) { error(errSyntaxError, -1, "DeviceN color space with too many ({0:d} > {1:d}) components", nCompsA, gfxColorMaxComps); nCompsA = gfxColorMaxComps; } for (i = 0; i < nCompsA; ++i) { if (!obj1.arrayGet(i, &obj2)->isName()) { error(errSyntaxError, -1, "Bad DeviceN color space (names)"); obj2.free(); goto err2; } namesA[i] = new GString(obj2.getName()); obj2.free(); } obj1.free(); arr->get(2, &obj1); if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) { error(errSyntaxError, -1, "Bad DeviceN color space (alternate color space)"); goto err3; } obj1.free(); arr->get(3, &obj1); if (!(funcA = Function::parse(&obj1))) { goto err4; } obj1.free(); cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA); return cs; err4: delete altA; err3: for (i = 0; i < nCompsA; ++i) { delete namesA[i]; } err2: obj1.free(); err1: return NULL; } void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) { double x[gfxColorMaxComps], c[gfxColorMaxComps]; GfxColor color2; int i; for (i = 0; i < nComps; ++i) { x[i] = colToDbl(color->c[i]); } func->transform(x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getGray(&color2, gray); } void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { double x[gfxColorMaxComps], c[gfxColorMaxComps]; GfxColor color2; int i; for (i = 0; i < nComps; ++i) { x[i] = colToDbl(color->c[i]); } func->transform(x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getRGB(&color2, rgb); } void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { double x[gfxColorMaxComps], c[gfxColorMaxComps]; GfxColor color2; int i; for (i = 0; i < nComps; ++i) { x[i] = colToDbl(color->c[i]); } func->transform(x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getCMYK(&color2, cmyk); } void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) { int i; for (i = 0; i < nComps; ++i) { color->c[i] = gfxColorComp1; } } //------------------------------------------------------------------------ // GfxPatternColorSpace //------------------------------------------------------------------------ GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) { under = underA; } GfxPatternColorSpace::~GfxPatternColorSpace() { if (under) { delete under; } } GfxColorSpace *GfxPatternColorSpace::copy() { GfxPatternColorSpace *cs; cs = new GfxPatternColorSpace(under ? under->copy() : (GfxColorSpace *)NULL); return cs; } GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) { GfxPatternColorSpace *cs; GfxColorSpace *underA; Object obj1; if (arr->getLength() != 1 && arr->getLength() != 2) { error(errSyntaxError, -1, "Bad Pattern color space"); return NULL; } underA = NULL; if (arr->getLength() == 2) { arr->get(1, &obj1); if (!(underA = GfxColorSpace::parse(&obj1, recursion + 1))) { error(errSyntaxError, -1, "Bad Pattern color space (underlying color space)"); obj1.free(); return NULL; } obj1.free(); } cs = new GfxPatternColorSpace(underA); return cs; } void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) { *gray = 0; } void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { rgb->r = rgb->g = rgb->b = 0; } void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { cmyk->c = cmyk->m = cmyk->y = 0; cmyk->k = 1; } void GfxPatternColorSpace::getDefaultColor(GfxColor *color) { // not used } //------------------------------------------------------------------------ // Pattern //------------------------------------------------------------------------ GfxPattern::GfxPattern(int typeA) { type = typeA; } GfxPattern::~GfxPattern() { } GfxPattern *GfxPattern::parse(Object *objRef, Object *obj ) { GfxPattern *pattern; Object typeObj; if (obj->isDict()) { obj->dictLookup("PatternType", &typeObj); } else if (obj->isStream()) { obj->streamGetDict()->lookup("PatternType", &typeObj); } else { return NULL; } pattern = NULL; if (typeObj.isInt() && typeObj.getInt() == 1) { pattern = GfxTilingPattern::parse(objRef, obj); } else if (typeObj.isInt() && typeObj.getInt() == 2) { pattern = GfxShadingPattern::parse(obj ); } typeObj.free(); return pattern; } //------------------------------------------------------------------------ // GfxTilingPattern //------------------------------------------------------------------------ GfxTilingPattern *GfxTilingPattern::parse(Object *patObjRef, Object *patObj) { GfxTilingPattern *pat; Dict *dict; int paintTypeA, tilingTypeA; double bboxA[4], matrixA[6]; double xStepA, yStepA; Object resDictA; Object obj1, obj2; int i; if (!patObj->isStream()) { return NULL; } dict = patObj->streamGetDict(); if (dict->lookup("PaintType", &obj1)->isInt()) { paintTypeA = obj1.getInt(); } else { paintTypeA = 1; error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern"); } obj1.free(); if (dict->lookup("TilingType", &obj1)->isInt()) { tilingTypeA = obj1.getInt(); } else { tilingTypeA = 1; error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern"); } obj1.free(); bboxA[0] = bboxA[1] = 0; bboxA[2] = bboxA[3] = 1; if (dict->lookup("BBox", &obj1)->isArray() && obj1.arrayGetLength() == 4) { for (i = 0; i < 4; ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { bboxA[i] = obj2.getNum(); } obj2.free(); } } else { error(errSyntaxError, -1, "Invalid or missing BBox in pattern"); } obj1.free(); if (dict->lookup("XStep", &obj1)->isNum()) { xStepA = obj1.getNum(); } else { xStepA = 1; error(errSyntaxError, -1, "Invalid or missing XStep in pattern"); } obj1.free(); if (dict->lookup("YStep", &obj1)->isNum()) { yStepA = obj1.getNum(); } else { yStepA = 1; error(errSyntaxError, -1, "Invalid or missing YStep in pattern"); } obj1.free(); if (!dict->lookup("Resources", &resDictA)->isDict()) { resDictA.free(); resDictA.initNull(); error(errSyntaxError, -1, "Invalid or missing Resources in pattern"); } matrixA[0] = 1; matrixA[1] = 0; matrixA[2] = 0; matrixA[3] = 1; matrixA[4] = 0; matrixA[5] = 0; if (dict->lookup("Matrix", &obj1)->isArray() && obj1.arrayGetLength() == 6) { for (i = 0; i < 6; ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { matrixA[i] = obj2.getNum(); } obj2.free(); } } obj1.free(); pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, &resDictA, matrixA, patObjRef); resDictA.free(); return pat; } GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, double *bboxA, double xStepA, double yStepA, Object *resDictA, double *matrixA, Object *contentStreamRefA): GfxPattern(1) { int i; paintType = paintTypeA; tilingType = tilingTypeA; for (i = 0; i < 4; ++i) { bbox[i] = bboxA[i]; } xStep = xStepA; yStep = yStepA; resDictA->copy(&resDict); for (i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } contentStreamRefA->copy(&contentStreamRef); } GfxTilingPattern::~GfxTilingPattern() { resDict.free(); contentStreamRef.free(); } GfxPattern *GfxTilingPattern::copy() { return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, &resDict, matrix, &contentStreamRef); } //------------------------------------------------------------------------ // GfxShadingPattern //------------------------------------------------------------------------ GfxShadingPattern *GfxShadingPattern::parse(Object *patObj ) { Dict *dict; GfxShading *shadingA; double matrixA[6]; Object obj1, obj2; int i; if (!patObj->isDict()) { return NULL; } dict = patObj->getDict(); dict->lookup("Shading", &obj1); shadingA = GfxShading::parse(&obj1 ); obj1.free(); if (!shadingA) { return NULL; } matrixA[0] = 1; matrixA[1] = 0; matrixA[2] = 0; matrixA[3] = 1; matrixA[4] = 0; matrixA[5] = 0; if (dict->lookup("Matrix", &obj1)->isArray() && obj1.arrayGetLength() == 6) { for (i = 0; i < 6; ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { matrixA[i] = obj2.getNum(); } obj2.free(); } } obj1.free(); return new GfxShadingPattern(shadingA, matrixA); } GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA): GfxPattern(2) { int i; shading = shadingA; for (i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } } GfxShadingPattern::~GfxShadingPattern() { delete shading; } GfxPattern *GfxShadingPattern::copy() { return new GfxShadingPattern(shading->copy(), matrix); } //------------------------------------------------------------------------ // GfxShading //------------------------------------------------------------------------ GfxShading::GfxShading(int typeA) { type = typeA; colorSpace = NULL; } GfxShading::GfxShading(GfxShading *shading) { int i; type = shading->type; colorSpace = shading->colorSpace->copy(); for (i = 0; i < gfxColorMaxComps; ++i) { background.c[i] = shading->background.c[i]; } hasBackground = shading->hasBackground; xMin = shading->xMin; yMin = shading->yMin; xMax = shading->xMax; yMax = shading->yMax; hasBBox = shading->hasBBox; } GfxShading::~GfxShading() { if (colorSpace) { delete colorSpace; } } GfxShading *GfxShading::parse(Object *obj ) { GfxShading *shading; Dict *dict; int typeA; Object obj1; if (obj->isDict()) { dict = obj->getDict(); } else if (obj->isStream()) { dict = obj->streamGetDict(); } else { return NULL; } if (!dict->lookup("ShadingType", &obj1)->isInt()) { error(errSyntaxError, -1, "Invalid ShadingType in shading dictionary"); obj1.free(); return NULL; } typeA = obj1.getInt(); obj1.free(); switch (typeA) { case 1: shading = GfxFunctionShading::parse(dict ); break; case 2: shading = GfxAxialShading::parse(dict ); break; case 3: shading = GfxRadialShading::parse(dict ); break; case 4: if (obj->isStream()) { shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream() ); } else { error(errSyntaxError, -1, "Invalid Type 4 shading object"); goto err1; } break; case 5: if (obj->isStream()) { shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream() ); } else { error(errSyntaxError, -1, "Invalid Type 5 shading object"); goto err1; } break; case 6: if (obj->isStream()) { shading = GfxPatchMeshShading::parse(6, dict, obj->getStream() ); } else { error(errSyntaxError, -1, "Invalid Type 6 shading object"); goto err1; } break; case 7: if (obj->isStream()) { shading = GfxPatchMeshShading::parse(7, dict, obj->getStream() ); } else { error(errSyntaxError, -1, "Invalid Type 7 shading object"); goto err1; } break; default: error(errSyntaxError, -1, "Unknown shading type {0:d}", typeA); goto err1; } return shading; err1: return NULL; } GBool GfxShading::init(Dict *dict ) { Object obj1, obj2; int i; dict->lookup("ColorSpace", &obj1); if (!(colorSpace = GfxColorSpace::parse(&obj1 ))) { error(errSyntaxError, -1, "Bad color space in shading dictionary"); obj1.free(); return gFalse; } obj1.free(); for (i = 0; i < gfxColorMaxComps; ++i) { background.c[i] = 0; } hasBackground = gFalse; if (dict->lookup("Background", &obj1)->isArray()) { if (obj1.arrayGetLength() == colorSpace->getNComps()) { hasBackground = gTrue; for (i = 0; i < colorSpace->getNComps(); ++i) { background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum()); obj2.free(); } } else { error(errSyntaxError, -1, "Bad Background in shading dictionary"); } } obj1.free(); xMin = yMin = xMax = yMax = 0; hasBBox = gFalse; if (dict->lookup("BBox", &obj1)->isArray()) { if (obj1.arrayGetLength() == 4) { hasBBox = gTrue; xMin = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); yMin = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); xMax = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); yMax = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); } else { error(errSyntaxError, -1, "Bad BBox in shading dictionary"); } } obj1.free(); return gTrue; } //------------------------------------------------------------------------ // GfxFunctionShading //------------------------------------------------------------------------ GfxFunctionShading::GfxFunctionShading(double x0A, double y0A, double x1A, double y1A, double *matrixA, Function **funcsA, int nFuncsA): GfxShading(1) { int i; x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; for (i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } nFuncs = nFuncsA; for (i = 0; i < nFuncs; ++i) { funcs[i] = funcsA[i]; } } GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading): GfxShading(shading) { int i; x0 = shading->x0; y0 = shading->y0; x1 = shading->x1; y1 = shading->y1; for (i = 0; i < 6; ++i) { matrix[i] = shading->matrix[i]; } nFuncs = shading->nFuncs; for (i = 0; i < nFuncs; ++i) { funcs[i] = shading->funcs[i]->copy(); } } GfxFunctionShading::~GfxFunctionShading() { int i; for (i = 0; i < nFuncs; ++i) { delete funcs[i]; } } GfxFunctionShading *GfxFunctionShading::parse(Dict *dict ) { GfxFunctionShading *shading; double x0A, y0A, x1A, y1A; double matrixA[6]; Function *funcsA[gfxColorMaxComps]; int nFuncsA; Object obj1, obj2; int i; x0A = y0A = 0; x1A = y1A = 1; if (dict->lookup("Domain", &obj1)->isArray() && obj1.arrayGetLength() == 4) { x0A = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); x1A = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); y0A = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); y1A = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); } obj1.free(); matrixA[0] = 1; matrixA[1] = 0; matrixA[2] = 0; matrixA[3] = 1; matrixA[4] = 0; matrixA[5] = 0; if (dict->lookup("Matrix", &obj1)->isArray() && obj1.arrayGetLength() == 6) { matrixA[0] = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); matrixA[1] = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); matrixA[2] = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); matrixA[3] = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); matrixA[4] = obj1.arrayGet(4, &obj2)->getNum(); obj2.free(); matrixA[5] = obj1.arrayGet(5, &obj2)->getNum(); obj2.free(); } obj1.free(); dict->lookup("Function", &obj1); if (obj1.isArray()) { nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxError, -1, "Invalid Function array in shading dictionary"); goto err1; } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); if (!(funcsA[i] = Function::parse(&obj2))) { goto err2; } obj2.free(); } } else { nFuncsA = 1; if (!(funcsA[0] = Function::parse(&obj1))) { goto err1; } } obj1.free(); shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA, funcsA, nFuncsA); if (!shading->init(dict )) { delete shading; return NULL; } return shading; err2: obj2.free(); err1: obj1.free(); return NULL; } GfxShading *GfxFunctionShading::copy() { return new GfxFunctionShading(this); } void GfxFunctionShading::getColor(double x, double y, GfxColor *color) { double in[2], out[gfxColorMaxComps]; int i; // NB: there can be one function with n outputs or n functions with // one output each (where n = number of color components) for (i = 0; i < gfxColorMaxComps; ++i) { out[i] = 0; } in[0] = x; in[1] = y; for (i = 0; i < nFuncs; ++i) { funcs[i]->transform(in, &out[i]); } for (i = 0; i < gfxColorMaxComps; ++i) { color->c[i] = dblToCol(out[i]); } } //------------------------------------------------------------------------ // GfxAxialShading //------------------------------------------------------------------------ GfxAxialShading::GfxAxialShading(double x0A, double y0A, double x1A, double y1A, double t0A, double t1A, Function **funcsA, int nFuncsA, GBool extend0A, GBool extend1A): GfxShading(2) { int i; x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; t0 = t0A; t1 = t1A; nFuncs = nFuncsA; for (i = 0; i < nFuncs; ++i) { funcs[i] = funcsA[i]; } extend0 = extend0A; extend1 = extend1A; } GfxAxialShading::GfxAxialShading(GfxAxialShading *shading): GfxShading(shading) { int i; x0 = shading->x0; y0 = shading->y0; x1 = shading->x1; y1 = shading->y1; t0 = shading->t0; t1 = shading->t1; nFuncs = shading->nFuncs; for (i = 0; i < nFuncs; ++i) { funcs[i] = shading->funcs[i]->copy(); } extend0 = shading->extend0; extend1 = shading->extend1; } GfxAxialShading::~GfxAxialShading() { int i; for (i = 0; i < nFuncs; ++i) { delete funcs[i]; } } GfxAxialShading *GfxAxialShading::parse(Dict *dict ) { GfxAxialShading *shading; double x0A, y0A, x1A, y1A; double t0A, t1A; Function *funcsA[gfxColorMaxComps]; int nFuncsA; GBool extend0A, extend1A; Object obj1, obj2; int i; x0A = y0A = x1A = y1A = 0; if (dict->lookup("Coords", &obj1)->isArray() && obj1.arrayGetLength() == 4) { x0A = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); y0A = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); x1A = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); y1A = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); } else { error(errSyntaxError, -1, "Missing or invalid Coords in shading dictionary"); goto err1; } obj1.free(); t0A = 0; t1A = 1; if (dict->lookup("Domain", &obj1)->isArray() && obj1.arrayGetLength() == 2) { t0A = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); t1A = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); } obj1.free(); dict->lookup("Function", &obj1); if (obj1.isArray()) { nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxError, -1, "Invalid Function array in shading dictionary"); goto err1; } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); if (!(funcsA[i] = Function::parse(&obj2))) { obj1.free(); obj2.free(); goto err1; } obj2.free(); } } else { nFuncsA = 1; if (!(funcsA[0] = Function::parse(&obj1))) { obj1.free(); goto err1; } } obj1.free(); extend0A = extend1A = gFalse; if (dict->lookup("Extend", &obj1)->isArray() && obj1.arrayGetLength() == 2) { extend0A = obj1.arrayGet(0, &obj2)->getBool(); obj2.free(); extend1A = obj1.arrayGet(1, &obj2)->getBool(); obj2.free(); } obj1.free(); shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A); if (!shading->init(dict )) { delete shading; return NULL; } return shading; err1: return NULL; } GfxShading *GfxAxialShading::copy() { return new GfxAxialShading(this); } void GfxAxialShading::getColor(double t, GfxColor *color) { double out[gfxColorMaxComps]; int i; // NB: there can be one function with n outputs or n functions with // one output each (where n = number of color components) for (i = 0; i < gfxColorMaxComps; ++i) { out[i] = 0; } for (i = 0; i < nFuncs; ++i) { funcs[i]->transform(&t, &out[i]); } for (i = 0; i < gfxColorMaxComps; ++i) { color->c[i] = dblToCol(out[i]); } } //------------------------------------------------------------------------ // GfxRadialShading //------------------------------------------------------------------------ GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A, double x1A, double y1A, double r1A, double t0A, double t1A, Function **funcsA, int nFuncsA, GBool extend0A, GBool extend1A): GfxShading(3) { int i; x0 = x0A; y0 = y0A; r0 = r0A; x1 = x1A; y1 = y1A; r1 = r1A; t0 = t0A; t1 = t1A; nFuncs = nFuncsA; for (i = 0; i < nFuncs; ++i) { funcs[i] = funcsA[i]; } extend0 = extend0A; extend1 = extend1A; } GfxRadialShading::GfxRadialShading(GfxRadialShading *shading): GfxShading(shading) { int i; x0 = shading->x0; y0 = shading->y0; r0 = shading->r0; x1 = shading->x1; y1 = shading->y1; r1 = shading->r1; t0 = shading->t0; t1 = shading->t1; nFuncs = shading->nFuncs; for (i = 0; i < nFuncs; ++i) { funcs[i] = shading->funcs[i]->copy(); } extend0 = shading->extend0; extend1 = shading->extend1; } GfxRadialShading::~GfxRadialShading() { int i; for (i = 0; i < nFuncs; ++i) { delete funcs[i]; } } GfxRadialShading *GfxRadialShading::parse(Dict *dict ) { GfxRadialShading *shading; double x0A, y0A, r0A, x1A, y1A, r1A; double t0A, t1A; Function *funcsA[gfxColorMaxComps]; int nFuncsA; GBool extend0A, extend1A; Object obj1, obj2; int i; x0A = y0A = r0A = x1A = y1A = r1A = 0; if (dict->lookup("Coords", &obj1)->isArray() && obj1.arrayGetLength() == 6) { x0A = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); y0A = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); r0A = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); x1A = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); y1A = obj1.arrayGet(4, &obj2)->getNum(); obj2.free(); r1A = obj1.arrayGet(5, &obj2)->getNum(); obj2.free(); } else { error(errSyntaxError, -1, "Missing or invalid Coords in shading dictionary"); goto err1; } obj1.free(); t0A = 0; t1A = 1; if (dict->lookup("Domain", &obj1)->isArray() && obj1.arrayGetLength() == 2) { t0A = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); t1A = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); } obj1.free(); dict->lookup("Function", &obj1); if (obj1.isArray()) { nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxError, -1, "Invalid Function array in shading dictionary"); goto err1; } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); if (!(funcsA[i] = Function::parse(&obj2))) { obj1.free(); obj2.free(); goto err1; } obj2.free(); } } else { nFuncsA = 1; if (!(funcsA[0] = Function::parse(&obj1))) { obj1.free(); goto err1; } } obj1.free(); extend0A = extend1A = gFalse; if (dict->lookup("Extend", &obj1)->isArray() && obj1.arrayGetLength() == 2) { extend0A = obj1.arrayGet(0, &obj2)->getBool(); obj2.free(); extend1A = obj1.arrayGet(1, &obj2)->getBool(); obj2.free(); } obj1.free(); shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A); if (!shading->init(dict )) { delete shading; return NULL; } return shading; err1: return NULL; } GfxShading *GfxRadialShading::copy() { return new GfxRadialShading(this); } void GfxRadialShading::getColor(double t, GfxColor *color) { double out[gfxColorMaxComps]; int i; // NB: there can be one function with n outputs or n functions with // one output each (where n = number of color components) for (i = 0; i < gfxColorMaxComps; ++i) { out[i] = 0; } for (i = 0; i < nFuncs; ++i) { funcs[i]->transform(&t, &out[i]); } for (i = 0; i < gfxColorMaxComps; ++i) { color->c[i] = dblToCol(out[i]); } } //------------------------------------------------------------------------ // GfxShadingBitBuf //------------------------------------------------------------------------ class GfxShadingBitBuf { public: GfxShadingBitBuf(Stream *strA); ~GfxShadingBitBuf(); GBool getBits(int n, Guint *val); void flushBits(); private: Stream *str; int bitBuf; int nBits; }; GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) { str = strA; str->reset(); bitBuf = 0; nBits = 0; } GfxShadingBitBuf::~GfxShadingBitBuf() { str->close(); } GBool GfxShadingBitBuf::getBits(int n, Guint *val) { int x; if (nBits >= n) { x = (bitBuf >> (nBits - n)) & ((1 << n) - 1); nBits -= n; } else { x = 0; if (nBits > 0) { x = bitBuf & ((1 << nBits) - 1); n -= nBits; nBits = 0; } while (n > 0) { if ((bitBuf = str->getChar()) == EOF) { nBits = 0; return gFalse; } if (n >= 8) { x = (x << 8) | bitBuf; n -= 8; } else { x = (x << n) | (bitBuf >> (8 - n)); nBits = 8 - n; n = 0; } } } *val = x; return gTrue; } void GfxShadingBitBuf::flushBits() { bitBuf = 0; nBits = 0; } //------------------------------------------------------------------------ // GfxGouraudTriangleShading //------------------------------------------------------------------------ GfxGouraudTriangleShading::GfxGouraudTriangleShading( int typeA, GfxGouraudVertex *verticesA, int nVerticesA, int (*trianglesA)[3], int nTrianglesA, int nCompsA, Function **funcsA, int nFuncsA): GfxShading(typeA) { int i; vertices = verticesA; nVertices = nVerticesA; triangles = trianglesA; nTriangles = nTrianglesA; nComps = nCompsA; nFuncs = nFuncsA; for (i = 0; i < nFuncs; ++i) { funcs[i] = funcsA[i]; } } GfxGouraudTriangleShading::GfxGouraudTriangleShading( GfxGouraudTriangleShading *shading): GfxShading(shading) { int i; nVertices = shading->nVertices; vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex)); memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex)); nTriangles = shading->nTriangles; triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int)); memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int)); nComps = shading->nComps; nFuncs = shading->nFuncs; for (i = 0; i < nFuncs; ++i) { funcs[i] = shading->funcs[i]->copy(); } } GfxGouraudTriangleShading::~GfxGouraudTriangleShading() { int i; gfree(vertices); gfree(triangles); for (i = 0; i < nFuncs; ++i) { delete funcs[i]; } } GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse( int typeA, Dict *dict, Stream *str ) { GfxGouraudTriangleShading *shading; Function *funcsA[gfxColorMaxComps]; int nFuncsA; int coordBits, compBits, flagBits, vertsPerRow, nRows; double xMin, xMax, yMin, yMax; double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps]; double xMul, yMul; double cMul[gfxColorMaxComps]; GfxGouraudVertex *verticesA; int (*trianglesA)[3]; int nCompsA, nVerticesA, nTrianglesA, vertSize, triSize; Guint x, y, flag; Guint c[gfxColorMaxComps]; GfxShadingBitBuf *bitBuf; Object obj1, obj2; int i, j, k, state; if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) { coordBits = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid BitsPerCoordinate in shading dictionary"); goto err2; } obj1.free(); if (dict->lookup("BitsPerComponent", &obj1)->isInt()) { compBits = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid BitsPerComponent in shading dictionary"); goto err2; } obj1.free(); flagBits = vertsPerRow = 0; // make gcc happy if (typeA == 4) { if (dict->lookup("BitsPerFlag", &obj1)->isInt()) { flagBits = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid BitsPerFlag in shading dictionary"); goto err2; } obj1.free(); } else { if (dict->lookup("VerticesPerRow", &obj1)->isInt()) { vertsPerRow = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid VerticesPerRow in shading dictionary"); goto err2; } obj1.free(); } if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() >= 6) { xMin = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); xMax = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1); yMin = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); yMax = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1); for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) { cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum(); obj2.free(); cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum(); obj2.free(); cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1); } nCompsA = i; } else { error(errSyntaxError, -1, "Missing or invalid Decode array in shading dictionary"); goto err2; } obj1.free(); if (!dict->lookup("Function", &obj1)->isNull()) { if (obj1.isArray()) { nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxError, -1, "Invalid Function array in shading dictionary"); goto err1; } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); if (!(funcsA[i] = Function::parse(&obj2))) { obj1.free(); obj2.free(); goto err1; } obj2.free(); } } else { nFuncsA = 1; if (!(funcsA[0] = Function::parse(&obj1))) { obj1.free(); goto err1; } } } else { nFuncsA = 0; } obj1.free(); nVerticesA = nTrianglesA = 0; verticesA = NULL; trianglesA = NULL; vertSize = triSize = 0; state = 0; flag = 0; // make gcc happy bitBuf = new GfxShadingBitBuf(str); while (1) { if (typeA == 4) { if (!bitBuf->getBits(flagBits, &flag)) { break; } } if (!bitBuf->getBits(coordBits, &x) || !bitBuf->getBits(coordBits, &y)) { break; } for (i = 0; i < nCompsA; ++i) { if (!bitBuf->getBits(compBits, &c[i])) { break; } } if (i < nCompsA) { break; } if (nVerticesA == vertSize) { vertSize = (vertSize == 0) ? 16 : 2 * vertSize; verticesA = (GfxGouraudVertex *) greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex)); } verticesA[nVerticesA].x = xMin + xMul * (double)x; verticesA[nVerticesA].y = yMin + yMul * (double)y; for (i = 0; i < nCompsA; ++i) { verticesA[nVerticesA].color[i] = cMin[i] + cMul[i] * (double)c[i]; } ++nVerticesA; bitBuf->flushBits(); if (typeA == 4) { if (state == 0 || state == 1) { ++state; } else if (state == 2 || flag > 0) { if (nTrianglesA == triSize) { triSize = (triSize == 0) ? 16 : 2 * triSize; trianglesA = (int (*)[3]) greallocn(trianglesA, triSize * 3, sizeof(int)); } if (state == 2) { trianglesA[nTrianglesA][0] = nVerticesA - 3; trianglesA[nTrianglesA][1] = nVerticesA - 2; trianglesA[nTrianglesA][2] = nVerticesA - 1; ++state; } else if (flag == 1) { trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1]; trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2]; trianglesA[nTrianglesA][2] = nVerticesA - 1; } else { // flag == 2 trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0]; trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2]; trianglesA[nTrianglesA][2] = nVerticesA - 1; } ++nTrianglesA; } else { // state == 3 && flag == 0 state = 1; } } } delete bitBuf; if (typeA == 5) { nRows = nVerticesA / vertsPerRow; nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1); trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int)); k = 0; for (i = 0; i < nRows - 1; ++i) { for (j = 0; j < vertsPerRow - 1; ++j) { trianglesA[k][0] = i * vertsPerRow + j; trianglesA[k][1] = i * vertsPerRow + j+1; trianglesA[k][2] = (i+1) * vertsPerRow + j; ++k; trianglesA[k][0] = i * vertsPerRow + j+1; trianglesA[k][1] = (i+1) * vertsPerRow + j; trianglesA[k][2] = (i+1) * vertsPerRow + j+1; ++k; } } } shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA, trianglesA, nTrianglesA, nCompsA, funcsA, nFuncsA); if (!shading->init(dict )) { delete shading; return NULL; } return shading; err2: obj1.free(); err1: return NULL; } GfxShading *GfxGouraudTriangleShading::copy() { return new GfxGouraudTriangleShading(this); } void GfxGouraudTriangleShading::getTriangle( int i, double *x0, double *y0, 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; for (j = 0; j < nComps; ++j) { color0[j] = vertices[v].color[j]; } v = triangles[i][1]; *x1 = vertices[v].x; *y1 = vertices[v].y; 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) { 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]); } } } //------------------------------------------------------------------------ // GfxPatchMeshShading //------------------------------------------------------------------------ GfxPatchMeshShading::GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA, int nCompsA, Function **funcsA, int nFuncsA): GfxShading(typeA) { int i; patches = patchesA; nPatches = nPatchesA; nComps = nCompsA; nFuncs = nFuncsA; for (i = 0; i < nFuncs; ++i) { funcs[i] = funcsA[i]; } } GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading): GfxShading(shading) { int i; nPatches = shading->nPatches; patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch)); memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch)); nComps = shading->nComps; nFuncs = shading->nFuncs; for (i = 0; i < nFuncs; ++i) { funcs[i] = shading->funcs[i]->copy(); } } GfxPatchMeshShading::~GfxPatchMeshShading() { int i; gfree(patches); for (i = 0; i < nFuncs; ++i) { delete funcs[i]; } } GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict, Stream *str ) { GfxPatchMeshShading *shading; Function *funcsA[gfxColorMaxComps]; int nFuncsA; int coordBits, compBits, flagBits; double xMin, xMax, yMin, yMax; double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps]; double xMul, yMul; double cMul[gfxColorMaxComps]; GfxPatch *patchesA, *p; int nCompsA, nPatchesA, patchesSize, nPts, nColors; Guint flag; double x[16], y[16]; Guint xi, yi; double c[4][gfxColorMaxComps]; Guint ci; GfxShadingBitBuf *bitBuf; Object obj1, obj2; int i, j; if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) { coordBits = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid BitsPerCoordinate in shading dictionary"); goto err2; } obj1.free(); if (dict->lookup("BitsPerComponent", &obj1)->isInt()) { compBits = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid BitsPerComponent in shading dictionary"); goto err2; } obj1.free(); if (dict->lookup("BitsPerFlag", &obj1)->isInt()) { flagBits = obj1.getInt(); } else { error(errSyntaxError, -1, "Missing or invalid BitsPerFlag in shading dictionary"); goto err2; } obj1.free(); if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() >= 6) { xMin = obj1.arrayGet(0, &obj2)->getNum(); obj2.free(); xMax = obj1.arrayGet(1, &obj2)->getNum(); obj2.free(); xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1); yMin = obj1.arrayGet(2, &obj2)->getNum(); obj2.free(); yMax = obj1.arrayGet(3, &obj2)->getNum(); obj2.free(); yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1); for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) { cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum(); obj2.free(); cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum(); obj2.free(); cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1); } nCompsA = i; } else { error(errSyntaxError, -1, "Missing or invalid Decode array in shading dictionary"); goto err2; } obj1.free(); if (!dict->lookup("Function", &obj1)->isNull()) { if (obj1.isArray()) { nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxError, -1, "Invalid Function array in shading dictionary"); goto err1; } for (i = 0; i < nFuncsA; ++i) { obj1.arrayGet(i, &obj2); if (!(funcsA[i] = Function::parse(&obj2))) { obj1.free(); obj2.free(); goto err1; } obj2.free(); } } else { nFuncsA = 1; if (!(funcsA[0] = Function::parse(&obj1))) { obj1.free(); goto err1; } } } else { nFuncsA = 0; } obj1.free(); nPatchesA = 0; patchesA = NULL; patchesSize = 0; bitBuf = new GfxShadingBitBuf(str); while (1) { if (!bitBuf->getBits(flagBits, &flag)) { break; } if (typeA == 6) { switch (flag) { case 0: nPts = 12; nColors = 4; break; case 1: case 2: case 3: default: nPts = 8; nColors = 2; break; } } else { switch (flag) { case 0: nPts = 16; nColors = 4; break; case 1: case 2: case 3: default: nPts = 12; nColors = 2; break; } } for (i = 0; i < nPts; ++i) { if (!bitBuf->getBits(coordBits, &xi) || !bitBuf->getBits(coordBits, &yi)) { break; } x[i] = xMin + xMul * (double)xi; y[i] = yMin + yMul * (double)yi; } if (i < nPts) { break; } for (i = 0; i < nColors; ++i) { for (j = 0; j < nCompsA; ++j) { if (!bitBuf->getBits(compBits, &ci)) { break; } c[i][j] = cMin[j] + cMul[j] * (double)ci; } if (j < nCompsA) { break; } } if (i < nColors) { break; } if (nPatchesA == patchesSize) { patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize; patchesA = (GfxPatch *)greallocn(patchesA, patchesSize, sizeof(GfxPatch)); } p = &patchesA[nPatchesA]; if (typeA == 6) { switch (flag) { case 0: p->x[0][0] = x[0]; p->y[0][0] = y[0]; p->x[0][1] = x[1]; p->y[0][1] = y[1]; p->x[0][2] = x[2]; p->y[0][2] = y[2]; p->x[0][3] = x[3]; p->y[0][3] = y[3]; p->x[1][3] = x[4]; p->y[1][3] = y[4]; p->x[2][3] = x[5]; p->y[2][3] = y[5]; p->x[3][3] = x[6]; p->y[3][3] = y[6]; p->x[3][2] = x[7]; p->y[3][2] = y[7]; p->x[3][1] = x[8]; p->y[3][1] = y[8]; p->x[3][0] = x[9]; p->y[3][0] = y[9]; p->x[2][0] = x[10]; p->y[2][0] = y[10]; p->x[1][0] = x[11]; p->y[1][0] = y[11]; for (j = 0; j < 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: p->x[0][0] = patchesA[nPatchesA-1].x[0][3]; p->y[0][0] = patchesA[nPatchesA-1].y[0][3]; p->x[0][1] = patchesA[nPatchesA-1].x[1][3]; p->y[0][1] = patchesA[nPatchesA-1].y[1][3]; p->x[0][2] = patchesA[nPatchesA-1].x[2][3]; p->y[0][2] = patchesA[nPatchesA-1].y[2][3]; p->x[0][3] = patchesA[nPatchesA-1].x[3][3]; p->y[0][3] = patchesA[nPatchesA-1].y[3][3]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; for (j = 0; j < 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: p->x[0][0] = patchesA[nPatchesA-1].x[3][3]; p->y[0][0] = patchesA[nPatchesA-1].y[3][3]; p->x[0][1] = patchesA[nPatchesA-1].x[3][2]; p->y[0][1] = patchesA[nPatchesA-1].y[3][2]; p->x[0][2] = patchesA[nPatchesA-1].x[3][1]; p->y[0][2] = patchesA[nPatchesA-1].y[3][1]; p->x[0][3] = patchesA[nPatchesA-1].x[3][0]; p->y[0][3] = patchesA[nPatchesA-1].y[3][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; for (j = 0; j < 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: p->x[0][0] = patchesA[nPatchesA-1].x[3][0]; p->y[0][0] = patchesA[nPatchesA-1].y[3][0]; p->x[0][1] = patchesA[nPatchesA-1].x[2][0]; p->y[0][1] = patchesA[nPatchesA-1].y[2][0]; p->x[0][2] = patchesA[nPatchesA-1].x[1][0]; p->y[0][2] = patchesA[nPatchesA-1].y[1][0]; p->x[0][3] = patchesA[nPatchesA-1].x[0][0]; p->y[0][3] = patchesA[nPatchesA-1].y[0][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; for (j = 0; j < 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; } } else { switch (flag) { case 0: p->x[0][0] = x[0]; p->y[0][0] = y[0]; p->x[0][1] = x[1]; p->y[0][1] = y[1]; p->x[0][2] = x[2]; p->y[0][2] = y[2]; p->x[0][3] = x[3]; p->y[0][3] = y[3]; p->x[1][3] = x[4]; p->y[1][3] = y[4]; p->x[2][3] = x[5]; p->y[2][3] = y[5]; p->x[3][3] = x[6]; p->y[3][3] = y[6]; p->x[3][2] = x[7]; p->y[3][2] = y[7]; p->x[3][1] = x[8]; p->y[3][1] = y[8]; p->x[3][0] = x[9]; p->y[3][0] = y[9]; p->x[2][0] = x[10]; p->y[2][0] = y[10]; p->x[1][0] = x[11]; p->y[1][0] = y[11]; p->x[1][1] = x[12]; p->y[1][1] = y[12]; p->x[1][2] = x[13]; p->y[1][2] = y[13]; p->x[2][2] = x[14]; p->y[2][2] = y[14]; p->x[2][1] = x[15]; p->y[2][1] = y[15]; for (j = 0; j < 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: p->x[0][0] = patchesA[nPatchesA-1].x[0][3]; p->y[0][0] = patchesA[nPatchesA-1].y[0][3]; p->x[0][1] = patchesA[nPatchesA-1].x[1][3]; p->y[0][1] = patchesA[nPatchesA-1].y[1][3]; p->x[0][2] = patchesA[nPatchesA-1].x[2][3]; p->y[0][2] = patchesA[nPatchesA-1].y[2][3]; p->x[0][3] = patchesA[nPatchesA-1].x[3][3]; p->y[0][3] = patchesA[nPatchesA-1].y[3][3]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; p->x[1][1] = x[8]; p->y[1][1] = y[8]; p->x[1][2] = x[9]; p->y[1][2] = y[9]; p->x[2][2] = x[10]; p->y[2][2] = y[10]; p->x[2][1] = x[11]; p->y[2][1] = y[11]; for (j = 0; j < 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: p->x[0][0] = patchesA[nPatchesA-1].x[3][3]; p->y[0][0] = patchesA[nPatchesA-1].y[3][3]; p->x[0][1] = patchesA[nPatchesA-1].x[3][2]; p->y[0][1] = patchesA[nPatchesA-1].y[3][2]; p->x[0][2] = patchesA[nPatchesA-1].x[3][1]; p->y[0][2] = patchesA[nPatchesA-1].y[3][1]; p->x[0][3] = patchesA[nPatchesA-1].x[3][0]; p->y[0][3] = patchesA[nPatchesA-1].y[3][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; p->x[1][1] = x[8]; p->y[1][1] = y[8]; p->x[1][2] = x[9]; p->y[1][2] = y[9]; p->x[2][2] = x[10]; p->y[2][2] = y[10]; p->x[2][1] = x[11]; p->y[2][1] = y[11]; for (j = 0; j < 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: p->x[0][0] = patchesA[nPatchesA-1].x[3][0]; p->y[0][0] = patchesA[nPatchesA-1].y[3][0]; p->x[0][1] = patchesA[nPatchesA-1].x[2][0]; p->y[0][1] = patchesA[nPatchesA-1].y[2][0]; p->x[0][2] = patchesA[nPatchesA-1].x[1][0]; p->y[0][2] = patchesA[nPatchesA-1].y[1][0]; p->x[0][3] = patchesA[nPatchesA-1].x[0][0]; p->y[0][3] = patchesA[nPatchesA-1].y[0][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; p->x[1][1] = x[8]; p->y[1][1] = y[8]; p->x[1][2] = x[9]; p->y[1][2] = y[9]; p->x[2][2] = x[10]; p->y[2][2] = y[10]; p->x[2][1] = x[11]; p->y[2][1] = y[11]; for (j = 0; j < 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; } } ++nPatchesA; bitBuf->flushBits(); } delete bitBuf; if (typeA == 6) { for (i = 0; i < nPatchesA; ++i) { p = &patchesA[i]; p->x[1][1] = (-4 * p->x[0][0] +6 * (p->x[0][1] + p->x[1][0]) -2 * (p->x[0][3] + p->x[3][0]) +3 * (p->x[3][1] + p->x[1][3]) - p->x[3][3]) / 9; p->y[1][1] = (-4 * p->y[0][0] +6 * (p->y[0][1] + p->y[1][0]) -2 * (p->y[0][3] + p->y[3][0]) +3 * (p->y[3][1] + p->y[1][3]) - p->y[3][3]) / 9; p->x[1][2] = (-4 * p->x[0][3] +6 * (p->x[0][2] + p->x[1][3]) -2 * (p->x[0][0] + p->x[3][3]) +3 * (p->x[3][2] + p->x[1][0]) - p->x[3][0]) / 9; p->y[1][2] = (-4 * p->y[0][3] +6 * (p->y[0][2] + p->y[1][3]) -2 * (p->y[0][0] + p->y[3][3]) +3 * (p->y[3][2] + p->y[1][0]) - p->y[3][0]) / 9; p->x[2][1] = (-4 * p->x[3][0] +6 * (p->x[3][1] + p->x[2][0]) -2 * (p->x[3][3] + p->x[0][0]) +3 * (p->x[0][1] + p->x[2][3]) - p->x[0][3]) / 9; p->y[2][1] = (-4 * p->y[3][0] +6 * (p->y[3][1] + p->y[2][0]) -2 * (p->y[3][3] + p->y[0][0]) +3 * (p->y[0][1] + p->y[2][3]) - p->y[0][3]) / 9; p->x[2][2] = (-4 * p->x[3][3] +6 * (p->x[3][2] + p->x[2][3]) -2 * (p->x[3][0] + p->x[0][3]) +3 * (p->x[0][2] + p->x[2][0]) - p->x[0][0]) / 9; p->y[2][2] = (-4 * p->y[3][3] +6 * (p->y[3][2] + p->y[2][3]) -2 * (p->y[3][0] + p->y[0][3]) +3 * (p->y[0][2] + p->y[2][0]) - p->y[0][0]) / 9; } } shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA, nCompsA, funcsA, nFuncsA); if (!shading->init(dict )) { delete shading; return NULL; } return shading; err2: obj1.free(); err1: return NULL; } GfxShading *GfxPatchMeshShading::copy() { return new GfxPatchMeshShading(this); } 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 //------------------------------------------------------------------------ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA) { GfxIndexedColorSpace *indexedCS; GfxSeparationColorSpace *sepCS; int maxPixel, indexHigh; Guchar *indexedLookup; Function *sepFunc; Object obj; double x[gfxColorMaxComps]; double y[gfxColorMaxComps]; int i, j, k; ok = gTrue; // bits per component and color space bits = bitsA; if (bits <= 8) { maxPixel = (1 << bits) - 1; } else { maxPixel = 0xff; } colorSpace = colorSpaceA; // initialize for (k = 0; k < gfxColorMaxComps; ++k) { lookup[k] = NULL; lookup2[k] = NULL; } // get decode map if (decode->isNull()) { nComps = colorSpace->getNComps(); colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel); } else if (decode->isArray()) { nComps = decode->arrayGetLength() / 2; if (nComps < colorSpace->getNComps()) { goto err1; } if (nComps > colorSpace->getNComps()) { error(errSyntaxWarning, -1, "Too many elements in Decode array"); nComps = colorSpace->getNComps(); } for (i = 0; i < nComps; ++i) { decode->arrayGet(2*i, &obj); if (!obj.isNum()) { goto err2; } decodeLow[i] = obj.getNum(); obj.free(); decode->arrayGet(2*i+1, &obj); if (!obj.isNum()) { goto err2; } decodeRange[i] = obj.getNum() - decodeLow[i]; obj.free(); } } else { goto err1; } // Construct a lookup table -- this stores pre-computed decoded // values for each component, i.e., the result of applying the // decode mapping to each possible image pixel component value. for (k = 0; k < nComps; ++k) { lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); for (i = 0; i <= maxPixel; ++i) { lookup[k][i] = dblToCol(decodeLow[k] + (i * decodeRange[k]) / maxPixel); } } // Optimization: for Indexed and Separation color spaces (which have // only one component), we pre-compute a second lookup table with // color values colorSpace2 = NULL; nComps2 = 0; if (colorSpace->getMode() == csIndexed) { // Note that indexHigh may not be the same as maxPixel -- // Distiller will remove unused palette entries, resulting in // indexHigh < maxPixel. indexedCS = (GfxIndexedColorSpace *)colorSpace; colorSpace2 = indexedCS->getBase(); indexHigh = indexedCS->getIndexHigh(); nComps2 = colorSpace2->getNComps(); indexedLookup = indexedCS->getLookup(); colorSpace2->getDefaultRanges(x, y, indexHigh); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); } for (i = 0; i <= maxPixel; ++i) { j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5); if (j < 0) { j = 0; } else if (j > indexHigh) { j = indexHigh; } for (k = 0; k < nComps2; ++k) { lookup2[k][i] = dblToCol(x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k]); } } } else if (colorSpace->getMode() == csSeparation) { sepCS = (GfxSeparationColorSpace *)colorSpace; colorSpace2 = sepCS->getAlt(); nComps2 = colorSpace2->getNComps(); sepFunc = sepCS->getFunc(); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); } for (i = 0; i <= maxPixel; ++i) { x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; sepFunc->transform(x, y); for (k = 0; k < nComps2; ++k) { lookup2[k][i] = dblToCol(y[k]); } } } return; err2: obj.free(); err1: ok = gFalse; } GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) { int n, i, k; colorSpace = colorMap->colorSpace->copy(); bits = colorMap->bits; nComps = colorMap->nComps; nComps2 = colorMap->nComps2; colorSpace2 = NULL; for (k = 0; k < gfxColorMaxComps; ++k) { lookup[k] = NULL; lookup2[k] = NULL; } 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)); } if (colorSpace->getMode() == csIndexed) { colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase(); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); } } else if (colorSpace->getMode() == csSeparation) { colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt(); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); } } for (i = 0; i < nComps; ++i) { decodeLow[i] = colorMap->decodeLow[i]; decodeRange[i] = colorMap->decodeRange[i]; } ok = gTrue; } GfxImageColorMap::~GfxImageColorMap() { int i; delete colorSpace; for (i = 0; i < gfxColorMaxComps; ++i) { gfree(lookup[i]); gfree(lookup2[i]); } } void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) { GfxColor color; int i; if (colorSpace2) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getGray(&color, gray); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][x[i]]; } colorSpace->getGray(&color, gray); } } void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) { GfxColor color; int i; if (colorSpace2) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getRGB(&color, rgb); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][x[i]]; } colorSpace->getRGB(&color, rgb); } } void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) { GfxColor color; int i; if (colorSpace2) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getCMYK(&color, cmyk); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][x[i]]; } colorSpace->getCMYK(&color, cmyk); } } void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) { int maxPixel, i; 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); } } void GfxImageColorMap::getGrayByteLine(Guchar *in, Guchar *out, int n) { GfxColor color; GfxGray gray; int i, j; if (colorSpace2) { for (j = 0; j < n; ++j) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][in[j]]; } colorSpace2->getGray(&color, &gray); out[j] = colToByte(gray); } } else { for (j = 0; j < n; ++j) { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][in[j * nComps + i]]; } colorSpace->getGray(&color, &gray); out[j] = colToByte(gray); } } } void GfxImageColorMap::getRGBByteLine(Guchar *in, Guchar *out, int n) { GfxColor color; GfxRGB rgb; int i, j; if (colorSpace2) { for (j = 0; j < n; ++j) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][in[j]]; } colorSpace2->getRGB(&color, &rgb); out[j*3] = colToByte(rgb.r); out[j*3 + 1] = colToByte(rgb.g); out[j*3 + 2] = colToByte(rgb.b); } } else { for (j = 0; j < n; ++j) { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][in[j * nComps + i]]; } colorSpace->getRGB(&color, &rgb); out[j*3] = colToByte(rgb.r); out[j*3 + 1] = colToByte(rgb.g); out[j*3 + 2] = colToByte(rgb.b); } } } void GfxImageColorMap::getCMYKByteLine(Guchar *in, Guchar *out, int n) { GfxColor color; GfxCMYK cmyk; int i, j; if (colorSpace2) { for (j = 0; j < n; ++j) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][in[j]]; } colorSpace2->getCMYK(&color, &cmyk); out[j*4] = colToByte(cmyk.c); out[j*4 + 1] = colToByte(cmyk.m); out[j*4 + 2] = colToByte(cmyk.y); out[j*4 + 3] = colToByte(cmyk.k); } } else { for (j = 0; j < n; ++j) { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][in[j * nComps + i]]; } colorSpace->getCMYK(&color, &cmyk); out[j*4] = colToByte(cmyk.c); out[j*4 + 1] = colToByte(cmyk.m); out[j*4 + 2] = colToByte(cmyk.y); out[j*4 + 3] = colToByte(cmyk.k); } } } //------------------------------------------------------------------------ // GfxSubpath and GfxPath //------------------------------------------------------------------------ GfxSubpath::GfxSubpath(double x1, double y1) { size = 16; x = (double *)gmallocn(size, sizeof(double)); y = (double *)gmallocn(size, sizeof(double)); curve = (GBool *)gmallocn(size, sizeof(GBool)); n = 1; x[0] = x1; y[0] = y1; curve[0] = gFalse; closed = gFalse; } GfxSubpath::~GfxSubpath() { gfree(x); gfree(y); gfree(curve); } // Used for copy(). GfxSubpath::GfxSubpath(GfxSubpath *subpath) { size = subpath->size; n = subpath->n; x = (double *)gmallocn(size, sizeof(double)); y = (double *)gmallocn(size, sizeof(double)); curve = (GBool *)gmallocn(size, sizeof(GBool)); memcpy(x, subpath->x, n * sizeof(double)); memcpy(y, subpath->y, n * sizeof(double)); memcpy(curve, subpath->curve, n * sizeof(GBool)); closed = subpath->closed; } void GfxSubpath::lineTo(double x1, double y1) { if (n >= size) { size *= 2; x = (double *)greallocn(x, size, sizeof(double)); y = (double *)greallocn(y, size, sizeof(double)); curve = (GBool *)greallocn(curve, size, sizeof(GBool)); } x[n] = x1; y[n] = y1; curve[n] = gFalse; ++n; } void GfxSubpath::curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (n+3 > size) { size *= 2; x = (double *)greallocn(x, size, sizeof(double)); y = (double *)greallocn(y, size, sizeof(double)); curve = (GBool *)greallocn(curve, size, sizeof(GBool)); } x[n] = x1; y[n] = y1; x[n+1] = x2; y[n+1] = y2; x[n+2] = x3; y[n+2] = y3; curve[n] = curve[n+1] = gTrue; curve[n+2] = gFalse; n += 3; } void GfxSubpath::close() { if (x[n-1] != x[0] || y[n-1] != y[0]) { lineTo(x[0], y[0]); } closed = gTrue; } void GfxSubpath::offset(double dx, double dy) { int i; for (i = 0; i < n; ++i) { x[i] += dx; y[i] += dy; } } GfxPath::GfxPath() { justMoved = gFalse; size = 16; n = 0; firstX = firstY = 0; subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *)); } GfxPath::~GfxPath() { int i; for (i = 0; i < n; ++i) delete subpaths[i]; gfree(subpaths); } // Used for copy(). GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1, GfxSubpath **subpaths1, int n1, int size1) { int i; justMoved = justMoved1; firstX = firstX1; firstY = firstY1; size = size1; n = n1; subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *)); for (i = 0; i < n; ++i) subpaths[i] = subpaths1[i]->copy(); } void GfxPath::moveTo(double x, double y) { justMoved = gTrue; firstX = x; firstY = y; } void GfxPath::lineTo(double x, double y) { if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) { if (n >= size) { size *= 2; subpaths = (GfxSubpath **) greallocn(subpaths, size, sizeof(GfxSubpath *)); } if (justMoved) { subpaths[n] = new GfxSubpath(firstX, firstY); } else { subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(), subpaths[n-1]->getLastY()); } ++n; justMoved = gFalse; } subpaths[n-1]->lineTo(x, y); } void GfxPath::curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) { if (n >= size) { size *= 2; subpaths = (GfxSubpath **) greallocn(subpaths, size, sizeof(GfxSubpath *)); } if (justMoved) { subpaths[n] = new GfxSubpath(firstX, firstY); } else { subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(), subpaths[n-1]->getLastY()); } ++n; justMoved = gFalse; } subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3); } void GfxPath::close() { // this is necessary to handle the pathological case of // moveto/closepath/clip, which defines an empty clipping region if (justMoved) { if (n >= size) { size *= 2; subpaths = (GfxSubpath **) greallocn(subpaths, size, sizeof(GfxSubpath *)); } subpaths[n] = new GfxSubpath(firstX, firstY); ++n; justMoved = gFalse; } subpaths[n-1]->close(); } void GfxPath::append(GfxPath *path) { int i; if (n + path->n > size) { size = n + path->n; subpaths = (GfxSubpath **) greallocn(subpaths, size, sizeof(GfxSubpath *)); } for (i = 0; i < path->n; ++i) { subpaths[n++] = path->subpaths[i]->copy(); } justMoved = gFalse; } void GfxPath::offset(double dx, double dy) { int i; for (i = 0; i < n; ++i) { subpaths[i]->offset(dx, dy); } } //------------------------------------------------------------------------ // GfxState //------------------------------------------------------------------------ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox, int rotateA, GBool upsideDown ) { double kx, ky; hDPI = hDPIA; vDPI = vDPIA; rotate = rotateA; px1 = pageBox->x1; py1 = pageBox->y1; px2 = pageBox->x2; py2 = pageBox->y2; kx = hDPI / 72.0; ky = vDPI / 72.0; if (rotate == 90) { ctm[0] = 0; ctm[1] = upsideDown ? ky : -ky; ctm[2] = kx; ctm[3] = 0; ctm[4] = -kx * py1; ctm[5] = ky * (upsideDown ? -px1 : px2); pageWidth = kx * (py2 - py1); pageHeight = ky * (px2 - px1); } else if (rotate == 180) { ctm[0] = -kx; ctm[1] = 0; ctm[2] = 0; ctm[3] = upsideDown ? ky : -ky; ctm[4] = kx * px2; ctm[5] = ky * (upsideDown ? -py1 : py2); pageWidth = kx * (px2 - px1); pageHeight = ky * (py2 - py1); } else if (rotate == 270) { ctm[0] = 0; ctm[1] = upsideDown ? -ky : ky; ctm[2] = -kx; ctm[3] = 0; ctm[4] = kx * py2; ctm[5] = ky * (upsideDown ? px2 : -px1); pageWidth = kx * (py2 - py1); pageHeight = ky * (px2 - px1); } else { ctm[0] = kx; ctm[1] = 0; ctm[2] = 0; ctm[3] = upsideDown ? -ky : ky; ctm[4] = -kx * px1; ctm[5] = ky * (upsideDown ? py2 : -py1); pageWidth = kx * (px2 - px1); pageHeight = ky * (py2 - py1); } fillColorSpace = GfxColorSpace::create(csDeviceGray); strokeColorSpace = GfxColorSpace::create(csDeviceGray); fillColor.c[0] = 0; strokeColor.c[0] = 0; fillPattern = NULL; strokePattern = NULL; blendMode = gfxBlendNormal; fillOpacity = 1; strokeOpacity = 1; fillOverprint = gFalse; strokeOverprint = gFalse; overprintMode = 0; transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL; lineWidth = 1; lineDash = NULL; lineDashLength = 0; lineDashStart = 0; flatness = 1; lineJoin = 0; lineCap = 0; miterLimit = 10; strokeAdjust = gFalse; font = NULL; fontSize = 0; textMat[0] = 1; textMat[1] = 0; textMat[2] = 0; textMat[3] = 1; textMat[4] = 0; textMat[5] = 0; charSpace = 0; wordSpace = 0; horizScaling = 1; leading = 0; rise = 0; render = 0; path = new GfxPath(); curX = curY = 0; lineX = lineY = 0; clipXMin = 0; clipYMin = 0; clipXMax = pageWidth; clipYMax = pageHeight; saved = NULL; } GfxState::~GfxState() { int i; if (fillColorSpace) { delete fillColorSpace; } if (strokeColorSpace) { delete strokeColorSpace; } if (fillPattern) { delete fillPattern; } if (strokePattern) { delete strokePattern; } for (i = 0; i < 4; ++i) { if (transfer[i]) { delete transfer[i]; } } gfree(lineDash); if (path) { // this gets set to NULL by restore() delete path; } } // Used for copy(); GfxState::GfxState(GfxState *state, GBool copyPath) { int i; memcpy(this, state, sizeof(GfxState)); if (fillColorSpace) { fillColorSpace = state->fillColorSpace->copy(); } if (strokeColorSpace) { strokeColorSpace = state->strokeColorSpace->copy(); } if (fillPattern) { fillPattern = state->fillPattern->copy(); } if (strokePattern) { strokePattern = state->strokePattern->copy(); } for (i = 0; i < 4; ++i) { if (transfer[i]) { transfer[i] = state->transfer[i]->copy(); } } if (lineDashLength > 0) { lineDash = (double *)gmallocn(lineDashLength, sizeof(double)); memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double)); } if (copyPath) { path = state->path->copy(); } saved = NULL; } void GfxState::setPath(GfxPath *pathA) { delete path; path = pathA; } void GfxState::getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax) { double ictm[6]; double xMin1, yMin1, xMax1, yMax1, det, tx, ty; // invert the CTM det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); ictm[0] = ctm[3] * det; ictm[1] = -ctm[1] * det; ictm[2] = -ctm[2] * det; ictm[3] = ctm[0] * det; ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; // transform all four corners of the clip bbox; find the min and max // x and y values xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4]; yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5]; tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4]; ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5]; if (tx < xMin1) { xMin1 = tx; } else if (tx > xMax1) { xMax1 = tx; } if (ty < yMin1) { yMin1 = ty; } else if (ty > yMax1) { yMax1 = ty; } tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4]; ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5]; if (tx < xMin1) { xMin1 = tx; } else if (tx > xMax1) { xMax1 = tx; } if (ty < yMin1) { yMin1 = ty; } else if (ty > yMax1) { yMax1 = ty; } tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4]; ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5]; if (tx < xMin1) { xMin1 = tx; } else if (tx > xMax1) { xMax1 = tx; } if (ty < yMin1) { yMin1 = ty; } else if (ty > yMax1) { yMax1 = ty; } *xMin = xMin1; *yMin = yMin1; *xMax = xMax1; *yMax = yMax1; } double GfxState::transformWidth(double w) { double x, y; x = ctm[0] + ctm[2]; y = ctm[1] + ctm[3]; return w * sqrt(0.5 * (x * x + y * y)); } double GfxState::getTransformedFontSize() { double x1, y1, x2, y2; x1 = textMat[2] * fontSize; y1 = textMat[3] * fontSize; x2 = ctm[0] * x1 + ctm[2] * y1; y2 = ctm[1] * x1 + ctm[3] * y1; return sqrt(x2 * x2 + y2 * y2); } void GfxState::getFontTransMat(double *m11, double *m12, double *m21, double *m22) { *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize; *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize; *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize; *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize; } void GfxState::setCTM(double a, double b, double c, double d, double e, double f) { int i; ctm[0] = a; ctm[1] = b; ctm[2] = c; ctm[3] = d; ctm[4] = e; ctm[5] = f; // avoid FP exceptions on badly messed up PDF files for (i = 0; i < 6; ++i) { if (ctm[i] > 1e10) { ctm[i] = 1e10; } else if (ctm[i] < -1e10) { ctm[i] = -1e10; } } } void GfxState::concatCTM(double a, double b, double c, double d, double e, double f) { double a1 = ctm[0]; double b1 = ctm[1]; double c1 = ctm[2]; double d1 = ctm[3]; int i; ctm[0] = a * a1 + b * c1; ctm[1] = a * b1 + b * d1; ctm[2] = c * a1 + d * c1; ctm[3] = c * b1 + d * d1; ctm[4] = e * a1 + f * c1 + ctm[4]; ctm[5] = e * b1 + f * d1 + ctm[5]; // avoid FP exceptions on badly messed up PDF files for (i = 0; i < 6; ++i) { if (ctm[i] > 1e10) { ctm[i] = 1e10; } else if (ctm[i] < -1e10) { ctm[i] = -1e10; } } } void GfxState::shiftCTM(double tx, double ty) { ctm[4] += tx; ctm[5] += ty; clipXMin += tx; clipYMin += ty; clipXMax += tx; clipYMax += ty; } void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) { if (fillColorSpace) { delete fillColorSpace; } fillColorSpace = colorSpace; } void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) { if (strokeColorSpace) { delete strokeColorSpace; } strokeColorSpace = colorSpace; } void GfxState::setFillPattern(GfxPattern *pattern) { if (fillPattern) { delete fillPattern; } fillPattern = pattern; } void GfxState::setStrokePattern(GfxPattern *pattern) { if (strokePattern) { delete strokePattern; } strokePattern = pattern; } void GfxState::setTransfer(Function **funcs) { int i; for (i = 0; i < 4; ++i) { if (transfer[i]) { delete transfer[i]; } transfer[i] = funcs[i]; } } void GfxState::setLineDash(double *dash, int length, double start) { if (lineDash) gfree(lineDash); lineDash = dash; lineDashLength = length; lineDashStart = start; } void GfxState::clearPath() { delete path; path = new GfxPath(); } void GfxState::clip() { double xMin, yMin, xMax, yMax, x, y; GfxSubpath *subpath; int i, j; xMin = xMax = yMin = yMax = 0; // make gcc happy for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); for (j = 0; j < subpath->getNumPoints(); ++j) { transform(subpath->getX(j), subpath->getY(j), &x, &y); if (i == 0 && j == 0) { xMin = xMax = x; yMin = yMax = y; } else { if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } } } } if (xMin > clipXMin) { clipXMin = xMin; } if (yMin > clipYMin) { clipYMin = yMin; } if (xMax < clipXMax) { clipXMax = xMax; } if (yMax < clipYMax) { clipYMax = yMax; } } void GfxState::clipToStrokePath() { double xMin, yMin, xMax, yMax, x, y, t0, t1; GfxSubpath *subpath; int i, j; xMin = xMax = yMin = yMax = 0; // make gcc happy for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); for (j = 0; j < subpath->getNumPoints(); ++j) { transform(subpath->getX(j), subpath->getY(j), &x, &y); if (i == 0 && j == 0) { xMin = xMax = x; yMin = yMax = y; } else { if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } } } } // allow for the line width //~ miter joins can extend farther than this t0 = fabs(ctm[0]); t1 = fabs(ctm[2]); if (t0 > t1) { xMin -= 0.5 * lineWidth * t0; xMax += 0.5 * lineWidth * t0; } else { xMin -= 0.5 * lineWidth * t1; xMax += 0.5 * lineWidth * t1; } t0 = fabs(ctm[0]); t1 = fabs(ctm[3]); if (t0 > t1) { yMin -= 0.5 * lineWidth * t0; yMax += 0.5 * lineWidth * t0; } else { yMin -= 0.5 * lineWidth * t1; yMax += 0.5 * lineWidth * t1; } if (xMin > clipXMin) { clipXMin = xMin; } if (yMin > clipYMin) { clipYMin = yMin; } if (xMax < clipXMax) { clipXMax = xMax; } if (yMax < clipYMax) { clipYMax = yMax; } } void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) { double x, y, xMin1, yMin1, xMax1, yMax1; transform(xMin, yMin, &x, &y); xMin1 = xMax1 = x; yMin1 = yMax1 = y; transform(xMax, yMin, &x, &y); if (x < xMin1) { xMin1 = x; } else if (x > xMax1) { xMax1 = x; } if (y < yMin1) { yMin1 = y; } else if (y > yMax1) { yMax1 = y; } transform(xMax, yMax, &x, &y); if (x < xMin1) { xMin1 = x; } else if (x > xMax1) { xMax1 = x; } if (y < yMin1) { yMin1 = y; } else if (y > yMax1) { yMax1 = y; } transform(xMin, yMax, &x, &y); if (x < xMin1) { xMin1 = x; } else if (x > xMax1) { xMax1 = x; } if (y < yMin1) { yMin1 = y; } else if (y > yMax1) { yMax1 = y; } if (xMin1 > clipXMin) { clipXMin = xMin1; } if (yMin1 > clipYMin) { clipYMin = yMin1; } if (xMax1 < clipXMax) { clipXMax = xMax1; } if (yMax1 < clipYMax) { clipYMax = yMax1; } } void GfxState::textShift(double tx, double ty) { double dx, dy; textTransformDelta(tx, ty, &dx, &dy); curX += dx; curY += dy; } void GfxState::shift(double dx, double dy) { curX += dx; curY += dy; } GfxState *GfxState::save() { GfxState *newState; newState = copy(); newState->saved = this; return newState; } GfxState *GfxState::restore() { GfxState *oldState; if (saved) { oldState = saved; // these attributes aren't saved/restored by the q/Q operators oldState->path = path; oldState->curX = curX; oldState->curY = curY; oldState->lineX = lineX; oldState->lineY = lineY; path = NULL; saved = NULL; delete this; } else { oldState = this; } return oldState; } GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) { Object obj2; int i, j; if (obj->isName()) { for (i = 0; i < nGfxBlendModeNames; ++i) { if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) { *mode = gfxBlendModeNames[i].mode; return gTrue; } } return gFalse; } else if (obj->isArray()) { for (i = 0; i < obj->arrayGetLength(); ++i) { obj->arrayGet(i, &obj2); if (!obj2.isName()) { obj2.free(); return gFalse; } for (j = 0; j < nGfxBlendModeNames; ++j) { if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) { obj2.free(); *mode = gfxBlendModeNames[j].mode; return gTrue; } } obj2.free(); } *mode = gfxBlendNormal; return gTrue; } else { return gFalse; } } xpdf-3.04/xpdf/Dict.h0000644000076400007640000000345212341430012013702 0ustar dereknderekn//======================================================================== // // Dict.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef DICT_H #define DICT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Object.h" struct DictEntry; //------------------------------------------------------------------------ // Dict //------------------------------------------------------------------------ class Dict { public: // Constructor. Dict(XRef *xrefA); // Destructor. ~Dict(); // Reference counting. int incRef() { return ++ref; } int decRef() { return --ref; } // Get number of entries. int getLength() { return length; } // Add an entry. NB: does not copy key. void add(char *key, Object *val); // Check if dictionary is of specified type. GBool is(const char *type); // Look up an entry and return the value. Returns a null object // if is not in the dictionary. Object *lookup(const char *key, Object *obj, int recursion = 0); Object *lookupNF(const char *key, Object *obj); // Iterative accessors. char *getKey(int i); Object *getVal(int i, Object *obj); Object *getValNF(int i, Object *obj); // Set the xref pointer. This is only used in one special case: the // trailer dictionary, which is read before the xref table is // parsed. void setXRef(XRef *xrefA) { xref = xrefA; } private: XRef *xref; // the xref table for this PDF file DictEntry *entries; // array of entries DictEntry **hashTab; // hash table pointers int size; // size of array int length; // number of entries in dictionary int ref; // reference count DictEntry *find(const char *key); void expand(); int hash(const char *key); }; #endif xpdf-3.04/xpdf/GfxFont.h0000644000076400007640000002561612341430012014400 0ustar dereknderekn//======================================================================== // // GfxFont.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GFXFONT_H #define GFXFONT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "GString.h" #include "Object.h" #include "CharTypes.h" class Dict; class CMap; class CharCodeToUnicode; class FoFiTrueType; struct GfxFontCIDWidths; struct Base14FontMapEntry; //------------------------------------------------------------------------ // GfxFontType //------------------------------------------------------------------------ enum GfxFontType { //----- Gfx8BitFont fontUnknownType, fontType1, fontType1C, fontType1COT, fontType3, fontTrueType, fontTrueTypeOT, //----- GfxCIDFont fontCIDType0, fontCIDType0C, fontCIDType0COT, fontCIDType2, fontCIDType2OT }; //------------------------------------------------------------------------ // GfxFontCIDWidths //------------------------------------------------------------------------ struct GfxFontCIDWidthExcep { CID first; // this record applies to CID last; // CIDs .. double width; // char width }; struct GfxFontCIDWidthExcepV { CID first; // this record applies to CID last; // CIDs .. double height; // char height double vx, vy; // origin position }; struct GfxFontCIDWidths { double defWidth; // default char width double defHeight; // default char height double defVY; // default origin position GfxFontCIDWidthExcep *exceps; // exceptions int nExceps; // number of valid entries in exceps GfxFontCIDWidthExcepV * // exceptions for vertical font excepsV; int nExcepsV; // number of valid entries in excepsV }; //------------------------------------------------------------------------ // GfxFontLoc //------------------------------------------------------------------------ enum GfxFontLocType { gfxFontLocEmbedded, // font embedded in PDF file gfxFontLocExternal, // external font file gfxFontLocResident // font resident in PS printer }; class GfxFontLoc { public: GfxFontLoc(); ~GfxFontLoc(); GfxFontLocType locType; GfxFontType fontType; Ref embFontID; // embedded stream obj ID // (if locType == gfxFontLocEmbedded) GString *path; // font file path // (if locType == gfxFontLocExternal) // PS font name // (if locType == gfxFontLocResident) int fontNum; // for TrueType collections and Mac dfonts // (if locType == gfxFontLocExternal) double oblique; // sheer factor to oblique this font // (used when substituting a plain // font for an oblique font) GString *encoding; // PS font encoding, only for 16-bit fonts // (if locType == gfxFontLocResident) int wMode; // writing mode, only for 16-bit fonts // (if locType == gfxFontLocResident) int substIdx; // substitute font index // (if locType == gfxFontLocExternal, // and a Base-14 substitution was made) }; //------------------------------------------------------------------------ // GfxFont //------------------------------------------------------------------------ #define fontFixedWidth (1 << 0) #define fontSerif (1 << 1) #define fontSymbolic (1 << 2) #define fontItalic (1 << 6) #define fontBold (1 << 18) class GfxFont { public: // Build a GfxFont object. static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict); GfxFont(char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA); virtual ~GfxFont(); GBool isOk() { return ok; } // Get font tag. GString *getTag() { return tag; } // Get font dictionary ID. Ref *getID() { return &id; } // Does this font match the tag? GBool matches(char *tagA) { return !tag->cmp(tagA); } // Get the original font name (ignornig any munging that might have // been done to map to a canonical Base-14 font name). GString *getName() { return name; } // Get font type. GfxFontType getType() { return type; } virtual GBool isCIDFont() { return gFalse; } // Get embedded font ID, i.e., a ref for the font file stream. // Returns false if there is no embedded font. GBool getEmbeddedFontID(Ref *embID) { *embID = embFontID; return embFontID.num >= 0; } // Get the PostScript font name for the embedded font. Returns // NULL if there is no embedded font. GString *getEmbeddedFontName() { return embFontName; } // Get font descriptor flags. int getFlags() { return flags; } GBool isFixedWidth() { return flags & fontFixedWidth; } GBool isSerif() { return flags & fontSerif; } GBool isSymbolic() { return flags & fontSymbolic; } GBool isItalic() { return flags & fontItalic; } GBool isBold() { return flags & fontBold; } // Return the font matrix. double *getFontMatrix() { return fontMat; } // Return the font bounding box. double *getFontBBox() { return fontBBox; } // Return the ascent and descent values. double getAscent() { return ascent; } double getDescent() { return descent; } // Return the writing mode (0=horizontal, 1=vertical). virtual int getWMode() { return 0; } // Locate the font file for this font. If is true, includes PS // printer-resident fonts. Returns NULL on failure. GfxFontLoc *locateFont(XRef *xref, GBool ps); // Locate a Base-14 font file for a specified font name. static GfxFontLoc *locateBase14Font(GString *base14Name); // Read an embedded font file into a buffer. char *readEmbFontFile(XRef *xref, int *len); // Get the next char from a string of bytes, returning the // char , its Unicode mapping , its displacement vector // (, ), and its origin offset vector (, ). // is the number of entries available in , and is set to // the number actually used. Returns the number of bytes used by // the char code. virtual int getNextChar(char *s, int len, CharCode *code, Unicode *u, int uSize, int *uLen, double *dx, double *dy, double *ox, double *oy) = 0; protected: static GfxFontType getFontType(XRef *xref, Dict *fontDict, Ref *embID); void readFontDescriptor(XRef *xref, Dict *fontDict); CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits, CharCodeToUnicode *ctu); static GfxFontLoc *getExternalFont(GString *path, int fontNum, double oblique, GBool cid); GString *tag; // PDF font tag Ref id; // reference (used as unique ID) GString *name; // font name GfxFontType type; // type of font int flags; // font descriptor flags GString *embFontName; // name of embedded font Ref embFontID; // ref to embedded font file stream double fontMat[6]; // font matrix double fontBBox[4]; // font bounding box double missingWidth; // "default" width double ascent; // max height above baseline double descent; // max depth below baseline GBool ok; }; //------------------------------------------------------------------------ // Gfx8BitFont //------------------------------------------------------------------------ class Gfx8BitFont: public GfxFont { public: Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict); virtual ~Gfx8BitFont(); virtual int getNextChar(char *s, int len, CharCode *code, Unicode *u, int uSize, int *uLen, double *dx, double *dy, double *ox, double *oy); // Return the encoding. char **getEncoding() { return enc; } // Return the Unicode map. CharCodeToUnicode *getToUnicode(); // Return the character name associated with . char *getCharName(int code) { return enc[code]; } // Returns true if the PDF font specified an encoding. GBool getHasEncoding() { return hasEncoding; } // Returns true if the PDF font specified MacRomanEncoding. GBool getUsesMacRomanEnc() { return usesMacRomanEnc; } // Get width of a character. double getWidth(Guchar c) { return widths[c]; } // Return a char code-to-GID mapping for the provided font file. // (This is only useful for TrueType fonts.) int *getCodeToGIDMap(FoFiTrueType *ff); // Return the Type 3 CharProc dictionary, or NULL if none. Dict *getCharProcs(); // Return the Type 3 CharProc for the character associated with . Object *getCharProc(int code, Object *proc); Object *getCharProcNF(int code, Object *proc); // Return the Type 3 Resources dictionary, or NULL if none. Dict *getResources(); private: Base14FontMapEntry *base14; // for Base-14 fonts only; NULL otherwise char *enc[256]; // char code --> char name char encFree[256]; // boolean for each char name: if set, // the string is malloc'ed CharCodeToUnicode *ctu; // char code --> Unicode GBool hasEncoding; GBool usesMacRomanEnc; double widths[256]; // character widths Object charProcs; // Type 3 CharProcs dictionary Object resources; // Type 3 Resources dictionary friend class GfxFont; }; //------------------------------------------------------------------------ // GfxCIDFont //------------------------------------------------------------------------ class GfxCIDFont: public GfxFont { public: GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict); virtual ~GfxCIDFont(); virtual GBool isCIDFont() { return gTrue; } virtual int getNextChar(char *s, int len, CharCode *code, Unicode *u, int uSize, int *uLen, double *dx, double *dy, double *ox, double *oy); // Return the writing mode (0=horizontal, 1=vertical). virtual int getWMode(); // Return the Unicode map. CharCodeToUnicode *getToUnicode(); // Get the collection name (-). GString *getCollection(); // Return the CID-to-GID mapping table. These should only be called // if type is fontCIDType2. int *getCIDToGID() { return cidToGID; } int getCIDToGIDLen() { return cidToGIDLen; } private: GString *collection; // collection name CMap *cMap; // char code --> CID CharCodeToUnicode *ctu; // CID/char code --> Unicode GBool ctuUsesCharCode; // true: ctu maps char code to Unicode; // false: ctu maps CID to Unicode GfxFontCIDWidths widths; // character widths int *cidToGID; // CID --> GID mapping (for embedded // TrueType fonts) int cidToGIDLen; }; //------------------------------------------------------------------------ // GfxFontDict //------------------------------------------------------------------------ class GfxFontDict { public: // Build the font dictionary, given the PDF font dictionary. GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict); // Destructor. ~GfxFontDict(); // Get the specified font. GfxFont *lookup(char *tag); GfxFont *lookupByRef(Ref ref); // Iterative access. int getNumFonts() { return numFonts; } GfxFont *getFont(int i) { return fonts[i]; } private: GfxFont **fonts; // list of fonts int numFonts; // number of fonts }; #endif xpdf-3.04/xpdf/PDFDocEncoding.h0000644000076400007640000000052312341430012015521 0ustar dereknderekn//======================================================================== // // PDFDocEncoding.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PDFDOCENCODING_H #define PDFDOCENCODING_H #include "CharTypes.h" extern Unicode pdfDocEncoding[256]; #endif xpdf-3.04/xpdf/Stream.h0000644000076400007640000006740112341430012014256 0ustar dereknderekn//======================================================================== // // Stream.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef STREAM_H #define STREAM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "gtypes.h" #include "gfile.h" #include "Object.h" class BaseStream; //------------------------------------------------------------------------ enum StreamKind { strFile, strASCIIHex, strASCII85, strLZW, strRunLength, strCCITTFax, strDCT, strFlate, strJBIG2, strJPX, strWeird // internal-use stream types }; enum StreamColorSpaceMode { streamCSNone, streamCSDeviceGray, streamCSDeviceRGB, streamCSDeviceCMYK }; //------------------------------------------------------------------------ // This is in Stream.h instead of Decrypt.h to avoid really annoying // include file dependency loops. enum CryptAlgorithm { cryptRC4, cryptAES, cryptAES256 }; //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ class Stream { public: // Constructor. Stream(); // Destructor. virtual ~Stream(); // Reference counting. int incRef() { return ++ref; } int decRef() { return --ref; } // Get kind of stream. virtual StreamKind getKind() = 0; // Reset stream to beginning. virtual void reset() = 0; // Close down the stream. virtual void close(); // Get next char from stream. virtual int getChar() = 0; // Peek at next char in stream. virtual int lookChar() = 0; // Get next char from stream without using the predictor. // This is only used by StreamPredictor. virtual int getRawChar(); // Get exactly bytes from stream. Returns the number of // bytes read -- the returned count will be less than at EOF. virtual int getBlock(char *blk, int size); // 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 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(GFileOffset pos, int dir = 0) = 0; // Get PostScript command for the filter(s). virtual GString *getPSFilter(int psLevel, const char *indent); // Does this stream type potentially contain non-printable chars? virtual GBool isBinary(GBool last = gTrue) = 0; // Get the BaseStream of this stream. virtual BaseStream *getBaseStream() = 0; // Get the stream after the last decoder (this may be a BaseStream // or a DecryptStream). virtual Stream *getUndecodedStream() = 0; // Get the dictionary associated with this stream. virtual Dict *getDict() = 0; // Is this an encoding filter? virtual GBool isEncoder() { return gFalse; } // Get image parameters which are defined by the stream contents. virtual void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) {} // Return the next stream in the "stack". virtual Stream *getNextStream() { return NULL; } // Add filters to this stream according to the parameters in . // Returns the new stream. Stream *addFilters(Object *dict, int recursion = 0); private: Stream *makeFilter(char *name, Stream *str, Object *params, int recursion); int ref; // reference count }; //------------------------------------------------------------------------ // BaseStream // // This is the base class for all streams that read directly from a file. //------------------------------------------------------------------------ class BaseStream: public Stream { public: BaseStream(Object *dictA); virtual ~BaseStream(); virtual Stream *makeSubStream(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; } virtual Dict *getDict() { return dict.getDict(); } virtual GString *getFileName() { return NULL; } // Get/set position of first byte of stream within the file. virtual GFileOffset getStart() = 0; virtual void moveStart(int delta) = 0; private: Object dict; }; //------------------------------------------------------------------------ // FilterStream // // This is the base class for all streams that filter another stream. //------------------------------------------------------------------------ class FilterStream: public Stream { public: FilterStream(Stream *strA); virtual ~FilterStream(); virtual void close(); virtual 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(); } virtual Stream *getNextStream() { return str; } protected: Stream *str; }; //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ class ImageStream { public: // Create an image stream object for an image with the specified // parameters. Note that these are the actual image parameters, // which may be different from the predictor parameters. ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); ~ImageStream(); // Reset the stream. void reset(); // 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); // Returns a pointer to the next line of pixels. Returns NULL at // end of file. Guchar *getLine(); // Skip an entire line from the image. void skipLine(); private: Stream *str; // base stream int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int inputLineSize; // input line buffer size char *inputLine; // input line buffer Guchar *imgLine; // line buffer int imgIdx; // current index in imgLine }; //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ class StreamPredictor { public: // Create a predictor object. Note that the parameters are for the // predictor, and may not match the actual image parameters. StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA); ~StreamPredictor(); GBool isOk() { return ok; } void reset(); int lookChar(); int getChar(); int getBlock(char *blk, int size); private: GBool getNextLine(); Stream *str; // base stream int predictor; // predictor int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int pixBytes; // bytes per pixel int rowBytes; // bytes per line Guchar *predLine; // line buffer int predIdx; // current index in predLine GBool ok; }; //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ #define fileStreamBufSize 256 class FileStream: public BaseStream { public: FileStream(FILE *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual ~FileStream(); virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return strFile; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); 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: GBool fillBuf(); FILE *f; GFileOffset start; GBool limited; GFileOffset length; char buf[fileStreamBufSize]; char *bufPtr; char *bufEnd; GFileOffset bufPos; GFileOffset savePos; GBool saved; }; //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ class MemStream: public BaseStream { public: MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA); virtual ~MemStream(); virtual Stream *makeSubStream(GFileOffset start, GBool limited, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; } virtual int lookChar() { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } virtual int getBlock(char *blk, int size); 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: char *buf; Guint start; Guint length; char *bufEnd; char *bufPtr; GBool needFree; }; //------------------------------------------------------------------------ // EmbedStream // // This is a special stream type used for embedded streams (inline // images). It reads directly from the base stream -- after the // EmbedStream is deleted, reads from the base stream will proceed where // the BaseStream left off. Note that this is very different behavior // that creating a new FileStream (using makeSubStream). //------------------------------------------------------------------------ class EmbedStream: public BaseStream { public: EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA); virtual ~EmbedStream(); 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 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; GFileOffset length; }; //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ class ASCIIHexStream: public FilterStream { public: ASCIIHexStream(Stream *strA); virtual ~ASCIIHexStream(); virtual StreamKind getKind() { return strASCIIHex; } virtual void reset(); virtual int getChar() { int c = lookChar(); buf = EOF; return c; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int buf; GBool eof; }; //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ class ASCII85Stream: public FilterStream { public: ASCII85Stream(Stream *strA); virtual ~ASCII85Stream(); virtual StreamKind getKind() { return strASCII85; } virtual void reset(); virtual int getChar() { int ch = lookChar(); ++index; return ch; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int c[5]; int b[4]; int index, n; GBool eof; }; //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ class LZWStream: public FilterStream { public: LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA); virtual ~LZWStream(); virtual StreamKind getKind() { return strLZW; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: StreamPredictor *pred; // predictor int early; // early parameter GBool eof; // true if at eof int inputBuf; // input buffer int inputBits; // number of bits in input buffer struct { // decoding table int length; int head; Guchar tail; } table[4097]; int nextCode; // next code to be used int nextBits; // number of bits in next code word int prevCode; // previous code used in stream int newChar; // next char to be added to table Guchar seqBuf[4097]; // buffer for current sequence int seqLength; // length of current sequence int seqIndex; // index into current sequence GBool first; // first code after a table clear GBool processNextCode(); void clearTable(); int getCode(); }; //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ class RunLengthStream: public FilterStream { public: RunLengthStream(Stream *strA); virtual ~RunLengthStream(); virtual StreamKind getKind() { return strRunLength; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: char buf[128]; // buffer char *bufPtr; // next char to read char *bufEnd; // end of buffer GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ struct CCITTCodeTable; class CCITTFaxStream: public FilterStream { public: CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, GBool byteAlignA, int columnsA, int rowsA, GBool endOfBlockA, GBool blackA); virtual ~CCITTFaxStream(); virtual StreamKind getKind() { return strCCITTFax; } virtual void reset(); virtual int getChar() { int c = lookChar(); buf = EOF; return c; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int encoding; // 'K' parameter GBool endOfLine; // 'EndOfLine' parameter GBool byteAlign; // 'EncodedByteAlign' parameter int columns; // 'Columns' parameter int rows; // 'Rows' parameter GBool endOfBlock; // 'EndOfBlock' parameter GBool black; // 'BlackIs1' parameter GBool eof; // true if at eof GBool nextLine2D; // true if next line uses 2D encoding int row; // current row Guint inputBuf; // input buffer int inputBits; // number of bits in input buffer int *codingLine; // coding line changing elements int *refLine; // reference line changing elements int a0i; // index into codingLine GBool err; // error on current line int outputBits; // remaining ouput bits int buf; // character buffer void addPixels(int a1, int blackPixels); void addPixelsNeg(int a1, int blackPixels); short getTwoDimCode(); short getWhiteCode(); short getBlackCode(); short lookBits(int n); void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; } }; //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ // DCT component info struct DCTCompInfo { int id; // component ID int hSample, vSample; // horiz/vert sampling resolutions int quantTable; // quantization table number int prevDC; // DC coefficient accumulator }; struct DCTScanInfo { GBool comp[4]; // comp[i] is set if component i is // included in this scan int numComps; // number of components in the scan int dcHuffTable[4]; // DC Huffman table numbers int acHuffTable[4]; // AC Huffman table numbers int firstCoeff, lastCoeff; // first and last DCT coefficient int ah, al; // successive approximation parameters }; // DCT Huffman decoding table struct DCTHuffTable { Guchar firstSym[17]; // first symbol for this bit length Gushort firstCode[17]; // first code for this bit length Gushort numCodes[17]; // number of codes of this bit length Guchar sym[256]; // symbols }; class DCTStream: public FilterStream { public: DCTStream(Stream *strA, int colorXformA); virtual ~DCTStream(); virtual StreamKind getKind() { return strDCT; } virtual void reset(); virtual void close(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); Stream *getRawStream() { return str; } private: GBool progressive; // set if in progressive mode GBool interleaved; // set if in interleaved mode int width, height; // image size int mcuWidth, mcuHeight; // size of min coding unit, in data units int bufWidth, bufHeight; // frameBuf size DCTCompInfo compInfo[4]; // info for each component DCTScanInfo scanInfo; // info for the current scan int numComps; // number of components in image int colorXform; // color transform: -1 = unspecified // 0 = none // 1 = YUV/YUVK -> RGB/CMYK GBool gotJFIFMarker; // set if APP0 JFIF marker was present GBool gotAdobeMarker; // set if APP14 Adobe marker was present int restartInterval; // restart interval, in MCUs Gushort quantTables[4][64]; // quantization tables int numQuantTables; // number of quantization tables DCTHuffTable dcHuffTables[4]; // DC Huffman tables DCTHuffTable acHuffTables[4]; // AC Huffman tables int numDCHuffTables; // number of DC Huffman tables int numACHuffTables; // number of AC Huffman tables Guchar *rowBuf; 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; // current position within image/MCU int restartCtr; // MCUs left until restart int restartMarker; // next restart marker int eobRun; // number of EOBs left in the current run int inputBuf; // input buffer for variable length codes int inputBits; // number of valid bits in input buffer void restart(); GBool readMCURow(); void readScan(); GBool readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); void decodeImage(); void transformDataUnit(Gushort *quantTable, int dataIn[64], Guchar dataOut[64]); int readHuffSym(DCTHuffTable *table); int readAmp(int size); int readBit(); GBool readHeader(); GBool readBaselineSOF(); GBool readProgressiveSOF(); GBool readScanInfo(); GBool readQuantTables(); GBool readHuffmanTables(); GBool readRestartInterval(); GBool readJFIFMarker(); GBool readAdobeMarker(); GBool readTrailer(); int readMarker(); int read16(); }; //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ #define flateWindow 32768 // buffer size #define flateMask (flateWindow-1) #define flateMaxHuffman 15 // max Huffman code length #define flateMaxCodeLenCodes 19 // max # code length codes #define flateMaxLitCodes 288 // max # literal codes #define flateMaxDistCodes 30 // max # distance codes // Huffman code table entry struct FlateCode { Gushort len; // code length, in bits Gushort val; // value represented by this code }; struct FlateHuffmanTab { FlateCode *codes; int maxLen; }; // Decoding info for length and distance code words struct FlateDecode { int bits; // # extra bits int first; // first length/distance }; class FlateStream: public FilterStream { public: FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); virtual ~FlateStream(); virtual StreamKind getKind() { return strFlate; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: StreamPredictor *pred; // predictor Guchar buf[flateWindow]; // output data buffer int index; // current index into output buffer int remain; // number valid bytes in output buffer int codeBuf; // input buffer int codeSize; // number of bits in input buffer int // literal and distance code lengths codeLengths[flateMaxLitCodes + flateMaxDistCodes]; FlateHuffmanTab litCodeTab; // literal code table FlateHuffmanTab distCodeTab; // distance code table GBool compressedBlock; // set if reading a compressed block int blockLen; // remaining length of uncompressed block GBool endOfBlock; // set when end of block is reached GBool eof; // set when end of stream is reached static int // code length code reordering codeLenCodeMap[flateMaxCodeLenCodes]; static FlateDecode // length decoding info lengthDecode[flateMaxLitCodes-257]; static FlateDecode // distance decoding info distDecode[flateMaxDistCodes]; static FlateHuffmanTab // fixed literal code table fixedLitCodeTab; static FlateHuffmanTab // fixed distance code table fixedDistCodeTab; void readSome(); GBool startBlock(); void loadFixedCodes(); GBool readDynamicCodes(); void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab); int getHuffmanCodeWord(FlateHuffmanTab *tab); int getCodeWord(int bits); }; //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ class EOFStream: public FilterStream { public: EOFStream(Stream *strA); virtual ~EOFStream(); virtual StreamKind getKind() { return strWeird; } virtual void reset() {} virtual int getChar() { return EOF; } virtual int lookChar() { return EOF; } virtual int getBlock(char *blk, int size) { return 0; } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } }; //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ class BufStream: public FilterStream { public: BufStream(Stream *strA, int bufSizeA); virtual ~BufStream(); 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); int lookChar(int idx); private: int *buf; int bufSize; }; //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ class FixedLengthEncoder: public FilterStream { public: FixedLengthEncoder(Stream *strA, int lengthA); ~FixedLengthEncoder(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue); virtual GBool isEncoder() { return gTrue; } private: int length; int count; }; //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ class ASCIIHexEncoder: public FilterStream { public: ASCIIHexEncoder(Stream *strA); virtual ~ASCIIHexEncoder(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } private: char buf[4]; char *bufPtr; char *bufEnd; int lineLen; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ class ASCII85Encoder: public FilterStream { public: ASCII85Encoder(Stream *strA); virtual ~ASCII85Encoder(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } private: char buf[8]; char *bufPtr; char *bufEnd; int lineLen; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ class RunLengthEncoder: public FilterStream { public: RunLengthEncoder(Stream *strA); virtual ~RunLengthEncoder(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: char buf[131]; char *bufPtr; char *bufEnd; char *nextEnd; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // 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 xpdf-3.04/xpdf/PSOutputDev.cc0000644000076400007640000056542012341430012015367 0ustar dereknderekn//======================================================================== // // PSOutputDev.cc // // Copyright 1996-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include "GString.h" #include "GList.h" #include "GHash.h" #include "config.h" #include "GlobalParams.h" #include "Object.h" #include "Error.h" #include "Function.h" #include "Gfx.h" #include "GfxState.h" #include "GfxFont.h" #include "UnicodeMap.h" #include "FoFiType1C.h" #include "FoFiTrueType.h" #include "Catalog.h" #include "Page.h" #include "Stream.h" #include "Annot.h" #include "PDFDoc.h" #include "XRef.h" #include "PreScanOutputDev.h" #include "CharCodeToUnicode.h" #include "Form.h" #include "TextString.h" #if HAVE_SPLASH # include "Splash.h" # include "SplashBitmap.h" # include "SplashOutputDev.h" #endif #include "PSOutputDev.h" #ifdef MACOS // needed for setting type/creator of MacOS files #include "ICSupport.h" #endif // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // PostScript prolog and setup //------------------------------------------------------------------------ // The '~' escapes mark prolog code that is emitted only in certain // levels: // // ~[123][sn] // ^ ^----- s=psLevel*Sep, n=psLevel* // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3* static const char *prolog[] = { "/xpdf 75 dict def xpdf begin", "% PDF special state", "/pdfDictSize 15 def", "~1sn", "/pdfStates 64 array def", " 0 1 63 {", " pdfStates exch pdfDictSize dict", " dup /pdfStateIdx 3 index put", " put", " } for", "~123sn", "/pdfSetup {", " /pdfDuplex exch def", " /setpagedevice where {", " pop 2 dict begin", " /Policies 1 dict dup begin /PageSize 6 def end def", " pdfDuplex { /Duplex true def } if", " currentdict end setpagedevice", " } if", " /pdfPageW 0 def", " /pdfPageH 0 def", "} def", "/pdfSetupPaper {", " 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", " } ifelse", "} def", "~1sn", "/pdfOpNames [", " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", "] def", "~123sn", "/pdfStartPage {", "~1sn", " pdfStates 0 get begin", "~23sn", " pdfDictSize dict begin", "~23n", " /pdfFillCS [] def", " /pdfFillXform {} def", " /pdfStrokeCS [] def", " /pdfStrokeXform {} def", "~1n", " /pdfFill 0 def", " /pdfStroke 0 def", "~1s", " /pdfFill [0 0 0 1] def", " /pdfStroke [0 0 0 1] def", "~23sn", " /pdfFill [0] def", " /pdfStroke [0] def", " /pdfFillOP false def", " /pdfStrokeOP false def", "~123sn", " /pdfLastFill false def", " /pdfLastStroke false def", " /pdfTextMat [1 0 0 1 0 0] def", " /pdfFontSize 0 def", " /pdfCharSpacing 0 def", " /pdfTextRender 0 def", " /pdfTextRise 0 def", " /pdfWordSpacing 0 def", " /pdfHorizScaling 1 def", " /pdfTextClipPath [] def", "} def", "/pdfEndPage { end } def", "~23s", "% separation convention operators", "/findcmykcustomcolor where {", " pop", "}{", " /findcmykcustomcolor { 5 array astore } def", "} ifelse", "/setcustomcolor where {", " pop", "}{", " /setcustomcolor {", " exch", " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", " 0 4 getinterval cvx", " [ exch /dup load exch { mul exch dup } /forall load", " /pop load dup ] cvx", " ] setcolorspace setcolor", " } def", "} ifelse", "/customcolorimage where {", " pop", "}{", " /customcolorimage {", " gsave", " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", " 0 4 getinterval", " [ exch /dup load exch { mul exch dup } /forall load", " /pop load dup ] cvx", " ] setcolorspace", " 10 dict begin", " /ImageType 1 def", " /DataSource exch def", " /ImageMatrix exch def", " /BitsPerComponent exch def", " /Height exch def", " /Width exch def", " /Decode [1 0] def", " currentdict end", " image", " grestore", " } def", "} ifelse", "~123sn", "% PDF color state", "~1n", "/g { dup /pdfFill exch def setgray", " /pdfLastFill true def /pdfLastStroke false def } def", "/G { dup /pdfStroke exch def setgray", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill setgray", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke setgray", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~1s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill aload pop setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke aload pop setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~23n", "/cs { /pdfFillXform exch def dup /pdfFillCS exch def", " setcolorspace } def", "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def", " setcolorspace } def", "/sc { pdfLastFill not { pdfFillCS setcolorspace } if", " dup /pdfFill exch def aload pop pdfFillXform setcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if", " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/op { /pdfFillOP exch def", " pdfLastFill { pdfFillOP setoverprint } if } def", "/OP { /pdfStrokeOP exch def", " pdfLastStroke { pdfStrokeOP setoverprint } if } def", "/fCol {", " pdfLastFill not {", " pdfFillCS setcolorspace", " pdfFill aload pop pdfFillXform setcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStrokeCS setcolorspace", " pdfStroke aload pop pdfStrokeXform setcolor", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~23s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/ck { 6 copy 6 array astore /pdfFill exch def", " findcmykcustomcolor exch setcustomcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/CK { 6 copy 6 array astore /pdfStroke exch def", " findcmykcustomcolor exch setcustomcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/op { /pdfFillOP exch def", " pdfLastFill { pdfFillOP setoverprint } if } def", "/OP { /pdfStrokeOP exch def", " pdfLastStroke { pdfStrokeOP setoverprint } if } def", "/fCol {", " pdfLastFill not {", " pdfFill aload length 4 eq {", " setcmykcolor", " }{", " findcmykcustomcolor exch setcustomcolor", " } ifelse", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke aload length 4 eq {", " setcmykcolor", " }{", " findcmykcustomcolor exch setcustomcolor", " } ifelse", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~123sn", "% build a font", "/pdfMakeFont {", " 4 3 roll findfont", " 4 2 roll matrix scale makefont", " dup length dict begin", " { 1 index /FID ne { def } { pop pop } ifelse } forall", " /Encoding exch def", " currentdict", " end", " definefont pop", "} def", "/pdfMakeFont16 {", " exch findfont", " dup length dict begin", " { 1 index /FID ne { def } { pop pop } ifelse } forall", " /WMode exch def", " currentdict", " end", " definefont pop", "} def", "~3sn", "/pdfMakeFont16L3 {", " 1 index /CIDFont resourcestatus {", " pop pop 1 index /CIDFont findresource /CIDFontType known", " } {", " false", " } ifelse", " {", " 0 eq { /Identity-H } { /Identity-V } ifelse", " exch 1 array astore composefont pop", " } {", " pdfMakeFont16", " } ifelse", "} def", "~123sn", "% graphics state operators", "~1sn", "/q {", " gsave", " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", " pdfStates pdfStateIdx 1 add get begin", " pdfOpNames { exch def } forall", "} def", "/Q { end grestore } def", "~23sn", "/q { gsave pdfDictSize dict begin } def", "/Q {", " end grestore", " /pdfLastFill where {", " pop", " pdfLastFill {", " pdfFillOP setoverprint", " } {", " pdfStrokeOP setoverprint", " } ifelse", " } if", "} def", "~123sn", "/cm { concat } def", "/d { setdash } def", "/i { setflat } def", "/j { setlinejoin } def", "/J { setlinecap } def", "/M { setmiterlimit } def", "/w { setlinewidth } def", "% path segment operators", "/m { moveto } def", "/l { lineto } def", "/c { curveto } def", "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", " neg 0 rlineto closepath } def", "/h { closepath } def", "% path painting operators", "/S { sCol stroke } def", "/Sf { fCol stroke } def", "/f { fCol fill } def", "/f* { fCol eofill } def", "% clipping operators", "/W { clip newpath } def", "/W* { eoclip newpath } def", "/Ws { strokepath clip newpath } def", "% text state operators", "/Tc { /pdfCharSpacing exch def } def", "/Tf { dup /pdfFontSize exch def", " dup pdfHorizScaling mul exch matrix scale", " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", " exch findfont exch makefont setfont } def", "/Tr { /pdfTextRender exch def } def", "/Ts { /pdfTextRise exch def } def", "/Tw { /pdfWordSpacing exch def } def", "/Tz { /pdfHorizScaling exch def } def", "% text positioning operators", "/Td { pdfTextMat transform moveto } def", "/Tm { /pdfTextMat exch def } def", "% text string operators", "/xyshow where {", " pop", " /xyshow2 {", " dup length array", " 0 2 2 index length 1 sub {", " 2 index 1 index 2 copy get 3 1 roll 1 add get", " pdfTextMat dtransform", " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", " } for", " exch pop", " xyshow", " } def", "}{", " /xyshow2 {", " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval show moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval show moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/cshow where {", " pop", " /xycp {", // xycharpath " 0 3 2 roll", " {", " pop pop currentpoint 3 2 roll", " 1 string dup 0 4 3 roll put false charpath moveto", " 2 copy get 2 index 2 index 1 add get", " pdfTextMat dtransform rmoveto", " 2 add", " } exch cshow", " pop pop", " } def", "}{", " /xycp {", // xycharpath " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval false charpath moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval false charpath moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/Tj {", " fCol", // because stringwidth has to draw Type 3 chars " 0 pdfTextRise pdfTextMat dtransform rmoveto", " currentpoint 4 2 roll", " pdfTextRender 1 and 0 eq {", " 2 copy xyshow2", " } if", " pdfTextRender 3 and dup 1 eq exch 2 eq or {", " 3 index 3 index moveto", " 2 copy", " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", " xycp currentpoint stroke moveto", " } if", " pdfTextRender 4 and 0 ne {", " 4 2 roll moveto xycp", " /pdfTextClipPath [ pdfTextClipPath aload pop", " {/moveto cvx}", " {/lineto cvx}", " {/curveto cvx}", " {/closepath cvx}", " pathforall ] def", " currentpoint newpath moveto", " } {", " pop pop pop pop", " } ifelse", " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", "} def", "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", " pdfTextMat dtransform rmoveto } def", "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", " pdfTextMat dtransform rmoveto } def", "/Tclip { pdfTextClipPath cvx exec clip newpath", " /pdfTextClipPath [] def } def", "~1ns", "% Level 1 image operators", "~1n", "/pdfIm1 {", " /pdfImBuf1 4 index string def", " { currentfile pdfImBuf1 readhexstring pop } image", "} def", "~1s", "/pdfIm1Sep {", " /pdfImBuf1 4 index string def", " /pdfImBuf2 4 index string def", " /pdfImBuf3 4 index string def", " /pdfImBuf4 4 index string def", " { currentfile pdfImBuf1 readhexstring pop }", " { currentfile pdfImBuf2 readhexstring pop }", " { currentfile pdfImBuf3 readhexstring pop }", " { currentfile pdfImBuf4 readhexstring pop }", " true 4 colorimage", "} def", "~1ns", "/pdfImM1 {", " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", " { currentfile pdfImBuf1 readhexstring pop } imagemask", "} def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/pdfImM1a {", " { pdfImStr } imagemask", " pop pop", "} def", "~23sn", "% Level 2/3 image operators", "/pdfImBuf 100 string def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/skipEOD {", " { currentfile pdfImBuf readline", " not { pop exit } if", " (%-EOD-) eq { exit } if } loop", "} def", "/pdfIm { image skipEOD } def", "~3sn", "/pdfMask {", " /ReusableStreamDecode filter", " skipEOD", " /maskStream exch def", "} def", "/pdfMaskEnd { maskStream closefile } def", "/pdfMaskInit {", " /maskArray exch def", " /maskIdx 0 def", "} def", "/pdfMaskSrc {", " maskIdx maskArray length lt {", " maskArray maskIdx get", " /maskIdx maskIdx 1 add def", " } {", " ()", " } ifelse", "} def", "~23s", "/pdfImSep {", " findcmykcustomcolor exch", " dup /Width get /pdfImBuf1 exch string def", " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", " /pdfImDecodeLow exch def", " begin Width Height BitsPerComponent ImageMatrix DataSource end", " /pdfImData exch def", " { pdfImData pdfImBuf1 readstring pop", " 0 1 2 index length 1 sub {", " 1 index exch 2 copy get", " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", " 255 exch sub put", " } for }", " 6 5 roll customcolorimage", " skipEOD", "} def", "~23sn", "/pdfImM { fCol imagemask skipEOD } def", "/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", "/colordelta {", " false 0 1 3 index length 1 sub {", " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {", " pop true", " } if", " } for", " exch pop exch pop", "} def", "/funcCol { func n array astore } def", "/funcSH {", " dup 0 eq {", " true", " } {", " dup 6 eq {", " false", " } {", " 4 index 4 index funcCol dup", " 6 index 4 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " 5 index 5 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " 6 index 8 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " colordelta or or or", " } ifelse", " } ifelse", " {", " 1 add", " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch", " 6 index 6 index 4 index 4 index 4 index funcSH", " 2 index 6 index 6 index 4 index 4 index funcSH", " 6 index 2 index 4 index 6 index 4 index funcSH", " 5 3 roll 3 2 roll funcSH pop pop", " } {", " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul", "~23n", " funcCol sc", "~23s", " funcCol aload pop k", "~23sn", " dup 4 index exch mat transform m", " 3 index 3 index mat transform l", " 1 index 3 index mat transform l", " mat transform l pop pop h f*", " } ifelse", "} def", "/axialCol {", " dup 0 lt {", " pop t0", " } {", " dup 1 gt {", " pop t1", " } {", " dt mul t0 add", " } ifelse", " } ifelse", " func n array astore", "} def", "/axialSH {", " dup 2 lt {", " true", " } {", " dup 8 eq {", " false", " } {", " 2 index axialCol 2 index axialCol colordelta", " } ifelse", " } ifelse", " {", " 1 add 3 1 roll 2 copy add 0.5 mul", " dup 4 3 roll exch 4 index axialSH", " exch 3 2 roll axialSH", " } {", " pop 2 copy add 0.5 mul", "~23n", " axialCol sc", "~23s", " axialCol aload pop k", "~23sn", " exch dup dx mul x0 add exch dy mul y0 add", " 3 2 roll dup dx mul x0 add exch dy mul y0 add", " dx abs dy abs ge {", " 2 copy yMin sub dy mul dx div add yMin m", " yMax sub dy mul dx div add yMax l", " 2 copy yMax sub dy mul dx div add yMax l", " yMin sub dy mul dx div add yMin l", " h f*", " } {", " exch 2 copy xMin sub dx mul dy div add xMin exch m", " xMax sub dx mul dy div add xMax exch l", " exch 2 copy xMax sub dx mul dy div add xMax exch l", " xMin sub dx mul dy div add xMin exch l", " h f*", " } ifelse", " } ifelse", "} def", "/radialCol {", " dup t0 lt {", " pop t0", " } {", " dup t1 gt {", " pop t1", " } if", " } ifelse", " func n array astore", "} def", "/radialSH {", " dup 0 eq {", " true", " } {", " dup 8 eq {", " false", " } {", " 2 index dt mul t0 add radialCol", " 2 index dt mul t0 add radialCol colordelta", " } ifelse", " } ifelse", " {", " 1 add 3 1 roll 2 copy add 0.5 mul", " dup 4 3 roll exch 4 index radialSH", " exch 3 2 roll radialSH", " } {", " pop 2 copy add 0.5 mul dt mul t0 add", "~23n", " radialCol sc", "~23s", " radialCol aload pop k", "~23sn", " encl {", " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " 0 360 arc h", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " 360 0 arcn h f", " } {", " 2 copy", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a1 a2 arcn", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a2 a1 arcn h", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a1 a2 arc", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a2 a1 arc h f", " } ifelse", " } ifelse", "} def", "~123sn", "end", NULL }; static 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", " begincmap", " /CMapType 1 def", " /CMapName /Identity-H def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "10 dict begin", " begincmap", " /CMapType 1 def", " /CMapName /Identity-V def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " /WMode 1 def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "end", NULL }; //------------------------------------------------------------------------ // Fonts //------------------------------------------------------------------------ struct PSSubstFont { const char *psName; // PostScript name double mWidth; // width of 'm' character }; // NB: must be in same order as base14SubstFonts in GfxFont.cc static PSSubstFont psBase14SubstFonts[14] = { {"Courier", 0.600}, {"Courier-Oblique", 0.600}, {"Courier-Bold", 0.600}, {"Courier-BoldOblique", 0.600}, {"Helvetica", 0.833}, {"Helvetica-Oblique", 0.833}, {"Helvetica-Bold", 0.889}, {"Helvetica-BoldOblique", 0.889}, {"Times-Roman", 0.788}, {"Times-Italic", 0.722}, {"Times-Bold", 0.833}, {"Times-BoldItalic", 0.778}, // the last two are never used for substitution {"Symbol", 0}, {"ZapfDingbats", 0} }; class PSFontInfo { public: PSFontInfo(Ref fontIDA) { fontID = fontIDA; ff = NULL; } Ref fontID; PSFontFileInfo *ff; // pointer to font file info; NULL indicates // font mapping failed }; 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 //------------------------------------------------------------------------ #define psProcessCyan 1 #define psProcessMagenta 2 #define psProcessYellow 4 #define psProcessBlack 8 #define psProcessCMYK 15 //------------------------------------------------------------------------ // PSOutCustomColor //------------------------------------------------------------------------ class PSOutCustomColor { public: PSOutCustomColor(double cA, double mA, double yA, double kA, GString *nameA); ~PSOutCustomColor(); double c, m, y, k; GString *name; PSOutCustomColor *next; }; PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, GString *nameA) { c = cA; m = mA; y = yA; k = kA; name = nameA; next = NULL; } PSOutCustomColor::~PSOutCustomColor() { delete name; } //------------------------------------------------------------------------ struct PSOutImgClipRect { int x0, x1, y0, y1; }; //------------------------------------------------------------------------ struct PSOutPaperSize { PSOutPaperSize(int wA, int hA) { w = wA; h = hA; } int w, h; }; //------------------------------------------------------------------------ // DeviceNRecoder //------------------------------------------------------------------------ class DeviceNRecoder: public FilterStream { public: DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); virtual ~DeviceNRecoder(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: GBool fillBuf(); int width, height; GfxImageColorMap *colorMap; Function *func; ImageStream *imgStr; int buf[gfxColorMaxComps]; int pixelIdx; int bufIdx; int bufSize; }; DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA): FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; imgStr = NULL; pixelIdx = 0; bufIdx = gfxColorMaxComps; bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getAlt()->getNComps(); func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getTintTransformFunc(); } DeviceNRecoder::~DeviceNRecoder() { if (imgStr) { delete imgStr; } if (str->isEncoder()) { delete str; } } void DeviceNRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } GBool DeviceNRecoder::fillBuf() { Guchar pixBuf[gfxColorMaxComps]; GfxColor color; double x[gfxColorMaxComps], y[gfxColorMaxComps]; int i; if (pixelIdx >= width * height) { return gFalse; } imgStr->getPixel(pixBuf); colorMap->getColor(pixBuf, &color); for (i = 0; i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); ++i) { x[i] = colToDbl(color.c[i]); } func->transform(x, y); for (i = 0; i < bufSize; ++i) { buf[i] = (int)(y[i] * 255 + 0.5); } bufIdx = 0; ++pixelIdx; return gTrue; } //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ extern "C" { typedef void (*SignalFunc)(int); } static void outputToFile(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, int firstPage, int lastPage, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA) { FILE *f; PSFileType fileTypeA; underlayCbk = NULL; underlayCbkData = NULL; overlayCbk = NULL; overlayCbkData = NULL; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; fontInfo = new GList(); fontFileInfo = new GHash(); imgIDs = NULL; formIDs = NULL; xobjStack = NULL; paperSizes = NULL; embFontList = NULL; customColors = NULL; haveTextClip = gFalse; t3String = NULL; // open file or pipe if (!strcmp(fileName, "-")) { fileTypeA = psStdout; f = stdout; } else if (fileName[0] == '|') { fileTypeA = psPipe; #ifdef HAVE_POPEN #ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_IGN); #endif if (!(f = popen(fileName + 1, "w"))) { error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); ok = gFalse; return; } #else error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); ok = gFalse; return; #endif } else { fileTypeA = psFile; if (!(f = fopen(fileName, "w"))) { error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); ok = gFalse; return; } } init(outputToFile, f, fileTypeA, docA, firstPage, lastPage, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); } PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, PDFDoc *docA, int firstPage, int lastPage, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA) { underlayCbk = NULL; underlayCbkData = NULL; overlayCbk = NULL; overlayCbkData = NULL; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; fontInfo = new GList(); fontFileInfo = new GHash(); imgIDs = NULL; formIDs = NULL; xobjStack = NULL; paperSizes = NULL; embFontList = NULL; customColors = NULL; haveTextClip = gFalse; t3String = NULL; init(outputFuncA, outputStreamA, psGeneric, docA, firstPage, lastPage, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); } void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, PDFDoc *docA, int firstPage, int lastPage, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA) { Catalog *catalog; Page *page; PDFRectangle *box; PSOutPaperSize *size; PSFontFileInfo *ff; GList *names; int pg, w, h, i; // initialize ok = gTrue; outputFunc = outputFuncA; outputStream = outputStreamA; fileType = fileTypeA; doc = docA; xref = doc->getXRef(); catalog = doc->getCatalog(); level = globalParams->getPSLevel(); mode = modeA; paperWidth = globalParams->getPSPaperWidth(); paperHeight = globalParams->getPSPaperHeight(); imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); } if (paperWidth < 0 || paperHeight < 0) { paperMatch = gTrue; paperSizes = new GList(); paperWidth = paperHeight = 1; // in case the document has zero pages for (pg = (firstPage >= 1) ? firstPage : 1; pg <= lastPage && pg <= catalog->getNumPages(); ++pg) { page = catalog->getPage(pg); 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) { break; } } if (i == paperSizes->getLength()) { paperSizes->append(new PSOutPaperSize(w, h)); } if (w > paperWidth) { paperWidth = w; } if (h > paperHeight) { paperHeight = h; } } // NB: img{LLX,LLY,URX,URY} will be set by startPage() } else { paperMatch = gFalse; } preload = globalParams->getPSPreload(); manualCtrl = manualCtrlA; if (mode == psModeForm) { lastPage = firstPage; } processColors = 0; inType3Char = gFalse; #if OPI_SUPPORT // initialize OPI nesting levels opi13Nest = 0; opi20Nest = 0; #endif tx0 = ty0 = -1; xScale0 = yScale0 = 0; rotate0 = -1; clipLLX0 = clipLLY0 = 0; clipURX0 = clipURY0 = -1; // initialize font lists, etc. for (i = 0; i < 14; ++i) { 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) { if (!fontFileInfo->lookup((GString *)names->get(i))) { ff = new PSFontFileInfo((GString *)names->get(i), fontType1, psFontFileResident); fontFileInfo->add(ff->psName, ff); } } delete names; imgIDLen = 0; imgIDSize = 0; formIDLen = 0; formIDSize = 0; xobjStack = new GList(); numSaves = 0; numTilingPatterns = 0; nextFunc = 0; // initialize embedded font resource comment list embFontList = new GString(); if (!manualCtrl) { // this check is needed in case the document has zero pages if (firstPage > 0 && firstPage <= catalog->getNumPages()) { writeHeader(firstPage, lastPage, catalog->getPage(firstPage)->getMediaBox(), catalog->getPage(firstPage)->getCropBox(), catalog->getPage(firstPage)->getRotate()); } else { box = new PDFRectangle(0, 0, 1, 1); writeHeader(firstPage, lastPage, box, box, 0); delete box; } if (mode != psModeForm) { writePS("%%BeginProlog\n"); } writeXpdfProcset(); if (mode != psModeForm) { writePS("%%EndProlog\n"); writePS("%%BeginSetup\n"); } writeDocSetup(catalog, firstPage, lastPage); if (mode != psModeForm) { writePS("%%EndSetup\n"); } } // initialize sequential page number seqPage = 1; } PSOutputDev::~PSOutputDev() { PSOutCustomColor *cc; if (ok) { if (!manualCtrl) { writePS("%%Trailer\n"); writeTrailer(); if (mode != psModeForm) { writePS("%%EOF\n"); } } if (fileType == psFile) { #ifdef MACOS ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle); #endif fclose((FILE *)outputStream); } #ifdef HAVE_POPEN else if (fileType == psPipe) { pclose((FILE *)outputStream); #ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_DFL); #endif } #endif } if (paperSizes) { deleteGList(paperSizes, PSOutPaperSize); } if (embFontList) { delete embFontList; } deleteGList(fontInfo, PSFontInfo); deleteGHash(fontFileInfo, PSFontFileInfo); gfree(imgIDs); gfree(formIDs); if (xobjStack) { delete xobjStack; } while (customColors) { cc = customColors; customColors = cc->next; delete cc; } } 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) { Object info, obj1; PSOutPaperSize *size; double x1, y1, x2, y2; int i; switch (mode) { case psModePS: writePS("%!PS-Adobe-3.0\n"); break; case psModeEPS: writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); break; case psModeForm: writePS("%!PS-Adobe-3.0 Resource-Form\n"); break; } writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion); xref->getDocInfo(&info); if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) { writePS("%%Creator: "); writePSTextLine(obj1.getString()); } obj1.free(); if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) { writePS("%%Title: "); writePSTextLine(obj1.getString()); } obj1.free(); info.free(); writePSFmt("%%LanguageLevel: {0:d}\n", (level == psLevel1 || level == psLevel1Sep) ? 1 : (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors: (atend)\n"); writePS("%%DocumentCustomColors: (atend)\n"); } writePS("%%DocumentSuppliedResources: (atend)\n"); switch (mode) { case psModePS: if (paperMatch) { for (i = 0; i < paperSizes->getLength(); ++i) { size = (PSOutPaperSize *)paperSizes->get(i); writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n", i==0 ? "DocumentMedia:" : "+", size->w, size->h); } } else { writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", paperWidth, paperHeight); } writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1); writePS("%%EndComments\n"); if (!paperMatch) { writePS("%%BeginDefaults\n"); writePS("%%PageMedia: plain\n"); writePS("%%EndDefaults\n"); } break; case psModeEPS: epsX1 = cropBox->x1; epsY1 = cropBox->y1; epsX2 = cropBox->x2; epsY2 = cropBox->y2; if (pageRotate == 0 || pageRotate == 180) { x1 = epsX1; y1 = epsY1; x2 = epsX2; y2 = epsY2; } else { // pageRotate == 90 || pageRotate == 270 x1 = 0; y1 = 0; x2 = epsY2 - epsY1; y2 = epsX2 - epsX1; } writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2)); if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) || floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) { writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", x1, y1, x2, y2); } writePS("%%EndComments\n"); break; case psModeForm: writePS("%%EndComments\n"); writePS("32 dict dup begin\n"); writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n", (int)floor(mediaBox->x1), (int)floor(mediaBox->y1), (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2)); writePS("/FormType 1 def\n"); writePS("/Matrix [1 0 0 1 0 0] def\n"); break; } } void PSOutputDev::writeXpdfProcset() { GBool lev1, lev2, lev3, sep, nonSep; const char **p; const char *q; double w; writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion); writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); lev1 = lev2 = lev3 = sep = nonSep = gTrue; for (p = prolog; *p; ++p) { if ((*p)[0] == '~') { lev1 = lev2 = lev3 = sep = nonSep = gFalse; for (q = *p + 1; *q; ++q) { switch (*q) { case '1': lev1 = gTrue; break; case '2': lev2 = gTrue; break; case '3': lev3 = gTrue; break; case 's': sep = gTrue; break; case 'n': nonSep = gTrue; break; } } } else if ((level == psLevel1 && lev1 && nonSep) || (level == psLevel1Sep && lev1 && sep) || (level == psLevel2 && lev2 && nonSep) || (level == psLevel2Sep && lev2 && sep) || (level == psLevel3 && lev3 && nonSep) || (level == psLevel3Sep && lev3 && sep)) { writePSFmt("{0:s}\n", *p); } } 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) { for (p = cmapProlog; *p; ++p) { writePSFmt("{0:s}\n", *p); } } } void PSOutputDev::writeDocSetup(Catalog *catalog, int firstPage, int lastPage) { Page *page; Dict *resDict; Annots *annots; Form *form; Object obj1, obj2, obj3; GString *s; int pg, i, j; if (mode == psModeForm) { // swap the form and xpdf dicts writePS("xpdf end begin dup begin\n"); } else { writePS("xpdf begin\n"); } for (pg = firstPage; pg <= lastPage; ++pg) { page = catalog->getPage(pg); if ((resDict = page->getResourceDict())) { setupResources(resDict); } annots = new Annots(doc, page->getAnnots(&obj1)); obj1.free(); for (i = 0; i < annots->getNumAnnots(); ++i) { if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { obj1.streamGetDict()->lookup("Resources", &obj2); if (obj2.isDict()) { setupResources(obj2.getDict()); } obj2.free(); } obj1.free(); } delete annots; } if ((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()); } obj2.free(); } } else if (obj1.isDict()) { setupResources(obj1.getDict()); } obj1.free(); } } if (mode != psModeForm) { if (mode != psModeEPS && !manualCtrl) { writePSFmt("{0:s} pdfSetup\n", globalParams->getPSDuplex() ? "true" : "false"); if (!paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); } } #if OPI_SUPPORT if (globalParams->getPSOPI()) { writePS("/opiMatrix matrix currentmatrix def\n"); } #endif } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, customCodeCbkData))) { writePS(s->getCString()); delete s; } } if (mode != psModeForm) { writePS("end\n"); } } void PSOutputDev::writePageTrailer() { if (mode != psModeForm) { writePS("pdfEndPage\n"); } } void PSOutputDev::writeTrailer() { PSOutCustomColor *cc; if (mode == psModeForm) { writePS("/Foo exch /Form defineresource pop\n"); } else { writePS("%%DocumentSuppliedResources:\n"); writePS(embFontList->getCString()); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors:"); if (processColors & psProcessCyan) { writePS(" Cyan"); } if (processColors & psProcessMagenta) { writePS(" Magenta"); } if (processColors & psProcessYellow) { writePS(" Yellow"); } if (processColors & psProcessBlack) { writePS(" Black"); } writePS("\n"); writePS("%%DocumentCustomColors:"); for (cc = customColors; cc; cc = cc->next) { writePS(" "); writePSString(cc->name); } writePS("\n"); writePS("%%CMYKCustomColor:\n"); for (cc = customColors; cc; cc = cc->next) { writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", cc->c, cc->m, cc->y, cc->k); writePSString(cc->name); writePS("\n"); } } } } void PSOutputDev::setupResources(Dict *resDict) { 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); //----- recursively scan XObjects resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { // avoid infinite recursion on XObjects skip = gFalse; if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { ref0 = xObjRef.getRef(); for (j = 0; j < xobjStack->getLength(); ++j) { ref1 = *(Ref *)xobjStack->get(j); if (ref1.num == ref0.num && ref1.gen == ref0.gen) { skip = gTrue; break; } } if (!skip) { xobjStack->append(&ref0); } } if (!skip) { // process the XObject's resource dictionary xObjDict.dictGetVal(i, &xObj); if (xObj.isStream()) { xObj.streamGetDict()->lookup("Resources", &resObj); if (resObj.isDict()) { setupResources(resObj.getDict()); } resObj.free(); } xObj.free(); } if (xObjRef.isRef() && !skip) { xobjStack->del(xobjStack->getLength() - 1); } xObjRef.free(); } } xObjDict.free(); //----- recursively scan Patterns resDict->lookup("Pattern", &patDict); if (patDict.isDict()) { inType3Char = gTrue; for (i = 0; i < patDict.dictGetLength(); ++i) { // avoid infinite recursion on Patterns skip = gFalse; if ((patDict.dictGetValNF(i, &patRef)->isRef())) { ref0 = patRef.getRef(); for (j = 0; j < xobjStack->getLength(); ++j) { ref1 = *(Ref *)xobjStack->get(j); if (ref1.num == ref0.num && ref1.gen == ref0.gen) { skip = gTrue; break; } } if (!skip) { xobjStack->append(&ref0); } } if (!skip) { // process the Pattern's resource dictionary patDict.dictGetVal(i, &pat); if (pat.isStream()) { pat.streamGetDict()->lookup("Resources", &resObj); if (resObj.isDict()) { setupResources(resObj.getDict()); } resObj.free(); } pat.free(); } if (patRef.isRef() && !skip) { xobjStack->del(xobjStack->getLength() - 1); } patRef.free(); } inType3Char = gFalse; } patDict.free(); //----- 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) { Object obj1, obj2; Ref r; GfxFontDict *gfxFontDict; GfxFont *font; int i; gfxFontDict = NULL; resDict->lookupNF("Font", &obj1); if (obj1.isRef()) { obj1.fetch(xref, &obj2); if (obj2.isDict()) { r = obj1.getRef(); gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); } obj2.free(); } else if (obj1.isDict()) { gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict()); } if (gfxFontDict) { for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { if ((font = gfxFontDict->getFont(i))) { setupFont(font, resDict); } } delete gfxFontDict; } obj1.free(); } void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { PSFontInfo *fi; GfxFontLoc *fontLoc; GBool subst; char buf[16]; UnicodeMap *uMap; char *charName; double xs, ys; int code; double w1, w2; int i, j; // check if font is already set up 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 fontInfo entry fi = new PSFontInfo(*font->getID()); fontInfo->append(fi); xs = ys = 1; subst = gFalse; if (font->getType() == fontType3) { 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; } break; 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; 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; } } if (!fi->ff) { if (font->isCIDFont()) { error(errSyntaxError, -1, "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)"); } else { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); } delete fontLoc; return; } // scale substituted 8-bit fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->substIdx >= 0) { subst = gTrue; for (code = 0; code < 256; ++code) { if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && charName[0] == 'm' && charName[1] == '\0') { break; } } if (code < 256) { w1 = ((Gfx8BitFont *)font)->getWidth(code); } else { w1 = 0; } w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; xs = w1 / w2; if (xs < 0.1) { xs = 1; } } // handle encodings for substituted CID fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->fontType >= fontCIDType0) { subst = gTrue; if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { 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); } } delete fontLoc; } // generate PostScript code to set up the font if (font->isCIDFont()) { if (level == psLevel3 || level == psLevel3Sep) { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, fi->ff->psName, font->getWMode()); } else { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", 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, fi->ff->psName, xs, ys); for (i = 0; i < 256; i += 8) { writePS((char *)((i == 0) ? "[ " : " ")); for (j = 0; j < 8; ++j) { if (font->getType() == fontTrueType && !subst && !((Gfx8BitFont *)font)->getHasEncoding()) { sprintf(buf, "c%02x", i+j); charName = buf; } else { charName = ((Gfx8BitFont *)font)->getCharName(i+j); } writePS("/"); writePSName(charName ? charName : (char *)".notdef"); // the empty name is legal in PDF and PostScript, but PostScript // uses a double-slash (//...) for "immediately evaluated names", // so we need to add a space character here if (charName && !charName[0]) { writePS(" "); } } writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n"); } writePS("pdfMakeFont\n"); } } PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) { static char hexChar[17] = "0123456789abcdef"; GString *psName; PSFontFileInfo *ff; Object refObj, strObj, obj1, obj2; Dict *dict; int length1, length2; int c; int start[6]; GBool binMode; int n, i; // check if font is already embedded if ((ff = (PSFontFileInfo *) fontFileInfo->lookup(font->getEmbeddedFontName()))) { return ff; } // 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); refObj.fetch(xref, &strObj); refObj.free(); if (!strObj.isStream()) { error(errSyntaxError, -1, "Embedded font file object is not a stream"); goto err1; } if (!(dict = strObj.streamGetDict())) { error(errSyntaxError, -1, "Embedded font stream is missing its dictionary"); goto err1; } dict->lookup("Length1", &obj1); dict->lookup("Length2", &obj2); if (!obj1.isInt() || !obj2.isInt()) { error(errSyntaxError, -1, "Missing length fields in embedded font stream dictionary"); obj1.free(); obj2.free(); goto err1; } length1 = obj1.getInt(); length2 = obj2.getInt(); obj1.free(); obj2.free(); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // check for PFB format strObj.streamReset(); 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'); // plain text (PFA) format } else { // copy ASCII portion of font writePSChar(start[0]); writePSChar(start[1]); for (i = 2; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { writePSChar(c); } // 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; } // convert binary data to ASCII if (binMode) { for (i = 0; i < 4; ++i) { writePSChar(hexChar[(start[i] >> 4) & 0x0f]); writePSChar(hexChar[start[i] & 0x0f]); } #if 0 // this causes trouble for various PostScript printers // if Length2 is incorrect (too small), font data gets chopped, so // we take a few extra characters from the trailer just in case length2 += length3 >= 8 ? 8 : length3; #endif while (i < length2) { if ((c = strObj.streamGetChar()) == EOF) { break; } writePSChar(hexChar[(c >> 4) & 0x0f]); writePSChar(hexChar[c & 0x0f]); if (++i % 32 == 0) { writePSChar('\n'); } } if (i % 32 > 0) { writePSChar('\n'); } // already in ASCII format -- just copy it } else { for (i = 0; i < 4; ++i) { writePSChar(start[i]); } for (i = 4; i < length2; ++i) { if ((c = strObj.streamGetChar()) == EOF) { break; } writePSChar(c); } } // write padding and "cleartomark" for (i = 0; i < 8; ++i) { writePS("00000000000000000000000000000000" "00000000000000000000000000000000\n"); } writePS("cleartomark\n"); } // ending comment writePS("%%EndResource\n"); 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; } PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font, GString *fileName) { static char hexChar[17] = "0123456789abcdef"; GString *psName; PSFontFileInfo *ff; FILE *fontFile; int buf[6]; int c, n, i; 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()); } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // open the font file if (!(fontFile = fopen(fileName->getCString(), "rb"))) { error(errIO, -1, "Couldn't open external font file"); return NULL; } // 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; } PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiType1C *ffT1C; GHashIter *iter; // 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) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 1 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { ffT1C->convertToType1(psName->getCString(), NULL, gTrue, outputFunc, outputStream); delete ffT1C; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; GHashIter *iter; // 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) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 1 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { if (ffTT->isOpenTypeCFF()) { ffTT->convertToType1(psName->getCString(), NULL, gTrue, outputFunc, outputStream); } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } 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); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 42 font 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; } 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); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 42 font 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; } PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiType1C *ffT1C; GHashIter *iter; // 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) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffT1C->convertToCIDType0(psName->getCString(), NULL, 0, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffT1C->convertToType0(psName->getCString(), NULL, 0, outputFunc, outputStream); } delete ffT1C; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } 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); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 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); } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; if (codeToGIDLen) { ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int)); ff->codeToGIDLen = codeToGIDLen; } fontFileInfo->add(ff->psName, ff); return ff; } 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); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font //~ this should use fontNum to load the correct font 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; } PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; GHashIter *iter; int n; // 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) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { if (ffTT->isOpenTypeCFF()) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } } delete ffTT; } gfree(fontBuf); } // 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; } PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font, Dict *parentResDict) { PSFontFileInfo *ff; GString *psName; Dict *resDict; Dict *charProcs; Object charProc; Gfx *gfx; PDFRectangle box; double *m; 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; setupResources(resDict); inType3Char = gFalse; } else { resDict = parentResDict; } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // font dictionary writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); m = font->getFontMatrix(); writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", m[0], m[1], m[2], m[3], m[4], m[5]); m = font->getFontBBox(); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", m[0], m[1], m[2], m[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength()); writePS("CharProcs begin\n"); box.x1 = m[0]; box.y1 = m[1]; box.x2 = m[2]; box.y2 = m[3]; gfx = new Gfx(doc, this, resDict, &box, NULL); inType3Char = gTrue; for (i = 0; i < charProcs->getLength(); ++i) { t3FillColorOnly = gFalse; t3Cacheable = gFalse; t3NeedsRestore = gFalse; writePS("/"); writePSName(charProcs->getKey(i)); writePS(" {\n"); gfx->display(charProcs->getValNF(i, &charProc)); charProc.free(); if (t3String) { if (t3Cacheable) { buf = GString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); } else { buf = GString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY); } (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); delete buf; (*outputFunc)(outputStream, t3String->getCString(), t3String->getLength()); delete t3String; t3String = NULL; } if (t3NeedsRestore) { (*outputFunc)(outputStream, "Q\n", 2); } writePS("} def\n"); } inType3Char = gFalse; delete gfx; writePS("end\n"); } writePS("currentdict end\n"); writePSFmt("/{0:t} exch definefont pop\n", psName); // ending comment writePS("%%EndResource\n"); 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 // font object, and an object ID (font file object for GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) { GString *psName, *s; if ((s = font->getEmbeddedFontName())) { psName = filterPSName(s); if (!fontFileInfo->lookup(psName)) { return psName; } delete psName; } if ((s = font->getName())) { psName = filterPSName(s); if (!fontFileInfo->lookup(psName)) { return psName; } delete psName; } psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen); if ((s = font->getEmbeddedFontName())) { s = filterPSName(s); psName->append('_')->append(s); delete s; } else if ((s = font->getName())) { s = filterPSName(s); psName->append('_')->append(s); delete s; } return psName; } void PSOutputDev::setupImages(Dict *resDict) { Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef; Ref imgID; int i, j; if (!(mode == psModeForm || inType3Char || preload)) { return; } resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { xObjDict.dictGetValNF(i, &xObjRef); xObjDict.dictGetVal(i, &xObj); if (xObj.isStream()) { xObj.streamGetDict()->lookup("Subtype", &subtypeObj); if (subtypeObj.isName("Image")) { if (xObjRef.isRef()) { imgID = xObjRef.getRef(); for (j = 0; j < imgIDLen; ++j) { if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) { break; } } if (j == imgIDLen) { if (imgIDLen >= imgIDSize) { if (imgIDSize == 0) { imgIDSize = 64; } else { imgIDSize *= 2; } imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); } imgIDs[imgIDLen++] = imgID; setupImage(imgID, xObj.getStream(), gFalse); if (level >= psLevel3 && xObj.streamGetDict()->lookup("Mask", &maskObj)->isStream()) { setupImage(imgID, maskObj.getStream(), gTrue); } maskObj.free(); } } else { error(errSyntaxError, -1, "Image in resource dict is not an indirect reference"); } } subtypeObj.free(); } xObj.free(); xObjRef.free(); } } xObjDict.free(); } void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) { GBool useLZW, useRLE, useCompressed, useASCIIHex; GString *s; int c; int size, line, col, i; // filters //~ this does not correctly handle the DeviceN color space //~ -- need to use DeviceNRecoder if (level < psLevel2) { useLZW = useRLE = gFalse; useCompressed = gFalse; useASCIIHex = gTrue; } else { if (globalParams->getPSUncompressPreloadedImages()) { useLZW = useRLE = gFalse; useCompressed = gFalse; } else { s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); if (s) { useLZW = useRLE = gFalse; useCompressed = gTrue; delete s; } else { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } } useASCIIHex = globalParams->getPSASCIIHex(); } if (useCompressed) { str = str->getUndecodedStream(); } if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } // compute image data size str->reset(); col = size = 0; do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { ++col; } else { ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } ++col; } } if (col > 225) { ++size; col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); // add one entry for the final line of data; add another entry // because the LZWDecode/RunLengthDecode filter may read past the end ++size; if (useLZW || useRLE) { ++size; } writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", size, mask ? "Mask" : "Im", id.num, id.gen); str->close(); // write the data into the array str->reset(); line = col = 0; writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~")); do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar(c); ++col; } else { writePSChar(c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar(c); ++col; } } // each line is: "dup nnnnn <~...data...~> put" // so max data length = 255 - 20 = 235 // chunks are 1 or 4 bytes each, so we have to stop at 232 // but make it 225 just to be safe if (col > 225) { writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); ++line; writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); if (useLZW || useRLE) { ++line; writePSFmt("{0:d} <> put\n", line); } else { writePS("pop\n"); } str->close(); delete str; } void PSOutputDev::setupForms(Dict *resDict) { Object xObjDict, xObj, xObjRef, subtypeObj; int i; if (!preload) { return; } resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { xObjDict.dictGetValNF(i, &xObjRef); xObjDict.dictGetVal(i, &xObj); if (xObj.isStream()) { xObj.streamGetDict()->lookup("Subtype", &subtypeObj); if (subtypeObj.isName("Form")) { if (xObjRef.isRef()) { setupForm(&xObjRef, &xObj); } else { error(errSyntaxError, -1, "Form in resource dict is not an indirect reference"); } } subtypeObj.free(); } xObj.free(); xObjRef.free(); } } xObjDict.free(); } void PSOutputDev::setupForm(Object *strRef, Object *strObj) { Dict *dict, *resDict; Object matrixObj, bboxObj, resObj, obj1; double m[6], bbox[4]; PDFRectangle box; Gfx *gfx; int i; // check if form is already defined for (i = 0; i < formIDLen; ++i) { if (formIDs[i].num == strRef->getRefNum() && formIDs[i].gen == strRef->getRefGen()) { return; } } // add entry to formIDs list if (formIDLen >= formIDSize) { if (formIDSize == 0) { formIDSize = 64; } else { formIDSize *= 2; } formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref)); } formIDs[formIDLen++] = strRef->getRef(); dict = strObj->streamGetDict(); // get bounding box dict->lookup("BBox", &bboxObj); if (!bboxObj.isArray()) { bboxObj.free(); error(errSyntaxError, -1, "Bad form bounding box"); return; } for (i = 0; i < 4; ++i) { bboxObj.arrayGet(i, &obj1); bbox[i] = obj1.getNum(); obj1.free(); } bboxObj.free(); // get matrix dict->lookup("Matrix", &matrixObj); if (matrixObj.isArray()) { for (i = 0; i < 6; ++i) { matrixObj.arrayGet(i, &obj1); m[i] = obj1.getNum(); obj1.free(); } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } matrixObj.free(); // get resources dict->lookup("Resources", &resObj); resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; writePSFmt("/f_{0:d}_{1:d} {{\n", 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]); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, &box); gfx->display(strRef); delete gfx; writePS("Q\n"); writePS("} def\n"); resObj.free(); } GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI, int rotateA, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { PreScanOutputDev *scan; GBool rasterize; #if HAVE_SPLASH GBool mono; GBool useLZW; double dpi; SplashOutputDev *splashOut; SplashColor paperColor; PDFRectangle box; GfxState *state; SplashBitmap *bitmap; Stream *str0, *str; 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 w, h, x, y, comp, i, n; #endif if (globalParams->getPSAlwaysRasterize()) { rasterize = gTrue; } else { scan = new PreScanOutputDev(); page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData); rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); delete scan; } if (!rasterize) { return gTrue; } #if HAVE_SPLASH // get the rasterization parameters dpi = globalParams->getPSRasterResolution(); mono = globalParams->getPSRasterMono(); useLZW = globalParams->getPSLZW(); // start the PS page page->makeBox(dpi, dpi, rotateA, useMediaBox, gFalse, sliceX, sliceY, sliceW, sliceH, &box, &crop); rotateA += page->getRotate(); if (rotateA >= 360) { rotateA -= 360; } else if (rotateA < 0) { rotateA += 360; } state = new GfxState(dpi, dpi, &box, rotateA, gFalse); startPage(page->getNum(), state); delete state; // set up the SplashOutputDev if (mono || level == psLevel1) { paperColor[0] = 0xff; splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); #if SPLASH_CMYK } else if (level == psLevel1Sep) { paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); #endif } else { paperColor[0] = paperColor[1] = paperColor[2] = 0xff; splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); } splashOut->startDoc(xref); // break the page into stripes hDPI2 = xScale * dpi; vDPI2 = yScale * dpi; if (sliceW < 0 || sliceH < 0) { if (useMediaBox) { box = *page->getMediaBox(); } else { box = *page->getCropBox(); } sliceX = sliceY = 0; sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); } nStripes = (int)ceil(((double)sliceW * (double)sliceH) / (double)globalParams->getPSRasterSliceSize()); stripeH = (sliceH + nStripes - 1) / nStripes; // render the stripes for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { // rasterize a stripe page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse, sliceX, stripeY, sliceW, stripeH, &box, &crop); m0 = box.x2 - box.x1; m1 = 0; m2 = 0; m3 = box.y2 - box.y1; m4 = box.x1; m5 = box.y1; page->displaySlice(splashOut, hDPI2, vDPI2, (360 - page->getRotate()) % 360, useMediaBox, crop, sliceX, stripeY, sliceW, stripeH, printing, abortCheckCbk, abortCheckCbkData); // draw the rasterized image bitmap = splashOut->getBitmap(); w = bitmap->getWidth(); h = bitmap->getHeight(); writePS("gsave\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", m0, m1, m2, m3, m4, m5); switch (level) { case psLevel1: writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", w, h, w, -h, h); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); i = 0; for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { writePSFmt("{0:02x}", *p++); if (++i == 32) { writePSChar('\n'); i = 0; } } } if (i != 0) { writePSChar('\n'); } break; case psLevel1Sep: writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", w, h, w, -h, h); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); i = 0; col[0] = col[1] = col[2] = col[3] = 0; for (y = 0; y < h; ++y) { for (comp = 0; comp < 4; ++comp) { for (x = 0; x < w; ++x) { writePSFmt("{0:02x}", p[4*x + comp]); col[comp] |= p[4*x + comp]; if (++i == 32) { writePSChar('\n'); i = 0; } } } p -= bitmap->getRowSize(); } if (i != 0) { writePSChar('\n'); } if (col[0]) { processColors |= psProcessCyan; } if (col[1]) { processColors |= psProcessMagenta; } if (col[2]) { processColors |= psProcessYellow; } if (col[3]) { processColors |= psProcessBlack; } break; case psLevel2: case psLevel2Sep: case psLevel3: case psLevel3Sep: if (mono) { writePS("/DeviceGray setcolorspace\n"); } else { writePS("/DeviceRGB setcolorspace\n"); } writePS("<<\n /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); writePS(" /BitsPerComponent 8\n"); if (mono) { writePS(" /Decode [0 1]\n"); } else { writePS(" /Decode [0 1 0 1 0 1]\n"); } writePS(" /DataSource currentfile\n"); if (globalParams->getPSASCIIHex()) { writePS(" /ASCIIHexDecode filter\n"); } else { writePS(" /ASCII85Decode filter\n"); } 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); 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 ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); delete str; delete str0; writePSChar('\n'); processColors |= mono ? psProcessBlack : psProcessCMYK; break; } writePS("grestore\n"); } delete splashOut; // finish the PS page endPage(); return gFalse; #else // HAVE_SPLASH error(errSyntaxWarning, -1, "PDF page uses transparency and PSOutputDev was built without" " the Splash rasterizer - output may not be correct"); return gTrue; #endif // HAVE_SPLASH } void PSOutputDev::startPage(int pageNum, GfxState *state) { Page *page; int x1, y1, x2, y2, width, height, t; int imgWidth, imgHeight, imgWidth2, imgHeight2; GBool landscape; GString *s; if (mode == psModePS) { writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); if (paperMatch) { page = doc->getCatalog()->getPage(pageNum); imgLLX = imgLLY = 0; 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; imgURY = t; } writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY); writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY); } writePS("%%BeginPageSetup\n"); } if (mode != psModeForm) { writePS("xpdf begin\n"); } // underlays if (underlayCbk) { (*underlayCbk)(this, underlayCbkData); } if (overlayCbk) { saveState(NULL); } switch (mode) { case psModePS: // rotate, translate, and scale page imgWidth = imgURX - imgLLX; imgHeight = imgURY - imgLLY; x1 = (int)floor(state->getX1()); y1 = (int)floor(state->getY1()); x2 = (int)ceil(state->getX2()); y2 = (int)ceil(state->getY2()); width = x2 - x1; height = y2 - y1; tx = ty = 0; // rotation and portrait/landscape mode if (paperMatch) { rotate = (360 - state->getRotate()) % 360; landscape = gFalse; } else if (rotate0 >= 0) { rotate = (360 - rotate0) % 360; landscape = gFalse; } else { rotate = (360 - state->getRotate()) % 360; if (rotate == 0 || rotate == 180) { if ((width < height && imgWidth > imgHeight && height > imgHeight) || (width > height && imgWidth < imgHeight && width > imgWidth)) { rotate += 90; landscape = gTrue; } else { landscape = gFalse; } } else { // rotate == 90 || rotate == 270 if ((height < width && imgWidth > imgHeight && width > imgHeight) || (height > width && imgWidth < imgHeight && height > imgWidth)) { rotate = 270 - rotate; landscape = gTrue; } else { landscape = gFalse; } } } writePSFmt("%%PageOrientation: {0:s}\n", landscape ? "Landscape" : "Portrait"); if (paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); } writePS("pdfStartPage\n"); if (rotate == 0) { imgWidth2 = imgWidth; imgHeight2 = imgHeight; } else if (rotate == 90) { writePS("90 rotate\n"); ty = -imgWidth; imgWidth2 = imgHeight; imgHeight2 = imgWidth; } else if (rotate == 180) { writePS("180 rotate\n"); imgWidth2 = imgWidth; imgHeight2 = imgHeight; tx = -imgWidth; ty = -imgHeight; } else { // rotate == 270 writePS("270 rotate\n"); tx = -imgHeight; imgWidth2 = imgHeight; imgHeight2 = imgWidth; } // shrink or expand if (xScale0 > 0 && yScale0 > 0) { xScale = xScale0; yScale = yScale0; } else if ((globalParams->getPSShrinkLarger() && (width > imgWidth2 || height > imgHeight2)) || (globalParams->getPSExpandSmaller() && (width < imgWidth2 && height < imgHeight2))) { xScale = (double)imgWidth2 / (double)width; yScale = (double)imgHeight2 / (double)height; if (yScale < xScale) { xScale = yScale; } else { yScale = xScale; } } else { xScale = yScale = 1; } // deal with odd bounding boxes or clipping if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx -= xScale * clipLLX0; ty -= yScale * clipLLY0; } else { tx -= xScale * x1; ty -= yScale * y1; } // center if (tx0 >= 0 && ty0 >= 0) { tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; } else if (globalParams->getPSCenter()) { if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2; } else { tx += (imgWidth2 - xScale * width) / 2; ty += (imgHeight2 - yScale * height) / 2; } } tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } if (xScale != 1 || yScale != 1) { writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale); } if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); } else { writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); } ++seqPage; break; case psModeEPS: writePS("pdfStartPage\n"); tx = ty = 0; rotate = (360 - state->getRotate()) % 360; if (rotate == 0) { } else if (rotate == 90) { writePS("90 rotate\n"); tx = -epsX1; ty = -epsY2; } else if (rotate == 180) { writePS("180 rotate\n"); tx = -(epsX1 + epsX2); ty = -(epsY1 + epsY2); } else { // rotate == 270 writePS("270 rotate\n"); tx = -epsX2; ty = -epsY1; } if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } xScale = yScale = 1; break; case psModeForm: writePS("/PaintProc {\n"); writePS("begin xpdf begin\n"); writePS("pdfStartPage\n"); tx = ty = 0; xScale = yScale = 1; rotate = 0; break; } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, customCodeCbkData))) { writePS(s->getCString()); delete s; } } if (mode == psModePS) { writePS("%%EndPageSetup\n"); } } void PSOutputDev::endPage() { if (overlayCbk) { restoreState(NULL); (*overlayCbk)(this, overlayCbkData); } if (mode == psModeForm) { writePS("pdfEndPage\n"); writePS("end end\n"); writePS("} def\n"); writePS("end end\n"); } else { if (!manualCtrl) { writePS("showpage\n"); } writePS("%%PageTrailer\n"); writePageTrailer(); writePS("end\n"); } } void PSOutputDev::saveState(GfxState *state) { writePS("q\n"); ++numSaves; } void PSOutputDev::restoreState(GfxState *state) { writePS("Q\n"); --numSaves; } void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { 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) { double *dash; double start; int length, i; state->getLineDash(&dash, &length, &start); writePS("["); for (i = 0; i < length; ++i) { writePSFmt("{0:.6g}{1:w}", dash[i] < 0 ? 0 : dash[i], (i == length-1) ? 0 : 1); } writePSFmt("] {0:.6g} d\n", start); } void PSOutputDev::updateFlatness(GfxState *state) { writePSFmt("{0:d} i\n", state->getFlatness()); } void PSOutputDev::updateLineJoin(GfxState *state) { writePSFmt("{0:d} j\n", state->getLineJoin()); } void PSOutputDev::updateLineCap(GfxState *state) { writePSFmt("{0:d} J\n", state->getLineCap()); } void PSOutputDev::updateMiterLimit(GfxState *state) { writePSFmt("{0:.4g} M\n", state->getMiterLimit()); } void PSOutputDev::updateLineWidth(GfxState *state) { writePSFmt("{0:.6g} w\n", state->getLineWidth()); } void PSOutputDev::updateFillColorSpace(GfxState *state) { switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse, gFalse); writePS(" cs\n"); } break; case psLevel2Sep: case psLevel3Sep: break; } } void PSOutputDev::updateStrokeColorSpace(GfxState *state) { switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getStrokeColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse, gFalse); writePS(" CS\n"); } break; case psLevel2Sep: case psLevel3Sep: break; } } void PSOutputDev::updateFillColor(GfxState *state) { GfxColor color; GfxColor *colorPtr; GfxGray gray; GfxCMYK cmyk; GfxSeparationColorSpace *sepCS; double c, m, y, k; int i; switch (level) { case psLevel1: state->getFillGray(&gray); writePSFmt("{0:.4g} g\n", colToDbl(gray)); break; case psLevel1Sep: state->getFillCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); addProcessColor(c, m, y, k); break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { colorPtr = state->getFillColor(); writePS("["); for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); } writePS("] sc\n"); } break; case psLevel2Sep: case psLevel3Sep: if (state->getFillColorSpace()->getMode() == csSeparation) { sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n", colToDbl(state->getFillColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); addCustomColor(sepCS); } else { state->getFillCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); addProcessColor(c, m, y, k); } break; } t3Cacheable = gFalse; } void PSOutputDev::updateStrokeColor(GfxState *state) { GfxColor color; GfxColor *colorPtr; GfxGray gray; GfxCMYK cmyk; GfxSeparationColorSpace *sepCS; double c, m, y, k; int i; switch (level) { case psLevel1: state->getStrokeGray(&gray); writePSFmt("{0:.4g} G\n", colToDbl(gray)); break; case psLevel1Sep: state->getStrokeCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); addProcessColor(c, m, y, k); break; case psLevel2: case psLevel3: if (state->getStrokeColorSpace()->getMode() != csPattern) { colorPtr = state->getStrokeColor(); writePS("["); for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); } writePS("] SC\n"); } break; case psLevel2Sep: case psLevel3Sep: if (state->getStrokeColorSpace()->getMode() == csSeparation) { sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n", colToDbl(state->getStrokeColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); addCustomColor(sepCS); } else { state->getStrokeCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); addProcessColor(c, m, y, k); } break; } t3Cacheable = gFalse; } void PSOutputDev::addProcessColor(double c, double m, double y, double k) { if (c > 0) { processColors |= psProcessCyan; } if (m > 0) { processColors |= psProcessMagenta; } if (y > 0) { processColors |= psProcessYellow; } if (k > 0) { processColors |= psProcessBlack; } } void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) { PSOutCustomColor *cc; GfxColor color; GfxCMYK cmyk; for (cc = customColors; cc; cc = cc->next) { if (!cc->name->cmp(sepCS->getName())) { return; } } color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk); cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()->copy()); cc->next = customColors; customColors = cc; } void PSOutputDev::updateFillOverprint(GfxState *state) { if (level >= psLevel2) { writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false"); } } void PSOutputDev::updateStrokeOverprint(GfxState *state) { if (level >= psLevel2) { writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false"); } } void PSOutputDev::updateTransfer(GfxState *state) { Function **funcs; int i; funcs = state->getTransfer(); if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { if (level >= psLevel2) { for (i = 0; i < 4; ++i) { cvtFunction(funcs[i]); } writePS("setcolortransfer\n"); } else { cvtFunction(funcs[3]); writePS("settransfer\n"); } } else if (funcs[0]) { cvtFunction(funcs[0]); writePS("settransfer\n"); } else { writePS("{} settransfer\n"); } } void PSOutputDev::updateFont(GfxState *state) { if (state->getFont()) { writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", state->getFont()->getID()->num, state->getFont()->getID()->gen, fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize()); } } void PSOutputDev::updateTextMat(GfxState *state) { double *mat; mat = state->getTextMat(); if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) { // avoid a singular (or close-to-singular) matrix writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]); } else { writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); } } void PSOutputDev::updateCharSpace(GfxState *state) { writePSFmt("{0:.6g} Tc\n", state->getCharSpace()); } void PSOutputDev::updateRender(GfxState *state) { int rm; rm = state->getRender(); writePSFmt("{0:d} Tr\n", rm); rm &= 3; if (rm != 0 && rm != 3) { t3Cacheable = gFalse; } } void PSOutputDev::updateRise(GfxState *state) { writePSFmt("{0:.6g} Ts\n", state->getRise()); } void PSOutputDev::updateWordSpace(GfxState *state) { writePSFmt("{0:.6g} Tw\n", state->getWordSpace()); } void PSOutputDev::updateHorizScaling(GfxState *state) { double h; h = state->getHorizScaling(); if (fabs(h) < 0.01) { h = 0.01; } writePSFmt("{0:.6g} Tz\n", h); } void PSOutputDev::updateTextPos(GfxState *state) { writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY()); } void PSOutputDev::updateTextShift(GfxState *state, double shift) { if (state->getFont()->getWMode()) { writePSFmt("{0:.6g} TJmV\n", shift); } else { writePSFmt("{0:.6g} TJm\n", shift); } } void PSOutputDev::saveTextPos(GfxState *state) { writePS("currentpoint\n"); } void PSOutputDev::restoreTextPos(GfxState *state) { writePS("m\n"); } void PSOutputDev::stroke(GfxState *state) { doPath(state->getPath()); if (inType3Char && t3FillColorOnly) { // if we're constructing a cacheable Type 3 glyph, we need to do // everything in the fill color writePS("Sf\n"); } else { writePS("S\n"); } } void PSOutputDev::fill(GfxState *state) { doPath(state->getPath()); writePS("f\n"); } void PSOutputDev::eoFill(GfxState *state) { doPath(state->getPath()); writePS("f*\n"); } void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef, int paintType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx2; // define a Type 3 font writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); writePS("/FontMatrix [1 0 0 1 0 0] def\n"); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS(" Encoding 120 /x put\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); writePS("/CharProcs 1 dict def\n"); writePS("CharProcs begin\n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; 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", xStep, bbox[0], bbox[1], bbox[2], bbox[3]); t3FillColorOnly = gTrue; } else { if (x1 - 1 <= x0) { writePS("1 0 setcharwidth\n"); } else { writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); } t3FillColorOnly = gFalse; } inType3Char = gTrue; ++numTilingPatterns; gfx2->display(strRef); --numTilingPatterns; inType3Char = gFalse; writePS("} def\n"); delete gfx2; writePS("end\n"); writePS("currentdict end\n"); writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); // draw the tiles writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); writePS("fCol\n"); writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); writePS("grestore\n"); } GBool PSOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { double x0, y0, x1, y1; double *mat; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return gFalse; } processColors |= psProcessCMYK; } shading->getDomain(&x0, &y0, &x1, &y1); mat = shading->getMatrix(); writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("2 copy\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("3 1 roll\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1); return gTrue; } GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, x1, y1, dx, dy, mul; double tMin, tMax, t, t0, t1; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return gFalse; } processColors |= psProcessCMYK; } // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // compute min and max t values, based on the four corners of the // clip region bbox shading->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; if (fabs(dx) < 0.01 && fabs(dy) < 0.01) { return gTrue; } else { mul = 1 / (dx * dx + dy * dy); tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } if (tMin < 0 && !shading->getExtend0()) { tMin = 0; } if (tMax > 1 && !shading->getExtend1()) { tMax = 1; } } // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); // generate the PS code writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/xMin {0:.6g} def\n", xMin); writePSFmt("/yMin {0:.6g} def\n", yMin); writePSFmt("/xMax {0:.6g} def\n", xMax); writePSFmt("/yMax {0:.6g} def\n", yMax); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax); return gTrue; } GBool PSOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, r0, x1, y1, r1, t0, t1; double xa, ya, ra; double sMin, sMax, h, ta; double sLeft, sRight, sTop, sBottom, sZero, sDiag; GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; GBool haveSMin, haveSMax; double theta, alpha, a1, a2; GBool enclosed; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return gFalse; } processColors |= psProcessCMYK; } // get the shading info shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); t0 = shading->getDomain0(); t1 = shading->getDomain1(); // Compute the point at which r(s) = 0; check for the enclosed // circles case; and compute the angles for the tangent lines. h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); if (h == 0) { enclosed = gTrue; theta = 0; // make gcc happy } else if (r1 - r0 == 0) { enclosed = gFalse; theta = 0; } else if (fabs(r1 - r0) >= h) { enclosed = gTrue; theta = 0; // make gcc happy } else { enclosed = gFalse; theta = asin((r1 - r0) / h); } if (enclosed) { a1 = 0; a2 = 360; } else { alpha = atan2(y1 - y0, x1 - x0); a1 = (180 / M_PI) * (alpha + theta) + 90; a2 = (180 / M_PI) * (alpha - theta) - 90; while (a2 < a1) { a2 += 360; } } // compute the (possibly extended) s range state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); if (enclosed) { sMin = 0; sMax = 1; } else { // solve x(sLeft) + r(sLeft) = xMin if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); } else { sLeft = 0; // make gcc happy } // solve x(sRight) - r(sRight) = xMax if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); } else { sRight = 0; // make gcc happy } // solve y(sBottom) + r(sBottom) = yMin if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); } else { sBottom = 0; // make gcc happy } // solve y(sTop) - r(sTop) = yMax if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); } else { sTop = 0; // make gcc happy } // solve r(sZero) = 0 if ((haveSZero = fabs(r1 - r0) > 0.000001)) { sZero = -r0 / (r1 - r0); } else { sZero = 0; // make gcc happy } // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) if (haveSZero) { sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); } else { sDiag = 0; // make gcc happy } // compute sMin if (shading->getExtend0()) { sMin = 0; haveSMin = gFalse; if (x0 < x1 && haveSLeft && sLeft < 0) { sMin = sLeft; haveSMin = gTrue; } else if (x0 > x1 && haveSRight && sRight < 0) { sMin = sRight; haveSMin = gTrue; } if (y0 < y1 && haveSBottom && sBottom < 0) { if (!haveSMin || sBottom > sMin) { sMin = sBottom; haveSMin = gTrue; } } else if (y0 > y1 && haveSTop && sTop < 0) { if (!haveSMin || sTop > sMin) { sMin = sTop; haveSMin = gTrue; } } if (haveSZero && sZero < 0) { if (!haveSMin || sZero > sMin) { sMin = sZero; } } } else { sMin = 0; } // compute sMax if (shading->getExtend1()) { sMax = 1; haveSMax = gFalse; if (x1 < x0 && haveSLeft && sLeft > 1) { sMax = sLeft; haveSMax = gTrue; } else if (x1 > x0 && haveSRight && sRight > 1) { sMax = sRight; haveSMax = gTrue; } if (y1 < y0 && haveSBottom && sBottom > 1) { if (!haveSMax || sBottom < sMax) { sMax = sBottom; haveSMax = gTrue; } } else if (y1 > y0 && haveSTop && sTop > 1) { if (!haveSMax || sTop < sMax) { sMax = sTop; haveSMax = gTrue; } } if (haveSZero && sDiag > 1) { if (!haveSMax || sDiag < sMax) { sMax = sDiag; } } } else { sMax = 1; } } // generate the PS code writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/r0 {0:.6g} def\n", r0); writePSFmt("/r1 {0:.6g} def\n", r1); writePSFmt("/dr {0:.6g} def\n", r1 - r0); writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false"); writePSFmt("/a1 {0:.6g} def\n", a1); writePSFmt("/a2 {0:.6g} def\n", a2); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax); // extend the 'enclosed' case if (enclosed) { // extend the smaller circle if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) { if (r0 <= r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra); } // extend the larger circle if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) { if (r0 > r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra); writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); } } return gTrue; } void PSOutputDev::clip(GfxState *state) { doPath(state->getPath()); writePS("W\n"); } void PSOutputDev::eoClip(GfxState *state) { doPath(state->getPath()); writePS("W*\n"); } void PSOutputDev::clipToStrokePath(GfxState *state) { doPath(state->getPath()); writePS("Ws\n"); } void PSOutputDev::doPath(GfxPath *path) { GfxSubpath *subpath; double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4; int n, m, i, j; n = path->getNumSubpaths(); if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) { subpath = path->getSubpath(0); x0 = subpath->getX(0); y0 = subpath->getY(0); x4 = subpath->getX(4); y4 = subpath->getY(4); if (x4 == x0 && y4 == y0) { x1 = subpath->getX(1); y1 = subpath->getY(1); x2 = subpath->getX(2); y2 = subpath->getY(2); x3 = subpath->getX(3); y3 = subpath->getY(3); if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, fabs(x2 - x0), fabs(y1 - y0)); return; } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, fabs(x1 - x0), fabs(y2 - y0)); return; } } } for (i = 0; i < n; ++i) { subpath = path->getSubpath(i); m = subpath->getNumPoints(); writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0)); j = 1; while (j < m) { if (subpath->getCurve(j)) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", subpath->getX(j), subpath->getY(j), subpath->getX(j+1), subpath->getY(j+1), subpath->getX(j+2), subpath->getY(j+2)); j += 3; } else { writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j)); ++j; } } if (subpath->isClosed()) { writePS("h\n"); } } } void PSOutputDev::drawString(GfxState *state, GString *s) { GfxFont *font; int wMode; int *codeToGID; GString *s2; double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0; char *p; PSFontInfo *fi; UnicodeMap *uMap; CharCode code; Unicode u[8]; char buf[8]; double *dxdy; int dxdySize, len, nChars, uLen, n, m, i, j; // check for invisible text -- this is used by Acrobat Capture if (state->getRender() == 3) { return; } // ignore empty strings if (s->getLength() == 0) { return; } // get the font if (!(font = state->getFont())) { return; } wMode = font->getWMode(); 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()) { 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 an 8-bit code-to-GID map } else { if (fi && fi->ff) { codeToGID = fi->ff->codeToGID; } } // compute the positioning (dx, dy) for each char in the string nChars = 0; p = s->getCString(); len = s->getLength(); 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) { dy += state->getCharSpace(); if (n == 1 && *p == ' ') { dy += state->getWordSpace(); } } else { dx += state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } } dx *= state->getHorizScaling(); if (font->isCIDFont()) { if (uMap) { if (nChars + uLen > dxdySize) { do { dxdySize *= 2; } while (nChars + uLen > dxdySize); dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } for (i = 0; i < uLen; ++i) { m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); for (j = 0; j < m; ++j) { s2->append(buf[j]); } //~ this really needs to get the number of chars in the target //~ encoding - which may be more than the number of Unicode //~ chars dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (nChars + 1 > dxdySize) { dxdySize *= 2; dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } s2->append((char)((code >> 8) & 0xff)); s2->append((char)(code & 0xff)); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (!codeToGID || codeToGID[code] >= 0) { s2->append((char)code); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } p += n; len -= n; } 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) { if (i > 0) { writePS("\n"); } writePSFmt("{0:.6g}", dxdy[i]); } writePS("] Tj\n"); if (wMode) { writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0); } } gfree(dxdy); delete s2; if (state->getRender() & 4) { haveTextClip = gTrue; } } void PSOutputDev::endTextObject(GfxState *state) { if (haveTextClip) { writePS("Tclip\n"); haveTextClip = gFalse; } } void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { int len; len = height * ((width + 7) / 8); switch (level) { case psLevel1: case psLevel1Sep: doImageL1(ref, NULL, invert, inlineImg, str, width, height, len); break; case psLevel2: case psLevel2Sep: doImageL2(ref, NULL, invert, inlineImg, str, width, height, len, NULL, NULL, 0, 0, gFalse); break; case psLevel3: case psLevel3Sep: doImageL3(ref, NULL, invert, inlineImg, str, width, height, len, NULL, NULL, 0, 0, gFalse); break; } } void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len); break; case psLevel2: case psLevel2Sep: doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len, maskColors, NULL, 0, 0, gFalse); break; case psLevel3: case psLevel3Sep: doImageL3(ref, colorMap, gFalse, inlineImg, str, width, height, len, maskColors, NULL, 0, 0, gFalse); break; } t3Cacheable = gFalse; } void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len); break; case psLevel2: case psLevel2Sep: doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len, NULL, maskStr, maskWidth, maskHeight, maskInvert); break; case psLevel3: case psLevel3Sep: doImageL3(ref, colorMap, gFalse, gFalse, str, width, height, len, NULL, maskStr, maskWidth, maskHeight, maskInvert); break; } t3Cacheable = gFalse; } void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len) { ImageStream *imgStr; Guchar pixBuf[gfxColorMaxComps]; GfxGray gray; int col, x, y, c, i; if ((inType3Char || preload) && !colorMap) { if (inlineImg) { // create an array str = new FixedLengthEncoder(str, len); str = new ASCIIHexEncoder(str); str->reset(); col = 0; writePS("[<"); do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == '>' || c == EOF) { break; } writePSChar(c); ++col; // each line is: "<...data...>" // so max data length = 255 - 4 = 251 // but make it 240 just to be safe // chunks are 2 bytes each, so we need to stop on an even col number if (col == 240) { writePS(">\n<"); col = 0; } } while (c != '>' && c != EOF); writePS(">]\n"); writePS("0\n"); str->close(); delete str; } else { // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); } } // image/imagemask command if ((inType3Char || preload) && !colorMap) { writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n", width, height, invert ? "true" : "false", width, -height, height); } else if (colorMap) { writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", width, height, width, -height, height); } else { writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n", width, height, invert ? "true" : "false", width, -height, height); } // image data if (!((inType3Char || preload) && !colorMap)) { if (colorMap) { // set up to process the data stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // process the data stream i = 0; for (y = 0; y < height; ++y) { // write the line for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getGray(pixBuf, &gray); writePSFmt("{0:02x}", colToByte(gray)); if (++i == 32) { writePSChar('\n'); i = 0; } } } if (i != 0) { writePSChar('\n'); } str->close(); delete imgStr; // imagemask } else { str->reset(); i = 0; for (y = 0; y < height; ++y) { for (x = 0; x < width; x += 8) { writePSFmt("{0:02x}", str->getChar() & 0xff); if (++i == 32) { writePSChar('\n'); i = 0; } } } if (i != 0) { writePSChar('\n'); } str->close(); } } } void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len) { ImageStream *imgStr; Guchar *lineBuf; Guchar pixBuf[gfxColorMaxComps]; GfxCMYK cmyk; int x, y, i, comp; // width, height, matrix, bits per component writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", width, height, width, -height, height); // allocate a line buffer lineBuf = (Guchar *)gmallocn(width, 4); // set up to process the data stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // process the data stream i = 0; for (y = 0; y < height; ++y) { // read the line for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); lineBuf[4*x+0] = colToByte(cmyk.c); lineBuf[4*x+1] = colToByte(cmyk.m); lineBuf[4*x+2] = colToByte(cmyk.y); lineBuf[4*x+3] = colToByte(cmyk.k); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } // write one line of each color component for (comp = 0; comp < 4; ++comp) { for (x = 0; x < width; ++x) { writePSFmt("{0:02x}", lineBuf[4*x + comp]); if (++i == 32) { writePSChar('\n'); i = 0; } } } } if (i != 0) { writePSChar('\n'); } str->close(); delete imgStr; gfree(lineBuf); } void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert) { Stream *str2; ImageStream *imgStr; Guchar *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; GBool emitRect, addRect, extendRect; GString *s; int n, numComps; GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed; GfxSeparationColorSpace *sepCS; GfxColor color; GfxCMYK cmyk; char buf[4096]; int c; int col, i, j, x0, x1, y, maskXor; // color key masking if (maskColors && colorMap && !inlineImg) { // can't read the stream twice for inline images -- but masking // isn't allowed with inline images anyway numComps = colorMap->getNumPixelComps(); imgStr = new ImageStream(str, width, numComps, colorMap->getBits()); imgStr->reset(); rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); for (y = 0; y < height; ++y) { if (!(line = imgStr->getLine())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < width; ++x0) { for (j = 0; j < numComps; ++j) { if (line[x0*numComps+j] < maskColors[2*j] || line[x0*numComps+j] > maskColors[2*j+1]) { break; } } if (j < numComps) { break; } } for (x1 = x0; x1 < width; ++x1) { for (j = 0; j < numComps; ++j) { if (line[x1*numComps+j] < maskColors[2*j] || line[x1*numComps+j] > maskColors[2*j+1]) { break; } } if (j == numComps) { break; } } while (x0 < width || i < rects0Len) { emitRect = addRect = extendRect = gFalse; if (x0 >= width) { emitRect = gTrue; } else if (i >= rects0Len) { addRect = gTrue; } else if (rects0[i].x0 < x0) { emitRect = gTrue; } else if (x0 < rects0[i].x0) { addRect = gTrue; } else if (rects0[i].x1 == x1) { extendRect = gTrue; } else { emitRect = addRect = gTrue; } if (emitRect) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = height - y - 1; rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; ++rectsOutLen; ++i; } if (addRect || extendRect) { if (rects1Len == rectsSize) { rectsSize *= 2; rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); } rects1[rects1Len].x0 = x0; rects1[rects1Len].x1 = x1; if (addRect) { rects1[rects1Len].y0 = y; } if (extendRect) { rects1[rects1Len].y0 = rects0[i].y0; ++i; } ++rects1Len; for (x0 = x1; x0 < width; ++x0) { for (j = 0; j < numComps; ++j) { if (line[x0*numComps+j] < maskColors[2*j] || line[x0*numComps+j] > maskColors[2*j+1]) { break; } } if (j < numComps) { break; } } for (x1 = x0; x1 < width; ++x1) { for (j = 0; j < numComps; ++j) { if (line[x1*numComps+j] < maskColors[2*j] || line[x1*numComps+j] > maskColors[2*j+1]) { break; } } if (j == numComps) { break; } } } } rectsTmp = rects0; rects0 = rects1; rects1 = rectsTmp; i = rects0Len; rects0Len = rects1Len; rects1Len = i; } for (i = 0; i < rects0Len; ++i) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = height - y - 1; rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; ++rectsOutLen; } writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0); } writePS("pop pop pdfImClip\n"); gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; str->close(); // explicit masking } else if (maskStr) { imgStr = new ImageStream(maskStr, maskWidth, 1, 1); imgStr->reset(); rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); maskXor = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; ++y) { if (!(line = imgStr->getLine())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; while (x0 < maskWidth || i < rects0Len) { emitRect = addRect = extendRect = gFalse; if (x0 >= maskWidth) { emitRect = gTrue; } else if (i >= rects0Len) { addRect = gTrue; } else if (rects0[i].x0 < x0) { emitRect = gTrue; } else if (x0 < rects0[i].x0) { addRect = gTrue; } else if (rects0[i].x1 == x1) { extendRect = gTrue; } else { emitRect = addRect = gTrue; } if (emitRect) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = maskHeight - y - 1; rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; ++rectsOutLen; ++i; } if (addRect || extendRect) { if (rects1Len == rectsSize) { rectsSize *= 2; rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); } rects1[rects1Len].x0 = x0; rects1[rects1Len].x1 = x1; if (addRect) { rects1[rects1Len].y0 = y; } if (extendRect) { rects1[rects1Len].y0 = rects0[i].y0; ++i; } ++rects1Len; for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; } } rectsTmp = rects0; rects0 = rects1; rects1 = rectsTmp; i = rects0Len; rects0Len = rects1Len; rects1Len = i; } for (i = 0; i < rects0Len; ++i) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = maskHeight - y - 1; rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; ++rectsOutLen; } writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0); } writePS("pop pop pdfImClip\n"); gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; maskStr->close(); } // color space if (colorMap) { dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse); writePS(" setcolorspace\n"); } useASCIIHex = globalParams->getPSASCIIHex(); // set up the image data if (mode == psModeForm || inType3Char || preload) { if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); if (globalParams->getPSLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar(c); ++col; } else { writePSChar(c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar(c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode // filter may read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); } } // image dictionary writePS("<<\n /ImageType 1\n"); // width, height, matrix, bits per component writePSFmt(" /Width {0:d}\n", width); writePSFmt(" /Height {0:d}\n", height); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height); if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) { // this matches up with the code in the pdfImSep operator n = (1 << colorMap->getBits()) - 1; writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n); } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getAlt()->getNComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePS("0 1"); } } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); } // data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource { pdfImStr }\n"); } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; useLZW = useRLE = gFalse; useCompressed = gFalse; useASCII = gFalse; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " "); if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useASCII = !(mode == psModeForm || inType3Char || preload); useCompressed = gFalse; } else { useLZW = useRLE = gFalse; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preload); useCompressed = gTrue; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->getCString()); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preload) { // end of image dictionary writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask"); // get rid of the array and index writePS("pop pop\n"); } else { // cut off inline image streams at appropriate length if (inlineImg) { str = new FixedLengthEncoder(str, len); } else if (useCompressed) { str = str->getUndecodedStream(); } // recode DeviceN data if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // end of image dictionary writePS(">>\n"); #if OPI_SUPPORT if (opi13Nest) { if (inlineImg) { // this can't happen -- OPI dictionaries are in XObjects error(errSyntaxError, -1, "OPI in inline image"); n = 0; } else { // need to read the stream to count characters -- the length // is data-dependent (because of ASCII and LZW/RunLength // filters) str->reset(); n = 0; do { i = str->discardChars(4096); n += i; } while (i == 4096); str->close(); } // +6/7 for "pdfIm\n" / "pdfImM\n" // +8 for newline + trailer n += colorMap ? 14 : 15; writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n); } #endif if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } // copy the stream data str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); #if OPI_SUPPORT if (opi13Nest) { writePS("%%EndData\n"); } #endif // delete encoders if (useLZW || useRLE || useASCII || inlineImg) { delete str; } } if ((maskColors && colorMap && !inlineImg) || maskStr) { writePS("pdfImClipEnd\n"); } } //~ this doesn't currently support OPI void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert) { Stream *str2; GString *s; int n, numComps; GBool 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(); 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 if (maskStr) { // mask data source if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; maskUseLZW = maskUseRLE = gFalse; maskUseCompressed = gFalse; maskUseASCII = gFalse; } else { s = maskStr->getPSFilter(3, " "); if (!s) { if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = !(mode == psModeForm || inType3Char || preload); maskUseCompressed = gFalse; } else { maskUseLZW = maskUseRLE = gFalse; maskUseASCII = maskStr->isBinary() && !(mode == psModeForm || inType3Char || preload); maskUseCompressed = gTrue; } } maskFilters = new GString(); if (maskUseASCII) { maskFilters->appendf(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (maskUseLZW) { maskFilters->append(" /LZWDecode filter\n"); } else if (maskUseRLE) { maskFilters->append(" /RunLengthDecode filter\n"); } if (maskUseCompressed) { maskFilters->append(s); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preload) { writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ref->getRefNum(), ref->getRefGen()); } else { writePS("currentfile\n"); writePS(maskFilters->getCString()); writePS("pdfMask\n"); // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (maskUseCompressed) { maskStr = maskStr->getUndecodedStream(); } if (maskUseLZW) { maskStr = new LZWEncoder(maskStr); } else if (maskUseRLE) { maskStr = new RunLengthEncoder(maskStr); } if (maskUseASCII) { if (useASCIIHex) { maskStr = new ASCIIHexEncoder(maskStr); } else { maskStr = new ASCII85Encoder(maskStr); } } // copy the stream data maskStr->reset(); while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } maskStr->close(); writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (maskUseLZW || maskUseRLE || maskUseASCII) { delete maskStr; } } } // color space if (colorMap) { dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse); writePS(" setcolorspace\n"); } // set up the image data if (mode == psModeForm || inType3Char || preload) { if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); if (globalParams->getPSLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar(c); ++col; } else { writePSChar(c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar(c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode // filter may read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); } } // explicit masking if (maskStr) { writePS("<<\n /ImageType 3\n"); writePS(" /InterleaveType 3\n"); writePS(" /DataDict\n"); } // image (data) dictionary writePSFmt("<<\n /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1); // color key masking if (maskColors && colorMap) { writePS(" /MaskColor [\n"); numComps = colorMap->getNumPixelComps(); for (i = 0; i < 2 * numComps; i += 2) { writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i+1]); } writePS(" ]\n"); } // width, height, matrix, bits per component writePSFmt(" /Width {0:d}\n", width); writePSFmt(" /Height {0:d}\n", height); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height); if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) { // this matches up with the code in the pdfImSep operator n = (1 << colorMap->getBits()) - 1; writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n); } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getAlt()->getNComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePS("0 1"); } } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); } // data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource { pdfImStr }\n"); } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; useLZW = useRLE = gFalse; useCompressed = gFalse; useASCII = gFalse; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " "); if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useASCII = !(mode == psModeForm || inType3Char || preload); useCompressed = gFalse; } else { useLZW = useRLE = gFalse; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preload); useCompressed = gTrue; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->getCString()); } if (s) { delete s; } // end of image (data) dictionary writePS(">>\n"); // explicit masking if (maskStr) { writePS(" /MaskDict\n"); writePS("<<\n"); writePS(" /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", maskWidth); writePSFmt(" /Height {0:d}\n", maskHeight); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", maskWidth, -maskHeight, maskHeight); writePS(" /BitsPerComponent 1\n"); writePSFmt(" /Decode [{0:d} {1:d}]\n", maskInvert ? 1 : 0, maskInvert ? 0 : 1); // mask data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource {pdfMaskSrc}\n"); writePS(maskFilters->getCString()); } else { writePS(" /DataSource maskStream\n"); } delete maskFilters; writePS(">>\n"); writePS(">>\n"); } if (mode == psModeForm || inType3Char || preload) { // image command writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); } else { if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } } // get rid of the array and index if (mode == psModeForm || inType3Char || preload) { writePS("pop pop\n"); // image data } else { // cut off inline image streams at appropriate length if (inlineImg) { str = new FixedLengthEncoder(str, len); } else if (useCompressed) { str = str->getUndecodedStream(); } // recode DeviceN data if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // copy the stream data str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (useLZW || useRLE || useASCII || inlineImg) { delete str; } } // close the mask stream if (maskStr) { if (!(mode == psModeForm || inType3Char || preload)) { writePS("pdfMaskEnd\n"); } } } void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace, GBool genXform, GBool updateColors, GBool map01) { GfxCalGrayColorSpace *calGrayCS; GfxCalRGBColorSpace *calRGBCS; GfxLabColorSpace *labCS; GfxIndexedColorSpace *indexedCS; GfxSeparationColorSpace *separationCS; GfxDeviceNColorSpace *deviceNCS; GfxColorSpace *baseCS; Guchar *lookup, *p; double x[gfxColorMaxComps], y[gfxColorMaxComps]; double low[gfxColorMaxComps], range[gfxColorMaxComps]; GfxColor color; GfxCMYK cmyk; Function *func; int n, numComps, numAltComps; int byte; int i, j, k; switch (colorSpace->getMode()) { case csDeviceGray: writePS("/DeviceGray"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } break; case csCalGray: calGrayCS = (GfxCalGrayColorSpace *)colorSpace; writePS("[/CIEBasedA <<\n"); writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma()); writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), calGrayCS->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), calGrayCS->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getBlackX(), calGrayCS->getBlackY(), calGrayCS->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } break; case csDeviceRGB: writePS("/DeviceRGB"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csCalRGB: calRGBCS = (GfxCalRGBColorSpace *)colorSpace; writePS("[/CIEBasedABC <<\n"); writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", calRGBCS->getGammaR(), calRGBCS->getGammaG(), calRGBCS->getGammaB()); writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5], calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], calRGBCS->getMatrix()[8]); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), calRGBCS->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", calRGBCS->getBlackX(), calRGBCS->getBlackY(), calRGBCS->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csDeviceCMYK: writePS("/DeviceCMYK"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csLab: labCS = (GfxLabColorSpace *)colorSpace; writePS("[/CIEBasedABC <<\n"); if (map01) { writePS(" /RangeABC [0 1 0 1 0 1]\n"); writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n", (labCS->getAMax() - labCS->getAMin()) / 500.0, labCS->getAMin() / 500.0, (labCS->getBMax() - labCS->getBMin()) / 200.0, labCS->getBMin() / 200.0); } else { writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", labCS->getAMin(), labCS->getAMax(), labCS->getBMin(), labCS->getBMax()); writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); } writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); writePS(" /DecodeLMN\n"); writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", labCS->getWhiteX()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", labCS->getWhiteY()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n", labCS->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csICCBased: // there is no transform function to the alternate color space, so // we can use it directly dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(), genXform, updateColors, gFalse); break; case csIndexed: indexedCS = (GfxIndexedColorSpace *)colorSpace; baseCS = indexedCS->getBase(); writePS("[/Indexed "); dumpColorSpaceL2(baseCS, gFalse, gFalse, gTrue); n = indexedCS->getIndexHigh(); numComps = baseCS->getNComps(); lookup = indexedCS->getLookup(); writePSFmt(" {0:d} <\n", n); if (baseCS->getMode() == csDeviceN) { func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh()); if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) { labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt(); } else { labCS = NULL; } numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps(); p = lookup; for (i = 0; i <= n; i += 8) { writePS(" "); for (j = i; j < i+8 && j <= n; ++j) { for (k = 0; k < numComps; ++k) { x[k] = low[k] + (*p++ / 255.0) * range[k]; } func->transform(x, y); if (labCS) { y[0] /= 100.0; y[1] = (y[1] - labCS->getAMin()) / (labCS->getAMax() - labCS->getAMin()); y[2] = (y[2] - labCS->getBMin()) / (labCS->getBMax() - labCS->getBMin()); } for (k = 0; k < numAltComps; ++k) { byte = (int)(y[k] * 255 + 0.5); if (byte < 0) { byte = 0; } else if (byte > 255) { byte = 255; } writePSFmt("{0:02x}", byte); } if (updateColors) { color.c[0] = dblToCol(j); indexedCS->getCMYK(&color, &cmyk); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } else { for (i = 0; i <= n; i += 8) { writePS(" "); for (j = i; j < i+8 && j <= n; ++j) { for (k = 0; k < numComps; ++k) { writePSFmt("{0:02x}", lookup[j * numComps + k]); } if (updateColors) { color.c[0] = dblToCol(j); indexedCS->getCMYK(&color, &cmyk); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } writePS(">]"); if (genXform) { writePS(" {}"); } break; case csSeparation: separationCS = (GfxSeparationColorSpace *)colorSpace; writePS("[/Separation "); writePSString(separationCS->getName()); writePS(" "); dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse, gFalse); writePS("\n"); cvtFunction(separationCS->getFunc()); writePS("]"); if (genXform) { writePS(" {}"); } if (updateColors) { addCustomColor(separationCS); } break; case csDeviceN: // DeviceN color spaces are a Level 3 PostScript feature. deviceNCS = (GfxDeviceNColorSpace *)colorSpace; dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors, map01); if (genXform) { writePS(" "); cvtFunction(deviceNCS->getTintTransformFunc()); } break; case csPattern: //~ unimplemented break; } } #if OPI_SUPPORT void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { Object dict; if (globalParams->getPSOPI()) { opiDict->lookup("2.0", &dict); if (dict.isDict()) { opiBegin20(state, dict.getDict()); dict.free(); } else { dict.free(); opiDict->lookup("1.3", &dict); if (dict.isDict()) { opiBegin13(state, dict.getDict()); } dict.free(); } } } void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { Object obj1, obj2, obj3, obj4; double width, height, left, right, top, bottom; int w, h; int i; writePS("%%BeginOPI: 2.0\n"); writePS("%%Distilled\n"); dict->lookup("F", &obj1); if (getFileSpec(&obj1, &obj2)) { writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString()); obj2.free(); } obj1.free(); dict->lookup("MainImage", &obj1); if (obj1.isString()) { writePSFmt("%%MainImage: {0:t}\n", obj1.getString()); } obj1.free(); //~ ignoring 'Tags' entry //~ need to use writePSString() and deal with >255-char lines dict->lookup("Size", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); width = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); height = obj2.getNum(); obj2.free(); writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height); } obj1.free(); dict->lookup("CropRect", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 4) { obj1.arrayGet(0, &obj2); left = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); top = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); right = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); bottom = obj2.getNum(); obj2.free(); writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", left, top, right, bottom); } obj1.free(); dict->lookup("Overprint", &obj1); if (obj1.isBool()) { writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1.free(); dict->lookup("Inks", &obj1); if (obj1.isName()) { writePSFmt("%%ImageInks: {0:s}\n", obj1.getName()); } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { obj1.arrayGet(0, &obj2); if (obj2.isName()) { writePSFmt("%%ImageInks: {0:s} {1:d}", obj2.getName(), (obj1.arrayGetLength() - 1) / 2); for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) { obj1.arrayGet(i, &obj3); obj1.arrayGet(i+1, &obj4); if (obj3.isString() && obj4.isNum()) { writePS(" "); writePSString(obj3.getString()); writePSFmt(" {0:.4g}", obj4.getNum()); } obj3.free(); obj4.free(); } writePS("\n"); } obj2.free(); } obj1.free(); writePS("gsave\n"); writePS("%%BeginIncludedImage\n"); dict->lookup("IncludedImageDimensions", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); w = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); h = obj2.getInt(); obj2.free(); writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h); } obj1.free(); dict->lookup("IncludedImageQuality", &obj1); if (obj1.isNum()) { writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum()); } obj1.free(); ++opi20Nest; } void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { Object obj1, obj2; int left, right, top, bottom, samples, bits, width, height; double c, m, y, k; double llx, lly, ulx, uly, urx, ury, lrx, lry; double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry; double horiz, vert; int i, j; writePS("save\n"); writePS("/opiMatrix2 matrix currentmatrix def\n"); writePS("opiMatrix setmatrix\n"); dict->lookup("F", &obj1); if (getFileSpec(&obj1, &obj2)) { writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString()); obj2.free(); } obj1.free(); dict->lookup("CropRect", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 4) { obj1.arrayGet(0, &obj2); left = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); top = obj2.getInt(); obj2.free(); obj1.arrayGet(2, &obj2); right = obj2.getInt(); obj2.free(); obj1.arrayGet(3, &obj2); bottom = obj2.getInt(); obj2.free(); writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", left, top, right, bottom); } obj1.free(); dict->lookup("Color", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 5) { obj1.arrayGet(0, &obj2); c = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); m = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); y = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); k = obj2.getNum(); obj2.free(); obj1.arrayGet(4, &obj2); if (obj2.isString()) { writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", c, m, y, k); writePSString(obj2.getString()); writePS("\n"); } obj2.free(); } obj1.free(); dict->lookup("ColorType", &obj1); if (obj1.isName()) { writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName()); } obj1.free(); //~ ignores 'Comments' entry //~ need to handle multiple lines dict->lookup("CropFixed", &obj1); if (obj1.isArray()) { obj1.arrayGet(0, &obj2); ulx = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); uly = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); lrx = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); lry = obj2.getNum(); obj2.free(); writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n", ulx, uly, lrx, lry); } obj1.free(); dict->lookup("GrayMap", &obj1); if (obj1.isArray()) { writePS("%ALDImageGrayMap:"); for (i = 0; i < obj1.arrayGetLength(); i += 16) { if (i > 0) { writePS("\n%%+"); } for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) { obj1.arrayGet(i+j, &obj2); writePSFmt(" {0:d}", obj2.getInt()); obj2.free(); } } writePS("\n"); } obj1.free(); dict->lookup("ID", &obj1); if (obj1.isString()) { writePSFmt("%ALDImageID: {0:t}\n", obj1.getString()); } obj1.free(); dict->lookup("ImageType", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); samples = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); bits = obj2.getInt(); obj2.free(); writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits); } obj1.free(); dict->lookup("Overprint", &obj1); if (obj1.isBool()) { writePSFmt("%ALDImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1.free(); dict->lookup("Position", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 8) { obj1.arrayGet(0, &obj2); llx = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); lly = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); ulx = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); uly = obj2.getNum(); obj2.free(); obj1.arrayGet(4, &obj2); urx = obj2.getNum(); obj2.free(); obj1.arrayGet(5, &obj2); ury = obj2.getNum(); obj2.free(); obj1.arrayGet(6, &obj2); lrx = obj2.getNum(); obj2.free(); obj1.arrayGet(7, &obj2); lry = obj2.getNum(); obj2.free(); opiTransform(state, llx, lly, &tllx, &tlly); opiTransform(state, ulx, uly, &tulx, &tuly); opiTransform(state, urx, ury, &turx, &tury); opiTransform(state, lrx, lry, &tlrx, &tlry); writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n", tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); obj2.free(); } obj1.free(); dict->lookup("Resolution", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); horiz = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); vert = obj2.getNum(); obj2.free(); writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert); obj2.free(); } obj1.free(); dict->lookup("Size", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); width = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); height = obj2.getInt(); obj2.free(); writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height); } obj1.free(); //~ ignoring 'Tags' entry //~ need to use writePSString() and deal with >255-char lines dict->lookup("Tint", &obj1); if (obj1.isNum()) { writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum()); } obj1.free(); dict->lookup("Transparency", &obj1); if (obj1.isBool()) { writePSFmt("%ALDImageTransparency: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1.free(); writePS("%%BeginObject: image\n"); writePS("opiMatrix2 setmatrix\n"); ++opi13Nest; } // Convert PDF user space coordinates to PostScript default user space // coordinates. This has to account for both the PDF CTM and the // PSOutputDev page-fitting transform. void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1) { double t; state->transform(x0, y0, x1, y1); *x1 += tx; *y1 += ty; if (rotate == 90) { t = *x1; *x1 = -*y1; *y1 = t; } else if (rotate == 180) { *x1 = -*x1; *y1 = -*y1; } else if (rotate == 270) { t = *x1; *x1 = *y1; *y1 = -t; } *x1 *= xScale; *y1 *= yScale; } void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { Object dict; if (globalParams->getPSOPI()) { opiDict->lookup("2.0", &dict); if (dict.isDict()) { writePS("%%EndIncludedImage\n"); writePS("%%EndOPI\n"); writePS("grestore\n"); --opi20Nest; dict.free(); } else { dict.free(); opiDict->lookup("1.3", &dict); if (dict.isDict()) { writePS("%%EndObject\n"); writePS("restore\n"); --opi13Nest; } dict.free(); } } } GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) { if (fileSpec->isString()) { fileSpec->copy(fileName); return gTrue; } if (fileSpec->isDict()) { fileSpec->dictLookup("DOS", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); fileSpec->dictLookup("Mac", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); fileSpec->dictLookup("Unix", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); fileSpec->dictLookup("F", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); } return gFalse; } #endif // OPI_SUPPORT void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy); writePS("q\n"); t3NeedsRestore = gTrue; } void PSOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { if (t3String) { error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc"); return; } t3WX = wx; t3WY = wy; t3LLX = llx; t3LLY = lly; t3URX = urx; t3URY = ury; t3String = new GString(); writePS("q\n"); t3FillColorOnly = gTrue; t3Cacheable = gTrue; t3NeedsRestore = gTrue; } void PSOutputDev::drawForm(Ref id) { writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen); } void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { Stream *str; char buf[4096]; int n; if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { str = level1Stream; } else { str = psStream; } str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); } //~ can nextFunc be reset to 0 -- maybe at the start of each page? //~ or maybe at the start of each color space / pattern? void PSOutputDev::cvtFunction(Function *func) { SampledFunction *func0; ExponentialFunction *func2; StitchingFunction *func3; PostScriptFunction *func4; int thisFunc, m, n, nSamples, i, j, k; switch (func->getType()) { case -1: // identity writePS("{}\n"); break; case 0: // sampled func0 = (SampledFunction *)func; thisFunc = nextFunc++; m = func0->getInputSize(); n = func0->getOutputSize(); nSamples = n; for (i = 0; i < m; ++i) { nSamples *= func0->getSampleSize(i); } writePSFmt("/xpdfSamples{0:d} [\n", thisFunc); for (i = 0; i < nSamples; ++i) { writePSFmt("{0:.6g}\n", func0->getSamples()[i]); } writePS("] def\n"); writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2); // [e01] [efrac] x0 x1 ... xm-1 for (i = m-1; i >= 0; --i) { // [e01] [efrac] x0 x1 ... xi writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", func0->getDomainMin(i), (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / (func0->getDomainMax(i) - func0->getDomainMin(i)), func0->getEncodeMin(i)); // [e01] [efrac] x0 x1 ... xi-1 xi' writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1); // [e01] [efrac] x0 x1 ... xi-1 xi' writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n"); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i); // [e01] [efrac] x0 x1 ... xi-1 } // [e01] [efrac] for (i = 0; i < n; ++i) { // [e01] [efrac] y(0) ... y(i-1) for (j = 0; j < (1<> k) & 1)); for (k = m - 2; k >= 0; --k) { writePSFmt("{0:d} mul {1:d} index {2:d} get add\n", func0->getSampleSize(k), i + j + 3, 2 * k + ((j >> k) & 1)); } if (n > 1) { writePSFmt("{0:d} mul {1:d} add ", n, i); } writePS("get\n"); } // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1) for (j = 0; j < m; ++j) { // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1) for (k = 0; k < (1 << (m - j)); k += 2) { // [e01] [efrac] y(0) ... y(i-1) <2^(m-j)-k s values> writePSFmt("{0:d} index {1:d} get dup\n", i + k/2 + (1 << (m-j)) - k, j); writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n"); writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1); } // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1) } // [e01] [efrac] y(0) ... y(i-1) s writePSFmt("{0:.6g} mul {1:.6g} add\n", func0->getDecodeMax(i) - func0->getDecodeMin(i), func0->getDecodeMin(i)); writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func0->getRangeMin(i), func0->getRangeMin(i), func0->getRangeMax(i), func0->getRangeMax(i)); // [e01] [efrac] y(0) ... y(i-1) y(i) } // [e01] [efrac] y(0) ... y(n-1) writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n); break; case 2: // exponential func2 = (ExponentialFunction *)func; n = func2->getOutputSize(); writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getDomainMin(0), func2->getDomainMin(0), func2->getDomainMax(0), func2->getDomainMax(0)); // x for (i = 0; i < n; ++i) { // x y(0) .. y(i-1) writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], func2->getC0()[i]); if (func2->getHasRange()) { writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getRangeMin(i), func2->getRangeMin(i), func2->getRangeMax(i), func2->getRangeMax(i)); } } // x y(0) .. y(n-1) writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n); break; case 3: // stitching func3 = (StitchingFunction *)func; thisFunc = nextFunc++; for (i = 0; i < func3->getNumFuncs(); ++i) { cvtFunction(func3->getFunc(i)); writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i); } writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func3->getDomainMin(0), func3->getDomainMin(0), func3->getDomainMax(0), func3->getDomainMax(0)); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", func3->getBounds()[i+1], func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2*i], thisFunc, i); } writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2*i], thisFunc, i); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePS("} ifelse\n"); } writePS("}\n"); break; case 4: // PostScript func4 = (PostScriptFunction *)func; writePS(func4->getCodeString()->getCString()); writePS("\n"); break; } } void PSOutputDev::writePSChar(char c) { if (t3String) { t3String->append(c); } else { (*outputFunc)(outputStream, &c, 1); } } void PSOutputDev::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); } else { (*outputFunc)(outputStream, s, (int)strlen(s)); } } void PSOutputDev::writePSFmt(const char *fmt, ...) { va_list args; GString *buf; va_start(args, fmt); if (t3String) { t3String->appendfv((char *)fmt, args); } else { buf = GString::formatv((char *)fmt, args); (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); delete buf; } va_end(args); } void PSOutputDev::writePSString(GString *s) { Guchar *p; int n, line; char buf[8]; writePSChar('('); line = 1; for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) { if (line >= 64) { writePSChar('\\'); writePSChar('\n'); line = 0; } if (*p == '(' || *p == ')' || *p == '\\') { writePSChar('\\'); writePSChar((char)*p); line += 2; } else if (*p < 0x20 || *p >= 0x80) { sprintf(buf, "\\%03o", *p); writePS(buf); line += 4; } else { writePSChar((char)*p); ++line; } } writePSChar(')'); } void PSOutputDev::writePSName(const char *s) { const char *p; char c; p = s; while ((c = *p++)) { if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { writePSFmt("#{0:02x}", c & 0xff); } else { writePSChar(c); } } } GString *PSOutputDev::filterPSName(GString *name) { GString *name2; char buf[8]; int i; char c; name2 = new GString(); // ghostscript chokes on names that begin with out-of-limits // numbers, e.g., 1e4foo is handled correctly (as a name), but // 1e999foo generates a limitcheck error c = name->getChar(0); if (c >= '0' && c <= '9') { name2->append('f'); } for (i = 0; i < name->getLength(); ++i) { c = name->getChar(i); if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { sprintf(buf, "#%02x", c & 0xff); name2->append(buf); } else { name2->append(c); } } return name2; } // Write a DSC-compliant . void PSOutputDev::writePSTextLine(GString *s) { TextString *ts; Unicode *u; int i, j; int c; // - DSC comments must be printable ASCII; control chars and // backslashes have to be escaped (we do cheap Unicode-to-ASCII // conversion by simply ignoring the high byte) // - lines are limited to 255 chars (we limit to 200 here to allow // for the keyword, which was emitted by the caller) // - lines that start with a left paren are treated as // instead of , so we escape a leading paren 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; } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) { writePSFmt("\\{0:03o}", c); j += 4; } else { writePSChar(c); ++j; } } writePS("\n"); delete ts; } xpdf-3.04/xpdf/UnicodeMapTables.h0000644000076400007640000002635512341430012016205 0ustar dereknderekn//======================================================================== // // UnicodeMapTables.h // // Copyright 2001-2009 Glyph & Cog, LLC // //======================================================================== static UnicodeMapRange latin1UnicodeMapRanges[] = { { 0x000a, 0x000a, 0x0a, 1 }, { 0x000c, 0x000d, 0x0c, 1 }, { 0x0020, 0x007e, 0x20, 1 }, { 0x00a0, 0x00a0, 0x20, 1 }, { 0x00a1, 0x00ac, 0xa1, 1 }, { 0x00ae, 0x00ff, 0xae, 1 }, { 0x010c, 0x010c, 0x43, 1 }, { 0x010d, 0x010d, 0x63, 1 }, { 0x0131, 0x0131, 0x69, 1 }, { 0x0141, 0x0141, 0x4c, 1 }, { 0x0142, 0x0142, 0x6c, 1 }, { 0x0152, 0x0152, 0x4f45, 2 }, { 0x0153, 0x0153, 0x6f65, 2 }, { 0x0160, 0x0160, 0x53, 1 }, { 0x0161, 0x0161, 0x73, 1 }, { 0x0178, 0x0178, 0x59, 1 }, { 0x017d, 0x017d, 0x5a, 1 }, { 0x017e, 0x017e, 0x7a, 1 }, { 0x02c6, 0x02c6, 0x5e, 1 }, { 0x02da, 0x02da, 0xb0, 1 }, { 0x02dc, 0x02dc, 0x7e, 1 }, { 0x2013, 0x2013, 0xad, 1 }, { 0x2014, 0x2014, 0x2d2d, 2 }, { 0x2018, 0x2018, 0x60, 1 }, { 0x2019, 0x2019, 0x27, 1 }, { 0x201a, 0x201a, 0x2c, 1 }, { 0x201c, 0x201c, 0x22, 1 }, { 0x201d, 0x201d, 0x22, 1 }, { 0x201e, 0x201e, 0x2c2c, 2 }, { 0x2022, 0x2022, 0xb7, 1 }, { 0x2026, 0x2026, 0x2e2e2e, 3 }, { 0x2039, 0x2039, 0x3c, 1 }, { 0x203a, 0x203a, 0x3e, 1 }, { 0x2044, 0x2044, 0x2f, 1 }, { 0x2122, 0x2122, 0x544d, 2 }, { 0x2212, 0x2212, 0x2d, 1 }, { 0xf6f9, 0xf6f9, 0x4c, 1 }, { 0xf6fa, 0xf6fa, 0x4f45, 2 }, { 0xf6fc, 0xf6fc, 0xb0, 1 }, { 0xf6fd, 0xf6fd, 0x53, 1 }, { 0xf6fe, 0xf6fe, 0x7e, 1 }, { 0xf6ff, 0xf6ff, 0x5a, 1 }, { 0xf721, 0xf721, 0x21, 1 }, { 0xf724, 0xf724, 0x24, 1 }, { 0xf726, 0xf726, 0x26, 1 }, { 0xf730, 0xf739, 0x30, 1 }, { 0xf73f, 0xf73f, 0x3f, 1 }, { 0xf761, 0xf77a, 0x41, 1 }, { 0xf7a1, 0xf7a2, 0xa1, 1 }, { 0xf7bf, 0xf7bf, 0xbf, 1 }, { 0xf7e0, 0xf7f6, 0xc0, 1 }, { 0xf7f8, 0xf7fe, 0xd8, 1 }, { 0xf7ff, 0xf7ff, 0x59, 1 }, { 0xfb00, 0xfb00, 0x6666, 2 }, { 0xfb01, 0xfb01, 0x6669, 2 }, { 0xfb02, 0xfb02, 0x666c, 2 }, { 0xfb03, 0xfb03, 0x666669, 3 }, { 0xfb04, 0xfb04, 0x66666c, 3 }, { 0xfb05, 0xfb05, 0x7374, 2 }, { 0xfb06, 0xfb06, 0x7374, 2 } }; #define latin1UnicodeMapLen (sizeof(latin1UnicodeMapRanges) / sizeof(UnicodeMapRange)) static UnicodeMapRange ascii7UnicodeMapRanges[] = { { 0x000a, 0x000a, 0x0a, 1 }, { 0x000c, 0x000d, 0x0c, 1 }, { 0x0020, 0x005f, 0x20, 1 }, { 0x0061, 0x007e, 0x61, 1 }, { 0x00a6, 0x00a6, 0x7c, 1 }, { 0x00a9, 0x00a9, 0x286329, 3 }, { 0x00ae, 0x00ae, 0x285229, 3 }, { 0x00b7, 0x00b7, 0x2a, 1 }, { 0x00bc, 0x00bc, 0x312f34, 3 }, { 0x00bd, 0x00bd, 0x312f32, 3 }, { 0x00be, 0x00be, 0x332f34, 3 }, { 0x00c0, 0x00c0, 0x41, 1 }, { 0x00c1, 0x00c1, 0x41, 1 }, { 0x00c2, 0x00c2, 0x41, 1 }, { 0x00c3, 0x00c3, 0x41, 1 }, { 0x00c4, 0x00c4, 0x41, 1 }, { 0x00c5, 0x00c5, 0x41, 1 }, { 0x00c6, 0x00c6, 0x4145, 2 }, { 0x00c7, 0x00c7, 0x43, 1 }, { 0x00c8, 0x00c8, 0x45, 1 }, { 0x00c9, 0x00c9, 0x45, 1 }, { 0x00ca, 0x00ca, 0x45, 1 }, { 0x00cb, 0x00cb, 0x45, 1 }, { 0x00cc, 0x00cc, 0x49, 1 }, { 0x00cd, 0x00cd, 0x49, 1 }, { 0x00ce, 0x00ce, 0x49, 1 }, { 0x00cf, 0x00cf, 0x49, 1 }, { 0x00d1, 0x00d2, 0x4e, 1 }, { 0x00d3, 0x00d3, 0x4f, 1 }, { 0x00d4, 0x00d4, 0x4f, 1 }, { 0x00d5, 0x00d5, 0x4f, 1 }, { 0x00d6, 0x00d6, 0x4f, 1 }, { 0x00d7, 0x00d7, 0x78, 1 }, { 0x00d8, 0x00d8, 0x4f, 1 }, { 0x00d9, 0x00d9, 0x55, 1 }, { 0x00da, 0x00da, 0x55, 1 }, { 0x00db, 0x00db, 0x55, 1 }, { 0x00dc, 0x00dc, 0x55, 1 }, { 0x00dd, 0x00dd, 0x59, 1 }, { 0x00e0, 0x00e0, 0x61, 1 }, { 0x00e1, 0x00e1, 0x61, 1 }, { 0x00e2, 0x00e2, 0x61, 1 }, { 0x00e3, 0x00e3, 0x61, 1 }, { 0x00e4, 0x00e4, 0x61, 1 }, { 0x00e5, 0x00e5, 0x61, 1 }, { 0x00e6, 0x00e6, 0x6165, 2 }, { 0x00e7, 0x00e7, 0x63, 1 }, { 0x00e8, 0x00e8, 0x65, 1 }, { 0x00e9, 0x00e9, 0x65, 1 }, { 0x00ea, 0x00ea, 0x65, 1 }, { 0x00eb, 0x00eb, 0x65, 1 }, { 0x00ec, 0x00ec, 0x69, 1 }, { 0x00ed, 0x00ed, 0x69, 1 }, { 0x00ee, 0x00ee, 0x69, 1 }, { 0x00ef, 0x00ef, 0x69, 1 }, { 0x00f1, 0x00f2, 0x6e, 1 }, { 0x00f3, 0x00f3, 0x6f, 1 }, { 0x00f4, 0x00f4, 0x6f, 1 }, { 0x00f5, 0x00f5, 0x6f, 1 }, { 0x00f6, 0x00f6, 0x6f, 1 }, { 0x00f7, 0x00f7, 0x2f, 1 }, { 0x00f8, 0x00f8, 0x6f, 1 }, { 0x00f9, 0x00f9, 0x75, 1 }, { 0x00fa, 0x00fa, 0x75, 1 }, { 0x00fb, 0x00fb, 0x75, 1 }, { 0x00fc, 0x00fc, 0x75, 1 }, { 0x00fd, 0x00fd, 0x79, 1 }, { 0x00ff, 0x00ff, 0x79, 1 }, { 0x0131, 0x0131, 0x69, 1 }, { 0x0141, 0x0141, 0x4c, 1 }, { 0x0152, 0x0152, 0x4f45, 2 }, { 0x0153, 0x0153, 0x6f65, 2 }, { 0x0160, 0x0160, 0x53, 1 }, { 0x0178, 0x0178, 0x59, 1 }, { 0x017d, 0x017d, 0x5a, 1 }, { 0x2013, 0x2013, 0x2d, 1 }, { 0x2014, 0x2014, 0x2d2d, 2 }, { 0x2018, 0x2018, 0x60, 1 }, { 0x2019, 0x2019, 0x27, 1 }, { 0x201c, 0x201c, 0x22, 1 }, { 0x201d, 0x201d, 0x22, 1 }, { 0x2022, 0x2022, 0x2a, 1 }, { 0x2026, 0x2026, 0x2e2e2e, 3 }, { 0x2122, 0x2122, 0x544d, 2 }, { 0x2212, 0x2212, 0x2d, 1 }, { 0xf6f9, 0xf6f9, 0x4c, 1 }, { 0xf6fa, 0xf6fa, 0x4f45, 2 }, { 0xf6fd, 0xf6fd, 0x53, 1 }, { 0xf6fe, 0xf6fe, 0x7e, 1 }, { 0xf6ff, 0xf6ff, 0x5a, 1 }, { 0xf721, 0xf721, 0x21, 1 }, { 0xf724, 0xf724, 0x24, 1 }, { 0xf726, 0xf726, 0x26, 1 }, { 0xf730, 0xf739, 0x30, 1 }, { 0xf73f, 0xf73f, 0x3f, 1 }, { 0xf761, 0xf77a, 0x41, 1 }, { 0xf7e0, 0xf7e0, 0x41, 1 }, { 0xf7e1, 0xf7e1, 0x41, 1 }, { 0xf7e2, 0xf7e2, 0x41, 1 }, { 0xf7e3, 0xf7e3, 0x41, 1 }, { 0xf7e4, 0xf7e4, 0x41, 1 }, { 0xf7e5, 0xf7e5, 0x41, 1 }, { 0xf7e6, 0xf7e6, 0x4145, 2 }, { 0xf7e7, 0xf7e7, 0x43, 1 }, { 0xf7e8, 0xf7e8, 0x45, 1 }, { 0xf7e9, 0xf7e9, 0x45, 1 }, { 0xf7ea, 0xf7ea, 0x45, 1 }, { 0xf7eb, 0xf7eb, 0x45, 1 }, { 0xf7ec, 0xf7ec, 0x49, 1 }, { 0xf7ed, 0xf7ed, 0x49, 1 }, { 0xf7ee, 0xf7ee, 0x49, 1 }, { 0xf7ef, 0xf7ef, 0x49, 1 }, { 0xf7f1, 0xf7f2, 0x4e, 1 }, { 0xf7f3, 0xf7f3, 0x4f, 1 }, { 0xf7f4, 0xf7f4, 0x4f, 1 }, { 0xf7f5, 0xf7f5, 0x4f, 1 }, { 0xf7f6, 0xf7f6, 0x4f, 1 }, { 0xf7f8, 0xf7f8, 0x4f, 1 }, { 0xf7f9, 0xf7f9, 0x55, 1 }, { 0xf7fa, 0xf7fa, 0x55, 1 }, { 0xf7fb, 0xf7fb, 0x55, 1 }, { 0xf7fc, 0xf7fc, 0x55, 1 }, { 0xf7fd, 0xf7fd, 0x59, 1 }, { 0xf7ff, 0xf7ff, 0x59, 1 }, { 0xfb00, 0xfb00, 0x6666, 2 }, { 0xfb01, 0xfb01, 0x6669, 2 }, { 0xfb02, 0xfb02, 0x666c, 2 }, { 0xfb03, 0xfb03, 0x666669, 3 }, { 0xfb04, 0xfb04, 0x66666c, 3 }, { 0xfb05, 0xfb05, 0x7374, 2 }, { 0xfb06, 0xfb06, 0x7374, 2 } }; #define ascii7UnicodeMapLen (sizeof(ascii7UnicodeMapRanges) / sizeof(UnicodeMapRange)) static UnicodeMapRange symbolUnicodeMapRanges[] = { { 0x0020, 0x0021, 0x20, 1 }, { 0x0023, 0x0023, 0x23, 1 }, { 0x0025, 0x0026, 0x25, 1 }, { 0x0028, 0x0029, 0x28, 1 }, { 0x002b, 0x002c, 0x2b, 1 }, { 0x002e, 0x003f, 0x2e, 1 }, { 0x005b, 0x005b, 0x5b, 1 }, { 0x005d, 0x005d, 0x5d, 1 }, { 0x005f, 0x005f, 0x5f, 1 }, { 0x007b, 0x007d, 0x7b, 1 }, { 0x00ac, 0x00ac, 0xd8, 1 }, { 0x00b0, 0x00b1, 0xb0, 1 }, { 0x00b5, 0x00b5, 0x6d, 1 }, { 0x00d7, 0x00d7, 0xb4, 1 }, { 0x00f7, 0x00f7, 0xb8, 1 }, { 0x0192, 0x0192, 0xa6, 1 }, { 0x0391, 0x0392, 0x41, 1 }, { 0x0393, 0x0393, 0x47, 1 }, { 0x0395, 0x0395, 0x45, 1 }, { 0x0396, 0x0396, 0x5a, 1 }, { 0x0397, 0x0397, 0x48, 1 }, { 0x0398, 0x0398, 0x51, 1 }, { 0x0399, 0x0399, 0x49, 1 }, { 0x039a, 0x039d, 0x4b, 1 }, { 0x039e, 0x039e, 0x58, 1 }, { 0x039f, 0x03a0, 0x4f, 1 }, { 0x03a1, 0x03a1, 0x52, 1 }, { 0x03a3, 0x03a5, 0x53, 1 }, { 0x03a6, 0x03a6, 0x46, 1 }, { 0x03a7, 0x03a7, 0x43, 1 }, { 0x03a8, 0x03a8, 0x59, 1 }, { 0x03b1, 0x03b2, 0x61, 1 }, { 0x03b3, 0x03b3, 0x67, 1 }, { 0x03b4, 0x03b5, 0x64, 1 }, { 0x03b6, 0x03b6, 0x7a, 1 }, { 0x03b7, 0x03b7, 0x68, 1 }, { 0x03b8, 0x03b8, 0x71, 1 }, { 0x03b9, 0x03b9, 0x69, 1 }, { 0x03ba, 0x03bb, 0x6b, 1 }, { 0x03bd, 0x03bd, 0x6e, 1 }, { 0x03be, 0x03be, 0x78, 1 }, { 0x03bf, 0x03c0, 0x6f, 1 }, { 0x03c1, 0x03c1, 0x72, 1 }, { 0x03c2, 0x03c2, 0x56, 1 }, { 0x03c3, 0x03c5, 0x73, 1 }, { 0x03c6, 0x03c6, 0x66, 1 }, { 0x03c7, 0x03c7, 0x63, 1 }, { 0x03c8, 0x03c8, 0x79, 1 }, { 0x03c9, 0x03c9, 0x77, 1 }, { 0x03d1, 0x03d1, 0x4a, 1 }, { 0x03d2, 0x03d2, 0xa1, 1 }, { 0x03d5, 0x03d5, 0x6a, 1 }, { 0x03d6, 0x03d6, 0x76, 1 }, { 0x2022, 0x2022, 0xb7, 1 }, { 0x2026, 0x2026, 0xbc, 1 }, { 0x2032, 0x2032, 0xa2, 1 }, { 0x2033, 0x2033, 0xb2, 1 }, { 0x2044, 0x2044, 0xa4, 1 }, { 0x2111, 0x2111, 0xc1, 1 }, { 0x2118, 0x2118, 0xc3, 1 }, { 0x211c, 0x211c, 0xc2, 1 }, { 0x2126, 0x2126, 0x57, 1 }, { 0x2135, 0x2135, 0xc0, 1 }, { 0x2190, 0x2193, 0xac, 1 }, { 0x2194, 0x2194, 0xab, 1 }, { 0x21b5, 0x21b5, 0xbf, 1 }, { 0x21d0, 0x21d3, 0xdc, 1 }, { 0x21d4, 0x21d4, 0xdb, 1 }, { 0x2200, 0x2200, 0x22, 1 }, { 0x2202, 0x2202, 0xb6, 1 }, { 0x2203, 0x2203, 0x24, 1 }, { 0x2205, 0x2205, 0xc6, 1 }, { 0x2206, 0x2206, 0x44, 1 }, { 0x2207, 0x2207, 0xd1, 1 }, { 0x2208, 0x2209, 0xce, 1 }, { 0x220b, 0x220b, 0x27, 1 }, { 0x220f, 0x220f, 0xd5, 1 }, { 0x2211, 0x2211, 0xe5, 1 }, { 0x2212, 0x2212, 0x2d, 1 }, { 0x2217, 0x2217, 0x2a, 1 }, { 0x221a, 0x221a, 0xd6, 1 }, { 0x221d, 0x221d, 0xb5, 1 }, { 0x221e, 0x221e, 0xa5, 1 }, { 0x2220, 0x2220, 0xd0, 1 }, { 0x2227, 0x2228, 0xd9, 1 }, { 0x2229, 0x222a, 0xc7, 1 }, { 0x222b, 0x222b, 0xf2, 1 }, { 0x2234, 0x2234, 0x5c, 1 }, { 0x223c, 0x223c, 0x7e, 1 }, { 0x2245, 0x2245, 0x40, 1 }, { 0x2248, 0x2248, 0xbb, 1 }, { 0x2260, 0x2261, 0xb9, 1 }, { 0x2264, 0x2264, 0xa3, 1 }, { 0x2265, 0x2265, 0xb3, 1 }, { 0x2282, 0x2282, 0xcc, 1 }, { 0x2283, 0x2283, 0xc9, 1 }, { 0x2284, 0x2284, 0xcb, 1 }, { 0x2286, 0x2286, 0xcd, 1 }, { 0x2287, 0x2287, 0xca, 1 }, { 0x2295, 0x2295, 0xc5, 1 }, { 0x2297, 0x2297, 0xc4, 1 }, { 0x22a5, 0x22a5, 0x5e, 1 }, { 0x22c5, 0x22c5, 0xd7, 1 }, { 0x2320, 0x2320, 0xf3, 1 }, { 0x2321, 0x2321, 0xf5, 1 }, { 0x2329, 0x2329, 0xe1, 1 }, { 0x232a, 0x232a, 0xf1, 1 }, { 0x25ca, 0x25ca, 0xe0, 1 }, { 0x2660, 0x2660, 0xaa, 1 }, { 0x2663, 0x2663, 0xa7, 1 }, { 0x2665, 0x2665, 0xa9, 1 }, { 0x2666, 0x2666, 0xa8, 1 }, { 0xf6d9, 0xf6d9, 0xd3, 1 }, { 0xf6da, 0xf6da, 0xd2, 1 }, { 0xf6db, 0xf6db, 0xd4, 1 }, { 0xf8e5, 0xf8e5, 0x60, 1 }, { 0xf8e6, 0xf8e7, 0xbd, 1 }, { 0xf8e8, 0xf8ea, 0xe2, 1 }, { 0xf8eb, 0xf8f4, 0xe6, 1 }, { 0xf8f5, 0xf8f5, 0xf4, 1 }, { 0xf8f6, 0xf8fe, 0xf6, 1 } }; #define symbolUnicodeMapLen (sizeof(symbolUnicodeMapRanges) / sizeof(UnicodeMapRange)) static UnicodeMapRange zapfDingbatsUnicodeMapRanges[] = { { 0x0020, 0x0020, 0x20, 1 }, { 0x2192, 0x2192, 0xd5, 1 }, { 0x2194, 0x2195, 0xd6, 1 }, { 0x2460, 0x2469, 0xac, 1 }, { 0x25a0, 0x25a0, 0x6e, 1 }, { 0x25b2, 0x25b2, 0x73, 1 }, { 0x25bc, 0x25bc, 0x74, 1 }, { 0x25c6, 0x25c6, 0x75, 1 }, { 0x25cf, 0x25cf, 0x6c, 1 }, { 0x25d7, 0x25d7, 0x77, 1 }, { 0x2605, 0x2605, 0x48, 1 }, { 0x260e, 0x260e, 0x25, 1 }, { 0x261b, 0x261b, 0x2a, 1 }, { 0x261e, 0x261e, 0x2b, 1 }, { 0x2660, 0x2660, 0xab, 1 }, { 0x2663, 0x2663, 0xa8, 1 }, { 0x2665, 0x2665, 0xaa, 1 }, { 0x2666, 0x2666, 0xa9, 1 }, { 0x2701, 0x2704, 0x21, 1 }, { 0x2706, 0x2709, 0x26, 1 }, { 0x270c, 0x2727, 0x2c, 1 }, { 0x2729, 0x274b, 0x49, 1 }, { 0x274d, 0x274d, 0x6d, 1 }, { 0x274f, 0x2752, 0x6f, 1 }, { 0x2756, 0x2756, 0x76, 1 }, { 0x2758, 0x275e, 0x78, 1 }, { 0x2761, 0x2767, 0xa1, 1 }, { 0x2776, 0x2794, 0xb6, 1 }, { 0x2798, 0x27af, 0xd8, 1 }, { 0x27b1, 0x27be, 0xf1, 1 } }; #define zapfDingbatsUnicodeMapLen (sizeof(zapfDingbatsUnicodeMapRanges) / sizeof(UnicodeMapRange)) xpdf-3.04/xpdf/BuiltinFontTables.h0000644000076400007640000000105412341430012016403 0ustar dereknderekn//======================================================================== // // BuiltinFontTables.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef BUILTINFONTTABLES_H #define BUILTINFONTTABLES_H #include "BuiltinFont.h" #define nBuiltinFonts 14 #define nBuiltinFontSubsts 12 extern BuiltinFont builtinFonts[nBuiltinFonts]; extern BuiltinFont *builtinFontSubst[nBuiltinFontSubsts]; extern void initBuiltinFontTables(); extern void freeBuiltinFontTables(); #endif xpdf-3.04/xpdf/Parser.cc0000644000076400007640000001314412341430012014410 0ustar dereknderekn//======================================================================== // // Parser.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "Object.h" #include "Array.h" #include "Dict.h" #include "Decrypt.h" #include "Parser.h" #include "XRef.h" #include "Error.h" // Max number of nested objects. This is used to catch infinite loops // in the object structure. #define recursionLimit 500 Parser::Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA) { xref = xrefA; lexer = lexerA; inlineImg = 0; allowStreams = allowStreamsA; lexer->getObj(&buf1); lexer->getObj(&buf2); } Parser::~Parser() { buf1.free(); buf2.free(); delete lexer; } Object *Parser::getObj(Object *obj, GBool simpleOnly, Guchar *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, int recursion) { char *key; Stream *str; Object obj2; int num; DecryptStream *decrypt; GString *s, *s2; int c; // refill buffer after inline image data if (inlineImg == 2) { buf1.free(); buf2.free(); lexer->getObj(&buf1); lexer->getObj(&buf2); inlineImg = 0; } // array if (!simpleOnly && recursion < recursionLimit && buf1.isCmd("[")) { shift(); obj->initArray(xref); while (!buf1.isCmd("]") && !buf1.isEOF()) obj->arrayAdd(getObj(&obj2, gFalse, fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1)); if (buf1.isEOF()) error(errSyntaxError, getPos(), "End of file inside array"); shift(); // dictionary or stream } else if (!simpleOnly && recursion < recursionLimit && buf1.isCmd("<<")) { shift(); obj->initDict(xref); while (!buf1.isCmd(">>") && !buf1.isEOF()) { if (!buf1.isName()) { error(errSyntaxError, getPos(), "Dictionary key must be a name object"); shift(); } else { key = copyString(buf1.getName()); shift(); if (buf1.isEOF() || buf1.isError()) { gfree(key); break; } obj->dictAdd(key, getObj(&obj2, gFalse, fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1)); } } if (buf1.isEOF()) error(errSyntaxError, getPos(), "End of file inside dictionary"); // stream objects are not allowed inside content streams or // object streams if (allowStreams && buf2.isCmd("stream")) { if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1))) { obj->initStream(str); } else { obj->free(); obj->initError(); } } else { shift(); } // indirect reference or integer } else if (buf1.isInt()) { num = buf1.getInt(); shift(); if (buf1.isInt() && buf2.isCmd("R")) { obj->initRef(num, buf1.getInt()); shift(); shift(); } else { obj->initInt(num); } // string } else if (buf1.isString() && fileKey) { s = buf1.getString(); s2 = new GString(); obj2.initNull(); decrypt = new DecryptStream(new MemStream(s->getCString(), 0, s->getLength(), &obj2), fileKey, encAlgorithm, keyLength, objNum, objGen); decrypt->reset(); while ((c = decrypt->getChar()) != EOF) { s2->append((char)c); } delete decrypt; obj->initString(s2); shift(); // simple object } else { buf1.copy(obj); shift(); } return obj; } Stream *Parser::makeStream(Object *dict, Guchar *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, int recursion) { Object obj; BaseStream *baseStr; Stream *str; GFileOffset pos, endPos, length; // get stream start position lexer->skipToNextLine(); if (!(str = lexer->getStream())) { return NULL; } pos = str->getPos(); // 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 // stream immediately after the "stream" token if (!lexer->getStream()) { return NULL; } baseStr = lexer->getStream()->getBaseStream(); // skip over stream data lexer->setPos(pos + length); // refill token buffers and check for 'endstream' shift(); // kill '>>' shift(); // kill 'stream' if (buf1.isCmd("endstream")) { shift(); } else { error(errSyntaxError, getPos(), "Missing 'endstream'"); // kludge for broken PDF files: just add 5k to the length, and // hope its enough length += 5000; } // make base stream str = baseStr->makeSubStream(pos, gTrue, length, dict); // handle decryption if (fileKey) { str = new DecryptStream(str, fileKey, encAlgorithm, keyLength, objNum, objGen); } // get filters str = str->addFilters(dict, recursion); return str; } void Parser::shift() { if (inlineImg > 0) { if (inlineImg < 2) { ++inlineImg; } else { // in a damaged content stream, if 'ID' shows up in the middle // of a dictionary, we need to reset inlineImg = 0; } } else if (buf2.isCmd("ID")) { lexer->skipChar(); // skip char after 'ID' command inlineImg = 1; } buf1.free(); buf1 = buf2; if (inlineImg > 0) // don't buffer inline image data buf2.initNull(); else lexer->getObj(&buf2); } xpdf-3.04/xpdf/Error.cc0000644000076400007640000000376112341430012014251 0ustar dereknderekn//======================================================================== // // Error.cc // // Copyright 1996-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include "GString.h" #include "GlobalParams.h" #include "Error.h" static const char *errorCategoryNames[] = { "Syntax Warning", "Syntax Error", "Config Error", "Command Line Error", "I/O Error", "Permission Error", "Unimplemented Feature", "Internal Error" }; static void (*errorCbk)(void *data, ErrorCategory category, int pos, char *msg) = NULL; static void *errorCbkData = NULL; void setErrorCallback(void (*cbk)(void *data, ErrorCategory category, int pos, char *msg), void *data) { errorCbk = cbk; errorCbkData = data; } void CDECL error(ErrorCategory category, GFileOffset pos, const char *msg, ...) { va_list args; GString *s, *sanitized; char c; int i; // NB: this can be called before the globalParams object is created if (!errorCbk && globalParams && globalParams->getErrQuiet()) { return; } va_start(args, msg); s = GString::formatv(msg, args); va_end(args); // remove non-printable characters, just in case they might cause // problems for the terminal program sanitized = new GString(); for (i = 0; i < s->getLength(); ++i) { c = s->getChar(i); if (c >= 0x20 && c <= 0x7e) { sanitized->append(c); } else { sanitized->appendf("<{0:02x}>", c & 0xff); } } if (errorCbk) { (*errorCbk)(errorCbkData, category, (int)pos, sanitized->getCString()); } else { if (pos >= 0) { fprintf(stderr, "%s (%d): %s\n", errorCategoryNames[category], (int)pos, sanitized->getCString()); } else { fprintf(stderr, "%s: %s\n", errorCategoryNames[category], sanitized->getCString()); } fflush(stderr); } delete s; delete sanitized; } xpdf-3.04/xpdf/Zoox.h0000644000076400007640000001240612341430012013755 0ustar dereknderekn//======================================================================== // // 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 xpdf-3.04/xpdf/Error.h0000644000076400007640000000237112341430012014107 0ustar dereknderekn//======================================================================== // // Error.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef ERROR_H #define ERROR_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "config.h" #include "gfile.h" enum ErrorCategory { errSyntaxWarning, // PDF syntax error which can be worked around; // output will probably be correct errSyntaxError, // PDF syntax error which cannot be worked around; // output will probably be incorrect errConfig, // error in Xpdf config info (xpdfrc file, etc.) errCommandLine, // error in user-supplied parameters, action not // allowed, etc. (only used by command-line tools) errIO, // error in file I/O errNotAllowed, // action not allowed by PDF permission bits errUnimplemented, // unimplemented PDF feature - display will be // incorrect errInternal // internal error - malfunction within the Xpdf code }; extern void setErrorCallback(void (*cbk)(void *data, ErrorCategory category, int pos, char *msg), void *data); extern void CDECL error(ErrorCategory category, GFileOffset pos, const char *msg, ...); #endif xpdf-3.04/xpdf/Page.h0000644000076400007640000001406012341430012013670 0ustar dereknderekn//======================================================================== // // Page.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PAGE_H #define PAGE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Object.h" class Dict; class PDFDoc; class XRef; class OutputDev; class Links; //------------------------------------------------------------------------ class PDFRectangle { public: double x1, y1, x2, y2; PDFRectangle() { x1 = y1 = x2 = y2 = 0; } PDFRectangle(double x1A, double y1A, double x2A, double y2A) { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; } GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; } void clipTo(PDFRectangle *rect); }; //------------------------------------------------------------------------ // PageAttrs //------------------------------------------------------------------------ class PageAttrs { public: // Construct a new PageAttrs object by merging a dictionary // (of type Pages or Page) into another PageAttrs object. If // is NULL, uses defaults. PageAttrs(PageAttrs *attrs, Dict *dict); // Construct a new PageAttrs object for an empty page (only used // when there is an error in the page tree). PageAttrs(); // Destructor. ~PageAttrs(); // Accessors. PDFRectangle *getMediaBox() { return &mediaBox; } PDFRectangle *getCropBox() { return &cropBox; } GBool isCropped() { return haveCropBox; } PDFRectangle *getBleedBox() { return &bleedBox; } PDFRectangle *getTrimBox() { return &trimBox; } PDFRectangle *getArtBox() { return &artBox; } int getRotate() { return rotate; } GString *getLastModified() { return lastModified.isString() ? lastModified.getString() : (GString *)NULL; } Dict *getBoxColorInfo() { return boxColorInfo.isDict() ? boxColorInfo.getDict() : (Dict *)NULL; } Dict *getGroup() { return group.isDict() ? group.getDict() : (Dict *)NULL; } Stream *getMetadata() { return metadata.isStream() ? metadata.getStream() : (Stream *)NULL; } Dict *getPieceInfo() { return pieceInfo.isDict() ? pieceInfo.getDict() : (Dict *)NULL; } Dict *getSeparationInfo() { return separationInfo.isDict() ? separationInfo.getDict() : (Dict *)NULL; } double getUserUnit() { return userUnit; } Dict *getResourceDict() { return resources.isDict() ? resources.getDict() : (Dict *)NULL; } // Clip all other boxes to the MediaBox. void clipBoxes(); private: GBool readBox(Dict *dict, const char *key, PDFRectangle *box); PDFRectangle mediaBox; PDFRectangle cropBox; GBool haveCropBox; PDFRectangle bleedBox; PDFRectangle trimBox; PDFRectangle artBox; int rotate; Object lastModified; Object boxColorInfo; Object group; Object metadata; Object pieceInfo; Object separationInfo; double userUnit; Object resources; }; //------------------------------------------------------------------------ // Page //------------------------------------------------------------------------ class Page { public: // Constructor. Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA); // Create an empty page (only used when there is an error in the // page tree). Page(PDFDoc *docA, int numA); // Destructor. ~Page(); // Is page valid? GBool isOk() { return ok; } // Get page parameters. int getNum() { return num; } PDFRectangle *getMediaBox() { return attrs->getMediaBox(); } PDFRectangle *getCropBox() { return attrs->getCropBox(); } GBool isCropped() { return attrs->isCropped(); } double getMediaWidth() { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; } double getMediaHeight() { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; } double getCropWidth() { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; } double getCropHeight() { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; } PDFRectangle *getBleedBox() { return attrs->getBleedBox(); } PDFRectangle *getTrimBox() { return attrs->getTrimBox(); } PDFRectangle *getArtBox() { return attrs->getArtBox(); } int getRotate() { return attrs->getRotate(); } GString *getLastModified() { return attrs->getLastModified(); } Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); } Dict *getGroup() { return attrs->getGroup(); } Stream *getMetadata() { return attrs->getMetadata(); } Dict *getPieceInfo() { return attrs->getPieceInfo(); } Dict *getSeparationInfo() { return attrs->getSeparationInfo(); } double getUserUnit() { return attrs->getUserUnit(); } // Get resource dictionary. Dict *getResourceDict() { return attrs->getResourceDict(); } // Get annotations array. Object *getAnnots(Object *obj) { return annots.fetch(xref, obj); } // Return a list of links. Links *getLinks(); // Get contents. Object *getContents(Object *obj) { return contents.fetch(xref, obj); } // Display a page. void display(OutputDev *out, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); // Display part of a page. void displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); void makeBox(double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool upsideDown, double sliceX, double sliceY, double sliceW, double sliceH, PDFRectangle *box, GBool *crop); void processLinks(OutputDev *out); // Get the page's default CTM. void getDefaultCTM(double *ctm, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool upsideDown); private: PDFDoc *doc; XRef *xref; // the xref table for this PDF file int num; // page number PageAttrs *attrs; // page attributes Object annots; // annotations array Object contents; // page contents GBool ok; // true if page is valid }; #endif xpdf-3.04/xpdf/pdftohtml.cc0000644000076400007640000001442512341430012015160 0ustar dereknderekn//======================================================================== // // 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; } xpdf-3.04/xpdf/find.xbm0000644000076400007640000000042012341430012014266 0ustar dereknderekn#define find_width 15 #define find_height 15 static unsigned char find_bits[] = { 0x38, 0x0e, 0x28, 0x0a, 0x2e, 0x3a, 0xfe, 0x3f, 0x7f, 0x7f, 0x61, 0x43, 0x61, 0x43, 0x61, 0x43, 0x61, 0x43, 0xe1, 0x43, 0x21, 0x42, 0x21, 0x42, 0x21, 0x42, 0x21, 0x42, 0x3f, 0x7e}; xpdf-3.04/xpdf/CMap.h0000644000076400007640000000613312341430012013636 0ustar dereknderekn//======================================================================== // // CMap.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef CMAP_H #define CMAP_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "CharTypes.h" #if MULTITHREADED #include "GMutex.h" #endif class GString; class Object; class Stream; struct CMapVectorEntry; class CMapCache; //------------------------------------------------------------------------ class CMap { public: // Parse a CMap from , which can be a name or a stream. Sets // the initial reference count to 1. Returns NULL on failure. static CMap *parse(CMapCache *cache, GString *collectionA, Object *obj); // Create the CMap specified by and . Sets // the initial reference count to 1. Returns NULL on failure. static CMap *parse(CMapCache *cache, GString *collectionA, GString *cMapNameA); // Parse a CMap from . Sets the initial reference count to 1. // Returns NULL on failure. static CMap *parse(CMapCache *cache, GString *collectionA, Stream *str); ~CMap(); void incRefCnt(); void decRefCnt(); // Return collection name (-). GString *getCollection() { return collection; } // Return true if this CMap matches the specified , and // . GBool match(GString *collectionA, GString *cMapNameA); // Return the CID corresponding to the character code starting at // , which contains bytes. Sets * to the char code, and // * to the number of bytes used by the char code. CID getCID(char *s, int len, CharCode *c, int *nUsed); // Return the writing mode (0=horizontal, 1=vertical). int getWMode() { return wMode; } private: void parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data); CMap(GString *collectionA, GString *cMapNameA); CMap(GString *collectionA, GString *cMapNameA, int wModeA); void useCMap(CMapCache *cache, char *useName); void useCMap(CMapCache *cache, Object *obj); void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src); void addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID); void freeCMapVector(CMapVectorEntry *vec); GString *collection; GString *cMapName; GBool isIdent; // true if this CMap is an identity mapping, // or is based on one (via usecmap) int wMode; // writing mode (0=horizontal, 1=vertical) CMapVectorEntry *vector; // vector for first byte (NULL for // identity CMap) int refCnt; #if MULTITHREADED GMutex mutex; #endif }; //------------------------------------------------------------------------ #define cMapCacheSize 4 class CMapCache { public: CMapCache(); ~CMapCache(); // Get the CMap for the specified character collection. // Increments its reference count; there will be one reference for // the cache plus one for the caller of this function. Returns NULL // on failure. CMap *getCMap(GString *collection, GString *cMapName); private: CMap *cache[cMapCacheSize]; }; #endif xpdf-3.04/xpdf/JArithmeticDecoder.h0000644000076400007640000000536212341430012016512 0ustar dereknderekn//======================================================================== // // JArithmeticDecoder.h // // Arithmetic decoder used by the JBIG2 and JPEG2000 decoders. // // Copyright 2002-2004 Glyph & Cog, LLC // //======================================================================== #ifndef JARITHMETICDECODER_H #define JARITHMETICDECODER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" class Stream; //------------------------------------------------------------------------ // JArithmeticDecoderStats //------------------------------------------------------------------------ class JArithmeticDecoderStats { public: JArithmeticDecoderStats(int contextSizeA); ~JArithmeticDecoderStats(); JArithmeticDecoderStats *copy(); void reset(); int getContextSize() { return contextSize; } void copyFrom(JArithmeticDecoderStats *stats); void setEntry(Guint cx, int i, int mps); private: Guchar *cxTab; // cxTab[cx] = (i[cx] << 1) + mps[cx] int contextSize; friend class JArithmeticDecoder; }; //------------------------------------------------------------------------ // JArithmeticDecoder //------------------------------------------------------------------------ class JArithmeticDecoder { public: JArithmeticDecoder(); ~JArithmeticDecoder(); void setStream(Stream *strA) { str = strA; dataLen = 0; limitStream = gFalse; } void setStream(Stream *strA, int dataLenA) { str = strA; dataLen = dataLenA; limitStream = gTrue; } // Start decoding on a new stream. This fills the byte buffers and // runs INITDEC. void start(); // Restart decoding on an interrupted stream. This refills the // buffers if needed, but does not run INITDEC. (This is used in // JPEG 2000 streams when codeblock data is split across multiple // packets/layers.) void restart(int dataLenA); // Read any leftover data in the stream. void cleanup(); // Decode one bit. int decodeBit(Guint context, JArithmeticDecoderStats *stats); // Decode eight bits. int decodeByte(Guint context, JArithmeticDecoderStats *stats); // Returns false for OOB, otherwise sets * and returns true. GBool decodeInt(int *x, JArithmeticDecoderStats *stats); Guint decodeIAID(Guint codeLen, JArithmeticDecoderStats *stats); void resetByteCounter() { nBytesRead = 0; } Guint getByteCounter() { return nBytesRead; } private: Guint readByte(); int decodeIntBit(JArithmeticDecoderStats *stats); void byteIn(); static Guint qeTab[47]; static int nmpsTab[47]; static int nlpsTab[47]; static int switchTab[47]; Guint buf0, buf1; Guint c, a; int ct; Guint prev; // for the integer decoder Stream *str; Guint nBytesRead; int dataLen; GBool limitStream; int readBuf; }; #endif xpdf-3.04/xpdf/leftArrowDis.xbm0000644000076400007640000000031212341430012015753 0ustar dereknderekn#define leftArrowDis_width 8 #define leftArrowDis_height 15 static unsigned char leftArrowDis_bits[] = { 0x80, 0x40, 0xa0, 0x50, 0xa8, 0x54, 0xaa, 0x55, 0xaa, 0x54, 0xa8, 0x50, 0xa0, 0x40, 0x80}; xpdf-3.04/xpdf/Link.h0000644000076400007640000002615212341430012013716 0ustar dereknderekn//======================================================================== // // Link.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef LINK_H #define LINK_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Object.h" class GString; class Array; class Dict; //------------------------------------------------------------------------ // LinkAction //------------------------------------------------------------------------ enum LinkActionKind { actionGoTo, // go to destination actionGoToR, // go to destination in new file actionLaunch, // launch app (or open document) actionURI, // URI actionNamed, // named action actionMovie, // movie action actionJavaScript, // run JavaScript actionSubmitForm, // submit form actionHide, // hide annotation actionUnknown // anything else }; class LinkAction { public: // Destructor. virtual ~LinkAction() {} // Was the LinkAction created successfully? virtual GBool isOk() = 0; // Check link action type. virtual LinkActionKind getKind() = 0; // Parse a destination (old-style action) name, string, or array. static LinkAction *parseDest(Object *obj); // Parse an action dictionary. static LinkAction *parseAction(Object *obj, GString *baseURI = NULL); // Extract a file name from a file specification (string or // dictionary). static GString *getFileSpecName(Object *fileSpecObj); }; //------------------------------------------------------------------------ // LinkDest //------------------------------------------------------------------------ enum LinkDestKind { destXYZ, destFit, destFitH, destFitV, destFitR, destFitB, destFitBH, destFitBV }; class LinkDest { public: // Build a LinkDest from the array. LinkDest(Array *a); // Copy a LinkDest. LinkDest *copy() { return new LinkDest(this); } // Was the LinkDest created successfully? GBool isOk() { return ok; } // Accessors. LinkDestKind getKind() { return kind; } GBool isPageRef() { return pageIsRef; } int getPageNum() { return pageNum; } Ref getPageRef() { return pageRef; } double getLeft() { return left; } double getBottom() { return bottom; } double getRight() { return right; } double getTop() { return top; } double getZoom() { return zoom; } GBool getChangeLeft() { return changeLeft; } GBool getChangeTop() { return changeTop; } GBool getChangeZoom() { return changeZoom; } private: LinkDestKind kind; // destination type GBool pageIsRef; // is the page a reference or number? union { Ref pageRef; // reference to page int pageNum; // one-relative page number }; double left, bottom; // position double right, top; double zoom; // zoom factor GBool changeLeft, changeTop; // which position components to change: GBool changeZoom; // destXYZ uses all three; // destFitH/BH use changeTop; // destFitV/BV use changeLeft GBool ok; // set if created successfully LinkDest(LinkDest *dest); }; //------------------------------------------------------------------------ // LinkGoTo //------------------------------------------------------------------------ class LinkGoTo: public LinkAction { public: // Build a LinkGoTo from a destination (dictionary, name, or string). LinkGoTo(Object *destObj); // Destructor. virtual ~LinkGoTo(); // Was the LinkGoTo created successfully? virtual GBool isOk() { return dest || namedDest; } // Accessors. virtual LinkActionKind getKind() { return actionGoTo; } LinkDest *getDest() { return dest; } GString *getNamedDest() { return namedDest; } private: LinkDest *dest; // regular destination (NULL for remote // link with bad destination) GString *namedDest; // named destination (only one of dest and // and namedDest may be non-NULL) }; //------------------------------------------------------------------------ // LinkGoToR //------------------------------------------------------------------------ class LinkGoToR: public LinkAction { public: // Build a LinkGoToR from a file spec (dictionary) and destination // (dictionary, name, or string). LinkGoToR(Object *fileSpecObj, Object *destObj); // Destructor. virtual ~LinkGoToR(); // Was the LinkGoToR created successfully? virtual GBool isOk() { return fileName && (dest || namedDest); } // Accessors. virtual LinkActionKind getKind() { return actionGoToR; } GString *getFileName() { return fileName; } LinkDest *getDest() { return dest; } GString *getNamedDest() { return namedDest; } private: GString *fileName; // file name LinkDest *dest; // regular destination (NULL for remote // link with bad destination) GString *namedDest; // named destination (only one of dest and // and namedDest may be non-NULL) }; //------------------------------------------------------------------------ // LinkLaunch //------------------------------------------------------------------------ class LinkLaunch: public LinkAction { public: // Build a LinkLaunch from an action dictionary. LinkLaunch(Object *actionObj); // Destructor. virtual ~LinkLaunch(); // Was the LinkLaunch created successfully? virtual GBool isOk() { return fileName != NULL; } // Accessors. virtual LinkActionKind getKind() { return actionLaunch; } GString *getFileName() { return fileName; } GString *getParams() { return params; } private: GString *fileName; // file name GString *params; // parameters }; //------------------------------------------------------------------------ // LinkURI //------------------------------------------------------------------------ class LinkURI: public LinkAction { public: // Build a LinkURI given the URI (string) and base URI. LinkURI(Object *uriObj, GString *baseURI); // Destructor. virtual ~LinkURI(); // Was the LinkURI created successfully? virtual GBool isOk() { return uri != NULL; } // Accessors. virtual LinkActionKind getKind() { return actionURI; } GString *getURI() { return uri; } private: GString *uri; // the URI }; //------------------------------------------------------------------------ // LinkNamed //------------------------------------------------------------------------ class LinkNamed: public LinkAction { public: // Build a LinkNamed given the action name. LinkNamed(Object *nameObj); virtual ~LinkNamed(); // Was the LinkNamed created successfully? virtual GBool isOk() { return name != NULL; } // Accessors. virtual LinkActionKind getKind() { return actionNamed; } GString *getName() { return name; } private: GString *name; }; //------------------------------------------------------------------------ // LinkMovie //------------------------------------------------------------------------ class LinkMovie: public LinkAction { public: LinkMovie(Object *annotObj, Object *titleObj); virtual ~LinkMovie(); // 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; } GString *getTitle() { return title; } private: Ref annotRef; GString *title; }; //------------------------------------------------------------------------ // 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 //------------------------------------------------------------------------ class LinkUnknown: public LinkAction { public: // Build a LinkUnknown with the specified action type. LinkUnknown(char *actionA); // Destructor. virtual ~LinkUnknown(); // Was the LinkUnknown create successfully? virtual GBool isOk() { return action != NULL; } // Accessors. virtual LinkActionKind getKind() { return actionUnknown; } GString *getAction() { return action; } private: GString *action; // action subtype }; //------------------------------------------------------------------------ // Link //------------------------------------------------------------------------ class Link { public: // Construct a link, given its dictionary. Link(Dict *dict, GString *baseURI); // Destructor. ~Link(); // Was the link created successfully? GBool isOk() { return ok; } // Check if point is inside the link rectangle. GBool inRect(double x, double y) { return x1 <= x && x <= x2 && y1 <= y && y <= y2; } // Get action. LinkAction *getAction() { return action; } // Get the link rectangle. void getRect(double *xa1, double *ya1, double *xa2, double *ya2) { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; } private: double x1, y1; // lower left corner double x2, y2; // upper right corner LinkAction *action; // action GBool ok; // is link valid? }; //------------------------------------------------------------------------ // Links //------------------------------------------------------------------------ class Links { public: // Extract links from array of annotations. Links(Object *annots, GString *baseURI); // Destructor. ~Links(); // Iterate through list of links. int getNumLinks() { return numLinks; } Link *getLink(int i) { return links[i]; } // If point , is in a link, return the associated action; // else return NULL. LinkAction *find(double x, double y); // Return true if , is in a link. GBool onLink(double x, double y); private: Link **links; int numLinks; }; #endif xpdf-3.04/xpdf/Gfx.h0000644000076400007640000002733612341430012013552 0ustar dereknderekn//======================================================================== // // Gfx.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GFX_H #define GFX_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "gfile.h" class GString; class GList; class PDFDoc; class XRef; class Array; class Stream; class Parser; class Dict; class Function; class OutputDev; class GfxFontDict; class GfxFont; class GfxPattern; class GfxTilingPattern; class GfxShadingPattern; class GfxShading; class GfxFunctionShading; class GfxAxialShading; class GfxRadialShading; class GfxGouraudTriangleShading; class GfxPatchMeshShading; struct GfxPatch; class GfxState; struct GfxColor; class GfxColorSpace; class Gfx; class PDFRectangle; class AnnotBorderStyle; //------------------------------------------------------------------------ enum GfxClipType { clipNone, clipNormal, clipEO }; enum TchkType { tchkBool, // boolean tchkInt, // integer tchkNum, // number (integer or real) tchkString, // string tchkName, // name tchkArray, // array tchkProps, // properties (dictionary or name) tchkSCN, // scn/SCN args (number of name) tchkNone // used to avoid empty initializer lists }; #define maxArgs 33 struct Operator { char name[4]; int numArgs; TchkType tchk[maxArgs]; void (Gfx::*func)(Object args[], int numArgs); }; //------------------------------------------------------------------------ class GfxResources { public: GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA); ~GfxResources(); GfxFont *lookupFont(char *name); 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; } private: GfxFontDict *fonts; Object xObjDict; Object colorSpaceDict; Object patternDict; Object shadingDict; Object gStateDict; Object propsDict; GfxResources *next; }; //------------------------------------------------------------------------ // GfxMarkedContent //------------------------------------------------------------------------ enum GfxMarkedContentKind { gfxMCOptionalContent, gfxMCActualText, gfxMCOther }; class GfxMarkedContent { public: GfxMarkedContent(GfxMarkedContentKind kindA, GBool ocStateA) { kind = kindA; ocState = ocStateA; } ~GfxMarkedContent() {} GfxMarkedContentKind kind; GBool ocState; // true if drawing is enabled, false if // disabled }; //------------------------------------------------------------------------ // Gfx //------------------------------------------------------------------------ class Gfx { public: // Constructor for regular output. Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict, double hDPI, double vDPI, PDFRectangle *box, PDFRectangle *cropBox, int rotate, GBool (*abortCheckCbkA)(void *data) = NULL, void *abortCheckCbkDataA = NULL); // Constructor for a sub-page object. Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict, PDFRectangle *box, PDFRectangle *cropBox, GBool (*abortCheckCbkA)(void *data) = NULL, void *abortCheckCbkDataA = NULL); ~Gfx(); // 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 *strRef, AnnotBorderStyle *borderStyle, double xMin, double yMin, double xMax, double yMax); // Save graphics state. void saveState(); // Restore graphics state. void restoreState(); // Get the current graphics state object. GfxState *getState() { return state; } 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; XRef *xref; // the xref table for this PDF file OutputDev *out; // output device GBool subPage; // is this a sub-page object? GBool printCommands; // print the drawing commands (for debugging) GfxResources *res; // resource stack int updateLevel; GfxState *state; // current graphics state GBool fontChanged; // set if font or text matrix has changed GfxClipType clip; // do a clip? int ignoreUndef; // current BX/EX nesting level double baseMatrix[6]; // default matrix for most recent // page/form/pattern int formDepth; double textClipBBox[4]; // text clipping bounding box GBool textClipBBoxEmpty; // true if textClipBBox has not been // initialized yet GBool ocState; // true if drawing is enabled, false if // disabled 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); void *abortCheckCbkData; static Operator opTab[]; // table of operators GBool checkForContentStreamLoop(Object *ref); void go(GBool topLevel); GBool execOp(Object *cmd, Object args[], int numArgs); Operator *findOp(char *name); GBool checkArg(Object *arg, TchkType type); GFileOffset getPos(); // graphics state operators void opSave(Object args[], int numArgs); void opRestore(Object args[], int numArgs); void opConcat(Object args[], int numArgs); void opSetDash(Object args[], int numArgs); void opSetFlat(Object args[], int numArgs); void opSetLineJoin(Object args[], int numArgs); void opSetLineCap(Object args[], int numArgs); void opSetMiterLimit(Object args[], int numArgs); void opSetLineWidth(Object args[], int numArgs); void opSetExtGState(Object args[], int numArgs); void doSoftMask(Object *str, Object *strRef, GBool alpha, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, Function *transferFunc, GfxColor *backdropColor); void opSetRenderingIntent(Object args[], int numArgs); // color operators void opSetFillGray(Object args[], int numArgs); void opSetStrokeGray(Object args[], int numArgs); void opSetFillCMYKColor(Object args[], int numArgs); void opSetStrokeCMYKColor(Object args[], int numArgs); void opSetFillRGBColor(Object args[], int numArgs); void opSetStrokeRGBColor(Object args[], int numArgs); void opSetFillColorSpace(Object args[], int numArgs); void opSetStrokeColorSpace(Object args[], int numArgs); void opSetFillColor(Object args[], int numArgs); void opSetStrokeColor(Object args[], int numArgs); void opSetFillColorN(Object args[], int numArgs); void opSetStrokeColorN(Object args[], int numArgs); // path segment operators void opMoveTo(Object args[], int numArgs); void opLineTo(Object args[], int numArgs); void opCurveTo(Object args[], int numArgs); void opCurveTo1(Object args[], int numArgs); void opCurveTo2(Object args[], int numArgs); void opRectangle(Object args[], int numArgs); void opClosePath(Object args[], int numArgs); // path painting operators void opEndPath(Object args[], int numArgs); void opStroke(Object args[], int numArgs); void opCloseStroke(Object args[], int numArgs); void opFill(Object args[], int numArgs); void opEOFill(Object args[], int numArgs); void opFillStroke(Object args[], int numArgs); void opCloseFillStroke(Object args[], int numArgs); void opEOFillStroke(Object args[], int numArgs); void opCloseEOFillStroke(Object args[], int numArgs); void doPatternFill(GBool eoFill); void doPatternStroke(); void doPatternText(); void doPatternImageMask(Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); void doTilingPatternFill(GfxTilingPattern *tPat, GBool stroke, GBool eoFill, GBool text); void doShadingPatternFill(GfxShadingPattern *sPat, GBool stroke, GBool eoFill, GBool text); void opShFill(Object args[], int numArgs); void doFunctionShFill(GfxFunctionShading *shading); void doFunctionShFill1(GfxFunctionShading *shading, double x0, double y0, double x1, double y1, GfxColor *colors, int depth); void doAxialShFill(GfxAxialShading *shading); void doRadialShFill(GfxRadialShading *shading); void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading); void gouraudFillTriangle(double x0, double y0, 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, GfxPatchMeshShading *shading, int depth); void doEndPath(); // path clipping operators void opClip(Object args[], int numArgs); void opEOClip(Object args[], int numArgs); // text object operators void opBeginText(Object args[], int numArgs); void opEndText(Object args[], int numArgs); // text state operators void opSetCharSpacing(Object args[], int numArgs); void opSetFont(Object args[], int numArgs); void doSetFont(GfxFont *font, double size); void opSetTextLeading(Object args[], int numArgs); void opSetTextRender(Object args[], int numArgs); void opSetTextRise(Object args[], int numArgs); void opSetWordSpacing(Object args[], int numArgs); void opSetHorizScaling(Object args[], int numArgs); // text positioning operators void opTextMove(Object args[], int numArgs); void opTextMoveSet(Object args[], int numArgs); void opSetTextMatrix(Object args[], int numArgs); void opTextNextLine(Object args[], int numArgs); // text string operators void opShowText(Object args[], int numArgs); void opMoveShowText(Object args[], int numArgs); void opMoveSetShowText(Object args[], int numArgs); void opShowSpaceText(Object args[], int numArgs); void doShowText(GString *s); void doIncCharCount(GString *s); // XObject operators void opXObject(Object args[], int numArgs); void doImage(Object *ref, Stream *str, GBool inlineImg); void doForm(Object *strRef, Object *str); // in-line image operators void opBeginImage(Object args[], int numArgs); Stream *buildImageStream(); void opImageData(Object args[], int numArgs); void opEndImage(Object args[], int numArgs); // type 3 font operators void opSetCharWidth(Object args[], int numArgs); void opSetCacheDevice(Object args[], int numArgs); // compatibility operators void opBeginIgnoreUndef(Object args[], int numArgs); void opEndIgnoreUndef(Object args[], int numArgs); // marked content operators void opBeginMarkedContent(Object args[], int numArgs); void opEndMarkedContent(Object args[], int numArgs); void opMarkPoint(Object args[], int numArgs); GfxState *saveStateStack(); void restoreStateStack(GfxState *oldState); void pushResources(Dict *resDict); void popResources(); }; #endif xpdf-3.04/xpdf/ImageOutputDev.h0000644000076400007640000000625312341430012015723 0ustar dereknderekn//======================================================================== // // ImageOutputDev.h // // Copyright 1998-2003 Glyph & Cog, LLC // //======================================================================== #ifndef IMAGEOUTPUTDEV_H #define IMAGEOUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "gtypes.h" #include "OutputDev.h" class GfxState; //------------------------------------------------------------------------ // ImageOutputDev //------------------------------------------------------------------------ class ImageOutputDev: public OutputDev { public: // Create an OutputDev which will write images to files named // -NNN.. Normally, all images are written as PBM // (.pbm) or PPM (.ppm) files. If is set, JPEG images are // written as JPEG (.jpg) files. ImageOutputDev(char *fileRootA, GBool dumpJPEGA); // Destructor. virtual ~ImageOutputDev(); // Check if file was successfully created. virtual GBool isOk() { return ok; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual GBool useTilingPatternFill() { return gTrue; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gFalse; } // Does this device need non-text content? virtual GBool needNonText() { return gTrue; } //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return gTrue; } // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() { return gFalse; } //----- path painting 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, double xStep, double yStep); //----- image drawing virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, 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: char *fileRoot; // root of output file names char *fileName; // buffer for output file names GBool dumpJPEG; // set to dump native JPEG files int imgNum; // current image number GBool ok; // set up ok? }; #endif xpdf-3.04/xpdf/Stream.cc0000644000076400007640000032777212341430012014426 0ustar dereknderekn//======================================================================== // // Stream.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #ifndef _WIN32 #include #endif #include #include #include "gmem.h" #include "gfile.h" #include "config.h" #include "Error.h" #include "Object.h" #include "Lexer.h" #include "GfxState.h" #include "Stream.h" #include "JBIG2Stream.h" #include "JPXStream.h" #include "Stream-CCITT.h" #ifdef __DJGPP__ static GBool setDJSYSFLAGS = gFalse; #endif #ifdef VMS #ifdef __GNUC__ #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #endif //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ Stream::Stream() { ref = 1; } Stream::~Stream() { } void Stream::close() { } int Stream::getRawChar() { error(errInternal, -1, "Called getRawChar() on non-predictor stream"); return EOF; } int Stream::getBlock(char *buf, int size) { int n, c; n = 0; while (n < size) { if ((c = getChar()) == EOF) { break; } buf[n++] = (char)c; } return n; } char *Stream::getLine(char *buf, int size) { int i; int c; if (lookChar() == EOF || size < 0) return NULL; for (i = 0; i < size - 1; ++i) { c = getChar(); if (c == EOF || c == '\n') break; if (c == '\r') { if ((c = lookChar()) == '\n') getChar(); break; } buf[i] = c; } buf[i] = '\0'; return buf; } 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, int recursion) { Object obj, obj2; Object params, params2; Stream *str; int i; str = this; dict->dictLookup("Filter", &obj); if (obj.isNull()) { obj.free(); dict->dictLookup("F", &obj); } dict->dictLookup("DecodeParms", ¶ms); if (params.isNull()) { params.free(); dict->dictLookup("DP", ¶ms); } if (obj.isName()) { str = makeFilter(obj.getName(), str, ¶ms, recursion); } else if (obj.isArray()) { for (i = 0; i < obj.arrayGetLength(); ++i) { obj.arrayGet(i, &obj2); if (params.isArray()) params.arrayGet(i, ¶ms2); else params2.initNull(); if (obj2.isName()) { str = makeFilter(obj2.getName(), str, ¶ms2, recursion); } else { error(errSyntaxError, getPos(), "Bad filter name"); str = new EOFStream(str); } obj2.free(); params2.free(); } } else if (!obj.isNull()) { error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream"); } obj.free(); params.free(); return str; } Stream *Stream::makeFilter(char *name, Stream *str, Object *params, int recursion) { int pred; // parameters int colors; int bits; int early; int encoding; GBool endOfLine, byteAlign, endOfBlock, black; int columns, rows; int colorXform; Object globals, obj; if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) { str = new ASCIIHexStream(str); } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) { str = new ASCII85Stream(str); } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) { pred = 1; columns = 1; colors = 1; bits = 8; early = 1; if (params->isDict()) { params->dictLookup("Predictor", &obj, recursion); if (obj.isInt()) pred = obj.getInt(); obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) columns = obj.getInt(); obj.free(); params->dictLookup("Colors", &obj, recursion); if (obj.isInt()) colors = obj.getInt(); obj.free(); params->dictLookup("BitsPerComponent", &obj, recursion); if (obj.isInt()) bits = obj.getInt(); obj.free(); params->dictLookup("EarlyChange", &obj, recursion); if (obj.isInt()) early = obj.getInt(); obj.free(); } str = new LZWStream(str, pred, columns, colors, bits, early); } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) { str = new RunLengthStream(str); } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) { encoding = 0; endOfLine = gFalse; byteAlign = gFalse; columns = 1728; rows = 0; endOfBlock = gTrue; black = gFalse; if (params->isDict()) { params->dictLookup("K", &obj, recursion); if (obj.isInt()) { encoding = obj.getInt(); } obj.free(); params->dictLookup("EndOfLine", &obj, recursion); if (obj.isBool()) { endOfLine = obj.getBool(); } obj.free(); params->dictLookup("EncodedByteAlign", &obj, recursion); if (obj.isBool()) { byteAlign = obj.getBool(); } obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) { columns = obj.getInt(); } obj.free(); params->dictLookup("Rows", &obj, recursion); if (obj.isInt()) { rows = obj.getInt(); } obj.free(); params->dictLookup("EndOfBlock", &obj, recursion); if (obj.isBool()) { endOfBlock = obj.getBool(); } obj.free(); params->dictLookup("BlackIs1", &obj, recursion); if (obj.isBool()) { black = obj.getBool(); } obj.free(); } str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign, columns, rows, endOfBlock, black); } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) { colorXform = -1; if (params->isDict()) { if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) { colorXform = obj.getInt(); } obj.free(); } str = new DCTStream(str, colorXform); } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) { pred = 1; columns = 1; colors = 1; bits = 8; if (params->isDict()) { params->dictLookup("Predictor", &obj, recursion); if (obj.isInt()) pred = obj.getInt(); obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) columns = obj.getInt(); obj.free(); params->dictLookup("Colors", &obj, recursion); if (obj.isInt()) colors = obj.getInt(); obj.free(); params->dictLookup("BitsPerComponent", &obj, recursion); if (obj.isInt()) bits = obj.getInt(); obj.free(); } str = new FlateStream(str, pred, columns, colors, bits); } else if (!strcmp(name, "JBIG2Decode")) { if (params->isDict()) { params->dictLookup("JBIG2Globals", &globals, recursion); } str = new JBIG2Stream(str, &globals); globals.free(); } else if (!strcmp(name, "JPXDecode")) { str = new JPXStream(str); } else { error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); str = new EOFStream(str); } return str; } //------------------------------------------------------------------------ // BaseStream //------------------------------------------------------------------------ BaseStream::BaseStream(Object *dictA) { dict = *dictA; } BaseStream::~BaseStream() { dict.free(); } //------------------------------------------------------------------------ // FilterStream //------------------------------------------------------------------------ FilterStream::FilterStream(Stream *strA) { str = strA; } FilterStream::~FilterStream() { } void FilterStream::close() { str->close(); } void FilterStream::setPos(GFileOffset pos, int dir) { error(errInternal, -1, "Called setPos() on FilterStream"); } //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) { int imgLineSize; str = strA; width = widthA; nComps = nCompsA; nBits = nBitsA; nVals = width * nComps; inputLineSize = (nVals * nBits + 7) >> 3; if (nVals > INT_MAX / nBits - 7) { // force a call to gmallocn(-1,...), which will throw an exception inputLineSize = -1; } inputLine = (char *)gmallocn(inputLineSize, sizeof(char)); if (nBits == 8) { imgLine = (Guchar *)inputLine; } else { if (nBits == 1) { imgLineSize = (nVals + 7) & ~7; } else { imgLineSize = nVals; } if (width > INT_MAX / nComps) { // force a call to gmallocn(-1,...), which will throw an exception imgLineSize = -1; } imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar)); } imgIdx = nVals; } ImageStream::~ImageStream() { if (imgLine != (Guchar *)inputLine) { gfree(imgLine); } gfree(inputLine); } void ImageStream::reset() { str->reset(); } void ImageStream::close() { str->close(); } GBool ImageStream::getPixel(Guchar *pix) { int i; if (imgIdx >= nVals) { if (!getLine()) { return gFalse; } imgIdx = 0; } for (i = 0; i < nComps; ++i) { pix[i] = imgLine[imgIdx++]; } return gTrue; } Guchar *ImageStream::getLine() { Gulong buf, bitMask; int bits; int c; int i; char *p; if (str->getBlock(inputLine, inputLineSize) != inputLineSize) { return NULL; } if (nBits == 1) { p = inputLine; for (i = 0; i < nVals; i += 8) { c = *p++; imgLine[i+0] = (Guchar)((c >> 7) & 1); imgLine[i+1] = (Guchar)((c >> 6) & 1); imgLine[i+2] = (Guchar)((c >> 5) & 1); imgLine[i+3] = (Guchar)((c >> 4) & 1); imgLine[i+4] = (Guchar)((c >> 3) & 1); imgLine[i+5] = (Guchar)((c >> 2) & 1); imgLine[i+6] = (Guchar)((c >> 1) & 1); imgLine[i+7] = (Guchar)(c & 1); } } else if (nBits == 8) { // 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; bits = 0; p = inputLine; for (i = 0; i < nVals; ++i) { if (bits < nBits) { buf = (buf << 8) | (*p++ & 0xff); bits += 8; } imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask); bits -= nBits; } } return imgLine; } void ImageStream::skipLine() { str->getBlock(inputLine, inputLineSize); } //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ StreamPredictor::StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA) { str = strA; predictor = predictorA; width = widthA; nComps = nCompsA; nBits = nBitsA; predLine = NULL; ok = gFalse; nVals = width * nComps; pixBytes = (nComps * nBits + 7) >> 3; rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes; if (width <= 0 || nComps <= 0 || nBits <= 0 || nComps > gfxColorMaxComps || nBits > 16 || width >= INT_MAX / nComps || // check for overflow in nVals nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes return; } predLine = (Guchar *)gmalloc(rowBytes); reset(); ok = gTrue; } StreamPredictor::~StreamPredictor() { gfree(predLine); } void StreamPredictor::reset() { memset(predLine, 0, rowBytes); predIdx = rowBytes; } int StreamPredictor::lookChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx]; } int StreamPredictor::getChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx++]; } int StreamPredictor::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (predIdx >= rowBytes) { if (!getNextLine()) { break; } } m = rowBytes - predIdx; if (m > size - n) { m = size - n; } memcpy(blk + n, predLine + predIdx, m); predIdx += m; n += m; } return n; } GBool StreamPredictor::getNextLine() { int curPred; Guchar upLeftBuf[gfxColorMaxComps * 2 + 1]; int left, up, upLeft, p, pa, pb, pc; int c; Gulong inBuf, outBuf, bitMask; int inBits, outBits; int i, j, k, kk; // get PNG optimum predictor number if (predictor >= 10) { if ((curPred = str->getRawChar()) == EOF) { return gFalse; } curPred += 10; } else { curPred = predictor; } // read the raw line, apply PNG (byte) predictor memset(upLeftBuf, 0, pixBytes + 1); for (i = pixBytes; i < rowBytes; ++i) { for (j = pixBytes; j > 0; --j) { upLeftBuf[j] = upLeftBuf[j-1]; } upLeftBuf[0] = predLine[i]; if ((c = str->getRawChar()) == EOF) { if (i > pixBytes) { // this ought to return false, but some (broken) PDF files // contain truncated image data, and Adobe apparently reads the // last partial line break; } return gFalse; } switch (curPred) { case 11: // PNG sub predLine[i] = predLine[i - pixBytes] + (Guchar)c; break; case 12: // PNG up predLine[i] = predLine[i] + (Guchar)c; break; case 13: // PNG average predLine[i] = ((predLine[i - pixBytes] + predLine[i]) >> 1) + (Guchar)c; break; case 14: // PNG Paeth left = predLine[i - pixBytes]; up = predLine[i]; upLeft = upLeftBuf[pixBytes]; p = left + up - upLeft; if ((pa = p - left) < 0) pa = -pa; if ((pb = p - up) < 0) pb = -pb; if ((pc = p - upLeft) < 0) pc = -pc; if (pa <= pb && pa <= pc) predLine[i] = left + (Guchar)c; else if (pb <= pc) predLine[i] = up + (Guchar)c; else predLine[i] = upLeft + (Guchar)c; break; case 10: // PNG none default: // no predictor or TIFF predictor predLine[i] = (Guchar)c; break; } } // apply TIFF (component) predictor if (predictor == 2) { if (nBits == 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); bitMask = (1 << nBits) - 1; inBuf = outBuf = 0; inBits = outBits = 0; j = k = pixBytes; for (i = 0; i < width; ++i) { for (kk = 0; kk < nComps; ++kk) { if (inBits < nBits) { inBuf = (inBuf << 8) | (predLine[j++] & 0xff); inBits += 8; } upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] + (inBuf >> (inBits - nBits))) & bitMask); inBits -= nBits; outBuf = (outBuf << nBits) | upLeftBuf[kk]; outBits += nBits; if (outBits >= 8) { predLine[k++] = (Guchar)(outBuf >> (outBits - 8)); outBits -= 8; } } } if (outBits > 0) { predLine[k++] = (Guchar)((outBuf << (8 - outBits)) + (inBuf & ((1 << (8 - outBits)) - 1))); } } } // reset to start of line predIdx = pixBytes; return gTrue; } //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA): BaseStream(dictA) { f = fA; start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; savePos = 0; saved = gFalse; } FileStream::~FileStream() { close(); } Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA) { return new FileStream(f, startA, limitedA, lengthA, dictA); } void FileStream::reset() { savePos = gftell(f); gfseek(f, start, SEEK_SET); saved = gTrue; bufPtr = bufEnd = buf; bufPos = start; } void FileStream::close() { if (saved) { gfseek(f, savePos, SEEK_SET); saved = gFalse; } } int FileStream::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > size - n) { m = size - n; } memcpy(blk + n, bufPtr, m); bufPtr += m; n += m; } return n; } GBool FileStream::fillBuf() { int n; bufPos += (int)(bufEnd - buf); bufPtr = bufEnd = buf; if (limited && bufPos >= start + length) { return gFalse; } if (limited && bufPos + fileStreamBufSize > start + length) { n = (int)(start + length - bufPos); } else { n = fileStreamBufSize; } n = (int)fread(buf, 1, n, f); bufEnd = buf + n; if (bufPtr >= bufEnd) { return gFalse; } return gTrue; } void FileStream::setPos(GFileOffset pos, int dir) { GFileOffset size; if (dir >= 0) { gfseek(f, pos, SEEK_SET); bufPos = pos; } else { gfseek(f, 0, SEEK_END); size = gftell(f); if (pos > size) { pos = size; } gfseek(f, -pos, SEEK_END); bufPos = gftell(f); } bufPtr = bufEnd = buf; } void FileStream::moveStart(int delta) { start += delta; bufPtr = bufEnd = buf; bufPos = start; } //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA): BaseStream(dictA) { buf = bufA; start = startA; length = lengthA; bufEnd = buf + start + length; bufPtr = buf + start; needFree = gFalse; } MemStream::~MemStream() { if (needFree) { gfree(buf); } } Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited, GFileOffset lengthA, Object *dictA) { MemStream *subStr; Guint newStart, newLength; if (startA < start) { newStart = start; } else if (startA > start + length) { newStart = start + (int)length; } else { newStart = (int)startA; } if (!limited || newStart + lengthA > start + length) { newLength = start + length - newStart; } else { newLength = (Guint)lengthA; } subStr = new MemStream(buf, newStart, newLength, dictA); return subStr; } void MemStream::reset() { bufPtr = buf + start; } void MemStream::close() { } int MemStream::getBlock(char *blk, int size) { int n; if (size <= 0) { return 0; } if (bufEnd - bufPtr < size) { n = (int)(bufEnd - bufPtr); } else { n = size; } memcpy(blk, bufPtr, n); bufPtr += n; return n; } void MemStream::setPos(GFileOffset pos, int dir) { Guint i; if (dir >= 0) { i = (Guint)pos; } else { i = (Guint)(start + length - pos); } if (i < start) { i = start; } else if (i > start + length) { i = start + length; } bufPtr = buf + i; } void MemStream::moveStart(int delta) { start += delta; length -= delta; bufPtr = buf + start; } //------------------------------------------------------------------------ // EmbedStream //------------------------------------------------------------------------ EmbedStream::EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA): BaseStream(dictA) { str = strA; limited = limitedA; length = lengthA; } EmbedStream::~EmbedStream() { } Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA, GFileOffset lengthA, Object *dictA) { error(errInternal, -1, "Called makeSubStream() on EmbedStream"); return NULL; } int EmbedStream::getChar() { if (limited && !length) { return EOF; } --length; return str->getChar(); } int EmbedStream::lookChar() { if (limited && !length) { return EOF; } return str->lookChar(); } int EmbedStream::getBlock(char *blk, int size) { if (size <= 0) { return 0; } if (limited && length < (Guint)size) { size = (int)length; } return str->getBlock(blk, size); } void EmbedStream::setPos(GFileOffset pos, int dir) { error(errInternal, -1, "Called setPos() on EmbedStream"); } GFileOffset EmbedStream::getStart() { error(errInternal, -1, "Called getStart() on EmbedStream"); return 0; } void EmbedStream::moveStart(int delta) { error(errInternal, -1, "Called moveStart() on EmbedStream"); } //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ ASCIIHexStream::ASCIIHexStream(Stream *strA): FilterStream(strA) { buf = EOF; eof = gFalse; } ASCIIHexStream::~ASCIIHexStream() { delete str; } void ASCIIHexStream::reset() { str->reset(); buf = EOF; eof = gFalse; } int ASCIIHexStream::lookChar() { int c1, c2, x; if (buf != EOF) return buf; if (eof) { buf = EOF; return EOF; } do { c1 = str->getChar(); } while (isspace(c1)); if (c1 == '>') { eof = gTrue; buf = EOF; return buf; } do { c2 = str->getChar(); } while (isspace(c2)); if (c2 == '>') { eof = gTrue; c2 = '0'; } if (c1 >= '0' && c1 <= '9') { x = (c1 - '0') << 4; } else if (c1 >= 'A' && c1 <= 'F') { x = (c1 - 'A' + 10) << 4; } else if (c1 >= 'a' && c1 <= 'f') { x = (c1 - 'a' + 10) << 4; } else if (c1 == EOF) { eof = gTrue; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c1); x = 0; } if (c2 >= '0' && c2 <= '9') { x += c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { x += c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { x += c2 - 'a' + 10; } else if (c2 == EOF) { eof = gTrue; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c2); } buf = x & 0xff; return buf; } GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/ASCIIHexDecode filter\n"); return s; } GBool ASCIIHexStream::isBinary(GBool last) { return str->isBinary(gFalse); } //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ ASCII85Stream::ASCII85Stream(Stream *strA): FilterStream(strA) { index = n = 0; eof = gFalse; } ASCII85Stream::~ASCII85Stream() { delete str; } void ASCII85Stream::reset() { str->reset(); index = n = 0; eof = gFalse; } int ASCII85Stream::lookChar() { int k; Gulong t; if (index >= n) { if (eof) return EOF; index = 0; do { c[0] = str->getChar(); } while (Lexer::isSpace(c[0])); if (c[0] == '~' || c[0] == EOF) { eof = gTrue; n = 0; return EOF; } else if (c[0] == 'z') { b[0] = b[1] = b[2] = b[3] = 0; n = 4; } else { for (k = 1; k < 5; ++k) { do { c[k] = str->getChar(); } while (Lexer::isSpace(c[k])); if (c[k] == '~' || c[k] == EOF) break; } n = k - 1; if (k < 5 && (c[k] == '~' || c[k] == EOF)) { for (++k; k < 5; ++k) c[k] = 0x21 + 84; eof = gTrue; } t = 0; for (k = 0; k < 5; ++k) t = t * 85 + (c[k] - 0x21); for (k = 3; k >= 0; --k) { b[k] = (int)(t & 0xff); t >>= 8; } } } return b[index]; } GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/ASCII85Decode filter\n"); return s; } GBool ASCII85Stream::isBinary(GBool last) { return str->isBinary(gFalse); } //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA): FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = NULL; } } else { pred = NULL; } early = earlyA; eof = gFalse; inputBits = 0; clearTable(); } LZWStream::~LZWStream() { if (pred) { delete pred; } delete str; } int LZWStream::getChar() { if (pred) { return pred->getChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::lookChar() { if (pred) { return pred->lookChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex]; } int LZWStream::getRawChar() { if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::getBlock(char *blk, int size) { int n, m; if (pred) { return pred->getBlock(blk, size); } if (eof) { return 0; } n = 0; while (n < size) { if (seqIndex >= seqLength) { if (!processNextCode()) { break; } } m = seqLength - seqIndex; if (m > size - n) { m = size - n; } memcpy(blk + n, seqBuf + seqIndex, m); seqIndex += m; n += m; } return n; } void LZWStream::reset() { str->reset(); if (pred) { pred->reset(); } eof = gFalse; inputBits = 0; clearTable(); } GBool LZWStream::processNextCode() { int code; int nextLength; int i, j; // check for EOF if (eof) { return gFalse; } // check for eod and clear-table codes start: code = getCode(); if (code == EOF || code == 257) { eof = gTrue; return gFalse; } if (code == 256) { clearTable(); goto start; } if (nextCode >= 4097) { error(errSyntaxError, getPos(), "Bad LZW stream - expected clear-table code"); clearTable(); } // process the next code nextLength = seqLength + 1; if (code < 256) { seqBuf[0] = code; seqLength = 1; } else if (code < nextCode) { seqLength = table[code].length; for (i = seqLength - 1, j = code; i > 0; --i) { seqBuf[i] = table[j].tail; j = table[j].head; } seqBuf[0] = j; } else if (code == nextCode) { seqBuf[seqLength] = newChar; ++seqLength; } else { error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code"); eof = gTrue; return gFalse; } newChar = seqBuf[0]; if (first) { first = gFalse; } else { table[nextCode].length = nextLength; table[nextCode].head = prevCode; table[nextCode].tail = newChar; ++nextCode; if (nextCode + early == 512) nextBits = 10; else if (nextCode + early == 1024) nextBits = 11; else if (nextCode + early == 2048) nextBits = 12; } prevCode = code; // reset buffer seqIndex = 0; return gTrue; } void LZWStream::clearTable() { nextCode = 258; nextBits = 9; seqIndex = seqLength = 0; first = gTrue; } int LZWStream::getCode() { int c; int code; while (inputBits < nextBits) { if ((c = str->getChar()) == EOF) return EOF; inputBuf = (inputBuf << 8) | (c & 0xff); inputBits += 8; } code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); inputBits -= nextBits; return code; } GString *LZWStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< "); if (!early) { s->append("/EarlyChange 0 "); } s->append(">> /LZWDecode filter\n"); return s; } GBool LZWStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ RunLengthStream::RunLengthStream(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; eof = gFalse; } RunLengthStream::~RunLengthStream() { delete str; } void RunLengthStream::reset() { str->reset(); bufPtr = bufEnd = buf; eof = gFalse; } int RunLengthStream::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > size - n) { m = size - n; } memcpy(blk + n, bufPtr, m); bufPtr += m; n += m; } return n; } GString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/RunLengthDecode filter\n"); return s; } GBool RunLengthStream::isBinary(GBool last) { return str->isBinary(gTrue); } GBool RunLengthStream::fillBuf() { int c; int n, i; if (eof) return gFalse; c = str->getChar(); if (c == 0x80 || c == EOF) { eof = gTrue; return gFalse; } if (c < 0x80) { n = c + 1; for (i = 0; i < n; ++i) buf[i] = (char)str->getChar(); } else { n = 0x101 - c; c = str->getChar(); for (i = 0; i < n; ++i) buf[i] = (char)c; } bufPtr = buf; bufEnd = buf + n; return gTrue; } //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, GBool byteAlignA, int columnsA, int rowsA, GBool endOfBlockA, GBool blackA): FilterStream(strA) { encoding = encodingA; endOfLine = endOfLineA; byteAlign = byteAlignA; columns = columnsA; if (columns < 1) { columns = 1; } else if (columns > INT_MAX - 2) { columns = INT_MAX - 2; } rows = rowsA; endOfBlock = endOfBlockA; black = blackA; // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns // ---> max codingLine size = columns + 1 // refLine has one extra guard entry at the end // ---> max refLine size = columns + 2 codingLine = (int *)gmallocn(columns + 1, sizeof(int)); refLine = (int *)gmallocn(columns + 2, sizeof(int)); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = columns; a0i = 0; outputBits = 0; buf = EOF; } CCITTFaxStream::~CCITTFaxStream() { delete str; gfree(refLine); gfree(codingLine); } void CCITTFaxStream::reset() { int code1; str->reset(); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = columns; a0i = 0; outputBits = 0; buf = EOF; // skip any initial zero bits and end-of-line marker, and get the 2D // encoding tag while ((code1 = lookBits(12)) == 0) { eatBits(1); } if (code1 == 0x001) { eatBits(12); endOfLine = gTrue; } if (encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } } inline void CCITTFaxStream::addPixels(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = gTrue; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } } inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = gTrue; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } else if (a1 < codingLine[a0i]) { if (a1 < 0) { error(errSyntaxError, getPos(), "Invalid CCITTFax code"); err = gTrue; a1 = 0; } while (a0i > 0 && a1 <= codingLine[a0i - 1]) { --a0i; } codingLine[a0i] = a1; } } int CCITTFaxStream::lookChar() { int code1, code2, code3; int b1i, blackPixels, i, bits; GBool gotEOL; if (buf != EOF) { return buf; } // read the next row if (outputBits == 0) { // if at eof just return EOF if (eof) { return EOF; } err = gFalse; // 2-D encoding if (nextLine2D) { for (i = 0; codingLine[i] < columns; ++i) { refLine[i] = codingLine[i]; } refLine[i++] = columns; refLine[i] = columns; codingLine[0] = 0; a0i = 0; b1i = 0; blackPixels = 0; // invariant: // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] // <= columns // exception at left edge: // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible // exception at right edge: // refLine[b1i] = refLine[b1i+1] = columns is possible while (codingLine[a0i] < columns) { code1 = getTwoDimCode(); switch (code1) { case twoDimPass: addPixels(refLine[b1i + 1], blackPixels); if (refLine[b1i + 1] < columns) { b1i += 2; } break; case twoDimHoriz: code1 = code2 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); do { code2 += code3 = getWhiteCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); do { code2 += code3 = getBlackCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); if (codingLine[a0i] < columns) { addPixels(codingLine[a0i] + code2, blackPixels ^ 1); } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } break; case twoDimVertR3: addPixels(refLine[b1i] + 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertR2: addPixels(refLine[b1i] + 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertR1: addPixels(refLine[b1i] + 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVert0: addPixels(refLine[b1i], blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL3: addPixelsNeg(refLine[b1i] - 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL2: addPixelsNeg(refLine[b1i] - 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL1: addPixelsNeg(refLine[b1i] - 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case EOF: addPixels(columns, 0); eof = gTrue; break; default: error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); addPixels(columns, 0); err = gTrue; break; } } // 1-D encoding } else { codingLine[0] = 0; a0i = 0; blackPixels = 0; while (codingLine[a0i] < columns) { code1 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); blackPixels ^= 1; } } // check for end-of-line marker, skipping over any extra zero bits // (if EncodedByteAlign is true and EndOfLine is false, there can // be "false" EOL markers -- i.e., if the last n unused bits in // row i are set to zero, and the first 11-n bits in row i+1 // happen to be zero -- so we don't look for EOL markers in this // case) gotEOL = gFalse; if (!endOfBlock && row == rows - 1) { eof = gTrue; } else if (endOfLine || !byteAlign) { code1 = lookBits(12); if (endOfLine) { while (code1 != EOF && code1 != 0x001) { eatBits(1); code1 = lookBits(12); } } else { while (code1 == 0) { eatBits(1); code1 = lookBits(12); } } if (code1 == 0x001) { eatBits(12); gotEOL = gTrue; } } // byte-align the row // (Adobe apparently doesn't do byte alignment after EOL markers // -- I've seen CCITT image data streams in two different formats, // both with the byteAlign flag set: // 1. xx:x0:01:yy:yy // 2. xx:00:1y:yy:yy // where xx is the previous line, yy is the next line, and colons // separate bytes.) if (byteAlign && !gotEOL) { inputBits &= ~7; } // check for end of stream if (lookBits(1) == EOF) { eof = gTrue; } // get 2D encoding tag if (!eof && encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } // check for end-of-block marker if (endOfBlock && !endOfLine && byteAlign) { // in this case, we didn't check for an EOL code above, so we // need to check here code1 = lookBits(24); if (code1 == 0x001001) { eatBits(12); gotEOL = gTrue; } } if (endOfBlock && gotEOL) { code1 = lookBits(12); if (code1 == 0x001) { eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } if (encoding >= 0) { for (i = 0; i < 4; ++i) { code1 = lookBits(12); if (code1 != 0x001) { error(errSyntaxError, getPos(), "Bad RTC code in CCITTFax stream"); } eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } } } eof = gTrue; } // look for an end-of-line marker after an error -- we only do // this if we know the stream contains end-of-line markers because // the "just plow on" technique tends to work better otherwise } else if (err && endOfLine) { while (1) { code1 = lookBits(13); if (code1 == EOF) { eof = gTrue; return EOF; } if ((code1 >> 1) == 0x001) { break; } eatBits(1); } eatBits(12); if (encoding > 0) { eatBits(1); nextLine2D = !(code1 & 1); } } // set up for output if (codingLine[0] > 0) { outputBits = codingLine[a0i = 0]; } else { outputBits = codingLine[a0i = 1]; } ++row; } // get a byte if (outputBits >= 8) { buf = (a0i & 1) ? 0x00 : 0xff; outputBits -= 8; if (outputBits == 0 && codingLine[a0i] < columns) { ++a0i; outputBits = codingLine[a0i] - codingLine[a0i - 1]; } } else { bits = 8; buf = 0; do { if (outputBits > bits) { buf <<= bits; if (!(a0i & 1)) { buf |= 0xff >> (8 - bits); } outputBits -= bits; bits = 0; } else { buf <<= outputBits; if (!(a0i & 1)) { buf |= 0xff >> (8 - outputBits); } bits -= outputBits; outputBits = 0; if (codingLine[a0i] < columns) { ++a0i; outputBits = codingLine[a0i] - codingLine[a0i - 1]; } else if (bits > 0) { buf <<= bits; bits = 0; } } } while (bits); } if (black) { buf ^= 0xff; } return buf; } short CCITTFaxStream::getTwoDimCode() { int code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { if ((code = lookBits(7)) != EOF) { p = &twoDimTab1[code]; if (p->bits > 0) { eatBits(p->bits); return p->n; } } } else { for (n = 1; n <= 7; ++n) { if ((code = lookBits(n)) == EOF) { break; } if (n < 7) { code <<= 7 - n; } p = &twoDimTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad two dim code ({0:04x}) in CCITTFax stream", code); return EOF; } short CCITTFaxStream::getWhiteCode() { short code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(12); if (code == EOF) { return 1; } if ((code >> 5) == 0) { p = &whiteTab1[code]; } else { p = &whiteTab2[code >> 3]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 1; n <= 9; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 9) { code <<= 9 - n; } p = &whiteTab2[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 11; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code <<= 12 - n; } p = &whiteTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad white code ({0:04x}) in CCITTFax stream", code); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::getBlackCode() { short code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(13); if (code == EOF) { return 1; } if ((code >> 7) == 0) { p = &blackTab1[code]; } else if ((code >> 9) == 0 && (code >> 7) != 0) { p = &blackTab2[(code >> 1) - 64]; } else { p = &blackTab3[code >> 7]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 2; n <= 6; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 6) { code <<= 6 - n; } p = &blackTab3[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 7; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code <<= 12 - n; } if (code >= 64) { p = &blackTab2[code - 64]; if (p->bits == n) { eatBits(n); return p->n; } } } for (n = 10; n <= 13; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 13) { code <<= 13 - n; } p = &blackTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad black code ({0:04x}) in CCITTFax stream", code); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::lookBits(int n) { int c; while (inputBits < n) { if ((c = str->getChar()) == EOF) { if (inputBits == 0) { return EOF; } // near the end of the stream, the caller may ask for more bits // than are available, but there may still be a valid code in // however many bits are available -- we need to return correct // data in this case return (inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n)); } inputBuf = (inputBuf << 8) + c; inputBits += 8; } return (inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n)); } GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { GString *s; char s1[50]; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< "); if (encoding != 0) { sprintf(s1, "/K %d ", encoding); s->append(s1); } if (endOfLine) { s->append("/EndOfLine true "); } if (byteAlign) { s->append("/EncodedByteAlign true "); } sprintf(s1, "/Columns %d ", columns); s->append(s1); if (rows != 0) { sprintf(s1, "/Rows %d ", rows); s->append(s1); } if (!endOfBlock) { s->append("/EndOfBlock false "); } if (black) { s->append("/BlackIs1 true "); } s->append(">> /CCITTFaxDecode filter\n"); return s; } GBool CCITTFaxStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ // IDCT constants (20.12 fixed point format) #define 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 #define dctCbToG -22553 // -0.3441363 #define dctCrToG -46802 // -0.71413636 #define dctCbToB 116130 // 1.772 // 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] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; DCTStream::DCTStream(Stream *strA, GBool colorXformA): FilterStream(strA) { int i; colorXform = colorXformA; progressive = interleaved = gFalse; width = height = 0; mcuWidth = mcuHeight = 0; numComps = 0; comp = 0; x = y = 0; for (i = 0; i < 4; ++i) { frameBuf[i] = NULL; } rowBuf = NULL; memset(dcHuffTables, 0, sizeof(dcHuffTables)); memset(acHuffTables, 0, sizeof(acHuffTables)); dctClipInit(); } DCTStream::~DCTStream() { close(); delete str; } void DCTStream::reset() { int i; str->reset(); progressive = interleaved = gFalse; width = height = 0; numComps = 0; numQuantTables = 0; numDCHuffTables = 0; numACHuffTables = 0; gotJFIFMarker = gFalse; gotAdobeMarker = gFalse; restartInterval = 0; if (!readHeader()) { // force an EOF condition progressive = gTrue; y = height; return; } // compute MCU size if (numComps == 1) { compInfo[0].hSample = compInfo[0].vSample = 1; } mcuWidth = compInfo[0].hSample; mcuHeight = compInfo[0].vSample; for (i = 1; i < numComps; ++i) { if (compInfo[i].hSample > mcuWidth) { mcuWidth = compInfo[i].hSample; } if (compInfo[i].vSample > mcuHeight) { mcuHeight = compInfo[i].vSample; } } mcuWidth *= 8; mcuHeight *= 8; // figure out color transform if (colorXform == -1) { if (numComps == 3) { if (gotJFIFMarker) { colorXform = 1; } else if (compInfo[0].id == 82 && compInfo[1].id == 71 && compInfo[2].id == 66) { // ASCII "RGB" colorXform = 0; } else { colorXform = 1; } } else { colorXform = 0; } } if (progressive || !interleaved) { // allocate a buffer for the whole image bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight; if (bufWidth <= 0 || bufHeight <= 0 || bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); y = height; return; } for (i = 0; i < numComps; ++i) { frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); } // read the image data do { restartMarker = 0xd0; restart(); readScan(); } while (readHeader()); // decode decodeImage(); // initialize counters comp = 0; x = 0; y = 0; } else { // allocate a buffer for one row of MCUs bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth); rowBufPtr = rowBufEnd = rowBuf; // initialize counters y = -mcuHeight; restartMarker = 0xd0; restart(); } } void DCTStream::close() { int i; for (i = 0; i < 4; ++i) { gfree(frameBuf[i]); frameBuf[i] = NULL; } gfree(rowBuf); rowBuf = NULL; FilterStream::close(); } int DCTStream::getChar() { int c; if (progressive || !interleaved) { if (y >= height) { return EOF; } c = frameBuf[comp][y * bufWidth + x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; } } } else { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { return EOF; } y += mcuHeight; if (!readMCURow()) { y = height; return EOF; } } c = *rowBufPtr++; } return c; } int DCTStream::lookChar() { if (progressive || !interleaved) { if (y >= height) { return EOF; } return frameBuf[comp][y * bufWidth + x]; } else { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { return EOF; } if (!readMCURow()) { y = height; return EOF; } } return *rowBufPtr; } } void DCTStream::restart() { int i; inputBits = 0; restartCtr = restartInterval; for (i = 0; i < numComps; ++i) { compInfo[i].prevDC = 0; } eobRun = 0; } // Read one row of MCUs from a sequential JPEG stream. GBool DCTStream::readMCURow() { int data1[64]; Guchar data2[64]; Guchar *p1, *p2; int pY, pCb, pCr, pR, pG, pB; int h, v, horiz, vert, hSub, vSub; int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int c; for (x1 = 0; x1 < width; x1 += mcuWidth) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return gFalse; } if (++restartMarker == 0xd8) restartMarker = 0xd0; restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data1)) { return gFalse; } transformDataUnit(quantTables[compInfo[cc].quantTable], data1, data2); if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { 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 && x1+x2+16 <= width) { for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { 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 && x1+x2+x4+x5 < width; ++x5) { p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i]; } } ++i; } } } } } } --restartCtr; } // 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; } // Read one scan from a progressive or non-interleaved JPEG stream. void DCTStream::readScan() { int data[64]; int x1, y1, dx1, dy1, x2, y2, y3, cc, i; int h, v, horiz, vert, vSub; int *p1; int c; if (scanInfo.numComps == 1) { for (cc = 0; cc < numComps; ++cc) { if (scanInfo.comp[cc]) { break; } } dx1 = mcuWidth / compInfo[cc].hSample; dy1 = mcuHeight / compInfo[cc].vSample; } else { dx1 = mcuWidth; dy1 = mcuHeight; } for (y1 = 0; y1 < height; y1 += dy1) { for (x1 = 0; x1 < width; x1 += dx1) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return; } if (++restartMarker == 0xd8) { restartMarker = 0xd0; } restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { if (!scanInfo.comp[cc]) { continue; } h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; vSub = vert / 8; for (y2 = 0; y2 < dy1; y2 += vert) { for (x2 = 0; x2 < dx1; x2 += horiz) { // pull out the current values p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { data[i] = p1[0]; data[i+1] = p1[1]; data[i+2] = p1[2]; data[i+3] = p1[3]; data[i+4] = p1[4]; data[i+5] = p1[5]; data[i+6] = p1[6]; data[i+7] = p1[7]; p1 += bufWidth * vSub; } // read one data unit if (progressive) { if (!readProgressiveDataUnit( &dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } else { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } // add the data unit into frameBuf p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = data[i]; p1[1] = data[i+1]; p1[2] = data[i+2]; p1[3] = data[i+3]; p1[4] = data[i+4]; p1[5] = data[i+5]; p1[6] = data[i+6]; p1[7] = data[i+7]; p1 += bufWidth * vSub; } } } } --restartCtr; } } } // Read one data unit from a sequential JPEG stream. GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp; int c; int i, j; if ((size = readHuffSym(dcHuffTable)) == 9999) { return gFalse; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return gFalse; } } else { amp = 0; } data[0] = *prevDC += amp; for (i = 1; i < 64; ++i) { data[i] = 0; } i = 1; while (i < 64) { run = 0; while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) { run += 0x10; } if (c == 9999) { return gFalse; } if (c == 0x00) { break; } else { run += (c >> 4) & 0x0f; size = c & 0x0f; amp = readAmp(size); if (amp == 9999) { return gFalse; } i += run; if (i < 64) { j = dctZigZag[i++]; data[j] = amp; } } } return gTrue; } // Read one data unit from a progressive JPEG stream. GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp, bit, c; int i, j, k; // get the DC coefficient i = scanInfo.firstCoeff; if (i == 0) { if (scanInfo.ah == 0) { if ((size = readHuffSym(dcHuffTable)) == 9999) { return gFalse; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return gFalse; } } else { amp = 0; } data[0] += (*prevDC += amp) << scanInfo.al; } else { if ((bit = readBit()) == 9999) { return gFalse; } if (bit) { if (data[0] >= 0) { data[0] += 1 << scanInfo.al; } else { data[0] -= 1 << scanInfo.al; } } } ++i; } if (scanInfo.lastCoeff == 0) { return gTrue; } // check for an EOB run if (eobRun > 0) { while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } --eobRun; return gTrue; } // read the AC coefficients while (i <= scanInfo.lastCoeff) { if ((c = readHuffSym(acHuffTable)) == 9999) { return gFalse; } // ZRL if (c == 0xf0) { k = 0; while (k < 16 && i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] == 0) { ++k; } else { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } // EOB run } else if ((c & 0x0f) == 0x00) { j = c >> 4; eobRun = 0; for (k = 0; k < j; ++k) { if ((bit = readBit()) == EOF) { return gFalse; } eobRun = (eobRun << 1) | bit; } eobRun += 1 << j; while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } --eobRun; break; // zero run and one AC coefficient } else { run = (c >> 4) & 0x0f; size = c & 0x0f; if ((amp = readAmp(size)) == 9999) { return gFalse; } j = 0; // make gcc happy for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) { j = dctZigZag[i++]; while (data[j] != 0 && i <= scanInfo.lastCoeff) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } j = dctZigZag[i++]; } } data[j] = amp << scanInfo.al; } } return gTrue; } // Decode a progressive JPEG image. void DCTStream::decodeImage() { int dataIn[64]; Guchar dataOut[64]; Gushort *quantTable; int pY, pCb, pCr, pR, pG, pB; int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int h, v, horiz, vert, hSub, vSub; int *p0, *p1, *p2; for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) { for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) { for (cc = 0; cc < numComps; ++cc) { quantTable = quantTables[compInfo[cc].quantTable]; h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { // pull out the coded data unit p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { dataIn[i] = p1[0]; dataIn[i+1] = p1[1]; dataIn[i+2] = p1[2]; dataIn[i+3] = p1[3]; dataIn[i+4] = p1[4]; dataIn[i+5] = p1[5]; dataIn[i+6] = p1[6]; dataIn[i+7] = p1[7]; p1 += bufWidth * vSub; } // transform transformDataUnit(quantTable, dataIn, dataOut); // store back into frameBuf, doing replication for // subsampled components p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; if (hSub == 1 && vSub == 1) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = dataOut[i] & 0xff; p1[1] = dataOut[i+1] & 0xff; p1[2] = dataOut[i+2] & 0xff; p1[3] = dataOut[i+3] & 0xff; p1[4] = dataOut[i+4] & 0xff; p1[5] = dataOut[i+5] & 0xff; p1[6] = dataOut[i+6] & 0xff; p1[7] = dataOut[i+7] & 0xff; p1 += bufWidth; } } else if (hSub == 2 && vSub == 2) { p2 = p1 + bufWidth; for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff; p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff; p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff; p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff; p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff; p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff; p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff; p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff; p1 += bufWidth * 2; p2 += bufWidth * 2; } } else { i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { p2 = p1 + x4; for (y5 = 0; y5 < vSub; ++y5) { for (x5 = 0; x5 < hSub; ++x5) { p2[x5] = dataOut[i] & 0xff; } p2 += bufWidth; } ++i; } p1 += bufWidth * vSub; } } } } } // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = dctClip(pB); } } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = 255 - dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = 255 - dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = 255 - dctClip(pB); } } } } } } } // Transform one data unit -- this performs the dequantization and // IDCT steps. This IDCT algorithm is taken from: // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, // 988-991. // The stage numbers mentioned in the comments refer to Figure 1 in this // paper. void DCTStream::transformDataUnit(Gushort *quantTable, int dataIn[64], Guchar dataOut[64]) { int v0, v1, v2, v3, v4, v5, v6, v7, t0, t1, t2; int *p; Gushort *q; int i; // 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) { 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 = 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 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 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; p[7] = v0 - v7; p[1] = v1 + v6; p[6] = v1 - v6; p[2] = v2 + v5; p[5] = v2 - v5; p[3] = v3 + v4; p[4] = v3 - v4; } // inverse DCT on columns for (i = 0; i < 8; ++i) { p = dataIn + i; // check for all-zero AC coefficients if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 && p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) { 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 = p[0*8]; v1 = p[4*8]; v2 = p[2*8]; v3 = p[6*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 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 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; p[7*8] = v0 - v7; p[1*8] = v1 + v6; p[6*8] = v1 - v6; p[2*8] = v2 + v5; p[5*8] = v2 - v5; p[3*8] = v3 + v4; p[4*8] = v3 - v4; } // convert to 8-bit integers for (i = 0; i < 64; ++i) { dataOut[i] = dctClip(128 + (dataIn[i] >> 3)); } } int DCTStream::readHuffSym(DCTHuffTable *table) { Gushort code; int bit; int codeBits; code = 0; codeBits = 0; do { // add a bit to the code if ((bit = readBit()) == EOF) { return 9999; } code = (code << 1) + bit; ++codeBits; // look up code if (code < table->firstCode[codeBits]) { break; } if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) { code -= table->firstCode[codeBits]; return table->sym[table->firstSym[codeBits] + code]; } } while (codeBits < 16); error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream"); return 9999; } int DCTStream::readAmp(int size) { int amp, bit; int bits; amp = 0; for (bits = 0; bits < size; ++bits) { if ((bit = readBit()) == EOF) return 9999; amp = (amp << 1) + bit; } if (amp < (1 << (size - 1))) amp -= (1 << size) - 1; return amp; } int DCTStream::readBit() { int bit; int c, c2; if (inputBits == 0) { if ((c = str->getChar()) == EOF) return EOF; if (c == 0xff) { do { c2 = str->getChar(); } while (c2 == 0xff); if (c2 != 0x00) { error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff"); return EOF; } } inputBuf = c; inputBits = 8; } bit = (inputBuf >> (inputBits - 1)) & 1; --inputBits; return bit; } GBool DCTStream::readHeader() { GBool doScan; int n; int c = 0; // read headers doScan = gFalse; while (!doScan) { c = readMarker(); switch (c) { case 0xc0: // SOF0 (sequential) case 0xc1: // SOF1 (extended sequential) if (!readBaselineSOF()) { return gFalse; } break; case 0xc2: // SOF2 (progressive) if (!readProgressiveSOF()) { return gFalse; } break; case 0xc4: // DHT if (!readHuffmanTables()) { return gFalse; } break; case 0xd8: // SOI break; case 0xd9: // EOI return gFalse; case 0xda: // SOS if (!readScanInfo()) { return gFalse; } doScan = gTrue; break; case 0xdb: // DQT if (!readQuantTables()) { return gFalse; } break; case 0xdd: // DRI if (!readRestartInterval()) { return gFalse; } break; case 0xe0: // APP0 if (!readJFIFMarker()) { return gFalse; } break; case 0xee: // APP14 if (!readAdobeMarker()) { return gFalse; } break; case EOF: error(errSyntaxError, getPos(), "Bad DCT header"); return gFalse; default: // skip APPn / COM / etc. if (c >= 0xe0) { n = read16() - 2; str->discardChars(n); } else { error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c); return gFalse; } break; } } return gTrue; } GBool DCTStream::readBaselineSOF() { int prec; int i; int c; read16(); // length prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return gFalse; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return gFalse; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return gFalse; } } progressive = gFalse; return gTrue; } GBool DCTStream::readProgressiveSOF() { int prec; int i; int c; read16(); // length prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return gFalse; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return gFalse; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return gFalse; } } progressive = gTrue; return gTrue; } GBool DCTStream::readScanInfo() { int length; int id, c; int i, j; length = read16() - 2; scanInfo.numComps = str->getChar(); if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); scanInfo.numComps = 0; return gFalse; } --length; if (length != 2 * scanInfo.numComps + 3) { error(errSyntaxError, getPos(), "Bad DCT scan info block"); return gFalse; } interleaved = scanInfo.numComps == numComps; for (j = 0; j < numComps; ++j) { scanInfo.comp[j] = gFalse; } for (i = 0; i < scanInfo.numComps; ++i) { id = str->getChar(); // some (broken) DCT streams reuse ID numbers, but at least they // keep the components in order, so we check compInfo[i] first to // work around the problem if (id == compInfo[i].id) { j = i; } else { for (j = 0; j < numComps; ++j) { if (id == compInfo[j].id) { break; } } if (j == numComps) { error(errSyntaxError, getPos(), "Bad DCT component ID in scan info block"); return gFalse; } } scanInfo.comp[j] = gTrue; c = str->getChar(); scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f; scanInfo.acHuffTable[j] = c & 0x0f; } scanInfo.firstCoeff = str->getChar(); scanInfo.lastCoeff = str->getChar(); if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 || scanInfo.firstCoeff > scanInfo.lastCoeff) { error(errSyntaxError, getPos(), "Bad DCT coefficient numbers in scan info block"); return gFalse; } c = str->getChar(); scanInfo.ah = (c >> 4) & 0x0f; scanInfo.al = c & 0x0f; return gTrue; } GBool DCTStream::readQuantTables() { int length, prec, i, index; length = read16() - 2; while (length > 0) { index = str->getChar(); prec = (index >> 4) & 0x0f; index &= 0x0f; if (prec > 1 || index >= 4) { error(errSyntaxError, getPos(), "Bad DCT quantization table"); return gFalse; } if (index == numQuantTables) { numQuantTables = index + 1; } for (i = 0; i < 64; ++i) { if (prec) { quantTables[index][dctZigZag[i]] = read16(); } else { quantTables[index][dctZigZag[i]] = str->getChar(); } } if (prec) { length -= 129; } else { length -= 65; } } return gTrue; } GBool DCTStream::readHuffmanTables() { DCTHuffTable *tbl; int length; int index; Gushort code; Guchar sym; int i; int c; length = read16() - 2; while (length > 0) { index = str->getChar(); --length; if ((index & 0x0f) >= 4) { error(errSyntaxError, getPos(), "Bad DCT Huffman table"); return gFalse; } if (index & 0x10) { index &= 0x0f; if (index >= numACHuffTables) numACHuffTables = index+1; tbl = &acHuffTables[index]; } else { index &= 0x0f; if (index >= numDCHuffTables) numDCHuffTables = index+1; tbl = &dcHuffTables[index]; } sym = 0; code = 0; for (i = 1; i <= 16; ++i) { c = str->getChar(); tbl->firstSym[i] = sym; tbl->firstCode[i] = code; tbl->numCodes[i] = c; sym += c; code = (code + c) << 1; } length -= 16; for (i = 0; i < sym; ++i) tbl->sym[i] = str->getChar(); length -= sym; } return gTrue; } GBool DCTStream::readRestartInterval() { int length; length = read16(); if (length != 4) { error(errSyntaxError, getPos(), "Bad DCT restart interval"); return gFalse; } restartInterval = read16(); return gTrue; } GBool DCTStream::readJFIFMarker() { int length, i; char buf[5]; int c; length = read16(); length -= 2; if (length >= 5) { for (i = 0; i < 5; ++i) { if ((c = str->getChar()) == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return gFalse; } buf[i] = c; } length -= 5; if (!memcmp(buf, "JFIF\0", 5)) { gotJFIFMarker = gTrue; } } while (length > 0) { if (str->getChar() == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return gFalse; } --length; } return gTrue; } GBool DCTStream::readAdobeMarker() { int length, i; char buf[12]; int c; length = read16(); if (length < 14) { goto err; } for (i = 0; i < 12; ++i) { if ((c = str->getChar()) == EOF) { goto err; } buf[i] = c; } if (strncmp(buf, "Adobe", 5)) { goto err; } colorXform = buf[11]; gotAdobeMarker = gTrue; for (i = 14; i < length; ++i) { if (str->getChar() == EOF) { goto err; } } return gTrue; err: error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker"); return gFalse; } GBool DCTStream::readTrailer() { int c; c = readMarker(); if (c != 0xd9) { // EOI error(errSyntaxError, getPos(), "Bad DCT trailer"); return gFalse; } return gTrue; } int DCTStream::readMarker() { int c; do { do { c = str->getChar(); } while (c != 0xff && c != EOF); do { c = str->getChar(); } while (c == 0xff); } while (c == 0x00); return c; } int DCTStream::read16() { int c1, c2; if ((c1 = str->getChar()) == EOF) return EOF; if ((c2 = str->getChar()) == EOF) return EOF; return (c1 << 8) + c2; } GString *DCTStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /DCTDecode filter\n"); return s; } GBool DCTStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = { {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, {0, 9}, {0, 10}, {1, 11}, {1, 13}, {1, 15}, {1, 17}, {2, 19}, {2, 23}, {2, 27}, {2, 31}, {3, 35}, {3, 43}, {3, 51}, {3, 59}, {4, 67}, {4, 83}, {4, 99}, {4, 115}, {5, 131}, {5, 163}, {5, 195}, {5, 227}, {0, 258}, {0, 258}, {0, 258} }; FlateDecode FlateStream::distDecode[flateMaxDistCodes] = { { 0, 1}, { 0, 2}, { 0, 3}, { 0, 4}, { 1, 5}, { 1, 7}, { 2, 9}, { 2, 13}, { 3, 17}, { 3, 25}, { 4, 33}, { 4, 49}, { 5, 65}, { 5, 97}, { 6, 129}, { 6, 193}, { 7, 257}, { 7, 385}, { 8, 513}, { 8, 769}, { 9, 1025}, { 9, 1537}, {10, 2049}, {10, 3073}, {11, 4097}, {11, 6145}, {12, 8193}, {12, 12289}, {13, 16385}, {13, 24577} }; static FlateCode flateFixedLitCodeTabCodes[512] = { {7, 0x0100}, {8, 0x0050}, {8, 0x0010}, {8, 0x0118}, {7, 0x0110}, {8, 0x0070}, {8, 0x0030}, {9, 0x00c0}, {7, 0x0108}, {8, 0x0060}, {8, 0x0020}, {9, 0x00a0}, {8, 0x0000}, {8, 0x0080}, {8, 0x0040}, {9, 0x00e0}, {7, 0x0104}, {8, 0x0058}, {8, 0x0018}, {9, 0x0090}, {7, 0x0114}, {8, 0x0078}, {8, 0x0038}, {9, 0x00d0}, {7, 0x010c}, {8, 0x0068}, {8, 0x0028}, {9, 0x00b0}, {8, 0x0008}, {8, 0x0088}, {8, 0x0048}, {9, 0x00f0}, {7, 0x0102}, {8, 0x0054}, {8, 0x0014}, {8, 0x011c}, {7, 0x0112}, {8, 0x0074}, {8, 0x0034}, {9, 0x00c8}, {7, 0x010a}, {8, 0x0064}, {8, 0x0024}, {9, 0x00a8}, {8, 0x0004}, {8, 0x0084}, {8, 0x0044}, {9, 0x00e8}, {7, 0x0106}, {8, 0x005c}, {8, 0x001c}, {9, 0x0098}, {7, 0x0116}, {8, 0x007c}, {8, 0x003c}, {9, 0x00d8}, {7, 0x010e}, {8, 0x006c}, {8, 0x002c}, {9, 0x00b8}, {8, 0x000c}, {8, 0x008c}, {8, 0x004c}, {9, 0x00f8}, {7, 0x0101}, {8, 0x0052}, {8, 0x0012}, {8, 0x011a}, {7, 0x0111}, {8, 0x0072}, {8, 0x0032}, {9, 0x00c4}, {7, 0x0109}, {8, 0x0062}, {8, 0x0022}, {9, 0x00a4}, {8, 0x0002}, {8, 0x0082}, {8, 0x0042}, {9, 0x00e4}, {7, 0x0105}, {8, 0x005a}, {8, 0x001a}, {9, 0x0094}, {7, 0x0115}, {8, 0x007a}, {8, 0x003a}, {9, 0x00d4}, {7, 0x010d}, {8, 0x006a}, {8, 0x002a}, {9, 0x00b4}, {8, 0x000a}, {8, 0x008a}, {8, 0x004a}, {9, 0x00f4}, {7, 0x0103}, {8, 0x0056}, {8, 0x0016}, {8, 0x011e}, {7, 0x0113}, {8, 0x0076}, {8, 0x0036}, {9, 0x00cc}, {7, 0x010b}, {8, 0x0066}, {8, 0x0026}, {9, 0x00ac}, {8, 0x0006}, {8, 0x0086}, {8, 0x0046}, {9, 0x00ec}, {7, 0x0107}, {8, 0x005e}, {8, 0x001e}, {9, 0x009c}, {7, 0x0117}, {8, 0x007e}, {8, 0x003e}, {9, 0x00dc}, {7, 0x010f}, {8, 0x006e}, {8, 0x002e}, {9, 0x00bc}, {8, 0x000e}, {8, 0x008e}, {8, 0x004e}, {9, 0x00fc}, {7, 0x0100}, {8, 0x0051}, {8, 0x0011}, {8, 0x0119}, {7, 0x0110}, {8, 0x0071}, {8, 0x0031}, {9, 0x00c2}, {7, 0x0108}, {8, 0x0061}, {8, 0x0021}, {9, 0x00a2}, {8, 0x0001}, {8, 0x0081}, {8, 0x0041}, {9, 0x00e2}, {7, 0x0104}, {8, 0x0059}, {8, 0x0019}, {9, 0x0092}, {7, 0x0114}, {8, 0x0079}, {8, 0x0039}, {9, 0x00d2}, {7, 0x010c}, {8, 0x0069}, {8, 0x0029}, {9, 0x00b2}, {8, 0x0009}, {8, 0x0089}, {8, 0x0049}, {9, 0x00f2}, {7, 0x0102}, {8, 0x0055}, {8, 0x0015}, {8, 0x011d}, {7, 0x0112}, {8, 0x0075}, {8, 0x0035}, {9, 0x00ca}, {7, 0x010a}, {8, 0x0065}, {8, 0x0025}, {9, 0x00aa}, {8, 0x0005}, {8, 0x0085}, {8, 0x0045}, {9, 0x00ea}, {7, 0x0106}, {8, 0x005d}, {8, 0x001d}, {9, 0x009a}, {7, 0x0116}, {8, 0x007d}, {8, 0x003d}, {9, 0x00da}, {7, 0x010e}, {8, 0x006d}, {8, 0x002d}, {9, 0x00ba}, {8, 0x000d}, {8, 0x008d}, {8, 0x004d}, {9, 0x00fa}, {7, 0x0101}, {8, 0x0053}, {8, 0x0013}, {8, 0x011b}, {7, 0x0111}, {8, 0x0073}, {8, 0x0033}, {9, 0x00c6}, {7, 0x0109}, {8, 0x0063}, {8, 0x0023}, {9, 0x00a6}, {8, 0x0003}, {8, 0x0083}, {8, 0x0043}, {9, 0x00e6}, {7, 0x0105}, {8, 0x005b}, {8, 0x001b}, {9, 0x0096}, {7, 0x0115}, {8, 0x007b}, {8, 0x003b}, {9, 0x00d6}, {7, 0x010d}, {8, 0x006b}, {8, 0x002b}, {9, 0x00b6}, {8, 0x000b}, {8, 0x008b}, {8, 0x004b}, {9, 0x00f6}, {7, 0x0103}, {8, 0x0057}, {8, 0x0017}, {8, 0x011f}, {7, 0x0113}, {8, 0x0077}, {8, 0x0037}, {9, 0x00ce}, {7, 0x010b}, {8, 0x0067}, {8, 0x0027}, {9, 0x00ae}, {8, 0x0007}, {8, 0x0087}, {8, 0x0047}, {9, 0x00ee}, {7, 0x0107}, {8, 0x005f}, {8, 0x001f}, {9, 0x009e}, {7, 0x0117}, {8, 0x007f}, {8, 0x003f}, {9, 0x00de}, {7, 0x010f}, {8, 0x006f}, {8, 0x002f}, {9, 0x00be}, {8, 0x000f}, {8, 0x008f}, {8, 0x004f}, {9, 0x00fe}, {7, 0x0100}, {8, 0x0050}, {8, 0x0010}, {8, 0x0118}, {7, 0x0110}, {8, 0x0070}, {8, 0x0030}, {9, 0x00c1}, {7, 0x0108}, {8, 0x0060}, {8, 0x0020}, {9, 0x00a1}, {8, 0x0000}, {8, 0x0080}, {8, 0x0040}, {9, 0x00e1}, {7, 0x0104}, {8, 0x0058}, {8, 0x0018}, {9, 0x0091}, {7, 0x0114}, {8, 0x0078}, {8, 0x0038}, {9, 0x00d1}, {7, 0x010c}, {8, 0x0068}, {8, 0x0028}, {9, 0x00b1}, {8, 0x0008}, {8, 0x0088}, {8, 0x0048}, {9, 0x00f1}, {7, 0x0102}, {8, 0x0054}, {8, 0x0014}, {8, 0x011c}, {7, 0x0112}, {8, 0x0074}, {8, 0x0034}, {9, 0x00c9}, {7, 0x010a}, {8, 0x0064}, {8, 0x0024}, {9, 0x00a9}, {8, 0x0004}, {8, 0x0084}, {8, 0x0044}, {9, 0x00e9}, {7, 0x0106}, {8, 0x005c}, {8, 0x001c}, {9, 0x0099}, {7, 0x0116}, {8, 0x007c}, {8, 0x003c}, {9, 0x00d9}, {7, 0x010e}, {8, 0x006c}, {8, 0x002c}, {9, 0x00b9}, {8, 0x000c}, {8, 0x008c}, {8, 0x004c}, {9, 0x00f9}, {7, 0x0101}, {8, 0x0052}, {8, 0x0012}, {8, 0x011a}, {7, 0x0111}, {8, 0x0072}, {8, 0x0032}, {9, 0x00c5}, {7, 0x0109}, {8, 0x0062}, {8, 0x0022}, {9, 0x00a5}, {8, 0x0002}, {8, 0x0082}, {8, 0x0042}, {9, 0x00e5}, {7, 0x0105}, {8, 0x005a}, {8, 0x001a}, {9, 0x0095}, {7, 0x0115}, {8, 0x007a}, {8, 0x003a}, {9, 0x00d5}, {7, 0x010d}, {8, 0x006a}, {8, 0x002a}, {9, 0x00b5}, {8, 0x000a}, {8, 0x008a}, {8, 0x004a}, {9, 0x00f5}, {7, 0x0103}, {8, 0x0056}, {8, 0x0016}, {8, 0x011e}, {7, 0x0113}, {8, 0x0076}, {8, 0x0036}, {9, 0x00cd}, {7, 0x010b}, {8, 0x0066}, {8, 0x0026}, {9, 0x00ad}, {8, 0x0006}, {8, 0x0086}, {8, 0x0046}, {9, 0x00ed}, {7, 0x0107}, {8, 0x005e}, {8, 0x001e}, {9, 0x009d}, {7, 0x0117}, {8, 0x007e}, {8, 0x003e}, {9, 0x00dd}, {7, 0x010f}, {8, 0x006e}, {8, 0x002e}, {9, 0x00bd}, {8, 0x000e}, {8, 0x008e}, {8, 0x004e}, {9, 0x00fd}, {7, 0x0100}, {8, 0x0051}, {8, 0x0011}, {8, 0x0119}, {7, 0x0110}, {8, 0x0071}, {8, 0x0031}, {9, 0x00c3}, {7, 0x0108}, {8, 0x0061}, {8, 0x0021}, {9, 0x00a3}, {8, 0x0001}, {8, 0x0081}, {8, 0x0041}, {9, 0x00e3}, {7, 0x0104}, {8, 0x0059}, {8, 0x0019}, {9, 0x0093}, {7, 0x0114}, {8, 0x0079}, {8, 0x0039}, {9, 0x00d3}, {7, 0x010c}, {8, 0x0069}, {8, 0x0029}, {9, 0x00b3}, {8, 0x0009}, {8, 0x0089}, {8, 0x0049}, {9, 0x00f3}, {7, 0x0102}, {8, 0x0055}, {8, 0x0015}, {8, 0x011d}, {7, 0x0112}, {8, 0x0075}, {8, 0x0035}, {9, 0x00cb}, {7, 0x010a}, {8, 0x0065}, {8, 0x0025}, {9, 0x00ab}, {8, 0x0005}, {8, 0x0085}, {8, 0x0045}, {9, 0x00eb}, {7, 0x0106}, {8, 0x005d}, {8, 0x001d}, {9, 0x009b}, {7, 0x0116}, {8, 0x007d}, {8, 0x003d}, {9, 0x00db}, {7, 0x010e}, {8, 0x006d}, {8, 0x002d}, {9, 0x00bb}, {8, 0x000d}, {8, 0x008d}, {8, 0x004d}, {9, 0x00fb}, {7, 0x0101}, {8, 0x0053}, {8, 0x0013}, {8, 0x011b}, {7, 0x0111}, {8, 0x0073}, {8, 0x0033}, {9, 0x00c7}, {7, 0x0109}, {8, 0x0063}, {8, 0x0023}, {9, 0x00a7}, {8, 0x0003}, {8, 0x0083}, {8, 0x0043}, {9, 0x00e7}, {7, 0x0105}, {8, 0x005b}, {8, 0x001b}, {9, 0x0097}, {7, 0x0115}, {8, 0x007b}, {8, 0x003b}, {9, 0x00d7}, {7, 0x010d}, {8, 0x006b}, {8, 0x002b}, {9, 0x00b7}, {8, 0x000b}, {8, 0x008b}, {8, 0x004b}, {9, 0x00f7}, {7, 0x0103}, {8, 0x0057}, {8, 0x0017}, {8, 0x011f}, {7, 0x0113}, {8, 0x0077}, {8, 0x0037}, {9, 0x00cf}, {7, 0x010b}, {8, 0x0067}, {8, 0x0027}, {9, 0x00af}, {8, 0x0007}, {8, 0x0087}, {8, 0x0047}, {9, 0x00ef}, {7, 0x0107}, {8, 0x005f}, {8, 0x001f}, {9, 0x009f}, {7, 0x0117}, {8, 0x007f}, {8, 0x003f}, {9, 0x00df}, {7, 0x010f}, {8, 0x006f}, {8, 0x002f}, {9, 0x00bf}, {8, 0x000f}, {8, 0x008f}, {8, 0x004f}, {9, 0x00ff} }; FlateHuffmanTab FlateStream::fixedLitCodeTab = { flateFixedLitCodeTabCodes, 9 }; static FlateCode flateFixedDistCodeTabCodes[32] = { {5, 0x0000}, {5, 0x0010}, {5, 0x0008}, {5, 0x0018}, {5, 0x0004}, {5, 0x0014}, {5, 0x000c}, {5, 0x001c}, {5, 0x0002}, {5, 0x0012}, {5, 0x000a}, {5, 0x001a}, {5, 0x0006}, {5, 0x0016}, {5, 0x000e}, {0, 0x0000}, {5, 0x0001}, {5, 0x0011}, {5, 0x0009}, {5, 0x0019}, {5, 0x0005}, {5, 0x0015}, {5, 0x000d}, {5, 0x001d}, {5, 0x0003}, {5, 0x0013}, {5, 0x000b}, {5, 0x001b}, {5, 0x0007}, {5, 0x0017}, {5, 0x000f}, {0, 0x0000} }; FlateHuffmanTab FlateStream::fixedDistCodeTab = { flateFixedDistCodeTabCodes, 5 }; FlateStream::FlateStream(Stream *strA, int predictor, int columns, int colors, int bits): FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = NULL; } } else { pred = NULL; } litCodeTab.codes = NULL; distCodeTab.codes = NULL; memset(buf, 0, flateWindow); } FlateStream::~FlateStream() { if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(litCodeTab.codes); } if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(distCodeTab.codes); } if (pred) { delete pred; } delete str; } void FlateStream::reset() { int cmf, flg; index = 0; remain = 0; codeBuf = 0; codeSize = 0; compressedBlock = gFalse; endOfBlock = gTrue; eof = gTrue; str->reset(); if (pred) { pred->reset(); } // read header //~ need to look at window size? endOfBlock = eof = gTrue; cmf = str->getChar(); flg = str->getChar(); if (cmf == EOF || flg == EOF) return; if ((cmf & 0x0f) != 0x08) { error(errSyntaxError, getPos(), "Unknown compression method in flate stream"); return; } if ((((cmf << 8) + flg) % 31) != 0) { error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); return; } if (flg & 0x20) { error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); return; } eof = gFalse; } int FlateStream::getChar() { int c; if (pred) { return pred->getChar(); } while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } int FlateStream::lookChar() { int c; if (pred) { return pred->lookChar(); } while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; return c; } int FlateStream::getRawChar() { int c; while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } int FlateStream::getBlock(char *blk, int size) { int n; if (pred) { return pred->getBlock(blk, size); } n = 0; while (n < size) { if (remain == 0) { if (endOfBlock && eof) { break; } readSome(); } while (remain && n < size) { blk[n++] = buf[index]; index = (index + 1) & flateMask; --remain; } } return n; } GString *FlateStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 3 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /FlateDecode filter\n"); return s; } GBool FlateStream::isBinary(GBool last) { return str->isBinary(gTrue); } void FlateStream::readSome() { int code1, code2; int len, dist; int i, j, k; int c; if (endOfBlock) { if (!startBlock()) return; } if (compressedBlock) { if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF) goto err; if (code1 < 256) { buf[index] = code1; remain = 1; } else if (code1 == 256) { endOfBlock = gTrue; remain = 0; } else { code1 -= 257; code2 = lengthDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; len = lengthDecode[code1].first + code2; if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF) goto err; code2 = distDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; dist = distDecode[code1].first + code2; i = index; j = (index - dist) & flateMask; for (k = 0; k < len; ++k) { buf[i] = buf[j]; i = (i + 1) & flateMask; j = (j + 1) & flateMask; } remain = len; } } else { len = (blockLen < flateWindow) ? blockLen : flateWindow; for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) { if ((c = str->getChar()) == EOF) { endOfBlock = eof = gTrue; break; } buf[j] = c & 0xff; } remain = i; blockLen -= len; if (blockLen == 0) endOfBlock = gTrue; } return; err: error(errSyntaxError, getPos(), "Unexpected end of file in flate stream"); endOfBlock = eof = gTrue; remain = 0; } GBool FlateStream::startBlock() { int blockHdr; int c; int check; // free the code tables from the previous block if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(litCodeTab.codes); } litCodeTab.codes = NULL; if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(distCodeTab.codes); } distCodeTab.codes = NULL; // read block header blockHdr = getCodeWord(3); if (blockHdr & 1) eof = gTrue; blockHdr >>= 1; // uncompressed block if (blockHdr == 0) { compressedBlock = gFalse; if ((c = str->getChar()) == EOF) goto err; blockLen = c & 0xff; if ((c = str->getChar()) == EOF) goto err; blockLen |= (c & 0xff) << 8; if ((c = str->getChar()) == EOF) goto err; check = c & 0xff; if ((c = str->getChar()) == EOF) goto err; check |= (c & 0xff) << 8; if (check != (~blockLen & 0xffff)) error(errSyntaxError, getPos(), "Bad uncompressed block length in flate stream"); codeBuf = 0; codeSize = 0; // compressed block with fixed codes } else if (blockHdr == 1) { compressedBlock = gTrue; loadFixedCodes(); // compressed block with dynamic codes } else if (blockHdr == 2) { compressedBlock = gTrue; if (!readDynamicCodes()) { goto err; } // unknown block type } else { goto err; } endOfBlock = gFalse; return gTrue; err: error(errSyntaxError, getPos(), "Bad block header in flate stream"); endOfBlock = eof = gTrue; return gFalse; } void FlateStream::loadFixedCodes() { litCodeTab.codes = fixedLitCodeTab.codes; litCodeTab.maxLen = fixedLitCodeTab.maxLen; distCodeTab.codes = fixedDistCodeTab.codes; distCodeTab.maxLen = fixedDistCodeTab.maxLen; } GBool FlateStream::readDynamicCodes() { int numCodeLenCodes; int numLitCodes; int numDistCodes; int codeLenCodeLengths[flateMaxCodeLenCodes]; FlateHuffmanTab codeLenCodeTab; int len, repeat, code; int i; codeLenCodeTab.codes = NULL; // read lengths if ((numLitCodes = getCodeWord(5)) == EOF) { goto err; } numLitCodes += 257; if ((numDistCodes = getCodeWord(5)) == EOF) { goto err; } numDistCodes += 1; if ((numCodeLenCodes = getCodeWord(4)) == EOF) { goto err; } numCodeLenCodes += 4; if (numLitCodes > flateMaxLitCodes || numDistCodes > flateMaxDistCodes || numCodeLenCodes > flateMaxCodeLenCodes) { goto err; } // build the code length code table for (i = 0; i < flateMaxCodeLenCodes; ++i) { codeLenCodeLengths[i] = 0; } for (i = 0; i < numCodeLenCodes; ++i) { if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) { goto err; } } compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab); // build the literal and distance code tables len = 0; repeat = 0; i = 0; while (i < numLitCodes + numDistCodes) { if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) { goto err; } if (code == 16) { if ((repeat = getCodeWord(2)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } for (; repeat > 0; --repeat) { codeLengths[i++] = len; } } else if (code == 17) { if ((repeat = getCodeWord(3)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else if (code == 18) { if ((repeat = getCodeWord(7)) == EOF) { goto err; } repeat += 11; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else { codeLengths[i++] = len = code; } } compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab); compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab); gfree(codeLenCodeTab.codes); return gTrue; err: error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream"); gfree(codeLenCodeTab.codes); return gFalse; } // Convert an array of lengths, in value order, into a // Huffman code lookup table. void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) { int tabSize, len, code, code2, skip, val, i, t; // find max code length tab->maxLen = 0; for (val = 0; val < n; ++val) { if (lengths[val] > tab->maxLen) { tab->maxLen = lengths[val]; } } // allocate the table tabSize = 1 << tab->maxLen; tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode)); // clear the table for (i = 0; i < tabSize; ++i) { tab->codes[i].len = 0; tab->codes[i].val = 0; } // build the table for (len = 1, code = 0, skip = 2; len <= tab->maxLen; ++len, code <<= 1, skip <<= 1) { for (val = 0; val < n; ++val) { if (lengths[val] == len) { // bit-reverse the code code2 = 0; t = code; for (i = 0; i < len; ++i) { code2 = (code2 << 1) | (t & 1); t >>= 1; } // fill in the table entries for (i = code2; i < tabSize; i += skip) { tab->codes[i].len = (Gushort)len; tab->codes[i].val = (Gushort)val; } ++code; } } } } int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { FlateCode *code; int c; while (codeSize < tab->maxLen) { if ((c = str->getChar()) == EOF) { break; } codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; if (codeSize == 0 || codeSize < code->len || code->len == 0) { return EOF; } codeBuf >>= code->len; codeSize -= code->len; return (int)code->val; } int FlateStream::getCodeWord(int bits) { int c; while (codeSize < bits) { if ((c = str->getChar()) == EOF) return EOF; codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } c = codeBuf & ((1 << bits) - 1); codeBuf >>= bits; codeSize -= bits; return c; } //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ EOFStream::EOFStream(Stream *strA): FilterStream(strA) { } EOFStream::~EOFStream() { delete str; } //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) { bufSize = bufSizeA; buf = (int *)gmallocn(bufSize, sizeof(int)); } BufStream::~BufStream() { gfree(buf); delete str; } void BufStream::reset() { int i; str->reset(); for (i = 0; i < bufSize; ++i) { buf[i] = str->getChar(); } } int BufStream::getChar() { int c, i; c = buf[0]; for (i = 1; i < bufSize; ++i) { buf[i-1] = buf[i]; } buf[bufSize - 1] = str->getChar(); return c; } int BufStream::lookChar() { return buf[0]; } int BufStream::lookChar(int idx) { return buf[idx]; } GBool BufStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA): FilterStream(strA) { length = lengthA; count = 0; } FixedLengthEncoder::~FixedLengthEncoder() { if (str->isEncoder()) delete str; } void FixedLengthEncoder::reset() { str->reset(); count = 0; } int FixedLengthEncoder::getChar() { if (length >= 0 && count >= length) return EOF; ++count; return str->getChar(); } int FixedLengthEncoder::lookChar() { if (length >= 0 && count >= length) return EOF; return str->getChar(); } GBool FixedLengthEncoder::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } ASCIIHexEncoder::~ASCIIHexEncoder() { if (str->isEncoder()) { delete str; } } void ASCIIHexEncoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } GBool ASCIIHexEncoder::fillBuf() { static const char *hex = "0123456789abcdef"; int c; if (eof) { return gFalse; } bufPtr = bufEnd = buf; if ((c = str->getChar()) == EOF) { *bufEnd++ = '>'; eof = gTrue; } else { if (lineLen >= 64) { *bufEnd++ = '\n'; lineLen = 0; } *bufEnd++ = hex[(c >> 4) & 0x0f]; *bufEnd++ = hex[c & 0x0f]; lineLen += 2; } return gTrue; } //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ ASCII85Encoder::ASCII85Encoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } ASCII85Encoder::~ASCII85Encoder() { if (str->isEncoder()) delete str; } void ASCII85Encoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } GBool ASCII85Encoder::fillBuf() { Guint t; char buf1[5]; int c0, c1, c2, c3; int n, i; if (eof) { return gFalse; } c0 = str->getChar(); c1 = str->getChar(); c2 = str->getChar(); c3 = str->getChar(); bufPtr = bufEnd = buf; if (c3 == EOF) { if (c0 == EOF) { n = 0; t = 0; } else { if (c1 == EOF) { n = 1; t = c0 << 24; } else if (c2 == EOF) { n = 2; t = (c0 << 24) | (c1 << 16); } else { n = 3; t = (c0 << 24) | (c1 << 16) | (c2 << 8); } for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= n; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } *bufEnd++ = '~'; *bufEnd++ = '>'; eof = gTrue; } else { t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; if (t == 0) { *bufEnd++ = 'z'; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } else { for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= 4; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } } return gTrue; } //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ RunLengthEncoder::RunLengthEncoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = nextEnd = buf; eof = gFalse; } RunLengthEncoder::~RunLengthEncoder() { if (str->isEncoder()) delete str; } void RunLengthEncoder::reset() { str->reset(); bufPtr = bufEnd = nextEnd = buf; eof = gFalse; } // // When fillBuf finishes, buf[] looks like this: // +-----+--------------+-----------------+-- // + tag | ... data ... | next 0, 1, or 2 | // +-----+--------------+-----------------+-- // ^ ^ ^ // bufPtr bufEnd nextEnd // GBool RunLengthEncoder::fillBuf() { int c, c1, c2; int n; // already hit EOF? if (eof) return gFalse; // grab two bytes if (nextEnd < bufEnd + 1) { if ((c1 = str->getChar()) == EOF) { eof = gTrue; return gFalse; } } else { c1 = bufEnd[0] & 0xff; } if (nextEnd < bufEnd + 2) { if ((c2 = str->getChar()) == EOF) { eof = gTrue; buf[0] = 0; buf[1] = c1; bufPtr = buf; bufEnd = &buf[2]; return gTrue; } } else { c2 = bufEnd[1] & 0xff; } // check for repeat c = 0; // make gcc happy if (c1 == c2) { n = 2; while (n < 128 && (c = str->getChar()) == c1) ++n; buf[0] = (char)(257 - n); buf[1] = c1; bufEnd = &buf[2]; if (c == EOF) { eof = gTrue; } else if (n < 128) { buf[2] = c; nextEnd = &buf[3]; } else { nextEnd = bufEnd; } // get up to 128 chars } else { buf[1] = c1; buf[2] = c2; n = 2; while (n < 128) { if ((c = str->getChar()) == EOF) { eof = gTrue; break; } ++n; buf[n] = c; if (buf[n] == buf[n-1]) break; } if (buf[n] == buf[n-1]) { buf[0] = (char)(n-2-1); bufEnd = &buf[n-1]; nextEnd = &buf[n+1]; } else { buf[0] = (char)(n-1); bufEnd = nextEnd = &buf[n+1]; } } bufPtr = buf; return gTrue; } //------------------------------------------------------------------------ // 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; } } xpdf-3.04/xpdf/CharTypes.h0000644000076400007640000000101612341430012014713 0ustar dereknderekn//======================================================================== // // CharTypes.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef CHARTYPES_H #define CHARTYPES_H // Unicode character. typedef unsigned int Unicode; // Character ID for CID character collections. typedef unsigned int CID; // This is large enough to hold any of the following: // - 8-bit char code // - 16-bit CID // - Unicode typedef unsigned int CharCode; #endif xpdf-3.04/xpdf/JBIG2Stream.h0000644000076400007640000001115112341430012014763 0ustar dereknderekn//======================================================================== // // JBIG2Stream.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef JBIG2STREAM_H #define JBIG2STREAM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" #include "Stream.h" class GList; class JBIG2Segment; class JBIG2Bitmap; class JArithmeticDecoder; class JArithmeticDecoderStats; class JBIG2HuffmanDecoder; struct JBIG2HuffmanTable; class JBIG2MMRDecoder; //------------------------------------------------------------------------ class JBIG2Stream: public FilterStream { public: JBIG2Stream(Stream *strA, Object *globalsStreamA); virtual ~JBIG2Stream(); virtual StreamKind getKind() { return strJBIG2; } virtual void reset(); virtual void close(); virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: void readSegments(); GBool readSymbolDictSeg(Guint segNum, Guint length, Guint *refSegs, Guint nRefSegs); void readTextRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length, Guint *refSegs, Guint nRefSegs); JBIG2Bitmap *readTextRegion(GBool huff, GBool refine, int w, int h, Guint numInstances, Guint logStrips, int numSyms, JBIG2HuffmanTable *symCodeTab, Guint symCodeLen, JBIG2Bitmap **syms, Guint defPixel, Guint combOp, Guint transposed, Guint refCorner, int sOffset, JBIG2HuffmanTable *huffFSTable, JBIG2HuffmanTable *huffDSTable, JBIG2HuffmanTable *huffDTTable, JBIG2HuffmanTable *huffRDWTable, JBIG2HuffmanTable *huffRDHTable, JBIG2HuffmanTable *huffRDXTable, JBIG2HuffmanTable *huffRDYTable, JBIG2HuffmanTable *huffRSizeTable, Guint templ, int *atx, int *aty); void readPatternDictSeg(Guint segNum, Guint length); void readHalftoneRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length, Guint *refSegs, Guint nRefSegs); void readGenericRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length); void mmrAddPixels(int a1, int blackPixels, int *codingLine, int *a0i, int w); void mmrAddPixelsNeg(int a1, int blackPixels, int *codingLine, int *a0i, int w); JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h, int templ, GBool tpgdOn, GBool useSkip, JBIG2Bitmap *skip, int *atx, int *aty, int mmrDataLength); void readGenericRefinementRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length, Guint *refSegs, Guint nRefSegs); JBIG2Bitmap *readGenericRefinementRegion(int w, int h, int templ, GBool tpgrOn, JBIG2Bitmap *refBitmap, int refDX, int refDY, int *atx, int *aty); void readPageInfoSeg(Guint length); void readEndOfStripeSeg(Guint length); void readProfilesSeg(Guint length); void readCodeTableSeg(Guint segNum, Guint length); void readExtensionSeg(Guint length); JBIG2Segment *findSegment(Guint segNum); void discardSegment(Guint segNum); void resetGenericStats(Guint templ, JArithmeticDecoderStats *prevStats); void resetRefinementStats(Guint templ, JArithmeticDecoderStats *prevStats); void resetIntStats(int symCodeLen); GBool readUByte(Guint *x); GBool readByte(int *x); GBool readUWord(Guint *x); GBool readULong(Guint *x); GBool readLong(int *x); Object globalsStream; Guint pageW, pageH, curPageH; Guint pageDefPixel; JBIG2Bitmap *pageBitmap; Guint defCombOp; GList *segments; // [JBIG2Segment] GList *globalSegments; // [JBIG2Segment] Stream *curStr; Guchar *dataPtr; Guchar *dataEnd; Guint byteCounter; JArithmeticDecoder *arithDecoder; JArithmeticDecoderStats *genericRegionStats; JArithmeticDecoderStats *refinementRegionStats; JArithmeticDecoderStats *iadhStats; JArithmeticDecoderStats *iadwStats; JArithmeticDecoderStats *iaexStats; JArithmeticDecoderStats *iaaiStats; JArithmeticDecoderStats *iadtStats; JArithmeticDecoderStats *iaitStats; JArithmeticDecoderStats *iafsStats; JArithmeticDecoderStats *iadsStats; JArithmeticDecoderStats *iardxStats; JArithmeticDecoderStats *iardyStats; JArithmeticDecoderStats *iardwStats; JArithmeticDecoderStats *iardhStats; JArithmeticDecoderStats *iariStats; JArithmeticDecoderStats *iaidStats; JBIG2HuffmanDecoder *huffDecoder; JBIG2MMRDecoder *mmrDecoder; }; #endif xpdf-3.04/xpdf/XRef.cc0000644000076400007640000006236212341430012014026 0ustar dereknderekn//======================================================================== // // XRef.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include "gmem.h" #include "gfile.h" #include "Object.h" #include "Stream.h" #include "Lexer.h" #include "Parser.h" #include "Dict.h" #include "Error.h" #include "ErrorCodes.h" #include "XRef.h" //------------------------------------------------------------------------ #define xrefSearchSize 1024 // read this many bytes at end of file // to look for 'startxref' //------------------------------------------------------------------------ // Permission bits //------------------------------------------------------------------------ #define permPrint (1<<2) #define permChange (1<<3) #define permCopy (1<<4) #define permNotes (1<<5) #define defPermFlags 0xfffc //------------------------------------------------------------------------ // 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 //------------------------------------------------------------------------ class ObjectStream { public: // Create an object stream, using object number , // generation 0. ObjectStream(XRef *xref, int objStrNumA); GBool isOk() { return ok; } ~ObjectStream(); // Return the object number of this object stream. int getObjStrNum() { return objStrNum; } // Get the th object from this stream, which should be // object number , generation 0. Object *getObject(int objIdx, int objNum, Object *obj); private: int objStrNum; // object number of the object stream int nObjects; // number of objects in the stream Object *objs; // the objects (length = nObjects) int *objNums; // the object numbers (length = nObjects) GBool ok; }; ObjectStream::ObjectStream(XRef *xref, int objStrNumA) { Stream *str; Parser *parser; int *offsets; Object objStr, obj1, obj2; int first, i; objStrNum = objStrNumA; nObjects = 0; objs = NULL; objNums = NULL; ok = gFalse; if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) { goto err1; } if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) { obj1.free(); goto err1; } nObjects = obj1.getInt(); obj1.free(); if (nObjects <= 0) { goto err1; } if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) { obj1.free(); goto err1; } first = obj1.getInt(); obj1.free(); if (first < 0) { goto err1; } // this is an arbitrary limit to avoid integer overflow problems // in the 'new Object[nObjects]' call (Acrobat apparently limits // object streams to 100-200 objects) if (nObjects > 1000000) { error(errSyntaxError, -1, "Too many objects in an object stream"); goto err1; } objs = new Object[nObjects]; objNums = (int *)gmallocn(nObjects, sizeof(int)); offsets = (int *)gmallocn(nObjects, sizeof(int)); // parse the header: object numbers and offsets objStr.streamReset(); obj1.initNull(); str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first); parser = new Parser(xref, new Lexer(xref, str), gFalse); for (i = 0; i < nObjects; ++i) { parser->getObj(&obj1, gTrue); parser->getObj(&obj2, gTrue); if (!obj1.isInt() || !obj2.isInt()) { obj1.free(); obj2.free(); delete parser; gfree(offsets); goto err2; } objNums[i] = obj1.getInt(); offsets[i] = obj2.getInt(); obj1.free(); obj2.free(); if (objNums[i] < 0 || offsets[i] < 0 || (i > 0 && offsets[i] < offsets[i-1])) { delete parser; gfree(offsets); goto err2; } } while (str->getChar() != EOF) ; delete parser; // skip to the first object - this shouldn't be necessary because // the First key is supposed to be equal to offsets[0], but just in // case... if (i < offsets[0]) { objStr.getStream()->discardChars(offsets[0] - i); } // parse the objects for (i = 0; i < nObjects; ++i) { obj1.initNull(); if (i == nObjects - 1) { str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0); } else { str = new EmbedStream(objStr.getStream(), &obj1, gTrue, offsets[i+1] - offsets[i]); } parser = new Parser(xref, new Lexer(xref, str), gFalse); parser->getObj(&objs[i]); while (str->getChar() != EOF) ; delete parser; } gfree(offsets); ok = gTrue; err2: objStr.streamClose(); err1: objStr.free(); } ObjectStream::~ObjectStream() { int i; if (objs) { for (i = 0; i < nObjects; ++i) { objs[i].free(); } delete[] objs; } gfree(objNums); } Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) { if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) { return obj->initNull(); } return objs[objIdx].copy(obj); } //------------------------------------------------------------------------ // XRef //------------------------------------------------------------------------ XRef::XRef(BaseStream *strA, GBool repair) { GFileOffset pos; Object obj; XRefPosSet *posSet; int i; ok = gTrue; errCode = errNone; size = 0; last = -1; entries = NULL; streamEnds = NULL; streamEndsLen = 0; 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(); // if the 'repair' flag is set, try to reconstruct the xref table if (repair) { if (!(ok = constructXRef())) { errCode = errDamaged; return; } // if the 'repair' flag is not set, read the xref table } else { // read the trailer pos = getStartXref(); if (pos == 0) { errCode = errDamaged; ok = gFalse; return; } // read the xref table posSet = new XRefPosSet(); while (readXRef(&pos, posSet)) ; delete posSet; if (!ok) { errCode = errDamaged; return; } } // get the root dictionary (catalog) object trailerDict.dictLookupNF("Root", &obj); if (obj.isRef()) { rootNum = obj.getRefNum(); rootGen = obj.getRefGen(); obj.free(); } else { obj.free(); if (!(ok = constructXRef())) { errCode = errDamaged; return; } } // now set the trailer dictionary's xref pointer so we can fetch // indirect objects from it trailerDict.getDict()->setXRef(this); } XRef::~XRef() { 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); } for (i = 0; i < objStrCacheSize; ++i) { if (objStrs[i]) { delete objStrs[i]; } } } // Read the 'startxref' position. GFileOffset XRef::getStartXref() { char buf[xrefSearchSize+1]; char *p; int n, i; // read last xrefSearchSize bytes str->setPos(xrefSearchSize, -1); n = str->getBlock(buf, xrefSearchSize); buf[n] = '\0'; // find startxref for (i = n - 9; i >= 0; --i) { if (!strncmp(&buf[i], "startxref", 9)) { break; } } if (i < 0) { return 0; } for (p = &buf[i+9]; isspace(*p & 0xff); ++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(GFileOffset *pos, XRefPosSet *posSet) { Parser *parser; Object obj; GBool more; char buf[100]; int n, i; // 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 (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 (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 err2; } obj.free(); if (!parser->getObj(&obj, gTrue)->isCmd("obj")) { goto err2; } obj.free(); if (!parser->getObj(&obj)->isStream()) { goto err2; } more = readXRefStream(obj.getStream(), pos); obj.free(); delete parser; } else { goto err1; } return more; err2: obj.free(); delete parser; err1: ok = gFalse; return gFalse; } GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) { XRefEntry entry; Parser *parser; Object obj, obj2; 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) { 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 (c < '0' || c > '9') { goto err1; } first = 0; do { first = (first * 10) + (c - '0'); c = str->getChar(); } while (c >= '0' && c <= '9'); if (!Lexer::isSpace(c)) { goto err1; } 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) { for (newSize = size ? 2 * size : 1024; first + n > newSize && newSize > 0; newSize <<= 1) ; if (newSize < 0) { goto err1; } entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = (GFileOffset)-1; entries[i].type = xrefEntryFree; } size = newSize; } for (i = first; i < first + n; ++i) { 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 = 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 = gen; do { c = str->getChar(); } while (Lexer::isSpace(c)); if (c == 'n') { entry.type = xrefEntryUncompressed; } else if (c == 'f') { entry.type = xrefEntryFree; } else { goto err1; } 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 // instead of 0. if (i == 1 && first == 1 && entries[1].offset == 0 && entries[1].gen == 65535 && entries[1].type == xrefEntryFree) { i = first = 0; entries[0] = entries[1]; entries[1].offset = (GFileOffset)-1; } if (i > last) { last = i; } } } } // read the trailer dictionary 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()) { *pos = (GFileOffset)(Guint)obj2.getInt(); more = gTrue; } else if (obj2.isRef()) { // certain buggy PDF generators generate "/Prev NNN 0 R" instead // of "/Prev NNN" *pos = (GFileOffset)(Guint)obj2.getRefNum(); more = gTrue; } else { more = gFalse; } obj2.free(); // save the first trailer dictionary if (trailerDict.isNone()) { obj.copy(&trailerDict); } // check for an 'XRefStm' key //~ this can be a 64-bit int (?) if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) { pos2 = (GFileOffset)(Guint)obj2.getInt(); readXRef(&pos2, posSet); if (!ok) { obj2.free(); goto err1; } } obj2.free(); obj.free(); return more; err1: ok = gFalse; return gFalse; } GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) { Dict *dict; int w[3]; GBool more; Object obj, obj2, idx; int newSize, first, n, i; dict = xrefStr->getDict(); if (!dict->lookupNF("Size", &obj)->isInt()) { goto err1; } newSize = obj.getInt(); obj.free(); if (newSize < 0) { goto err1; } if (newSize > size) { entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = (GFileOffset)-1; entries[i].type = xrefEntryFree; } size = newSize; } if (!dict->lookupNF("W", &obj)->isArray() || obj.arrayGetLength() < 3) { goto err1; } for (i = 0; i < 3; ++i) { if (!obj.arrayGet(i, &obj2)->isInt()) { obj2.free(); goto err1; } w[i] = obj2.getInt(); obj2.free(); } 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); if (idx.isArray()) { for (i = 0; i+1 < idx.arrayGetLength(); i += 2) { if (!idx.arrayGet(i, &obj)->isInt()) { idx.free(); goto err1; } first = obj.getInt(); obj.free(); if (!idx.arrayGet(i+1, &obj)->isInt()) { idx.free(); goto err1; } n = obj.getInt(); obj.free(); if (first < 0 || n < 0 || !readXRefStreamSection(xrefStr, w, first, n)) { idx.free(); goto err0; } } } else { if (!readXRefStreamSection(xrefStr, w, 0, newSize)) { idx.free(); goto err0; } } idx.free(); //~ this can be a 64-bit int (?) dict->lookupNF("Prev", &obj); if (obj.isInt()) { *pos = (GFileOffset)(Guint)obj.getInt(); more = gTrue; } else { more = gFalse; } obj.free(); if (trailerDict.isNone()) { trailerDict.initDict(dict); } return more; err1: obj.free(); err0: ok = gFalse; return gFalse; } GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) { GFileOffset offset; int type, gen, c, newSize, i, j; if (first + n < 0) { return gFalse; } if (first + n > size) { for (newSize = size ? 2 * size : 1024; first + n > newSize && newSize > 0; newSize <<= 1) ; if (newSize < 0) { return gFalse; } entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = (GFileOffset)-1; entries[i].type = xrefEntryFree; } size = newSize; } for (i = first; i < first + n; ++i) { if (w[0] == 0) { type = 1; } else { for (type = 0, j = 0; j < w[0]; ++j) { if ((c = xrefStr->getChar()) == EOF) { return gFalse; } type = (type << 8) + c; } } for (offset = 0, j = 0; j < w[1]; ++j) { if ((c = xrefStr->getChar()) == EOF) { return gFalse; } offset = (offset << 8) + c; } for (gen = 0, j = 0; j < w[2]; ++j) { if ((c = xrefStr->getChar()) == EOF) { return gFalse; } gen = (gen << 8) + c; } if (entries[i].offset == (GFileOffset)-1) { switch (type) { case 0: entries[i].offset = offset; entries[i].gen = gen; entries[i].type = xrefEntryFree; break; case 1: entries[i].offset = offset; entries[i].gen = gen; entries[i].type = xrefEntryUncompressed; break; case 2: entries[i].offset = offset; entries[i].gen = gen; entries[i].type = xrefEntryCompressed; break; default: return gFalse; } if (i > last) { last = i; } } } return gTrue; } // Attempt to construct an xref table for a damaged file. GBool XRef::constructXRef() { Parser *parser; Object newTrailerDict, obj; char buf[256]; GFileOffset pos; int num, gen; int newSize; int streamEndsSize; char *p; int i; GBool gotRoot; gfree(entries); size = 0; entries = NULL; gotRoot = gFalse; streamEndsLen = streamEndsSize = 0; str->reset(); while (1) { pos = str->getPos(); if (!str->getLine(buf, 256)) { break; } p = buf; // skip whitespace while (*p && Lexer::isSpace(*p & 0xff)) ++p; // got trailer dictionary if (!strncmp(p, "trailer", 7)) { obj.initNull(); parser = new Parser(NULL, new Lexer(NULL, str->makeSubStream(pos + 7, gFalse, 0, &obj)), gFalse); parser->getObj(&newTrailerDict); if (newTrailerDict.isDict()) { newTrailerDict.dictLookupNF("Root", &obj); if (obj.isRef()) { rootNum = obj.getRefNum(); rootGen = obj.getRefGen(); if (!trailerDict.isNone()) { trailerDict.free(); } newTrailerDict.copy(&trailerDict); gotRoot = gTrue; } obj.free(); } newTrailerDict.free(); delete parser; // look for object } else if (isdigit(*p & 0xff)) { num = atoi(p); if (num > 0) { do { ++p; } while (*p && isdigit(*p & 0xff)); if (isspace(*p & 0xff)) { do { ++p; } while (*p && isspace(*p & 0xff)); if (isdigit(*p & 0xff)) { gen = atoi(p); do { ++p; } while (*p && isdigit(*p & 0xff)); if (isspace(*p & 0xff)) { do { ++p; } while (*p && isspace(*p & 0xff)); if (!strncmp(p, "obj", 3)) { if (num >= size) { newSize = (num + 1 + 255) & ~255; if (newSize < 0) { error(errSyntaxError, -1, "Bad object number"); return gFalse; } entries = (XRefEntry *) greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = (GFileOffset)-1; entries[i].type = xrefEntryFree; } size = newSize; } if (entries[num].type == xrefEntryFree || gen >= entries[num].gen) { entries[num].offset = pos - start; entries[num].gen = gen; entries[num].type = xrefEntryUncompressed; if (num > last) { last = num; } } } } } } } } else if (!strncmp(p, "endstream", 9)) { if (streamEndsLen == streamEndsSize) { streamEndsSize += 64; streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize, sizeof(GFileOffset)); } streamEnds[streamEndsLen++] = pos; } } if (gotRoot) { return gTrue; } error(errSyntaxError, -1, "Couldn't find trailer dictionary"); return gFalse; } void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA, Guchar *fileKeyA, int keyLengthA, int encVersionA, CryptAlgorithm encAlgorithmA) { int i; encrypted = gTrue; permFlags = permFlagsA; ownerPasswordOk = ownerPasswordOkA; if (keyLengthA <= 32) { keyLength = keyLengthA; } else { keyLength = 32; } for (i = 0; i < keyLength; ++i) { fileKey[i] = fileKeyA[i]; } encVersion = encVersionA; encAlgorithm = encAlgorithmA; } GBool XRef::okToPrint(GBool ignoreOwnerPW) { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint); } GBool XRef::okToChange(GBool ignoreOwnerPW) { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange); } GBool XRef::okToCopy(GBool ignoreOwnerPW) { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy); } GBool XRef::okToAddNotes(GBool ignoreOwnerPW) { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes); } Object *XRef::fetch(int num, int gen, Object *obj, 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) { case xrefEntryUncompressed: if (e->gen != gen) { goto err; } obj1.initNull(); parser = new Parser(this, new Lexer(this, str->makeSubStream(start + e->offset, gFalse, 0, &obj1)), gTrue); parser->getObj(&obj1, gTrue); parser->getObj(&obj2, gTrue); parser->getObj(&obj3, gTrue); if (!obj1.isInt() || obj1.getInt() != num || !obj2.isInt() || obj2.getInt() != gen || !obj3.isCmd("obj")) { obj1.free(); obj2.free(); obj3.free(); delete parser; goto err; } parser->getObj(obj, gFalse, encrypted ? fileKey : (Guchar *)NULL, encAlgorithm, keyLength, num, gen, recursion); obj1.free(); obj2.free(); obj3.free(); delete parser; break; case xrefEntryCompressed: #if 0 // Adobe apparently ignores the generation number on compressed objects if (gen != 0) { goto err; } #endif if (e->offset >= (GFileOffset)size || entries[e->offset].type != xrefEntryUncompressed) { error(errSyntaxError, -1, "Invalid object stream"); goto err; } if (!(objStr = getObjectStream((int)e->offset))) { goto err; } objStr->getObject(e->gen, num, obj); break; default: 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); } // Added for the pdftex project. Object *XRef::getDocInfoNF(Object *obj) { return trailerDict.dictLookupNF("Info", obj); } GBool XRef::getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd) { int a, b, m; if (streamEndsLen == 0 || streamStart > streamEnds[streamEndsLen - 1]) { return gFalse; } a = -1; b = streamEndsLen - 1; // invariant: streamEnds[a] < streamStart <= streamEnds[b] while (b - a > 1) { m = (a + b) / 2; if (streamStart <= streamEnds[m]) { b = m; } else { a = m; } } *streamEnd = streamEnds[b]; return gTrue; } 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 > (GFILEOFFSET_MAX - d) / 10) { break; } x = 10 * x + d; } return x; } xpdf-3.04/xpdf/UnicodeMap.cc0000644000076400007640000001417012341430012015200 0ustar dereknderekn//======================================================================== // // UnicodeMap.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "gfile.h" #include "GString.h" #include "GList.h" #include "Error.h" #include "GlobalParams.h" #include "UnicodeMap.h" //------------------------------------------------------------------------ #define maxExtCode 16 struct UnicodeMapExt { Unicode u; // Unicode char char code[maxExtCode]; Guint nBytes; }; //------------------------------------------------------------------------ UnicodeMap *UnicodeMap::parse(GString *encodingNameA) { FILE *f; UnicodeMap *map; UnicodeMapRange *range; UnicodeMapExt *eMap; int size, eMapsSize; char buf[256]; int line, nBytes, i, x; char *tok1, *tok2, *tok3; if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) { error(errSyntaxError, -1, "Couldn't find unicodeMap file for the '{0:t}' encoding", encodingNameA); return NULL; } map = new UnicodeMap(encodingNameA->copy()); size = 8; map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange)); eMapsSize = 0; line = 1; while (getLine(buf, sizeof(buf), f)) { if ((tok1 = strtok(buf, " \t\r\n")) && (tok2 = strtok(NULL, " \t\r\n"))) { if (!(tok3 = strtok(NULL, " \t\r\n"))) { tok3 = tok2; tok2 = tok1; } nBytes = (int)strlen(tok3) / 2; if (nBytes <= 4) { if (map->len == size) { size *= 2; map->ranges = (UnicodeMapRange *) greallocn(map->ranges, size, sizeof(UnicodeMapRange)); } range = &map->ranges[map->len]; sscanf(tok1, "%x", &range->start); sscanf(tok2, "%x", &range->end); sscanf(tok3, "%x", &range->code); range->nBytes = nBytes; ++map->len; } else if (tok2 == tok1) { if (map->eMapsLen == eMapsSize) { eMapsSize += 16; map->eMaps = (UnicodeMapExt *) greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt)); } eMap = &map->eMaps[map->eMapsLen]; sscanf(tok1, "%x", &eMap->u); for (i = 0; i < nBytes; ++i) { sscanf(tok3 + i*2, "%2x", &x); eMap->code[i] = (char)x; } eMap->nBytes = nBytes; ++map->eMapsLen; } else { error(errSyntaxError, -1, "Bad line ({0:d}) in unicodeMap file for the '{1:t}' encoding", line, encodingNameA); } } else { error(errSyntaxError, -1, "Bad line ({0:d}) in unicodeMap file for the '{1:t}' encoding", line, encodingNameA); } ++line; } fclose(f); return map; } UnicodeMap::UnicodeMap(GString *encodingNameA) { encodingName = encodingNameA; unicodeOut = gFalse; kind = unicodeMapUser; ranges = NULL; len = 0; eMaps = NULL; eMapsLen = 0; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } UnicodeMap::UnicodeMap(const char *encodingNameA, GBool unicodeOutA, UnicodeMapRange *rangesA, int lenA) { encodingName = new GString(encodingNameA); unicodeOut = unicodeOutA; kind = unicodeMapResident; ranges = rangesA; len = lenA; eMaps = NULL; eMapsLen = 0; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } UnicodeMap::UnicodeMap(const char *encodingNameA, GBool unicodeOutA, UnicodeMapFunc funcA) { encodingName = new GString(encodingNameA); unicodeOut = unicodeOutA; kind = unicodeMapFunc; func = funcA; eMaps = NULL; eMapsLen = 0; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } UnicodeMap::~UnicodeMap() { delete encodingName; if (kind == unicodeMapUser && ranges) { gfree(ranges); } if (eMaps) { gfree(eMaps); } #if MULTITHREADED gDestroyMutex(&mutex); #endif } void UnicodeMap::incRefCnt() { #if MULTITHREADED gLockMutex(&mutex); #endif ++refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif } void UnicodeMap::decRefCnt() { GBool done; #if MULTITHREADED gLockMutex(&mutex); #endif done = --refCnt == 0; #if MULTITHREADED gUnlockMutex(&mutex); #endif if (done) { delete this; } } GBool UnicodeMap::match(GString *encodingNameA) { return !encodingName->cmp(encodingNameA); } int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) { int a, b, m, n, i, j; Guint code; if (kind == unicodeMapFunc) { return (*func)(u, buf, bufSize); } a = 0; b = len; if (u >= ranges[a].start) { // invariant: ranges[a].start <= u < ranges[b].start while (b - a > 1) { m = (a + b) / 2; if (u >= ranges[m].start) { a = m; } else if (u < ranges[m].start) { b = m; } } if (u <= ranges[a].end) { n = ranges[a].nBytes; if (n > bufSize) { return 0; } code = ranges[a].code + (u - ranges[a].start); for (i = n - 1; i >= 0; --i) { buf[i] = (char)(code & 0xff); code >>= 8; } return n; } } for (i = 0; i < eMapsLen; ++i) { if (eMaps[i].u == u) { n = eMaps[i].nBytes; for (j = 0; j < n; ++j) { buf[j] = eMaps[i].code[j]; } return n; } } return 0; } //------------------------------------------------------------------------ UnicodeMapCache::UnicodeMapCache() { int i; for (i = 0; i < unicodeMapCacheSize; ++i) { cache[i] = NULL; } } UnicodeMapCache::~UnicodeMapCache() { int i; for (i = 0; i < unicodeMapCacheSize; ++i) { if (cache[i]) { cache[i]->decRefCnt(); } } } UnicodeMap *UnicodeMapCache::getUnicodeMap(GString *encodingName) { UnicodeMap *map; int i, j; if (cache[0] && cache[0]->match(encodingName)) { cache[0]->incRefCnt(); return cache[0]; } for (i = 1; i < unicodeMapCacheSize; ++i) { if (cache[i] && cache[i]->match(encodingName)) { map = cache[i]; for (j = i; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = map; map->incRefCnt(); return map; } } if ((map = UnicodeMap::parse(encodingName))) { if (cache[unicodeMapCacheSize - 1]) { cache[unicodeMapCacheSize - 1]->decRefCnt(); } for (j = unicodeMapCacheSize - 1; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = map; map->incRefCnt(); return map; } return NULL; } xpdf-3.04/xpdf/pdfinfo.cc0000644000076400007640000002453512341430012014607 0ustar dereknderekn//======================================================================== // // pdfinfo.cc // // Copyright 1998-2013 Glyph & Cog, LLC // //======================================================================== #include #include #include #include #include #include #include #include "parseargs.h" #include "GString.h" #include "gmem.h" #include "gfile.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "CharTypes.h" #include "UnicodeMap.h" #include "TextString.h" #include "Error.h" #include "config.h" static void printInfoString(Dict *infoDict, const char *key, const char *text, UnicodeMap *uMap); static void printInfoDate(Dict *infoDict, const char *key, const char *text); static void printBox(const char *text, PDFRectangle *box); static int firstPage = 1; static int lastPage = 0; static GBool printBoxes = gFalse; static GBool printMetadata = gFalse; static GBool rawDates = gFalse; static char textEncName[128] = ""; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static char cfgFileName[256] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-f", argInt, &firstPage, 0, "first page to convert"}, {"-l", argInt, &lastPage, 0, "last page to convert"}, {"-box", argFlag, &printBoxes, 0, "print the page bounding boxes"}, {"-meta", argFlag, &printMetadata, 0, "print the document metadata (XML)"}, {"-rawdates", argFlag, &rawDates, 0, "print the undecoded date strings directly from the PDF file"}, {"-enc", argString, textEncName, sizeof(textEncName), "output text encoding name"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; int main(int argc, char *argv[]) { PDFDoc *doc; GString *fileName; GString *ownerPW, *userPW; UnicodeMap *uMap; Page *page; Object info, xfa; Object *acroForm; char buf[256]; double w, h, wISO, hISO; FILE *f; GString *metadata; GBool ok; int exitCode; int pg, i; GBool multiPage; exitCode = 99; // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 2 || printVersion || printHelp) { fprintf(stderr, "pdfinfo version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfinfo", "", argDesc); } goto err0; } fileName = new GString(argv[1]); // read config file globalParams = new GlobalParams(cfgFileName); if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } // get mapping to output encoding if (!(uMap = globalParams->getTextEncoding())) { error(errConfig, -1, "Couldn't get text encoding"); delete fileName; goto err1; } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = new GString(ownerPassword); } else { ownerPW = NULL; } if (userPassword[0] != '\001') { userPW = new GString(userPassword); } else { userPW = NULL; } doc = new PDFDoc(fileName, ownerPW, userPW); if (userPW) { delete userPW; } if (ownerPW) { delete ownerPW; } if (!doc->isOk()) { exitCode = 1; goto err2; } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage == 0) { multiPage = gFalse; lastPage = 1; } else { multiPage = gTrue; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } // print doc info doc->getDocInfo(&info); if (info.isDict()) { printInfoString(info.getDict(), "Title", "Title: ", uMap); printInfoString(info.getDict(), "Subject", "Subject: ", uMap); printInfoString(info.getDict(), "Keywords", "Keywords: ", uMap); printInfoString(info.getDict(), "Author", "Author: ", uMap); printInfoString(info.getDict(), "Creator", "Creator: ", uMap); printInfoString(info.getDict(), "Producer", "Producer: ", uMap); if (rawDates) { printInfoString(info.getDict(), "CreationDate", "CreationDate: ", uMap); printInfoString(info.getDict(), "ModDate", "ModDate: ", uMap); } else { printInfoDate(info.getDict(), "CreationDate", "CreationDate: "); printInfoDate(info.getDict(), "ModDate", "ModDate: "); } } info.free(); // print tagging info printf("Tagged: %s\n", doc->getStructTreeRoot()->isDict() ? "yes" : "no"); // print form info if ((acroForm = doc->getCatalog()->getAcroForm())->isDict()) { acroForm->dictLookup("XFA", &xfa); if (xfa.isStream() || xfa.isArray()) { printf("Form: XFA\n"); } else { printf("Form: AcroForm\n"); } xfa.free(); } else { printf("Form: none\n"); } // print page count printf("Pages: %d\n", doc->getNumPages()); // print encryption info printf("Encrypted: "); if (doc->isEncrypted()) { printf("yes (print:%s copy:%s change:%s addNotes:%s)\n", doc->okToPrint(gTrue) ? "yes" : "no", doc->okToCopy(gTrue) ? "yes" : "no", doc->okToChange(gTrue) ? "yes" : "no", doc->okToAddNotes(gTrue) ? "yes" : "no"); } else { printf("no\n"); } // print page size for (pg = firstPage; pg <= lastPage; ++pg) { w = doc->getPageCropWidth(pg); h = doc->getPageCropHeight(pg); if (multiPage) { printf("Page %4d size: %g x %g pts", pg, w, h); } else { printf("Page size: %g x %g pts", w, h); } if ((fabs(w - 612) < 0.1 && fabs(h - 792) < 0.1) || (fabs(w - 792) < 0.1 && fabs(h - 612) < 0.1)) { printf(" (letter)"); } else { hISO = sqrt(sqrt(2.0)) * 7200 / 2.54; wISO = hISO / sqrt(2.0); for (i = 0; i <= 6; ++i) { if ((fabs(w - wISO) < 1 && fabs(h - hISO) < 1) || (fabs(w - hISO) < 1 && fabs(h - wISO) < 1)) { printf(" (A%d)", i); break; } hISO = wISO; wISO /= sqrt(2.0); } } printf(" (rotated %d degrees)", doc->getPageRotate(pg)); printf("\n"); } // print the boxes if (printBoxes) { if (multiPage) { for (pg = firstPage; pg <= lastPage; ++pg) { page = doc->getCatalog()->getPage(pg); sprintf(buf, "Page %4d MediaBox: ", pg); printBox(buf, page->getMediaBox()); sprintf(buf, "Page %4d CropBox: ", pg); printBox(buf, page->getCropBox()); sprintf(buf, "Page %4d BleedBox: ", pg); printBox(buf, page->getBleedBox()); sprintf(buf, "Page %4d TrimBox: ", pg); printBox(buf, page->getTrimBox()); sprintf(buf, "Page %4d ArtBox: ", pg); printBox(buf, page->getArtBox()); } } else { page = doc->getCatalog()->getPage(firstPage); printBox("MediaBox: ", page->getMediaBox()); printBox("CropBox: ", page->getCropBox()); printBox("BleedBox: ", page->getBleedBox()); printBox("TrimBox: ", page->getTrimBox()); printBox("ArtBox: ", page->getArtBox()); } } // print file size #ifdef VMS f = fopen(fileName->getCString(), "rb", "ctx=stm"); #else f = fopen(fileName->getCString(), "rb"); #endif if (f) { gfseek(f, 0, SEEK_END); printf("File size: %u bytes\n", (Guint)gftell(f)); fclose(f); } // print linearization info printf("Optimized: %s\n", doc->isLinearized() ? "yes" : "no"); // print PDF version printf("PDF version: %.1f\n", doc->getPDFVersion()); // print the metadata if (printMetadata && (metadata = doc->readMetadata())) { fputs("Metadata:\n", stdout); fputs(metadata->getCString(), stdout); fputc('\n', stdout); delete metadata; } exitCode = 0; // clean up err2: uMap->decRefCnt(); delete doc; err1: delete globalParams; err0: // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); return exitCode; } static void printInfoString(Dict *infoDict, const char *key, const char *text, UnicodeMap *uMap) { Object obj; TextString *s; Unicode *u; char buf[8]; int i, n; if (infoDict->lookup(key, &obj)->isString()) { fputs(text, stdout); 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(); } static void printInfoDate(Dict *infoDict, const char *key, const char *text) { Object obj; char *s; int year, mon, day, hour, min, sec, n; struct tm tmStruct; char buf[256]; if (infoDict->lookup(key, &obj)->isString()) { fputs(text, stdout); s = obj.getString()->getCString(); if (s[0] == 'D' && s[1] == ':') { s += 2; } if ((n = sscanf(s, "%4d%2d%2d%2d%2d%2d", &year, &mon, &day, &hour, &min, &sec)) >= 1) { switch (n) { case 1: mon = 1; case 2: day = 1; case 3: hour = 0; case 4: min = 0; case 5: sec = 0; } tmStruct.tm_year = year - 1900; tmStruct.tm_mon = mon - 1; tmStruct.tm_mday = day; tmStruct.tm_hour = hour; tmStruct.tm_min = min; tmStruct.tm_sec = sec; tmStruct.tm_wday = -1; tmStruct.tm_yday = -1; tmStruct.tm_isdst = -1; // compute the tm_wday and tm_yday fields if (mktime(&tmStruct) != (time_t)-1 && strftime(buf, sizeof(buf), "%c", &tmStruct)) { fputs(buf, stdout); } else { fputs(s, stdout); } } else { fputs(s, stdout); } fputc('\n', stdout); } obj.free(); } static void printBox(const char *text, PDFRectangle *box) { printf("%s%8.2f %8.2f %8.2f %8.2f\n", text, box->x1, box->y1, box->x2, box->y2); } xpdf-3.04/xpdf/GlobalParams.cc0000644000076400007640000025252312341430012015526 0ustar dereknderekn//======================================================================== // // GlobalParams.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #ifdef _WIN32 # define _WIN32_WINNT 0x0500 // for GetSystemWindowsDirectory # include #endif #include #include #include #ifdef ENABLE_PLUGINS # ifndef _WIN32 # include # endif #endif #ifdef _WIN32 # include #endif #if HAVE_PAPER_H #include #endif #include "gmem.h" #include "GString.h" #include "GList.h" #include "GHash.h" #include "gfile.h" #include "FoFiIdentifier.h" #include "Error.h" #include "NameToCharCode.h" #include "CharCodeToUnicode.h" #include "UnicodeMap.h" #include "CMap.h" #include "BuiltinFontTables.h" #include "FontEncodingTables.h" #ifdef ENABLE_PLUGINS # include "XpdfPluginAPI.h" #endif #include "GlobalParams.h" #ifdef _WIN32 # define strcasecmp stricmp # define strncasecmp strnicmp #endif #if MULTITHREADED # define lockGlobalParams gLockMutex(&mutex) # define lockUnicodeMapCache gLockMutex(&unicodeMapCacheMutex) # define lockCMapCache gLockMutex(&cMapCacheMutex) # define unlockGlobalParams gUnlockMutex(&mutex) # define unlockUnicodeMapCache gUnlockMutex(&unicodeMapCacheMutex) # define unlockCMapCache gUnlockMutex(&cMapCacheMutex) #else # define lockGlobalParams # define lockUnicodeMapCache # define lockCMapCache # define unlockGlobalParams # define unlockUnicodeMapCache # define unlockCMapCache #endif #include "NameToUnicodeTable.h" #include "UnicodeMapTables.h" #include "UTF8.h" #ifdef ENABLE_PLUGINS # ifdef _WIN32 extern XpdfPluginVecTable xpdfPluginVecTable; # endif #endif //------------------------------------------------------------------------ #define cidToUnicodeCacheSize 4 #define unicodeToUnicodeCacheSize 4 //------------------------------------------------------------------------ static struct { 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", "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 static const char *displayFontDirs[] = { "c:/windows/fonts", "c:/winnt/fonts", NULL }; #else static const char *displayFontDirs[] = { "/usr/share/ghostscript/fonts", "/usr/local/share/ghostscript/fonts", "/usr/share/fonts/default/Type1", "/usr/share/fonts/default/ghostscript", "/usr/share/fonts/type1/gsfonts", #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; //------------------------------------------------------------------------ // PSFontParam16 //------------------------------------------------------------------------ PSFontParam16::PSFontParam16(GString *nameA, int wModeA, GString *psFontNameA, GString *encodingA) { name = nameA; wMode = wModeA; psFontName = psFontNameA; encoding = encodingA; } PSFontParam16::~PSFontParam16() { delete name; delete psFontName; delete encoding; } //------------------------------------------------------------------------ // SysFontInfo //------------------------------------------------------------------------ class SysFontInfo { public: GString *name; GBool bold; GBool italic; GString *path; SysFontType type; int fontNum; // for TrueType collections SysFontInfo(GString *nameA, GBool boldA, GBool italicA, GString *pathA, SysFontType typeA, int fontNumA); ~SysFontInfo(); GBool match(SysFontInfo *fi); GBool match(GString *nameA, GBool boldA, GBool italicA); }; SysFontInfo::SysFontInfo(GString *nameA, GBool boldA, GBool italicA, GString *pathA, SysFontType typeA, int fontNumA) { name = nameA; bold = boldA; italic = italicA; path = pathA; type = typeA; fontNum = fontNumA; } SysFontInfo::~SysFontInfo() { delete name; delete path; } GBool SysFontInfo::match(SysFontInfo *fi) { return !strcasecmp(name->getCString(), fi->name->getCString()) && bold == fi->bold && italic == fi->italic; } GBool SysFontInfo::match(GString *nameA, GBool boldA, GBool italicA) { return !strcasecmp(name->getCString(), nameA->getCString()) && bold == boldA && italic == italicA; } //------------------------------------------------------------------------ // SysFontList //------------------------------------------------------------------------ class SysFontList { public: SysFontList(); ~SysFontList(); SysFontInfo *find(GString *name); #ifdef _WIN32 void scanWindowsFonts(char *winFontDir); #endif private: #ifdef _WIN32 SysFontInfo *makeWindowsFont(char *name, int fontNum, char *path); #endif GList *fonts; // [SysFontInfo] }; SysFontList::SysFontList() { fonts = new GList(); } SysFontList::~SysFontList() { deleteGList(fonts, SysFontInfo); } SysFontInfo *SysFontList::find(GString *name) { GString *name2; GBool bold, italic; SysFontInfo *fi; char c; int n, i; name2 = name->copy(); // remove space, comma, dash chars i = 0; while (i < name2->getLength()) { c = name2->getChar(i); if (c == ' ' || c == ',' || c == '-') { name2->del(i); } else { ++i; } } n = name2->getLength(); // font names like "Arial-BoldMT,Bold" are occasionally used, // so run this loop twice bold = italic = gFalse; for (i = 0; i < 2; ++i) { // 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 "Regular" if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) { name2->del(n - 7, 7); n -= 7; } // look for "Italic" if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) { name2->del(n - 6, 6); italic = gTrue; n -= 6; } // look for "Bold" if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) { name2->del(n - 4, 4); bold = gTrue; n -= 4; } } // remove trailing "PS" if (n > 2 && !strcmp(name2->getCString() + n - 2, "PS")) { name2->del(n - 2, 2); n -= 2; } // remove trailing "IdentityH" if (n > 9 && !strcmp(name2->getCString() + n - 9, "IdentityH")) { name2->del(n - 9, 9); n -= 9; } // search for the font fi = NULL; for (i = 0; i < fonts->getLength(); ++i) { fi = (SysFontInfo *)fonts->get(i); if (fi->match(name2, bold, italic)) { break; } fi = NULL; } if (!fi && bold) { // try ignoring the bold flag for (i = 0; i < fonts->getLength(); ++i) { fi = (SysFontInfo *)fonts->get(i); if (fi->match(name2, gFalse, italic)) { break; } fi = NULL; } } if (!fi && (bold || italic)) { // try ignoring the bold and italic flags for (i = 0; i < fonts->getLength(); ++i) { fi = (SysFontInfo *)fonts->get(i); if (fi->match(name2, gFalse, gFalse)) { break; } fi = NULL; } } delete name2; return fi; } #ifdef _WIN32 void SysFontList::scanWindowsFonts(char *winFontDir) { OSVERSIONINFO version; char *path; DWORD idx, valNameLen, dataLen, type; HKEY regKey; char valName[1024], data[1024]; int n, fontNum; char *p0, *p1; GString *fontPath; version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\"; } else { path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\"; } if (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 (RegEnumValueA(regKey, idx, valName, &valNameLen, NULL, &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) { break; } if (type == REG_SZ && valNameLen > 0 && valNameLen < sizeof(valName) && dataLen > 0 && dataLen < sizeof(data)) { valName[valNameLen] = '\0'; data[dataLen] = '\0'; n = (int)strlen(data); if (!strcasecmp(data + n - 4, ".ttf") || !strcasecmp(data + n - 4, ".ttc")) { fontPath = new GString(data); if (!(dataLen >= 3 && data[1] == ':' && data[2] == '\\')) { fontPath->insert(0, '\\'); fontPath->insert(0, winFontDir); } p0 = valName; fontNum = 0; while (*p0) { p1 = strstr(p0, " & "); if (p1) { *p1 = '\0'; p1 = p1 + 3; } else { p1 = p0 + strlen(p0); } fonts->append(makeWindowsFont(p0, fontNum, fontPath->getCString())); p0 = p1; ++fontNum; } delete fontPath; } } ++idx; } RegCloseKey(regKey); } } SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum, char *path) { int n; GBool bold, italic; GString *s; char c; int i; SysFontType type; n = (int)strlen(name); bold = italic = gFalse; // remove trailing ' (TrueType)' if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) { n -= 11; } // remove trailing ' Italic' if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) { n -= 7; italic = gTrue; } // remove trailing ' Bold' if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) { n -= 5; bold = gTrue; } // remove trailing ' Regular' if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) { n -= 8; } //----- normalize the font name s = new GString(name, n); i = 0; while (i < s->getLength()) { c = s->getChar(i); if (c == ' ' || c == ',' || c == '-') { s->del(i); } else { ++i; } } if (!strcasecmp(path + strlen(path) - 4, ".ttc")) { type = sysFontTTC; } else { type = sysFontTTF; } return new SysFontInfo(s, bold, italic, new GString(path), type, fontNum); } #endif //------------------------------------------------------------------------ // KeyBinding //------------------------------------------------------------------------ KeyBinding::KeyBinding(int codeA, int modsA, int contextA, const char *cmd0) { code = codeA; mods = modsA; context = contextA; cmds = new GList(); cmds->append(new GString(cmd0)); } KeyBinding::KeyBinding(int codeA, int modsA, int contextA, const char *cmd0, const char *cmd1) { code = codeA; mods = modsA; context = contextA; cmds = new GList(); cmds->append(new GString(cmd0)); cmds->append(new GString(cmd1)); } KeyBinding::KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA) { code = codeA; mods = modsA; context = contextA; cmds = cmdsA; } KeyBinding::~KeyBinding() { deleteGList(cmds, GString); } #ifdef ENABLE_PLUGINS //------------------------------------------------------------------------ // Plugin //------------------------------------------------------------------------ class Plugin { public: static Plugin *load(char *type, char *name); ~Plugin(); private: #ifdef _WIN32 Plugin(HMODULE libA); HMODULE lib; #else Plugin(void *dlA); void *dl; #endif }; Plugin *Plugin::load(char *type, char *name) { GString *path; Plugin *plugin; XpdfPluginVecTable *vt; XpdfBool (*xpdfInitPlugin)(void); #ifdef _WIN32 HMODULE libA; #else void *dlA; #endif path = globalParams->getBaseDir(); appendToPath(path, "plugins"); appendToPath(path, type); appendToPath(path, name); #ifdef _WIN32 path->append(".dll"); if (!(libA = LoadLibraryA(path->getCString()))) { error(errIO, -1, "Failed to load plugin '{0:t}'", path); goto err1; } if (!(vt = (XpdfPluginVecTable *) GetProcAddress(libA, "xpdfPluginVecTable"))) { error(errIO, -1, "Failed to find xpdfPluginVecTable in plugin '{0:t}'", path); goto err2; } #else //~ need to deal with other extensions here path->append(".so"); if (!(dlA = dlopen(path->getCString(), RTLD_NOW))) { error(errIO, -1, "Failed to load plugin '{0:t}': {1:s}", path, dlerror()); goto err1; } if (!(vt = (XpdfPluginVecTable *)dlsym(dlA, "xpdfPluginVecTable"))) { error(errIO, -1, "Failed to find xpdfPluginVecTable in plugin '{0:t}'", path); goto err2; } #endif if (vt->version != xpdfPluginVecTable.version) { error(errIO, -1, "Plugin '{0:t}' is wrong version", path); goto err2; } memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable)); #ifdef _WIN32 if (!(xpdfInitPlugin = (XpdfBool (*)(void)) GetProcAddress(libA, "xpdfInitPlugin"))) { error(errIO, -1, "Failed to find xpdfInitPlugin in plugin '{0:t}'", path); goto err2; } #else if (!(xpdfInitPlugin = (XpdfBool (*)(void))dlsym(dlA, "xpdfInitPlugin"))) { error(errIO, -1, "Failed to find xpdfInitPlugin in plugin '{0:t}'", path); goto err2; } #endif if (!(*xpdfInitPlugin)()) { error(errIO, -1, "Initialization of plugin '{0:t}' failed", path); goto err2; } #ifdef _WIN32 plugin = new Plugin(libA); #else plugin = new Plugin(dlA); #endif delete path; return plugin; err2: #ifdef _WIN32 FreeLibrary(libA); #else dlclose(dlA); #endif err1: delete path; return NULL; } #ifdef _WIN32 Plugin::Plugin(HMODULE libA) { lib = libA; } #else Plugin::Plugin(void *dlA) { dl = dlA; } #endif Plugin::~Plugin() { void (*xpdfFreePlugin)(void); #ifdef _WIN32 if ((xpdfFreePlugin = (void (*)(void)) GetProcAddress(lib, "xpdfFreePlugin"))) { (*xpdfFreePlugin)(); } FreeLibrary(lib); #else if ((xpdfFreePlugin = (void (*)(void))dlsym(dl, "xpdfFreePlugin"))) { (*xpdfFreePlugin)(); } dlclose(dl); #endif } #endif // ENABLE_PLUGINS //------------------------------------------------------------------------ // parsing //------------------------------------------------------------------------ GlobalParams::GlobalParams(const char *cfgFileName) { UnicodeMap *map; GString *fileName; FILE *f; int i; #if MULTITHREADED gInitMutex(&mutex); gInitMutex(&unicodeMapCacheMutex); gInitMutex(&cMapCacheMutex); #endif initBuiltinFontTables(); // scan the encoding in reverse because we want the lowest-numbered // index for each char name ('space' is encoded twice) macRomanReverseMap = new NameToCharCode(); for (i = 255; i >= 0; --i) { if (macRomanEncoding[i]) { macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i); } } #ifdef _WIN32 // baseDir will be set by a call to setBaseDir baseDir = new GString(); #else baseDir = appendToPath(getHomeDir(), ".xpdf"); #endif nameToUnicode = new NameToCharCode(); cidToUnicodes = new GHash(gTrue); unicodeToUnicodes = new GHash(gTrue); residentUnicodeMaps = new GHash(); unicodeMaps = new GHash(gTrue); cMapDirs = new GHash(gTrue); toUnicodeDirs = new GList(); fontFiles = new GHash(gTrue); fontDirs = new GList(); ccFontFiles = new GHash(gTrue); base14SysFonts = new GHash(gTrue); sysFonts = new SysFontList(); #if HAVE_PAPER_H char *paperName; const struct paper *paperType; paperinit(); if ((paperName = systempapername())) { paperType = paperinfo(paperName); psPaperWidth = (int)paperpswidth(paperType); psPaperHeight = (int)paperpsheight(paperType); } else { error(errConfig, -1, "No paper information available - using defaults"); psPaperWidth = defPaperWidth; psPaperHeight = defPaperHeight; } paperdone(); #else psPaperWidth = defPaperWidth; psPaperHeight = defPaperHeight; #endif psImageableLLX = psImageableLLY = 0; psImageableURX = psPaperWidth; psImageableURY = psPaperHeight; psCrop = gTrue; psUseCropBoxAsPage = gFalse; psExpandSmaller = gFalse; psShrinkLarger = gTrue; psCenter = gTrue; psDuplex = gFalse; psLevel = psLevel2; psFile = NULL; psResidentFonts = new GHash(gTrue); psResidentFonts16 = new GList(); psResidentFontsCC = new GList(); psEmbedType1 = gTrue; psEmbedTrueType = gTrue; psEmbedCIDPostScript = gTrue; psEmbedCIDTrueType = gTrue; psFontPassthrough = gFalse; psPreload = gFalse; psOPI = gFalse; psASCIIHex = gFalse; psLZW = gTrue; psUncompressPreloadedImages = gFalse; psMinLineWidth = 0; psRasterResolution = 300; psRasterMono = gFalse; psRasterSliceSize = 20000000; psAlwaysRasterize = gFalse; textEncoding = new GString("Latin1"); #if defined(_WIN32) textEOL = eolDOS; #elif defined(MACOS) textEOL = eolMac; #else textEOL = eolUnix; #endif textPageBreaks = gTrue; textKeepTinyChars = gTrue; initialZoom = new GString("125"); continuousView = gFalse; enableFreeType = gTrue; disableFreeTypeHinting = gFalse; antialias = gTrue; vectorAntialias = gTrue; antialiasPrinting = gFalse; strokeAdjust = gTrue; screenType = screenUnset; screenSize = -1; screenDotRadius = -1; screenGamma = 1.0; screenBlackThreshold = 0.0; screenWhiteThreshold = 1.0; minLineWidth = 0.0; drawAnnotations = gTrue; overprintPreview = gFalse; launchCommand = NULL; urlCommand = NULL; movieCommand = NULL; mapNumericCharNames = gTrue; mapUnknownCharNames = gFalse; mapExtTrueTypeFontsViaUnicode = gTrue; enableXFA = gTrue; createDefaultKeyBindings(); printCommands = gFalse; errQuiet = gFalse; cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize); unicodeToUnicodeCache = new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize); unicodeMapCache = new UnicodeMapCache(); cMapCache = new CMapCache(); #ifdef ENABLE_PLUGINS plugins = new GList(); securityHandlers = new GList(); #endif // set up the initial nameToUnicode table for (i = 0; nameToUnicodeTab[i].name; ++i) { nameToUnicode->add(nameToUnicodeTab[i].name, nameToUnicodeTab[i].u); } // set up the residentUnicodeMaps table map = new UnicodeMap("Latin1", gFalse, latin1UnicodeMapRanges, latin1UnicodeMapLen); residentUnicodeMaps->add(map->getEncodingName(), map); map = new UnicodeMap("ASCII7", gFalse, ascii7UnicodeMapRanges, ascii7UnicodeMapLen); residentUnicodeMaps->add(map->getEncodingName(), map); map = new UnicodeMap("Symbol", gFalse, symbolUnicodeMapRanges, symbolUnicodeMapLen); residentUnicodeMaps->add(map->getEncodingName(), map); map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges, zapfDingbatsUnicodeMapLen); residentUnicodeMaps->add(map->getEncodingName(), map); map = new UnicodeMap("UTF-8", gTrue, &mapUTF8); residentUnicodeMaps->add(map->getEncodingName(), map); map = new UnicodeMap("UCS-2", gTrue, &mapUCS2); residentUnicodeMaps->add(map->getEncodingName(), map); // look for a user config file, then a system-wide config file f = NULL; fileName = NULL; if (cfgFileName && cfgFileName[0]) { fileName = new GString(cfgFileName); if (!(f = fopen(fileName->getCString(), "r"))) { delete fileName; } } if (!f) { fileName = appendToPath(getHomeDir(), xpdfUserConfigFile); if (!(f = fopen(fileName->getCString(), "r"))) { delete fileName; } } if (!f) { #ifdef _WIN32 char buf[512]; 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'; } fileName = grabPath(buf); appendToPath(fileName, xpdfSysConfigFile); #else fileName = new GString(xpdfSysConfigFile); #endif if (!(f = fopen(fileName->getCString(), "r"))) { delete fileName; } } if (f) { parseFile(fileName, f); delete fileName; fclose(f); } } void GlobalParams::createDefaultKeyBindings() { keyBindings = new GList(); //----- mouse buttons keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone, xpdfKeyContextAny, "startSelection")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone, xpdfKeyContextAny, "endSelection", "followLinkNoSel")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress2, xpdfKeyModNone, xpdfKeyContextAny, "startPan")); keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease2, xpdfKeyModNone, xpdfKeyContextAny, "endPan")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress3, xpdfKeyModNone, xpdfKeyContextAny, "postPopupMenu")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress4, xpdfKeyModNone, xpdfKeyContextAny, "scrollUpPrevPage(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress5, xpdfKeyModNone, xpdfKeyContextAny, "scrollDownNextPage(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress6, xpdfKeyModNone, xpdfKeyContextAny, "scrollLeft(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress7, xpdfKeyModNone, xpdfKeyContextAny, "scrollRight(16)")); //----- keys keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModCtrl, xpdfKeyContextAny, "gotoPage(1)")); keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModNone, xpdfKeyContextAny, "scrollToTopLeft")); keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModCtrl, xpdfKeyContextAny, "gotoLastPage")); keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModNone, xpdfKeyContextAny, "scrollToBottomRight")); keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModNone, xpdfKeyContextAny, "pageUp")); keyBindings->append(new KeyBinding(xpdfKeyCodeBackspace, xpdfKeyModNone, xpdfKeyContextAny, "pageUp")); keyBindings->append(new KeyBinding(xpdfKeyCodeDelete, xpdfKeyModNone, xpdfKeyContextAny, "pageUp")); keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModNone, xpdfKeyContextAny, "pageDown")); keyBindings->append(new KeyBinding(' ', xpdfKeyModNone, xpdfKeyContextAny, "pageDown")); keyBindings->append(new KeyBinding(xpdfKeyCodeLeft, xpdfKeyModNone, xpdfKeyContextAny, "scrollLeft(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeRight, xpdfKeyModNone, xpdfKeyContextAny, "scrollRight(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeUp, xpdfKeyModNone, xpdfKeyContextAny, "scrollUp(16)")); keyBindings->append(new KeyBinding(xpdfKeyCodeDown, xpdfKeyModNone, xpdfKeyContextAny, "scrollDown(16)")); keyBindings->append(new KeyBinding('o', xpdfKeyModNone, xpdfKeyContextAny, "open")); keyBindings->append(new KeyBinding('O', xpdfKeyModNone, xpdfKeyContextAny, "open")); keyBindings->append(new KeyBinding('r', xpdfKeyModNone, xpdfKeyContextAny, "reload")); keyBindings->append(new KeyBinding('R', xpdfKeyModNone, xpdfKeyContextAny, "reload")); keyBindings->append(new KeyBinding('f', xpdfKeyModNone, xpdfKeyContextAny, "find")); keyBindings->append(new KeyBinding('F', xpdfKeyModNone, xpdfKeyContextAny, "find")); keyBindings->append(new KeyBinding('f', xpdfKeyModCtrl, xpdfKeyContextAny, "find")); keyBindings->append(new KeyBinding('g', xpdfKeyModCtrl, xpdfKeyContextAny, "findNext")); keyBindings->append(new KeyBinding('p', xpdfKeyModCtrl, xpdfKeyContextAny, "print")); keyBindings->append(new KeyBinding('n', xpdfKeyModNone, xpdfKeyContextScrLockOff, "nextPage")); keyBindings->append(new KeyBinding('N', xpdfKeyModNone, xpdfKeyContextScrLockOff, "nextPage")); keyBindings->append(new KeyBinding('n', xpdfKeyModNone, xpdfKeyContextScrLockOn, "nextPageNoScroll")); keyBindings->append(new KeyBinding('N', xpdfKeyModNone, xpdfKeyContextScrLockOn, "nextPageNoScroll")); keyBindings->append(new KeyBinding('p', xpdfKeyModNone, xpdfKeyContextScrLockOff, "prevPage")); keyBindings->append(new KeyBinding('P', xpdfKeyModNone, xpdfKeyContextScrLockOff, "prevPage")); keyBindings->append(new KeyBinding('p', xpdfKeyModNone, xpdfKeyContextScrLockOn, "prevPageNoScroll")); keyBindings->append(new KeyBinding('P', xpdfKeyModNone, xpdfKeyContextScrLockOn, "prevPageNoScroll")); keyBindings->append(new KeyBinding('v', xpdfKeyModNone, xpdfKeyContextAny, "goForward")); keyBindings->append(new KeyBinding('b', xpdfKeyModNone, xpdfKeyContextAny, "goBackward")); keyBindings->append(new KeyBinding('g', xpdfKeyModNone, xpdfKeyContextAny, "focusToPageNum")); keyBindings->append(new KeyBinding('0', xpdfKeyModNone, xpdfKeyContextAny, "zoomPercent(125)")); keyBindings->append(new KeyBinding('+', xpdfKeyModNone, xpdfKeyContextAny, "zoomIn")); keyBindings->append(new KeyBinding('-', xpdfKeyModNone, xpdfKeyContextAny, "zoomOut")); keyBindings->append(new KeyBinding('z', xpdfKeyModNone, xpdfKeyContextAny, "zoomFitPage")); keyBindings->append(new KeyBinding('w', xpdfKeyModNone, xpdfKeyContextAny, "zoomFitWidth")); keyBindings->append(new KeyBinding('f', xpdfKeyModAlt, xpdfKeyContextAny, "toggleFullScreenMode")); keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl, xpdfKeyContextAny, "redraw")); keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl, xpdfKeyContextAny, "closeWindowOrQuit")); keyBindings->append(new KeyBinding('?', xpdfKeyModNone, xpdfKeyContextAny, "about")); keyBindings->append(new KeyBinding('q', xpdfKeyModNone, xpdfKeyContextAny, "quit")); keyBindings->append(new KeyBinding('Q', xpdfKeyModNone, xpdfKeyContextAny, "quit")); } void GlobalParams::parseFile(GString *fileName, FILE *f) { int line; char buf[512]; line = 1; while (getLine(buf, sizeof(buf) - 1, f)) { parseLine(buf, fileName, line); ++line; } } void GlobalParams::parseLine(char *buf, GString *fileName, int line) { GList *tokens; GString *cmd, *incFile; char *p1, *p2; FILE *f2; // break the line into tokens tokens = new GList(); p1 = buf; while (*p1) { for (; *p1 && isspace(*p1); ++p1) ; if (!*p1) { break; } if (*p1 == '"' || *p1 == '\'') { for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ; ++p1; } else { for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ; } tokens->append(new GString(p1, (int)(p2 - p1))); p1 = *p2 ? p2 + 1 : p2; } // parse the line if (tokens->getLength() > 0 && ((GString *)tokens->get(0))->getChar(0) != '#') { cmd = (GString *)tokens->get(0); if (!cmd->cmp("include")) { if (tokens->getLength() == 2) { incFile = (GString *)tokens->get(1); if ((f2 = openFile(incFile->getCString(), "r"))) { parseFile(incFile, f2); fclose(f2); } else { error(errConfig, -1, "Couldn't find included config file: '{0:t}' ({1:t}:{2:d})", incFile, fileName, line); } } else { error(errConfig, -1, "Bad 'include' config file command ({0:t}:{1:d})", fileName, line); } } else if (!cmd->cmp("nameToUnicode")) { parseNameToUnicode(tokens, fileName, line); } else if (!cmd->cmp("cidToUnicode")) { parseCIDToUnicode(tokens, fileName, line); } else if (!cmd->cmp("unicodeToUnicode")) { parseUnicodeToUnicode(tokens, fileName, line); } else if (!cmd->cmp("unicodeMap")) { parseUnicodeMap(tokens, fileName, line); } else if (!cmd->cmp("cMapDir")) { parseCMapDir(tokens, fileName, line); } else if (!cmd->cmp("toUnicodeDir")) { parseToUnicodeDir(tokens, fileName, line); } else if (!cmd->cmp("fontFile")) { parseFontFile(tokens, fileName, line); } else if (!cmd->cmp("fontDir")) { parseFontDir(tokens, fileName, line); } else if (!cmd->cmp("fontFileCC")) { parseFontFileCC(tokens, fileName, line); } else if (!cmd->cmp("psFile")) { parsePSFile(tokens, fileName, line); } else if (!cmd->cmp("psPaperSize")) { parsePSPaperSize(tokens, fileName, line); } else if (!cmd->cmp("psImageableArea")) { parsePSImageableArea(tokens, fileName, line); } else if (!cmd->cmp("psCrop")) { parseYesNo("psCrop", &psCrop, tokens, fileName, line); } else if (!cmd->cmp("psUseCropBoxAsPage")) { parseYesNo("psUseCropBoxAsPage", &psUseCropBoxAsPage, tokens, fileName, line); } else if (!cmd->cmp("psExpandSmaller")) { parseYesNo("psExpandSmaller", &psExpandSmaller, tokens, fileName, line); } else if (!cmd->cmp("psShrinkLarger")) { parseYesNo("psShrinkLarger", &psShrinkLarger, tokens, fileName, line); } else if (!cmd->cmp("psCenter")) { parseYesNo("psCenter", &psCenter, tokens, fileName, line); } else if (!cmd->cmp("psDuplex")) { parseYesNo("psDuplex", &psDuplex, tokens, fileName, line); } else if (!cmd->cmp("psLevel")) { parsePSLevel(tokens, fileName, line); } else if (!cmd->cmp("psResidentFont")) { parsePSResidentFont(tokens, fileName, line); } else if (!cmd->cmp("psResidentFont16")) { parsePSResidentFont16(tokens, fileName, line); } else if (!cmd->cmp("psResidentFontCC")) { parsePSResidentFontCC(tokens, fileName, line); } else if (!cmd->cmp("psEmbedType1Fonts")) { parseYesNo("psEmbedType1", &psEmbedType1, tokens, fileName, line); } else if (!cmd->cmp("psEmbedTrueTypeFonts")) { parseYesNo("psEmbedTrueType", &psEmbedTrueType, tokens, fileName, line); } else if (!cmd->cmp("psEmbedCIDPostScriptFonts")) { parseYesNo("psEmbedCIDPostScript", &psEmbedCIDPostScript, tokens, fileName, line); } else if (!cmd->cmp("psEmbedCIDTrueTypeFonts")) { parseYesNo("psEmbedCIDTrueType", &psEmbedCIDTrueType, tokens, fileName, line); } else if (!cmd->cmp("psFontPassthrough")) { parseYesNo("psFontPassthrough", &psFontPassthrough, tokens, fileName, line); } else if (!cmd->cmp("psPreload")) { parseYesNo("psPreload", &psPreload, tokens, fileName, line); } else if (!cmd->cmp("psOPI")) { parseYesNo("psOPI", &psOPI, tokens, fileName, line); } else if (!cmd->cmp("psASCIIHex")) { parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line); } else if (!cmd->cmp("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); } else if (!cmd->cmp("textEncoding")) { parseTextEncoding(tokens, fileName, line); } else if (!cmd->cmp("textEOL")) { parseTextEOL(tokens, fileName, line); } else if (!cmd->cmp("textPageBreaks")) { parseYesNo("textPageBreaks", &textPageBreaks, tokens, fileName, line); } else if (!cmd->cmp("textKeepTinyChars")) { parseYesNo("textKeepTinyChars", &textKeepTinyChars, tokens, fileName, line); } else if (!cmd->cmp("initialZoom")) { parseInitialZoom(tokens, fileName, line); } else if (!cmd->cmp("continuousView")) { parseYesNo("continuousView", &continuousView, tokens, fileName, line); } else if (!cmd->cmp("enableFreeType")) { parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line); } else if (!cmd->cmp("disableFreeTypeHinting")) { parseYesNo("disableFreeTypeHinting", &disableFreeTypeHinting, tokens, fileName, line); } else if (!cmd->cmp("antialias")) { parseYesNo("antialias", &antialias, tokens, fileName, line); } else if (!cmd->cmp("vectorAntialias")) { parseYesNo("vectorAntialias", &vectorAntialias, tokens, fileName, line); } else if (!cmd->cmp("antialiasPrinting")) { parseYesNo("antialiasPrinting", &antialiasPrinting, tokens, fileName, line); } else if (!cmd->cmp("strokeAdjust")) { parseYesNo("strokeAdjust", &strokeAdjust, tokens, fileName, line); } else if (!cmd->cmp("screenType")) { parseScreenType(tokens, fileName, line); } else if (!cmd->cmp("screenSize")) { parseInteger("screenSize", &screenSize, tokens, fileName, line); } else if (!cmd->cmp("screenDotRadius")) { parseInteger("screenDotRadius", &screenDotRadius, tokens, fileName, line); } else if (!cmd->cmp("screenGamma")) { parseFloat("screenGamma", &screenGamma, tokens, fileName, line); } else if (!cmd->cmp("screenBlackThreshold")) { parseFloat("screenBlackThreshold", &screenBlackThreshold, tokens, fileName, line); } else if (!cmd->cmp("screenWhiteThreshold")) { parseFloat("screenWhiteThreshold", &screenWhiteThreshold, tokens, fileName, line); } else if (!cmd->cmp("minLineWidth")) { parseFloat("minLineWidth", &minLineWidth, tokens, fileName, line); } else if (!cmd->cmp("drawAnnotations")) { parseYesNo("drawAnnotations", &drawAnnotations, tokens, fileName, line); } else if (!cmd->cmp("overprintPreview")) { parseYesNo("overprintPreview", &overprintPreview, tokens, fileName, line); } else if (!cmd->cmp("launchCommand")) { parseCommand("launchCommand", &launchCommand, tokens, fileName, line); } else if (!cmd->cmp("urlCommand")) { parseCommand("urlCommand", &urlCommand, tokens, fileName, line); } else if (!cmd->cmp("movieCommand")) { parseCommand("movieCommand", &movieCommand, tokens, fileName, line); } else if (!cmd->cmp("mapNumericCharNames")) { parseYesNo("mapNumericCharNames", &mapNumericCharNames, tokens, fileName, line); } else if (!cmd->cmp("mapUnknownCharNames")) { parseYesNo("mapUnknownCharNames", &mapUnknownCharNames, tokens, fileName, line); } else if (!cmd->cmp("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")) { parseUnbind(tokens, fileName, line); } else if (!cmd->cmp("printCommands")) { parseYesNo("printCommands", &printCommands, tokens, fileName, line); } else if (!cmd->cmp("errQuiet")) { parseYesNo("errQuiet", &errQuiet, tokens, fileName, line); } else { error(errConfig, -1, "Unknown config file command '{0:t}' ({1:t}:{2:d})", cmd, fileName, line); if (!cmd->cmp("displayFontX") || !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"); } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) { error(errConfig, -1, "The config file format has changed since Xpdf 0.9x"); } } } deleteGList(tokens, GString); } void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName, int line) { GString *name; char *tok1, *tok2; FILE *f; char buf[256]; int line2; Unicode u; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'nameToUnicode' config file command ({0:t}:{1:d})", fileName, line); return; } name = (GString *)tokens->get(1); if (!(f = openFile(name->getCString(), "r"))) { error(errConfig, -1, "Couldn't open 'nameToUnicode' file '{0:t}'", name); return; } line2 = 1; while (getLine(buf, sizeof(buf), f)) { tok1 = strtok(buf, " \t\r\n"); tok2 = strtok(NULL, " \t\r\n"); if (tok1 && tok2) { sscanf(tok1, "%x", &u); nameToUnicode->add(tok2, u); } else { error(errConfig, -1, "Bad line in 'nameToUnicode' file ({0:t}:{1:d})", name, line2); } ++line2; } fclose(f); } void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName, int line) { GString *collection, *name, *old; if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'cidToUnicode' config file command ({0:t}:{1:d})", fileName, line); return; } collection = (GString *)tokens->get(1); name = (GString *)tokens->get(2); if ((old = (GString *)cidToUnicodes->remove(collection))) { delete old; } cidToUnicodes->add(collection->copy(), name->copy()); } void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName, int line) { GString *font, *file, *old; if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'unicodeToUnicode' config file command ({0:t}:{1:d})", fileName, line); return; } font = (GString *)tokens->get(1); file = (GString *)tokens->get(2); if ((old = (GString *)unicodeToUnicodes->remove(font))) { delete old; } unicodeToUnicodes->add(font->copy(), file->copy()); } void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName, int line) { GString *encodingName, *name, *old; if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'unicodeMap' config file command ({0:t}:{1:d})", fileName, line); return; } encodingName = (GString *)tokens->get(1); name = (GString *)tokens->get(2); if ((old = (GString *)unicodeMaps->remove(encodingName))) { delete old; } unicodeMaps->add(encodingName->copy(), name->copy()); } void GlobalParams::parseCMapDir(GList *tokens, GString *fileName, int line) { GString *collection, *dir; GList *list; if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'cMapDir' config file command ({0:t}:{1:d})", fileName, line); return; } collection = (GString *)tokens->get(1); dir = (GString *)tokens->get(2); if (!(list = (GList *)cMapDirs->lookup(collection))) { list = new GList(); cMapDirs->add(collection->copy(), list); } list->append(dir->copy()); } void GlobalParams::parseToUnicodeDir(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'toUnicodeDir' config file command ({0:t}:{1:d})", fileName, line); return; } toUnicodeDirs->append(((GString *)tokens->get(1))->copy()); } void GlobalParams::parseFontFile(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'fontFile' config file command ({0:t}:{1:d})", fileName, line); return; } fontFiles->add(((GString *)tokens->get(1))->copy(), ((GString *)tokens->get(2))->copy()); } void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'fontDir' config file command ({0:t}:{1:d})", fileName, line); return; } fontDirs->append(((GString *)tokens->get(1))->copy()); } void GlobalParams::parseFontFileCC(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'fontFileCC' config file command ({0:t}:{1:d})", fileName, line); return; } ccFontFiles->add(((GString *)tokens->get(1))->copy(), ((GString *)tokens->get(2))->copy()); } void GlobalParams::parsePSFile(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'psFile' config file command ({0:t}:{1:d})", fileName, line); return; } if (psFile) { delete psFile; } psFile = ((GString *)tokens->get(1))->copy(); } void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName, int line) { GString *tok; if (tokens->getLength() == 2) { tok = (GString *)tokens->get(1); if (!setPSPaperSize(tok->getCString())) { error(errConfig, -1, "Bad 'psPaperSize' config file command ({0:s}:{1:d})", fileName, line); } } else if (tokens->getLength() == 3) { tok = (GString *)tokens->get(1); psPaperWidth = atoi(tok->getCString()); tok = (GString *)tokens->get(2); psPaperHeight = atoi(tok->getCString()); psImageableLLX = psImageableLLY = 0; psImageableURX = psPaperWidth; psImageableURY = psPaperHeight; } else { error(errConfig, -1, "Bad 'psPaperSize' config file command ({0:t}:{1:d})", fileName, line); } } void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 5) { error(errConfig, -1, "Bad 'psImageableArea' config file command ({0:t}:{1:d})", fileName, line); return; } psImageableLLX = atoi(((GString *)tokens->get(1))->getCString()); psImageableLLY = atoi(((GString *)tokens->get(2))->getCString()); psImageableURX = atoi(((GString *)tokens->get(3))->getCString()); psImageableURY = atoi(((GString *)tokens->get(4))->getCString()); } void GlobalParams::parsePSLevel(GList *tokens, GString *fileName, int line) { GString *tok; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'psLevel' config file command ({0:t}:{1:d})", fileName, line); return; } tok = (GString *)tokens->get(1); if (!tok->cmp("level1")) { psLevel = psLevel1; } else if (!tok->cmp("level1sep")) { psLevel = psLevel1Sep; } else if (!tok->cmp("level2")) { psLevel = psLevel2; } else if (!tok->cmp("level2sep")) { psLevel = psLevel2Sep; } else if (!tok->cmp("level3")) { psLevel = psLevel3; } else if (!tok->cmp("level3Sep")) { psLevel = psLevel3Sep; } else { error(errConfig, -1, "Bad 'psLevel' config file command ({0:t}:{1:d})", fileName, line); } } void GlobalParams::parsePSResidentFont(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'psResidentFont' config file command ({0:t}:{1:d})", fileName, line); return; } psResidentFonts->add(((GString *)tokens->get(1))->copy(), ((GString *)tokens->get(2))->copy()); } void GlobalParams::parsePSResidentFont16(GList *tokens, GString *fileName, int line) { PSFontParam16 *param; int wMode; GString *tok; if (tokens->getLength() != 5) { error(errConfig, -1, "Bad 'psResidentFont16' config file command ({0:t}:{1:d})", fileName, line); return; } tok = (GString *)tokens->get(2); if (!tok->cmp("H")) { wMode = 0; } else if (!tok->cmp("V")) { wMode = 1; } else { error(errConfig, -1, "Bad wMode in psResidentFont16 config file command ({1:t}:{2:d})", fileName, line); return; } param = new PSFontParam16(((GString *)tokens->get(1))->copy(), wMode, ((GString *)tokens->get(3))->copy(), ((GString *)tokens->get(4))->copy()); psResidentFonts16->append(param); } void GlobalParams::parsePSResidentFontCC(GList *tokens, GString *fileName, int line) { PSFontParam16 *param; int wMode; GString *tok; if (tokens->getLength() != 5) { error(errConfig, -1, "Bad 'psResidentFontCC' config file command ({0:t}:{1:d})", fileName, line); return; } tok = (GString *)tokens->get(2); if (!tok->cmp("H")) { wMode = 0; } else if (!tok->cmp("V")) { wMode = 1; } else { error(errConfig, -1, "Bad wMode in psResidentFontCC config file command ({1:t}:{2:d})", fileName, line); return; } param = new PSFontParam16(((GString *)tokens->get(1))->copy(), wMode, ((GString *)tokens->get(3))->copy(), ((GString *)tokens->get(4))->copy()); psResidentFontsCC->append(param); } void GlobalParams::parseTextEncoding(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'textEncoding' config file command ({0:s}:{1:d})", fileName, line); return; } delete textEncoding; textEncoding = ((GString *)tokens->get(1))->copy(); } void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) { GString *tok; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'textEOL' config file command ({0:t}:{1:d})", fileName, line); return; } tok = (GString *)tokens->get(1); if (!tok->cmp("unix")) { textEOL = eolUnix; } else if (!tok->cmp("dos")) { textEOL = eolDOS; } else if (!tok->cmp("mac")) { textEOL = eolMac; } else { error(errConfig, -1, "Bad 'textEOL' config file command ({0:t}:{1:d})", fileName, line); } } void GlobalParams::parseInitialZoom(GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'initialZoom' config file command ({0:t}:{1:d})", fileName, line); return; } delete initialZoom; initialZoom = ((GString *)tokens->get(1))->copy(); } void GlobalParams::parseScreenType(GList *tokens, GString *fileName, int line) { GString *tok; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad 'screenType' config file command ({0:t}:{1:d})", fileName, line); return; } tok = (GString *)tokens->get(1); if (!tok->cmp("dispersed")) { screenType = screenDispersed; } else if (!tok->cmp("clustered")) { screenType = screenClustered; } else if (!tok->cmp("stochasticClustered")) { screenType = screenStochasticClustered; } else { error(errConfig, -1, "Bad 'screenType' config file command ({0:t}:{1:d})", fileName, line); } } void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) { KeyBinding *binding; GList *cmds; int code, mods, context, i; if (tokens->getLength() < 4) { error(errConfig, -1, "Bad 'bind' config file command ({0:t}:{1:d})", fileName, line); return; } if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2), &code, &mods, &context, "bind", tokens, fileName, line)) { return; } for (i = 0; i < keyBindings->getLength(); ++i) { binding = (KeyBinding *)keyBindings->get(i); if (binding->code == code && binding->mods == mods && binding->context == context) { delete (KeyBinding *)keyBindings->del(i); break; } } cmds = new GList(); for (i = 3; i < tokens->getLength(); ++i) { cmds->append(((GString *)tokens->get(i))->copy()); } keyBindings->append(new KeyBinding(code, mods, context, cmds)); } void GlobalParams::parseUnbind(GList *tokens, GString *fileName, int line) { KeyBinding *binding; int code, mods, context, i; if (tokens->getLength() != 3) { error(errConfig, -1, "Bad 'unbind' config file command ({0:t}:{1:d})", fileName, line); return; } if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2), &code, &mods, &context, "unbind", tokens, fileName, line)) { return; } for (i = 0; i < keyBindings->getLength(); ++i) { binding = (KeyBinding *)keyBindings->get(i); if (binding->code == code && binding->mods == mods && binding->context == context) { delete (KeyBinding *)keyBindings->del(i); break; } } } GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr, int *code, int *mods, int *context, const char *cmdName, GList *tokens, GString *fileName, int line) { char *p0; int btn; *mods = xpdfKeyModNone; p0 = modKeyStr->getCString(); while (1) { if (!strncmp(p0, "shift-", 6)) { *mods |= xpdfKeyModShift; p0 += 6; } else if (!strncmp(p0, "ctrl-", 5)) { *mods |= xpdfKeyModCtrl; p0 += 5; } else if (!strncmp(p0, "alt-", 4)) { *mods |= xpdfKeyModAlt; p0 += 4; } else { break; } } if (!strcmp(p0, "space")) { *code = ' '; } else if (!strcmp(p0, "tab")) { *code = xpdfKeyCodeTab; } else if (!strcmp(p0, "return")) { *code = xpdfKeyCodeReturn; } else if (!strcmp(p0, "enter")) { *code = xpdfKeyCodeEnter; } else if (!strcmp(p0, "backspace")) { *code = xpdfKeyCodeBackspace; } else if (!strcmp(p0, "insert")) { *code = xpdfKeyCodeInsert; } else if (!strcmp(p0, "delete")) { *code = xpdfKeyCodeDelete; } else if (!strcmp(p0, "home")) { *code = xpdfKeyCodeHome; } else if (!strcmp(p0, "end")) { *code = xpdfKeyCodeEnd; } else if (!strcmp(p0, "pgup")) { *code = xpdfKeyCodePgUp; } else if (!strcmp(p0, "pgdn")) { *code = xpdfKeyCodePgDn; } else if (!strcmp(p0, "left")) { *code = xpdfKeyCodeLeft; } else if (!strcmp(p0, "right")) { *code = xpdfKeyCodeRight; } else if (!strcmp(p0, "up")) { *code = xpdfKeyCodeUp; } else if (!strcmp(p0, "down")) { *code = xpdfKeyCodeDown; } else if (p0[0] == 'f' && p0[1] >= '1' && p0[1] <= '9' && !p0[2]) { *code = xpdfKeyCodeF1 + (p0[1] - '1'); } else if (p0[0] == 'f' && ((p0[1] >= '1' && p0[1] <= '2' && p0[2] >= '0' && p0[2] <= '9') || (p0[1] == '3' && p0[2] >= '0' && p0[2] <= '5')) && !p0[3]) { *code = xpdfKeyCodeF1 + 10 * (p0[1] - '0') + (p0[2] - '0') - 1; } else if (!strncmp(p0, "mousePress", 10) && p0[10] >= '0' && p0[10] <= '9' && (!p0[11] || (p0[11] >= '0' && p0[11] <= '9' && !p0[12])) && (btn = atoi(p0 + 10)) >= 1 && btn <= 32) { *code = xpdfKeyCodeMousePress1 + btn - 1; } else if (!strncmp(p0, "mouseRelease", 12) && p0[12] >= '0' && p0[12] <= '9' && (!p0[13] || (p0[13] >= '0' && p0[13] <= '9' && !p0[14])) && (btn = atoi(p0 + 12)) >= 1 && btn <= 32) { *code = xpdfKeyCodeMouseRelease1 + btn - 1; } else if (*p0 >= 0x20 && *p0 <= 0x7e && !p0[1]) { *code = (int)*p0; } else { error(errConfig, -1, "Bad key/modifier in '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return gFalse; } p0 = contextStr->getCString(); if (!strcmp(p0, "any")) { *context = xpdfKeyContextAny; } else { *context = xpdfKeyContextAny; while (1) { if (!strncmp(p0, "fullScreen", 10)) { *context |= xpdfKeyContextFullScreen; p0 += 10; } else if (!strncmp(p0, "window", 6)) { *context |= xpdfKeyContextWindow; p0 += 6; } else if (!strncmp(p0, "continuous", 10)) { *context |= xpdfKeyContextContinuous; p0 += 10; } else if (!strncmp(p0, "singlePage", 10)) { *context |= xpdfKeyContextSinglePage; p0 += 10; } else if (!strncmp(p0, "overLink", 8)) { *context |= xpdfKeyContextOverLink; p0 += 8; } else if (!strncmp(p0, "offLink", 7)) { *context |= xpdfKeyContextOffLink; p0 += 7; } else if (!strncmp(p0, "outline", 7)) { *context |= xpdfKeyContextOutline; p0 += 7; } else if (!strncmp(p0, "mainWin", 7)) { *context |= xpdfKeyContextMainWin; p0 += 7; } else if (!strncmp(p0, "scrLockOn", 9)) { *context |= xpdfKeyContextScrLockOn; p0 += 9; } else if (!strncmp(p0, "scrLockOff", 10)) { *context |= xpdfKeyContextScrLockOff; p0 += 10; } else { error(errConfig, -1, "Bad context in '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return gFalse; } if (!*p0) { break; } if (*p0 != ',') { error(errConfig, -1, "Bad context in '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return gFalse; } ++p0; } } return gTrue; } void GlobalParams::parseCommand(const char *cmdName, GString **val, GList *tokens, GString *fileName, int line) { if (tokens->getLength() != 2) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } if (*val) { delete *val; } *val = ((GString *)tokens->get(1))->copy(); } void GlobalParams::parseYesNo(const char *cmdName, GBool *flag, GList *tokens, GString *fileName, int line) { GString *tok; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } tok = (GString *)tokens->get(1); if (!parseYesNo2(tok->getCString(), flag)) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); } } GBool GlobalParams::parseYesNo2(char *token, GBool *flag) { if (!strcmp(token, "yes")) { *flag = gTrue; } else if (!strcmp(token, "no")) { *flag = gFalse; } else { return gFalse; } return gTrue; } void GlobalParams::parseInteger(const char *cmdName, int *val, GList *tokens, GString *fileName, int line) { GString *tok; int i; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } tok = (GString *)tokens->get(1); if (tok->getLength() == 0) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } if (tok->getChar(0) == '-') { i = 1; } else { i = 0; } for (; i < tok->getLength(); ++i) { if (tok->getChar(i) < '0' || tok->getChar(i) > '9') { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } } *val = atoi(tok->getCString()); } void GlobalParams::parseFloat(const char *cmdName, double *val, GList *tokens, GString *fileName, int line) { GString *tok; int i; if (tokens->getLength() != 2) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } tok = (GString *)tokens->get(1); if (tok->getLength() == 0) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } if (tok->getChar(0) == '-') { i = 1; } else { i = 0; } for (; i < tok->getLength(); ++i) { if (!((tok->getChar(i) >= '0' && tok->getChar(i) <= '9') || tok->getChar(i) == '.')) { error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})", cmdName, fileName, line); return; } } *val = atof(tok->getCString()); } GlobalParams::~GlobalParams() { GHashIter *iter; GString *key; GList *list; freeBuiltinFontTables(); delete macRomanReverseMap; delete baseDir; delete nameToUnicode; deleteGHash(cidToUnicodes, GString); deleteGHash(unicodeToUnicodes, GString); deleteGHash(residentUnicodeMaps, UnicodeMap); deleteGHash(unicodeMaps, GString); deleteGList(toUnicodeDirs, GString); deleteGHash(fontFiles, GString); deleteGList(fontDirs, GString); deleteGHash(ccFontFiles, GString); deleteGHash(base14SysFonts, Base14FontInfo); delete sysFonts; if (psFile) { delete psFile; } deleteGHash(psResidentFonts, GString); deleteGList(psResidentFonts16, PSFontParam16); deleteGList(psResidentFontsCC, PSFontParam16); delete textEncoding; delete initialZoom; if (launchCommand) { delete launchCommand; } if (urlCommand) { delete urlCommand; } if (movieCommand) { delete movieCommand; } deleteGList(keyBindings, KeyBinding); cMapDirs->startIter(&iter); while (cMapDirs->getNext(&iter, &key, (void **)&list)) { deleteGList(list, GString); } delete cMapDirs; delete cidToUnicodeCache; delete unicodeToUnicodeCache; delete unicodeMapCache; delete cMapCache; #ifdef ENABLE_PLUGINS delete securityHandlers; deleteGList(plugins, Plugin); #endif #if MULTITHREADED gDestroyMutex(&mutex); gDestroyMutex(&unicodeMapCacheMutex); gDestroyMutex(&cMapCacheMutex); #endif } //------------------------------------------------------------------------ void GlobalParams::setBaseDir(char *dir) { delete baseDir; baseDir = new GString(dir); } #ifdef _WIN32 static void getWinFontDir(char *winFontDir) { HMODULE shell32Lib; BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate); char *p; int i; // SHGetSpecialFolderPath isn't available in older versions of // shell32.dll (Win95 and WinNT4), so do a dynamic load winFontDir[0] = '\0'; if ((shell32Lib = LoadLibraryA("shell32.dll"))) { if ((SHGetSpecialFolderPathFunc = (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)) { continue; } 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"))) { fclose(f); } else { delete fileName; fileName = NULL; } } #ifdef _WIN32 if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) { fileName = appendToPath(new GString(winFontDir), displayFontTab[i].ttFileName); if ((f = fopen(fileName->getCString(), "rb"))) { fclose(f); } else { delete fileName; fileName = NULL; } } #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]), s); if ((f = fopen(fileName->getCString(), "rb"))) { fclose(f); } else { delete fileName; fileName = NULL; } } } if (!fileName) { delete fontName; continue; } base14SysFonts->add(fontName, new Base14FontInfo(fileName, fontNum, 0)); } #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); } #endif } //------------------------------------------------------------------------ // accessors //------------------------------------------------------------------------ CharCode GlobalParams::getMacRomanCharCode(char *charName) { // no need to lock - macRomanReverseMap is constant return macRomanReverseMap->lookup(charName); } GString *GlobalParams::getBaseDir() { GString *s; lockGlobalParams; s = baseDir->copy(); unlockGlobalParams; return s; } Unicode GlobalParams::mapNameToUnicode(const char *charName) { // no need to lock - nameToUnicode is constant return nameToUnicode->lookup(charName); } UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) { UnicodeMap *map; lockGlobalParams; map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName); unlockGlobalParams; if (map) { map->incRefCnt(); } return map; } FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) { GString *fileName; FILE *f; lockGlobalParams; if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) { f = openFile(fileName->getCString(), "r"); } else { f = NULL; } unlockGlobalParams; return f; } FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) { GList *list; GString *dir; GString *fileName; FILE *f; int i; lockGlobalParams; if (!(list = (GList *)cMapDirs->lookup(collection))) { unlockGlobalParams; return NULL; } for (i = 0; i < list->getLength(); ++i) { dir = (GString *)list->get(i); fileName = appendToPath(dir->copy(), cMapName->getCString()); f = openFile(fileName->getCString(), "r"); delete fileName; if (f) { unlockGlobalParams; return f; } } unlockGlobalParams; return NULL; } FILE *GlobalParams::findToUnicodeFile(GString *name) { GString *dir, *fileName; FILE *f; int i; lockGlobalParams; for (i = 0; i < toUnicodeDirs->getLength(); ++i) { dir = (GString *)toUnicodeDirs->get(i); fileName = appendToPath(dir->copy(), name->getCString()); f = openFile(fileName->getCString(), "r"); delete fileName; if (f) { unlockGlobalParams; return f; } } unlockGlobalParams; return NULL; } GString *GlobalParams::findFontFile(GString *fontName) { static const char *exts[] = { ".pfa", ".pfb", ".ttf", ".ttc" }; GString *path, *dir; #ifdef _WIN32 GString *fontNameU; #endif const char *ext; FILE *f; int i, j; lockGlobalParams; if ((path = (GString *)fontFiles->lookup(fontName))) { path = path->copy(); unlockGlobalParams; return path; } for (i = 0; i < fontDirs->getLength(); ++i) { dir = (GString *)fontDirs->get(i); for (j = 0; j < (int)(sizeof(exts) / sizeof(exts[0])); ++j) { ext = exts[j]; #ifdef _WIN32 fontNameU = fileNameToUTF8(fontName->getCString()); path = appendToPath(dir->copy(), fontNameU->getCString()); delete fontNameU; #else path = appendToPath(dir->copy(), fontName->getCString()); #endif path->append(ext); if ((f = openFile(path->getCString(), "rb"))) { fclose(f); unlockGlobalParams; return path; } delete path; } } unlockGlobalParams; 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) { SysFontInfo *fi; GString *path; path = NULL; lockGlobalParams; if ((fi = sysFonts->find(fontName))) { path = fi->path->copy(); *type = fi->type; *fontNum = fi->fontNum; } unlockGlobalParams; return path; } GString *GlobalParams::findCCFontFile(GString *collection) { GString *path; lockGlobalParams; if ((path = (GString *)ccFontFiles->lookup(collection))) { path = path->copy(); } unlockGlobalParams; return path; } GString *GlobalParams::getPSFile() { GString *s; lockGlobalParams; s = psFile ? psFile->copy() : (GString *)NULL; unlockGlobalParams; return s; } int GlobalParams::getPSPaperWidth() { int w; lockGlobalParams; w = psPaperWidth; unlockGlobalParams; return w; } int GlobalParams::getPSPaperHeight() { int h; lockGlobalParams; h = psPaperHeight; unlockGlobalParams; return h; } void GlobalParams::getPSImageableArea(int *llx, int *lly, int *urx, int *ury) { lockGlobalParams; *llx = psImageableLLX; *lly = psImageableLLY; *urx = psImageableURX; *ury = psImageableURY; unlockGlobalParams; } GBool GlobalParams::getPSCrop() { GBool f; lockGlobalParams; f = psCrop; unlockGlobalParams; return f; } GBool GlobalParams::getPSUseCropBoxAsPage() { GBool f; lockGlobalParams; f = psUseCropBoxAsPage; unlockGlobalParams; return f; } GBool GlobalParams::getPSExpandSmaller() { GBool f; lockGlobalParams; f = psExpandSmaller; unlockGlobalParams; return f; } GBool GlobalParams::getPSShrinkLarger() { GBool f; lockGlobalParams; f = psShrinkLarger; unlockGlobalParams; return f; } GBool GlobalParams::getPSCenter() { GBool f; lockGlobalParams; f = psCenter; unlockGlobalParams; return f; } GBool GlobalParams::getPSDuplex() { GBool d; lockGlobalParams; d = psDuplex; unlockGlobalParams; return d; } PSLevel GlobalParams::getPSLevel() { PSLevel level; lockGlobalParams; level = psLevel; unlockGlobalParams; return level; } GString *GlobalParams::getPSResidentFont(GString *fontName) { GString *psName; lockGlobalParams; if ((psName = (GString *)psResidentFonts->lookup(fontName))) { psName = psName->copy(); } unlockGlobalParams; return psName; } GList *GlobalParams::getPSResidentFonts() { GList *names; GHashIter *iter; GString *name; GString *psName; names = new GList(); lockGlobalParams; psResidentFonts->startIter(&iter); while (psResidentFonts->getNext(&iter, &name, (void **)&psName)) { names->append(psName->copy()); } unlockGlobalParams; return names; } PSFontParam16 *GlobalParams::getPSResidentFont16(GString *fontName, int wMode) { PSFontParam16 *p; int i; lockGlobalParams; p = NULL; for (i = 0; i < psResidentFonts16->getLength(); ++i) { p = (PSFontParam16 *)psResidentFonts16->get(i); if (!(p->name->cmp(fontName)) && p->wMode == wMode) { break; } p = NULL; } unlockGlobalParams; return p; } PSFontParam16 *GlobalParams::getPSResidentFontCC(GString *collection, int wMode) { PSFontParam16 *p; int i; lockGlobalParams; p = NULL; for (i = 0; i < psResidentFontsCC->getLength(); ++i) { p = (PSFontParam16 *)psResidentFontsCC->get(i); if (!(p->name->cmp(collection)) && p->wMode == wMode) { break; } p = NULL; } unlockGlobalParams; return p; } GBool GlobalParams::getPSEmbedType1() { GBool e; lockGlobalParams; e = psEmbedType1; unlockGlobalParams; return e; } GBool GlobalParams::getPSEmbedTrueType() { GBool e; lockGlobalParams; e = psEmbedTrueType; unlockGlobalParams; return e; } GBool GlobalParams::getPSEmbedCIDPostScript() { GBool e; lockGlobalParams; e = psEmbedCIDPostScript; unlockGlobalParams; return e; } GBool GlobalParams::getPSEmbedCIDTrueType() { GBool e; lockGlobalParams; e = psEmbedCIDTrueType; unlockGlobalParams; return e; } GBool GlobalParams::getPSFontPassthrough() { GBool e; lockGlobalParams; e = psFontPassthrough; unlockGlobalParams; return e; } GBool GlobalParams::getPSPreload() { GBool preload; lockGlobalParams; preload = psPreload; unlockGlobalParams; return preload; } GBool GlobalParams::getPSOPI() { GBool opi; lockGlobalParams; opi = psOPI; unlockGlobalParams; return opi; } GBool GlobalParams::getPSASCIIHex() { GBool ah; lockGlobalParams; ah = psASCIIHex; unlockGlobalParams; return ah; } GBool GlobalParams::getPSLZW() { GBool ah; lockGlobalParams; ah = psLZW; unlockGlobalParams; return ah; } GBool GlobalParams::getPSUncompressPreloadedImages() { GBool ah; lockGlobalParams; ah = psUncompressPreloadedImages; unlockGlobalParams; return ah; } double GlobalParams::getPSMinLineWidth() { double w; lockGlobalParams; w = psMinLineWidth; unlockGlobalParams; return w; } double GlobalParams::getPSRasterResolution() { double res; lockGlobalParams; res = psRasterResolution; unlockGlobalParams; return res; } GBool GlobalParams::getPSRasterMono() { GBool mono; lockGlobalParams; mono = psRasterMono; unlockGlobalParams; return mono; } int GlobalParams::getPSRasterSliceSize() { int slice; lockGlobalParams; slice = psRasterSliceSize; unlockGlobalParams; return slice; } GBool GlobalParams::getPSAlwaysRasterize() { GBool rast; lockGlobalParams; rast = psAlwaysRasterize; unlockGlobalParams; return rast; } GString *GlobalParams::getTextEncodingName() { GString *s; lockGlobalParams; s = textEncoding->copy(); unlockGlobalParams; return s; } EndOfLineKind GlobalParams::getTextEOL() { EndOfLineKind eol; lockGlobalParams; eol = textEOL; unlockGlobalParams; return eol; } GBool GlobalParams::getTextPageBreaks() { GBool pageBreaks; lockGlobalParams; pageBreaks = textPageBreaks; unlockGlobalParams; return pageBreaks; } GBool GlobalParams::getTextKeepTinyChars() { GBool tiny; lockGlobalParams; tiny = textKeepTinyChars; unlockGlobalParams; return tiny; } GString *GlobalParams::getInitialZoom() { GString *s; lockGlobalParams; s = initialZoom->copy(); unlockGlobalParams; return s; } GBool GlobalParams::getContinuousView() { GBool f; lockGlobalParams; f = continuousView; unlockGlobalParams; return f; } GBool GlobalParams::getEnableFreeType() { GBool f; lockGlobalParams; f = enableFreeType; unlockGlobalParams; return f; } GBool GlobalParams::getDisableFreeTypeHinting() { GBool f; lockGlobalParams; f = disableFreeTypeHinting; unlockGlobalParams; return f; } GBool GlobalParams::getAntialias() { GBool f; lockGlobalParams; f = antialias; unlockGlobalParams; return f; } GBool GlobalParams::getVectorAntialias() { GBool f; lockGlobalParams; f = vectorAntialias; unlockGlobalParams; return f; } GBool GlobalParams::getAntialiasPrinting() { GBool f; lockGlobalParams; f = antialiasPrinting; unlockGlobalParams; return f; } GBool GlobalParams::getStrokeAdjust() { GBool f; lockGlobalParams; f = strokeAdjust; unlockGlobalParams; return f; } ScreenType GlobalParams::getScreenType() { ScreenType t; lockGlobalParams; t = screenType; unlockGlobalParams; return t; } int GlobalParams::getScreenSize() { int size; lockGlobalParams; size = screenSize; unlockGlobalParams; return size; } int GlobalParams::getScreenDotRadius() { int r; lockGlobalParams; r = screenDotRadius; unlockGlobalParams; return r; } double GlobalParams::getScreenGamma() { double gamma; lockGlobalParams; gamma = screenGamma; unlockGlobalParams; return gamma; } double GlobalParams::getScreenBlackThreshold() { double thresh; lockGlobalParams; thresh = screenBlackThreshold; unlockGlobalParams; return thresh; } double GlobalParams::getScreenWhiteThreshold() { double thresh; lockGlobalParams; thresh = screenWhiteThreshold; unlockGlobalParams; return thresh; } double GlobalParams::getMinLineWidth() { double w; lockGlobalParams; w = minLineWidth; unlockGlobalParams; return w; } GBool GlobalParams::getDrawAnnotations() { GBool draw; lockGlobalParams; draw = drawAnnotations; unlockGlobalParams; return draw; } GBool GlobalParams::getMapNumericCharNames() { GBool map; lockGlobalParams; map = mapNumericCharNames; unlockGlobalParams; return map; } GBool GlobalParams::getMapUnknownCharNames() { GBool map; lockGlobalParams; map = mapUnknownCharNames; unlockGlobalParams; 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; int modMask; int i, j; lockGlobalParams; cmds = NULL; // for ASCII chars, ignore the shift modifier modMask = code <= 0xff ? ~xpdfKeyModShift : ~0; for (i = 0; i < keyBindings->getLength(); ++i) { binding = (KeyBinding *)keyBindings->get(i); if (binding->code == code && (binding->mods & modMask) == (mods & modMask) && (~binding->context | context) == ~0) { cmds = new GList(); for (j = 0; j < binding->cmds->getLength(); ++j) { cmds->append(((GString *)binding->cmds->get(j))->copy()); } break; } } unlockGlobalParams; return cmds; } GBool GlobalParams::getPrintCommands() { GBool p; lockGlobalParams; p = printCommands; unlockGlobalParams; return p; } GBool GlobalParams::getErrQuiet() { // no locking -- this function may get called from inside a locked // section return errQuiet; } CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) { GString *fileName; CharCodeToUnicode *ctu; lockGlobalParams; if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) { if ((fileName = (GString *)cidToUnicodes->lookup(collection)) && (ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) { cidToUnicodeCache->add(ctu); } } unlockGlobalParams; return ctu; } CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) { CharCodeToUnicode *ctu; GHashIter *iter; GString *fontPattern, *fileName; lockGlobalParams; fileName = NULL; unicodeToUnicodes->startIter(&iter); while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) { if (strstr(fontName->getCString(), fontPattern->getCString())) { unicodeToUnicodes->killIter(&iter); break; } fileName = NULL; } if (fileName) { if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) { if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) { unicodeToUnicodeCache->add(ctu); } } } else { ctu = NULL; } unlockGlobalParams; return ctu; } UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) { return getUnicodeMap2(encodingName); } UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) { UnicodeMap *map; if (!(map = getResidentUnicodeMap(encodingName))) { lockUnicodeMapCache; map = unicodeMapCache->getUnicodeMap(encodingName); unlockUnicodeMapCache; } return map; } CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) { CMap *cMap; lockCMapCache; cMap = cMapCache->getCMap(collection, cMapName); unlockCMapCache; return cMap; } UnicodeMap *GlobalParams::getTextEncoding() { return getUnicodeMap2(textEncoding); } //------------------------------------------------------------------------ // functions to set parameters //------------------------------------------------------------------------ void GlobalParams::addFontFile(GString *fontName, GString *path) { lockGlobalParams; fontFiles->add(fontName, path); unlockGlobalParams; } void GlobalParams::setPSFile(char *file) { lockGlobalParams; if (psFile) { delete psFile; } psFile = new GString(file); unlockGlobalParams; } GBool GlobalParams::setPSPaperSize(char *size) { lockGlobalParams; if (!strcmp(size, "match")) { psPaperWidth = psPaperHeight = -1; } else if (!strcmp(size, "letter")) { psPaperWidth = 612; psPaperHeight = 792; } else if (!strcmp(size, "legal")) { psPaperWidth = 612; psPaperHeight = 1008; } else if (!strcmp(size, "A4")) { psPaperWidth = 595; psPaperHeight = 842; } else if (!strcmp(size, "A3")) { psPaperWidth = 842; psPaperHeight = 1190; } else { unlockGlobalParams; return gFalse; } psImageableLLX = psImageableLLY = 0; psImageableURX = psPaperWidth; psImageableURY = psPaperHeight; unlockGlobalParams; return gTrue; } void GlobalParams::setPSPaperWidth(int width) { lockGlobalParams; psPaperWidth = width; psImageableLLX = 0; psImageableURX = psPaperWidth; unlockGlobalParams; } void GlobalParams::setPSPaperHeight(int height) { lockGlobalParams; psPaperHeight = height; psImageableLLY = 0; psImageableURY = psPaperHeight; unlockGlobalParams; } void GlobalParams::setPSImageableArea(int llx, int lly, int urx, int ury) { lockGlobalParams; psImageableLLX = llx; psImageableLLY = lly; psImageableURX = urx; psImageableURY = ury; unlockGlobalParams; } void GlobalParams::setPSCrop(GBool crop) { lockGlobalParams; psCrop = crop; unlockGlobalParams; } void GlobalParams::setPSUseCropBoxAsPage(GBool crop) { lockGlobalParams; psUseCropBoxAsPage = crop; unlockGlobalParams; } void GlobalParams::setPSExpandSmaller(GBool expand) { lockGlobalParams; psExpandSmaller = expand; unlockGlobalParams; } void GlobalParams::setPSShrinkLarger(GBool shrink) { lockGlobalParams; psShrinkLarger = shrink; unlockGlobalParams; } void GlobalParams::setPSCenter(GBool center) { lockGlobalParams; psCenter = center; unlockGlobalParams; } void GlobalParams::setPSDuplex(GBool duplex) { lockGlobalParams; psDuplex = duplex; unlockGlobalParams; } void GlobalParams::setPSLevel(PSLevel level) { lockGlobalParams; psLevel = level; unlockGlobalParams; } void GlobalParams::setPSEmbedType1(GBool embed) { lockGlobalParams; psEmbedType1 = embed; unlockGlobalParams; } void GlobalParams::setPSEmbedTrueType(GBool embed) { lockGlobalParams; psEmbedTrueType = embed; unlockGlobalParams; } void GlobalParams::setPSEmbedCIDPostScript(GBool embed) { lockGlobalParams; psEmbedCIDPostScript = embed; unlockGlobalParams; } void GlobalParams::setPSEmbedCIDTrueType(GBool embed) { lockGlobalParams; psEmbedCIDTrueType = embed; unlockGlobalParams; } void GlobalParams::setPSFontPassthrough(GBool passthrough) { lockGlobalParams; psFontPassthrough = passthrough; unlockGlobalParams; } void GlobalParams::setPSPreload(GBool preload) { lockGlobalParams; psPreload = preload; unlockGlobalParams; } void GlobalParams::setPSOPI(GBool opi) { lockGlobalParams; psOPI = opi; unlockGlobalParams; } void GlobalParams::setPSASCIIHex(GBool hex) { lockGlobalParams; psASCIIHex = hex; unlockGlobalParams; } void GlobalParams::setTextEncoding(const char *encodingName) { lockGlobalParams; delete textEncoding; textEncoding = new GString(encodingName); unlockGlobalParams; } GBool GlobalParams::setTextEOL(char *s) { lockGlobalParams; if (!strcmp(s, "unix")) { textEOL = eolUnix; } else if (!strcmp(s, "dos")) { textEOL = eolDOS; } else if (!strcmp(s, "mac")) { textEOL = eolMac; } else { unlockGlobalParams; return gFalse; } unlockGlobalParams; return gTrue; } void GlobalParams::setTextPageBreaks(GBool pageBreaks) { lockGlobalParams; textPageBreaks = pageBreaks; unlockGlobalParams; } void GlobalParams::setTextKeepTinyChars(GBool keep) { lockGlobalParams; textKeepTinyChars = keep; unlockGlobalParams; } void GlobalParams::setInitialZoom(char *s) { lockGlobalParams; delete initialZoom; initialZoom = new GString(s); unlockGlobalParams; } void GlobalParams::setContinuousView(GBool cont) { lockGlobalParams; continuousView = cont; unlockGlobalParams; } GBool GlobalParams::setEnableFreeType(char *s) { GBool ok; lockGlobalParams; ok = parseYesNo2(s, &enableFreeType); unlockGlobalParams; return ok; } GBool GlobalParams::setAntialias(char *s) { GBool ok; lockGlobalParams; ok = parseYesNo2(s, &antialias); unlockGlobalParams; return ok; } GBool GlobalParams::setVectorAntialias(char *s) { GBool ok; lockGlobalParams; ok = parseYesNo2(s, &vectorAntialias); unlockGlobalParams; return ok; } void GlobalParams::setScreenType(ScreenType t) { lockGlobalParams; screenType = t; unlockGlobalParams; } void GlobalParams::setScreenSize(int size) { lockGlobalParams; screenSize = size; unlockGlobalParams; } void GlobalParams::setScreenDotRadius(int r) { lockGlobalParams; screenDotRadius = r; unlockGlobalParams; } void GlobalParams::setScreenGamma(double gamma) { lockGlobalParams; screenGamma = gamma; unlockGlobalParams; } void GlobalParams::setScreenBlackThreshold(double thresh) { lockGlobalParams; screenBlackThreshold = thresh; unlockGlobalParams; } void GlobalParams::setScreenWhiteThreshold(double thresh) { lockGlobalParams; screenWhiteThreshold = thresh; unlockGlobalParams; } void GlobalParams::setMapNumericCharNames(GBool map) { lockGlobalParams; mapNumericCharNames = map; unlockGlobalParams; } void GlobalParams::setMapUnknownCharNames(GBool map) { lockGlobalParams; mapUnknownCharNames = map; unlockGlobalParams; } void GlobalParams::setMapExtTrueTypeFontsViaUnicode(GBool map) { lockGlobalParams; mapExtTrueTypeFontsViaUnicode = map; unlockGlobalParams; } void GlobalParams::setEnableXFA(GBool enable) { lockGlobalParams; enableXFA = enable; unlockGlobalParams; } void GlobalParams::setPrintCommands(GBool printCommandsA) { lockGlobalParams; printCommands = printCommandsA; unlockGlobalParams; } void GlobalParams::setErrQuiet(GBool errQuietA) { lockGlobalParams; errQuiet = errQuietA; unlockGlobalParams; } void GlobalParams::addSecurityHandler(XpdfSecurityHandler *handler) { #ifdef ENABLE_PLUGINS lockGlobalParams; securityHandlers->append(handler); unlockGlobalParams; #endif } XpdfSecurityHandler *GlobalParams::getSecurityHandler(char *name) { #ifdef ENABLE_PLUGINS XpdfSecurityHandler *hdlr; int i; lockGlobalParams; for (i = 0; i < securityHandlers->getLength(); ++i) { hdlr = (XpdfSecurityHandler *)securityHandlers->get(i); if (!strcasecmp(hdlr->name, name)) { unlockGlobalParams; return hdlr; } } unlockGlobalParams; if (!loadPlugin("security", name)) { return NULL; } lockGlobalParams; for (i = 0; i < securityHandlers->getLength(); ++i) { hdlr = (XpdfSecurityHandler *)securityHandlers->get(i); if (!strcmp(hdlr->name, name)) { unlockGlobalParams; return hdlr; } } unlockGlobalParams; #endif return NULL; } #ifdef ENABLE_PLUGINS //------------------------------------------------------------------------ // plugins //------------------------------------------------------------------------ GBool GlobalParams::loadPlugin(char *type, char *name) { Plugin *plugin; if (!(plugin = Plugin::load(type, name))) { return gFalse; } lockGlobalParams; plugins->append(plugin); unlockGlobalParams; return gTrue; } #endif // ENABLE_PLUGINS xpdf-3.04/xpdf/about-text.h0000644000076400007640000000131612341430012015110 0ustar dereknderekn//======================================================================== // // about-text.h // // Copyright 2002-2007 Glyph & Cog, LLC // //======================================================================== static const char *aboutWinText[] = { "http://www.foolabs.com/xpdf/", "derekn@foolabs.com", " ", "Licensed under the GNU General Public License (GPL) v2 or v3.", "See the 'README' file for details.", " ", "Supports PDF version " supportedPDFVersionStr ".", " ", "The PDF data structures, operators, and specification", "are copyright 1985-2006 Adobe Systems Inc.", " ", "For more information (including key and mouse bindings)", "please read the xpdf(1) man page.", NULL }; xpdf-3.04/xpdf/XFAForm.h0000644000076400007640000000630112341430012014255 0ustar dereknderekn//======================================================================== // // 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 xpdf-3.04/xpdf/UnicodeTypeTable.cc0000644000076400007640000011661612341430012016364 0ustar dereknderekn//======================================================================== // // UnicodeTypeTable.cc // // Copyright 2004-2013 Glyph & Cog, LLC // //======================================================================== #include #include "CharTypes.h" #include "UnicodeTypeTable.h" struct UnicodeMapTableEntry { const char *vector; char type; }; struct UnicodeCaseTableVector { Unicode codes[256]; }; static UnicodeMapTableEntry typeTable[256] = { { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N....NNNNLNNNNN..##NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' }, { "RRRR.........RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN#################.##RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' }, { "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { NULL, 'N' }, { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL..LLLLLLLNNNNN", 'X' }, { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL..NNNNNNNNNNNNNN", 'X' }, { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN.NLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN.....LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL.LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' }, { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN.....NNNNNNNNNNNNNNN.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L########..NNNL##########..NNN...................................NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL.LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNN..NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN####################LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'L' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'N' }, { NULL, 'N' }, { "NNNNNLLLNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNNNNNNNLLLLLNNLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLL", 'X' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN", 'X' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { NULL, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR.RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' }, { NULL, 'R' }, { "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N.NN.NNNNNNNNN.NN..NNNNN..NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' }, { "NNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL.....NNN..NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' } }; static UnicodeCaseTableVector caseTable00 = {{ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x03bc, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }}; static UnicodeCaseTableVector caseTable01 = {{ 0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107, 0x0109, 0x0109, 0x010b, 0x010b, 0x010d, 0x010d, 0x010f, 0x010f, 0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117, 0x0119, 0x0119, 0x011b, 0x011b, 0x011d, 0x011d, 0x011f, 0x011f, 0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127, 0x0129, 0x0129, 0x012b, 0x012b, 0x012d, 0x012d, 0x012f, 0x012f, 0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137, 0x0138, 0x013a, 0x013a, 0x013c, 0x013c, 0x013e, 0x013e, 0x0140, 0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148, 0x0148, 0x0149, 0x014b, 0x014b, 0x014d, 0x014d, 0x014f, 0x014f, 0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157, 0x0159, 0x0159, 0x015b, 0x015b, 0x015d, 0x015d, 0x015f, 0x015f, 0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167, 0x0169, 0x0169, 0x016b, 0x016b, 0x016d, 0x016d, 0x016f, 0x016f, 0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177, 0x00ff, 0x017a, 0x017a, 0x017c, 0x017c, 0x017e, 0x017e, 0x0073, 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018c, 0x018c, 0x018d, 0x01dd, 0x0259, 0x025b, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019a, 0x019b, 0x026f, 0x0272, 0x019e, 0x0275, 0x01a1, 0x01a1, 0x01a3, 0x01a3, 0x01a5, 0x01a5, 0x0280, 0x01a8, 0x01a8, 0x0283, 0x01aa, 0x01ab, 0x01ad, 0x01ad, 0x0288, 0x01b0, 0x01b0, 0x028a, 0x028b, 0x01b4, 0x01b4, 0x01b6, 0x01b6, 0x0292, 0x01b9, 0x01b9, 0x01ba, 0x01bb, 0x01bd, 0x01bd, 0x01be, 0x01bf, 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c6, 0x01c6, 0x01c6, 0x01c9, 0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01cc, 0x01ce, 0x01ce, 0x01d0, 0x01d0, 0x01d2, 0x01d2, 0x01d4, 0x01d4, 0x01d6, 0x01d6, 0x01d8, 0x01d8, 0x01da, 0x01da, 0x01dc, 0x01dc, 0x01dd, 0x01df, 0x01df, 0x01e1, 0x01e1, 0x01e3, 0x01e3, 0x01e5, 0x01e5, 0x01e7, 0x01e7, 0x01e9, 0x01e9, 0x01eb, 0x01eb, 0x01ed, 0x01ed, 0x01ef, 0x01ef, 0x01f0, 0x01f3, 0x01f3, 0x01f3, 0x01f5, 0x01f5, 0x0195, 0x01bf, 0x01f9, 0x01f9, 0x01fb, 0x01fb, 0x01fd, 0x01fd, 0x01ff, 0x01ff }}; static UnicodeCaseTableVector caseTable02 = {{ 0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207, 0x0209, 0x0209, 0x020b, 0x020b, 0x020d, 0x020d, 0x020f, 0x020f, 0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217, 0x0219, 0x0219, 0x021b, 0x021b, 0x021d, 0x021d, 0x021f, 0x021f, 0x019e, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0227, 0x0227, 0x0229, 0x0229, 0x022b, 0x022b, 0x022d, 0x022d, 0x022f, 0x022f, 0x0231, 0x0231, 0x0233, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237, 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f, 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f, 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, 0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7, 0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, 0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df, 0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7, 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff }}; static UnicodeCaseTableVector caseTable03 = {{ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x03b9, 0x0346, 0x0347, 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, 0x0378, 0x0379, 0x037a, 0x037b, 0x037c, 0x037d, 0x037e, 0x037f, 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03ac, 0x0387, 0x03ad, 0x03ae, 0x03af, 0x038b, 0x03cc, 0x038d, 0x03cd, 0x03ce, 0x0390, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03a2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c3, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x03cf, 0x03b2, 0x03b8, 0x03d2, 0x03d3, 0x03d4, 0x03c6, 0x03c0, 0x03d7, 0x03d9, 0x03d9, 0x03db, 0x03db, 0x03dd, 0x03dd, 0x03df, 0x03df, 0x03e1, 0x03e1, 0x03e3, 0x03e3, 0x03e5, 0x03e5, 0x03e7, 0x03e7, 0x03e9, 0x03e9, 0x03eb, 0x03eb, 0x03ed, 0x03ed, 0x03ef, 0x03ef, 0x03ba, 0x03c1, 0x03f2, 0x03f3, 0x03b8, 0x03b5, 0x03f6, 0x03f8, 0x03f8, 0x03f2, 0x03fb, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff }}; static UnicodeCaseTableVector caseTable04 = {{ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f, 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046b, 0x046b, 0x046d, 0x046d, 0x046f, 0x046f, 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477, 0x0479, 0x0479, 0x047b, 0x047b, 0x047d, 0x047d, 0x047f, 0x047f, 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048b, 0x048b, 0x048d, 0x048d, 0x048f, 0x048f, 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049b, 0x049b, 0x049d, 0x049d, 0x049f, 0x049f, 0x04a1, 0x04a1, 0x04a3, 0x04a3, 0x04a5, 0x04a5, 0x04a7, 0x04a7, 0x04a9, 0x04a9, 0x04ab, 0x04ab, 0x04ad, 0x04ad, 0x04af, 0x04af, 0x04b1, 0x04b1, 0x04b3, 0x04b3, 0x04b5, 0x04b5, 0x04b7, 0x04b7, 0x04b9, 0x04b9, 0x04bb, 0x04bb, 0x04bd, 0x04bd, 0x04bf, 0x04bf, 0x04c0, 0x04c2, 0x04c2, 0x04c4, 0x04c4, 0x04c6, 0x04c6, 0x04c8, 0x04c8, 0x04ca, 0x04ca, 0x04cc, 0x04cc, 0x04ce, 0x04ce, 0x04cf, 0x04d1, 0x04d1, 0x04d3, 0x04d3, 0x04d5, 0x04d5, 0x04d7, 0x04d7, 0x04d9, 0x04d9, 0x04db, 0x04db, 0x04dd, 0x04dd, 0x04df, 0x04df, 0x04e1, 0x04e1, 0x04e3, 0x04e3, 0x04e5, 0x04e5, 0x04e7, 0x04e7, 0x04e9, 0x04e9, 0x04eb, 0x04eb, 0x04ed, 0x04ed, 0x04ef, 0x04ef, 0x04f1, 0x04f1, 0x04f3, 0x04f3, 0x04f5, 0x04f5, 0x04f6, 0x04f7, 0x04f9, 0x04f9, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff }}; static UnicodeCaseTableVector caseTable05 = {{ 0x0501, 0x0501, 0x0503, 0x0503, 0x0505, 0x0505, 0x0507, 0x0507, 0x0509, 0x0509, 0x050b, 0x050b, 0x050d, 0x050d, 0x050f, 0x050f, 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, 0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f, 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f, 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f, 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, 0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f, 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f, 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, 0x0588, 0x0589, 0x058a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f, 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059a, 0x059b, 0x059c, 0x059d, 0x059e, 0x059f, 0x05a0, 0x05a1, 0x05a2, 0x05a3, 0x05a4, 0x05a5, 0x05a6, 0x05a7, 0x05a8, 0x05a9, 0x05aa, 0x05ab, 0x05ac, 0x05ad, 0x05ae, 0x05af, 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05ba, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05c4, 0x05c5, 0x05c6, 0x05c7, 0x05c8, 0x05c9, 0x05ca, 0x05cb, 0x05cc, 0x05cd, 0x05ce, 0x05cf, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x05eb, 0x05ec, 0x05ed, 0x05ee, 0x05ef, 0x05f0, 0x05f1, 0x05f2, 0x05f3, 0x05f4, 0x05f5, 0x05f6, 0x05f7, 0x05f8, 0x05f9, 0x05fa, 0x05fb, 0x05fc, 0x05fd, 0x05fe, 0x05ff }}; static UnicodeCaseTableVector caseTable1e = {{ 0x1e01, 0x1e01, 0x1e03, 0x1e03, 0x1e05, 0x1e05, 0x1e07, 0x1e07, 0x1e09, 0x1e09, 0x1e0b, 0x1e0b, 0x1e0d, 0x1e0d, 0x1e0f, 0x1e0f, 0x1e11, 0x1e11, 0x1e13, 0x1e13, 0x1e15, 0x1e15, 0x1e17, 0x1e17, 0x1e19, 0x1e19, 0x1e1b, 0x1e1b, 0x1e1d, 0x1e1d, 0x1e1f, 0x1e1f, 0x1e21, 0x1e21, 0x1e23, 0x1e23, 0x1e25, 0x1e25, 0x1e27, 0x1e27, 0x1e29, 0x1e29, 0x1e2b, 0x1e2b, 0x1e2d, 0x1e2d, 0x1e2f, 0x1e2f, 0x1e31, 0x1e31, 0x1e33, 0x1e33, 0x1e35, 0x1e35, 0x1e37, 0x1e37, 0x1e39, 0x1e39, 0x1e3b, 0x1e3b, 0x1e3d, 0x1e3d, 0x1e3f, 0x1e3f, 0x1e41, 0x1e41, 0x1e43, 0x1e43, 0x1e45, 0x1e45, 0x1e47, 0x1e47, 0x1e49, 0x1e49, 0x1e4b, 0x1e4b, 0x1e4d, 0x1e4d, 0x1e4f, 0x1e4f, 0x1e51, 0x1e51, 0x1e53, 0x1e53, 0x1e55, 0x1e55, 0x1e57, 0x1e57, 0x1e59, 0x1e59, 0x1e5b, 0x1e5b, 0x1e5d, 0x1e5d, 0x1e5f, 0x1e5f, 0x1e61, 0x1e61, 0x1e63, 0x1e63, 0x1e65, 0x1e65, 0x1e67, 0x1e67, 0x1e69, 0x1e69, 0x1e6b, 0x1e6b, 0x1e6d, 0x1e6d, 0x1e6f, 0x1e6f, 0x1e71, 0x1e71, 0x1e73, 0x1e73, 0x1e75, 0x1e75, 0x1e77, 0x1e77, 0x1e79, 0x1e79, 0x1e7b, 0x1e7b, 0x1e7d, 0x1e7d, 0x1e7f, 0x1e7f, 0x1e81, 0x1e81, 0x1e83, 0x1e83, 0x1e85, 0x1e85, 0x1e87, 0x1e87, 0x1e89, 0x1e89, 0x1e8b, 0x1e8b, 0x1e8d, 0x1e8d, 0x1e8f, 0x1e8f, 0x1e91, 0x1e91, 0x1e93, 0x1e93, 0x1e95, 0x1e95, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e61, 0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea1, 0x1ea1, 0x1ea3, 0x1ea3, 0x1ea5, 0x1ea5, 0x1ea7, 0x1ea7, 0x1ea9, 0x1ea9, 0x1eab, 0x1eab, 0x1ead, 0x1ead, 0x1eaf, 0x1eaf, 0x1eb1, 0x1eb1, 0x1eb3, 0x1eb3, 0x1eb5, 0x1eb5, 0x1eb7, 0x1eb7, 0x1eb9, 0x1eb9, 0x1ebb, 0x1ebb, 0x1ebd, 0x1ebd, 0x1ebf, 0x1ebf, 0x1ec1, 0x1ec1, 0x1ec3, 0x1ec3, 0x1ec5, 0x1ec5, 0x1ec7, 0x1ec7, 0x1ec9, 0x1ec9, 0x1ecb, 0x1ecb, 0x1ecd, 0x1ecd, 0x1ecf, 0x1ecf, 0x1ed1, 0x1ed1, 0x1ed3, 0x1ed3, 0x1ed5, 0x1ed5, 0x1ed7, 0x1ed7, 0x1ed9, 0x1ed9, 0x1edb, 0x1edb, 0x1edd, 0x1edd, 0x1edf, 0x1edf, 0x1ee1, 0x1ee1, 0x1ee3, 0x1ee3, 0x1ee5, 0x1ee5, 0x1ee7, 0x1ee7, 0x1ee9, 0x1ee9, 0x1eeb, 0x1eeb, 0x1eed, 0x1eed, 0x1eef, 0x1eef, 0x1ef1, 0x1ef1, 0x1ef3, 0x1ef3, 0x1ef5, 0x1ef5, 0x1ef7, 0x1ef7, 0x1ef9, 0x1ef9, 0x1efa, 0x1efb, 0x1efc, 0x1efd, 0x1efe, 0x1eff }}; static UnicodeCaseTableVector caseTable1f = {{ 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f16, 0x1f17, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f1e, 0x1f1f, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f46, 0x1f47, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f4e, 0x1f4f, 0x1f50, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57, 0x1f58, 0x1f51, 0x1f5a, 0x1f53, 0x1f5c, 0x1f55, 0x1f5e, 0x1f57, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f70, 0x1f71, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1f76, 0x1f77, 0x1f78, 0x1f79, 0x1f7a, 0x1f7b, 0x1f7c, 0x1f7d, 0x1f7e, 0x1f7f, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb0, 0x1fb1, 0x1fb2, 0x1fb3, 0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1fbd, 0x03b9, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3, 0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd0, 0x1fd1, 0x1fd2, 0x1fd3, 0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe0, 0x1fe1, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe5, 0x1fe6, 0x1fe7, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x1ffd, 0x1ffe, 0x1fff }}; static UnicodeCaseTableVector caseTable21 = {{ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b, 0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b, 0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x03c9, 0x2127, 0x2128, 0x2129, 0x006b, 0x00e5, 0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b, 0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b, 0x215c, 0x215d, 0x215e, 0x215f, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x2189, 0x218a, 0x218b, 0x218c, 0x218d, 0x218e, 0x218f, 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x219a, 0x219b, 0x219c, 0x219d, 0x219e, 0x219f, 0x21a0, 0x21a1, 0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7, 0x21a8, 0x21a9, 0x21aa, 0x21ab, 0x21ac, 0x21ad, 0x21ae, 0x21af, 0x21b0, 0x21b1, 0x21b2, 0x21b3, 0x21b4, 0x21b5, 0x21b6, 0x21b7, 0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc, 0x21bd, 0x21be, 0x21bf, 0x21c0, 0x21c1, 0x21c2, 0x21c3, 0x21c4, 0x21c5, 0x21c6, 0x21c7, 0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21cd, 0x21ce, 0x21cf, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7, 0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df, 0x21e0, 0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7, 0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ee, 0x21ef, 0x21f0, 0x21f1, 0x21f2, 0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7, 0x21f8, 0x21f9, 0x21fa, 0x21fb, 0x21fc, 0x21fd, 0x21fe, 0x21ff }}; static UnicodeCaseTableVector caseTable24 = {{ 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, 0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, 0x2420, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427, 0x2428, 0x2429, 0x242a, 0x242b, 0x242c, 0x242d, 0x242e, 0x242f, 0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437, 0x2438, 0x2439, 0x243a, 0x243b, 0x243c, 0x243d, 0x243e, 0x243f, 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447, 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d, 0x244e, 0x244f, 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457, 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246a, 0x246b, 0x246c, 0x246d, 0x246e, 0x246f, 0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0x2477, 0x2478, 0x2479, 0x247a, 0x247b, 0x247c, 0x247d, 0x247e, 0x247f, 0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487, 0x2488, 0x2489, 0x248a, 0x248b, 0x248c, 0x248d, 0x248e, 0x248f, 0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497, 0x2498, 0x2499, 0x249a, 0x249b, 0x249c, 0x249d, 0x249e, 0x249f, 0x24a0, 0x24a1, 0x24a2, 0x24a3, 0x24a4, 0x24a5, 0x24a6, 0x24a7, 0x24a8, 0x24a9, 0x24aa, 0x24ab, 0x24ac, 0x24ad, 0x24ae, 0x24af, 0x24b0, 0x24b1, 0x24b2, 0x24b3, 0x24b4, 0x24b5, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x24ea, 0x24eb, 0x24ec, 0x24ed, 0x24ee, 0x24ef, 0x24f0, 0x24f1, 0x24f2, 0x24f3, 0x24f4, 0x24f5, 0x24f6, 0x24f7, 0x24f8, 0x24f9, 0x24fa, 0x24fb, 0x24fc, 0x24fd, 0x24fe, 0x24ff }}; static UnicodeCaseTableVector caseTableff = {{ 0xff00, 0xff01, 0xff02, 0xff03, 0xff04, 0xff05, 0xff06, 0xff07, 0xff08, 0xff09, 0xff0a, 0xff0b, 0xff0c, 0xff0d, 0xff0e, 0xff0f, 0xff10, 0xff11, 0xff12, 0xff13, 0xff14, 0xff15, 0xff16, 0xff17, 0xff18, 0xff19, 0xff1a, 0xff1b, 0xff1c, 0xff1d, 0xff1e, 0xff1f, 0xff20, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0xff3b, 0xff3c, 0xff3d, 0xff3e, 0xff3f, 0xff40, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1, 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9, 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1, 0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9, 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1, 0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9, 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1, 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9, 0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1, 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9, 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff }}; static UnicodeCaseTableVector *caseTable[256] = { &caseTable00, &caseTable01, &caseTable02, &caseTable03, &caseTable04, &caseTable05, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &caseTable1e, &caseTable1f, NULL, &caseTable21, NULL, NULL, &caseTable24, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &caseTableff }; static inline char getType(Unicode c) { int i; char type; if (c > 0xffff) { type = 'X'; } else { i = (c >> 8) & 0xff; if ((type = typeTable[i].type) == 'X') { type = typeTable[i].vector[c & 0xff]; } } return type; } GBool unicodeTypeL(Unicode c) { return getType(c) == 'L'; } GBool unicodeTypeR(Unicode c) { return getType(c) == 'R'; } GBool unicodeTypeNum(Unicode 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 == '#'; } Unicode unicodeToUpper(Unicode c) { int i; if (c > 0xffff) { return c; } i = (c >> 8) & 0xff; if (caseTable[i]) { return caseTable[i]->codes[c & 0xff]; } return c; } xpdf-3.04/xpdf/ImageOutputDev.cc0000644000076400007640000001420212341430012016052 0ustar dereknderekn//======================================================================== // // ImageOutputDev.cc // // Copyright 1998-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "config.h" #include "Error.h" #include "GfxState.h" #include "Object.h" #include "Stream.h" #include "ImageOutputDev.h" ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA) { fileRoot = copyString(fileRootA); fileName = (char *)gmalloc((int)strlen(fileRoot) + 20); dumpJPEG = dumpJPEGA; imgNum = 0; ok = gTrue; } ImageOutputDev::~ImageOutputDev() { gfree(fileName); gfree(fileRoot); } 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, double xStep, double yStep) { // do nothing -- this avoids the potentially slow loop in Gfx.cc } void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { FILE *f; char buf[4096]; int size, n, i; // dump JPEG file if (dumpJPEG && str->getKind() == strDCT && !inlineImg) { // open the image file sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum); ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); return; } // initialize stream str = ((DCTStream *)str)->getRawStream(); str->reset(); // copy the stream while ((n = str->getBlock(buf, sizeof(buf))) > 0) { fwrite(buf, 1, n, f); } str->close(); fclose(f); // dump PBM file } else { // open the image file and write the PBM header sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum); ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); return; } fprintf(f, "P4\n"); fprintf(f, "%d %d\n", width, height); // initialize stream str->reset(); // copy the stream size = height * ((width + 7) / 8); 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(); fclose(f); } } void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate) { FILE *f; ImageStream *imgStr; Guchar *p; GfxRGB rgb; int x, y; char buf[4096]; int size, n, i; // dump JPEG file if (dumpJPEG && str->getKind() == strDCT && (colorMap->getNumPixelComps() == 1 || colorMap->getNumPixelComps() == 3) && !inlineImg) { // open the image file sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum); ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); return; } // initialize stream str = ((DCTStream *)str)->getRawStream(); str->reset(); // copy the stream while ((n = str->getBlock(buf, sizeof(buf))) > 0) { fwrite(buf, 1, n, f); } str->close(); fclose(f); // dump PBM file } else if (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1) { // open the image file and write the PBM header sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum); ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); return; } fprintf(f, "P4\n"); fprintf(f, "%d %d\n", width, height); // initialize stream str->reset(); // copy the stream size = height * ((width + 7) / 8); 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(); fclose(f); // dump PPM file } else { // open the image file and write the PPM header sprintf(fileName, "%s-%04d.ppm", fileRoot, imgNum); ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); return; } fprintf(f, "P6\n"); fprintf(f, "%d %d\n", width, height); fprintf(f, "255\n"); // initialize stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // for each line... for (y = 0; y < height; ++y) { // write the line if ((p = imgStr->getLine())) { for (x = 0; x < width; ++x) { colorMap->getRGB(p, &rgb); fputc(colToByte(rgb.r), f); fputc(colToByte(rgb.g), f); fputc(colToByte(rgb.b), f); p += colorMap->getNumPixelComps(); } } else { for (x = 0; x < width; ++x) { fputc(0, f); fputc(0, f); fputc(0, f); } } } 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); } xpdf-3.04/xpdf/Makefile.in0000644000076400007640000003270712341430012014720 0ustar dereknderekn#======================================================================== # # Xpdf Makefile # # Copyright 1996-2003 Glyph & Cog, LLC # #======================================================================== SHELL = /bin/sh prefix = @prefix@ srcdir = @srcdir@ VPATH = @srcdir@ GOOSRCDIR = $(srcdir)/../goo GOOLIBDIR = ../goo FOFISRCDIR = $(srcdir)/../fofi FOFILIBDIR = ../fofi SPLASHSRCDIR = $(srcdir)/../splash SPLASHLIBDIR = ../splash CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(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@ 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@ @EXTRA_LIBS@ \ -L$(FOFILIBDIR) -lfofi \ -L$(GOOLIBDIR) -lGoo CXX = @CXX@ LIBPREFIX = @LIBPREFIX@ EXE = @EXE@ #------------------------------------------------------------------------ .SUFFIXES: .cc .cc.o: $(CXX) $(CXXFLAGS) -c $< #------------------------------------------------------------------------ CXX_SRC = \ $(srcdir)/AcroForm.cc \ $(srcdir)/Annot.cc \ $(srcdir)/Array.cc \ $(srcdir)/BuiltinFont.cc \ $(srcdir)/BuiltinFontTables.cc \ $(srcdir)/CMap.cc \ $(srcdir)/Catalog.cc \ $(srcdir)/CharCodeToUnicode.cc \ $(srcdir)/CoreOutputDev.cc \ $(srcdir)/Decrypt.cc \ $(srcdir)/Dict.cc \ $(srcdir)/Error.cc \ $(srcdir)/FontEncodingTables.cc \ $(srcdir)/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 \ $(srcdir)/JPXStream.cc \ $(srcdir)/Lexer.cc \ $(srcdir)/Link.cc \ $(srcdir)/NameToCharCode.cc \ $(srcdir)/Object.cc \ $(srcdir)/OptionalContent.cc \ $(srcdir)/Outline.cc \ $(srcdir)/OutputDev.cc \ $(srcdir)/PDFCore.cc \ $(srcdir)/PDFDoc.cc \ $(srcdir)/PDFDocEncoding.cc \ $(srcdir)/PSOutputDev.cc \ $(srcdir)/PSTokenizer.cc \ $(srcdir)/Page.cc \ $(srcdir)/Parser.cc \ $(srcdir)/PreScanOutputDev.cc \ $(srcdir)/SecurityHandler.cc \ $(srcdir)/SplashOutputDev.cc \ $(srcdir)/Stream.cc \ $(srcdir)/TextOutputDev.cc \ $(srcdir)/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) 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o xpdf$(EXE) $(XPDF_OBJS) $(XPDF_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftops$(EXE) $(PDFTOPS_OBJS) \ $(PDFTOPS_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftotext$(EXE) $(PDFTOTEXT_OBJS) \ $(PDFTOTEXT_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdfinfo$(EXE) $(PDFINFO_OBJS) \ $(PDFINFO_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdffonts$(EXE) $(PDFFONTS_OBJS) \ $(PDFFONTS_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdfdetach$(EXE) $(PDFDETACH_OBJS) \ $(PDFDETACH_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftoppm$(EXE) $(PDFTOPPM_OBJS) \ $(PDFTOPPM_LIBS) #------------------------------------------------------------------------ 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 $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdfimages$(EXE) $(PDFIMAGES_OBJS) \ $(PDFIMAGES_LIBS) #------------------------------------------------------------------------ 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) #------------------------------------------------------------------------ depend: $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep -include Makefile.dep xpdf-3.04/xpdf/JArithmeticDecoder.cc0000644000076400007640000002005512341430012016644 0ustar dereknderekn//======================================================================== // // JArithmeticDecoder.cc // // Copyright 2002-2004 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "Object.h" #include "Stream.h" #include "JArithmeticDecoder.h" //------------------------------------------------------------------------ // JArithmeticDecoderStates //------------------------------------------------------------------------ JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) { contextSize = contextSizeA; cxTab = (Guchar *)gmallocn(contextSize, sizeof(Guchar)); reset(); } JArithmeticDecoderStats::~JArithmeticDecoderStats() { gfree(cxTab); } JArithmeticDecoderStats *JArithmeticDecoderStats::copy() { JArithmeticDecoderStats *stats; stats = new JArithmeticDecoderStats(contextSize); memcpy(stats->cxTab, cxTab, contextSize); return stats; } void JArithmeticDecoderStats::reset() { memset(cxTab, 0, contextSize); } void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) { memcpy(cxTab, stats->cxTab, contextSize); } void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) { cxTab[cx] = (i << 1) + mps; } //------------------------------------------------------------------------ // JArithmeticDecoder //------------------------------------------------------------------------ Guint JArithmeticDecoder::qeTab[47] = { 0x56010000, 0x34010000, 0x18010000, 0x0AC10000, 0x05210000, 0x02210000, 0x56010000, 0x54010000, 0x48010000, 0x38010000, 0x30010000, 0x24010000, 0x1C010000, 0x16010000, 0x56010000, 0x54010000, 0x51010000, 0x48010000, 0x38010000, 0x34010000, 0x30010000, 0x28010000, 0x24010000, 0x22010000, 0x1C010000, 0x18010000, 0x16010000, 0x14010000, 0x12010000, 0x11010000, 0x0AC10000, 0x09C10000, 0x08A10000, 0x05210000, 0x04410000, 0x02A10000, 0x02210000, 0x01410000, 0x01110000, 0x00850000, 0x00490000, 0x00250000, 0x00150000, 0x00090000, 0x00050000, 0x00010000, 0x56010000 }; int JArithmeticDecoder::nmpsTab[47] = { 1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46 }; int JArithmeticDecoder::nlpsTab[47] = { 1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46 }; int JArithmeticDecoder::switchTab[47] = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; JArithmeticDecoder::JArithmeticDecoder() { str = NULL; dataLen = 0; limitStream = gFalse; 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; } } ++nBytesRead; return (Guint)str->getChar() & 0xff; } JArithmeticDecoder::~JArithmeticDecoder() { cleanup(); } void JArithmeticDecoder::start() { buf0 = readByte(); buf1 = readByte(); // INITDEC c = (buf0 ^ 0xff) << 16; byteIn(); c <<= 7; ct -= 7; a = 0x80000000; } void JArithmeticDecoder::restart(int dataLenA) { Guint cAdd; GBool prevFF; int k, nBits; if (dataLen >= 0) { dataLen = dataLenA; } else if (dataLen == -1) { dataLen = dataLenA; buf1 = readByte(); } else { k = (-dataLen - 1) * 8 - ct; dataLen = dataLenA; cAdd = 0; prevFF = gFalse; while (k > 0) { buf0 = readByte(); if (prevFF) { cAdd += 0xfe00 - (buf0 << 9); nBits = 7; } else { cAdd += 0xff00 - (buf0 << 8); nBits = 8; } prevFF = buf0 == 0xff; if (k > nBits) { cAdd <<= nBits; k -= nBits; } else { cAdd <<= k; ct = nBits - k; k = 0; } } c += cAdd; buf1 = readByte(); } } 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) { readBuf = -1; readBuf = readByte(); } } } int JArithmeticDecoder::decodeBit(Guint context, JArithmeticDecoderStats *stats) { int bit; Guint qe; int iCX, mpsCX; iCX = stats->cxTab[context] >> 1; mpsCX = stats->cxTab[context] & 1; qe = qeTab[iCX]; a -= qe; if (c < a) { if (a & 0x80000000) { bit = mpsCX; } else { // MPS_EXCHANGE if (a < qe) { bit = 1 - mpsCX; if (switchTab[iCX]) { stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX); } else { stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX; } } else { bit = mpsCX; stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX; } // RENORMD do { if (ct == 0) { byteIn(); } a <<= 1; c <<= 1; --ct; } while (!(a & 0x80000000)); } } else { c -= a; // LPS_EXCHANGE if (a < qe) { bit = mpsCX; stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX; } else { bit = 1 - mpsCX; if (switchTab[iCX]) { stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX); } else { stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX; } } a = qe; // RENORMD do { if (ct == 0) { byteIn(); } a <<= 1; c <<= 1; --ct; } while (!(a & 0x80000000)); } return bit; } int JArithmeticDecoder::decodeByte(Guint context, JArithmeticDecoderStats *stats) { int byte; int i; byte = 0; for (i = 0; i < 8; ++i) { byte = (byte << 1) | decodeBit(context, stats); } return byte; } GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) { int s; Guint v; int i; prev = 1; s = decodeIntBit(stats); if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { v = 0; for (i = 0; i < 32; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 4436; } else { v = 0; for (i = 0; i < 12; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 340; } } else { v = 0; for (i = 0; i < 8; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 84; } } else { v = 0; for (i = 0; i < 6; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 20; } } else { v = decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); v += 4; } } else { v = decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); } if (s) { if (v == 0) { return gFalse; } *x = -(int)v; } else { *x = (int)v; } return gTrue; } int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) { int bit; bit = decodeBit(prev, stats); if (prev < 0x100) { prev = (prev << 1) | bit; } else { prev = (((prev << 1) | bit) & 0x1ff) | 0x100; } return bit; } Guint JArithmeticDecoder::decodeIAID(Guint codeLen, JArithmeticDecoderStats *stats) { Guint i; int bit; prev = 1; for (i = 0; i < codeLen; ++i) { bit = decodeBit(prev, stats); prev = (prev << 1) | bit; } return prev - (1 << codeLen); } void JArithmeticDecoder::byteIn() { if (buf0 == 0xff) { if (buf1 > 0x8f) { if (limitStream) { buf0 = buf1; buf1 = readByte(); c = c + 0xff00 - (buf0 << 8); } ct = 8; } else { buf0 = buf1; buf1 = readByte(); c = c + 0xfe00 - (buf0 << 9); ct = 7; } } else { buf0 = buf1; buf1 = readByte(); c = c + 0xff00 - (buf0 << 8); ct = 8; } } xpdf-3.04/xpdf/PDFCore.cc0000644000076400007640000015654312341430012014411 0ustar dereknderekn//======================================================================== // // PDFCore.cc // // Copyright 2004-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "GString.h" #include "GList.h" #include "GlobalParams.h" #include "Splash.h" #include "SplashBitmap.h" #include "SplashPattern.h" #include "SplashPath.h" #include "Error.h" #include "ErrorCodes.h" #include "PDFDoc.h" #include "Link.h" #include "TextOutputDev.h" #include "CoreOutputDev.h" #include "PDFCore.h" //------------------------------------------------------------------------ // PDFCorePage //------------------------------------------------------------------------ PDFCorePage::PDFCorePage(int pageA, int wA, int hA, int tileWA, int tileHA) { page = pageA; tiles = new GList(); w = wA; h = hA; tileW = tileWA; tileH = tileHA; links = NULL; text = NULL; } PDFCorePage::~PDFCorePage() { deleteGList(tiles, PDFCoreTile); if (links) { delete links; } if (text) { delete text; } } //------------------------------------------------------------------------ // PDFCoreTile //------------------------------------------------------------------------ PDFCoreTile::PDFCoreTile(int xDestA, int yDestA) { xMin = 0; yMin = 0; xMax = 0; yMax = 0; xDest = xDestA; yDest = yDestA; bitmap = NULL; } PDFCoreTile::~PDFCoreTile() { if (bitmap) { delete bitmap; } } //------------------------------------------------------------------------ // PDFCore //------------------------------------------------------------------------ PDFCore::PDFCore(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool incrementalUpdate) { int i; doc = NULL; continuousMode = globalParams->getContinuousView(); drawAreaWidth = drawAreaHeight = 0; maxPageW = totalDocH = 0; pageY = NULL; topPage = 0; midPage = 0; scrollX = scrollY = 0; zoom = defZoom; dpi = 0; rotate = 0; selectPage = 0; selectULX = selectLRX = 0; selectULY = selectLRY = 0; dragging = gFalse; lastDragLeft = lastDragTop = gTrue; selectXorColor[0] = selectXorColor[1] = selectXorColor[2] = reverseVideoA ? 0xff : 0x00; splashColorXor(selectXorColor, paperColorA); historyCur = pdfHistorySize - 1; historyBLen = historyFLen = 0; for (i = 0; i < pdfHistorySize; ++i) { history[i].fileName = NULL; } pages = new GList(); curTile = NULL; splashColorCopy(paperColor, paperColorA); out = new CoreOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA, incrementalUpdate, &redrawCbk, this); out->startDoc(NULL); } PDFCore::~PDFCore() { int i; if (doc) { delete doc; } for (i = 0; i < pdfHistorySize; ++i) { if (history[i].fileName) { #ifdef _WIN32 delete[] history[i].fileName; #else delete history[i].fileName; #endif } } gfree(pageY); deleteGList(pages, PDFCorePage); delete out; } int PDFCore::loadFile(GString *fileName, GString *ownerPassword, GString *userPassword) { int err; setBusyCursor(gTrue); err = loadFile2(new PDFDoc(fileName->copy(), ownerPassword, userPassword, this)); setBusyCursor(gFalse); return err; } #ifdef _WIN32 int PDFCore::loadFile(wchar_t *fileName, int fileNameLen, GString *ownerPassword, GString *userPassword) { int err; setBusyCursor(gTrue); err = loadFile2(new PDFDoc(fileName, fileNameLen, ownerPassword, userPassword, this)); setBusyCursor(gFalse); return err; } #endif int PDFCore::loadFile(BaseStream *stream, GString *ownerPassword, GString *userPassword) { int err; setBusyCursor(gTrue); err = loadFile2(new PDFDoc(stream, ownerPassword, userPassword, this)); setBusyCursor(gFalse); return err; } void PDFCore::loadDoc(PDFDoc *docA) { setBusyCursor(gTrue); loadFile2(docA); setBusyCursor(gFalse); } int PDFCore::loadFile2(PDFDoc *newDoc) { int err; double w, h, t; int i; // open the PDF file if (!newDoc->isOk()) { err = newDoc->getErrorCode(); delete newDoc; return err; } // replace old document if (doc) { delete doc; } doc = newDoc; if (out) { out->startDoc(doc->getXRef()); } // nothing displayed yet topPage = -99; midPage = -99; while (pages->getLength() > 0) { delete (PDFCorePage *)pages->del(0); } // compute the max unscaled page size maxUnscaledPageW = maxUnscaledPageH = 0; for (i = 1; i <= doc->getNumPages(); ++i) { w = doc->getPageCropWidth(i); h = doc->getPageCropHeight(i); if (doc->getPageRotate(i) == 90 || doc->getPageRotate(i) == 270) { t = w; w = h; h = t; } if (w > maxUnscaledPageW) { maxUnscaledPageW = w; } if (h > maxUnscaledPageH) { maxUnscaledPageH = h; } } return errNone; } void PDFCore::clear() { if (!doc) { return; } // no document delete doc; doc = NULL; out->clear(); // no page displayed topPage = -99; midPage = -99; while (pages->getLength() > 0) { delete (PDFCorePage *)pages->del(0); } // redraw scrollX = scrollY = 0; redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, gTrue); updateScrollbars(); } PDFDoc *PDFCore::takeDoc(GBool redraw) { PDFDoc *docA; if (!doc) { return NULL; } // no document docA = doc; doc = NULL; out->clear(); // no page displayed topPage = -99; midPage = -99; while (pages->getLength() > 0) { delete (PDFCorePage *)pages->del(0); } // redraw scrollX = scrollY = 0; if (redraw) { redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, gTrue); updateScrollbars(); } return docA; } void PDFCore::displayPage(int topPageA, double zoomA, int rotateA, GBool scrollToTop, GBool addToHist) { int scrollXA, scrollYA; scrollXA = scrollX; if (continuousMode) { scrollYA = -1; } else if (scrollToTop) { scrollYA = 0; } else { scrollYA = scrollY; } if (zoomA != zoom) { scrollXA = 0; scrollYA = continuousMode ? -1 : 0; } dragging = gFalse; lastDragLeft = lastDragTop = gTrue; update(topPageA, scrollXA, scrollYA, zoomA, rotateA, gTrue, addToHist, gTrue); } void PDFCore::displayDest(LinkDest *dest, double zoomA, int rotateA, GBool addToHist) { Ref pageRef; int topPageA; int dx, dy, scrollXA, scrollYA; if (dest->isPageRef()) { pageRef = dest->getPageRef(); topPageA = doc->findPage(pageRef.num, pageRef.gen); } else { topPageA = dest->getPageNum(); } if (topPageA <= 0 || topPageA > doc->getNumPages()) { topPageA = 1; } scrollXA = scrollX; scrollYA = continuousMode ? -1 : scrollY; switch (dest->getKind()) { case destXYZ: cvtUserToDev(topPageA, dest->getLeft(), dest->getTop(), &dx, &dy); scrollXA = dest->getChangeLeft() ? dx : scrollX; if (continuousMode) { if (topPage <= 0) { scrollYA = -1; } else if (dest->getChangeTop()) { scrollYA = pageY[topPageA - 1] + dy; } else { scrollYA = pageY[topPageA - 1] + (scrollY - pageY[topPage - 1]); } } else { if (dest->getChangeTop()) { scrollYA = dy; } else if (topPage > 0) { scrollYA = scrollY; } else { scrollYA = 0; } } //~ this doesn't currently handle the zoom parameter update(topPageA, scrollXA, scrollYA, zoom, rotate, gFalse, addToHist && topPageA != topPage, gTrue); break; case destFit: case destFitB: scrollXA = 0; scrollYA = continuousMode ? -1 : 0; update(topPageA, scrollXA, scrollYA, zoomPage, rotate, gFalse, addToHist && topPageA != topPage, gTrue); break; case destFitH: case destFitBH: //~ do fit: need a function similar to zoomToRect which will //~ accept an absolute top coordinate (rather than centering) scrollXA = 0; cvtUserToDev(topPageA, 0, dest->getTop(), &dx, &dy); if (continuousMode) { if (topPage <= 0) { scrollYA = -1; } else if (dest->getChangeTop()) { scrollYA = pageY[topPageA - 1] + dy; } else { scrollYA = pageY[topPageA - 1] + (scrollY - pageY[topPage - 1]); } } else { if (dest->getChangeTop()) { scrollYA = dy; } else if (topPage > 0) { scrollYA = scrollY; } else { scrollYA = 0; } } update(topPageA, scrollXA, scrollYA, zoom, rotate, gFalse, addToHist && topPageA != topPage, gTrue); break; case destFitV: case destFitBV: //~ do fit: need a function similar to zoomToRect which will //~ accept an absolute left coordinate (rather than centering) if (dest->getChangeLeft()) { cvtUserToDev(topPageA, dest->getLeft(), 0, &dx, &dy); scrollXA = dx; } else { scrollXA = scrollX; } scrollYA = continuousMode ? -1 : 0; update(topPageA, scrollXA, scrollYA, zoom, rotate, gFalse, addToHist && topPageA != topPage, gTrue); break; case destFitR: zoomToRect(topPageA, dest->getLeft(), dest->getTop(), dest->getRight(), dest->getBottom()); break; } } void PDFCore::update(int topPageA, int scrollXA, int scrollYA, double zoomA, int rotateA, GBool force, GBool addToHist, GBool adjustScrollX) { double hDPI, vDPI, dpiA, uw, uh, ut; int w, h, t, x0, x1, y0, y1, x, y; int rot; int pg0, pg1; PDFCoreTile *tile; PDFCorePage *page; PDFHistory *hist; GBool needUpdate; int i, j; // check for document and valid page number if (!doc) { // save the new settings zoom = zoomA; rotate = rotateA; return; } if (topPageA <= 0 || topPageA > doc->getNumPages()) { return; } needUpdate = gFalse; // check for changes to the PDF file if ((force || (!continuousMode && topPage != topPageA)) && doc->getFileName() && checkForNewFile()) { if (loadFile(doc->getFileName()) == errNone) { if (topPageA > doc->getNumPages()) { topPageA = doc->getNumPages(); } needUpdate = gTrue; } } // compute the DPI if (continuousMode) { uw = maxUnscaledPageW; uh = maxUnscaledPageH; rot = rotateA; } else { uw = doc->getPageCropWidth(topPageA); uh = doc->getPageCropHeight(topPageA); rot = rotateA + doc->getPageRotate(topPageA); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } } if (rot == 90 || rot == 270) { ut = uw; uw = uh; uh = ut; } if (zoomA == zoomPage) { hDPI = (drawAreaWidth / uw) * 72; if (continuousMode) { vDPI = ((drawAreaHeight - continuousModePageSpacing) / uh) * 72; } else { vDPI = (drawAreaHeight / uh) * 72; } dpiA = (hDPI < vDPI) ? hDPI : vDPI; } else if (zoomA == zoomWidth) { dpiA = (drawAreaWidth / uw) * 72; } else { dpiA = 0.01 * zoomA * 72; } // this can happen if the window hasn't been sized yet if (dpiA <= 0) { dpiA = 1; } // if the display properties have changed, create a new PDFCorePage // object if (force || pages->getLength() == 0 || (!continuousMode && topPageA != topPage) || fabs(zoomA - zoom) > 1e-8 || fabs(dpiA - dpi) > 1e-8 || rotateA != rotate) { needUpdate = gTrue; setSelection(0, 0, 0, 0, 0); while (pages->getLength() > 0) { delete (PDFCorePage *)pages->del(0); } zoom = zoomA; rotate = rotateA; dpi = dpiA; if (continuousMode) { maxPageW = totalDocH = 0; pageY = (int *)greallocn(pageY, doc->getNumPages(), sizeof(int)); for (i = 1; i <= doc->getNumPages(); ++i) { pageY[i-1] = totalDocH; w = (int)((doc->getPageCropWidth(i) * dpi) / 72 + 0.5); h = (int)((doc->getPageCropHeight(i) * dpi) / 72 + 0.5); rot = rotate + doc->getPageRotate(i); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } if (rot == 90 || rot == 270) { t = w; w = h; h = t; } if (w > maxPageW) { maxPageW = w; } totalDocH += h; if (i < doc->getNumPages()) { totalDocH += continuousModePageSpacing; } } } else { rot = rotate + doc->getPageRotate(topPageA); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } addPage(topPageA, rot); } } else { // erase the selection if (selectULX != selectLRX && selectULY != selectLRY) { xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY, new SplashSolidColor(selectXorColor)); } } if (continuousMode) { page = NULL; // make gcc happy } else { page = (PDFCorePage *)pages->get(0); } topPage = topPageA; midPage = topPage; // adjust the scroll position scrollX = scrollXA; if (continuousMode && scrollYA < 0) { scrollY = pageY[topPage - 1]; } else { scrollY = scrollYA; } if (continuousMode && adjustScrollX) { rot = rotate + doc->getPageRotate(topPage); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } if (rot == 90 || rot == 270) { w = (int)((doc->getPageCropHeight(topPage) * dpi) / 72 + 0.5); } else { w = (int)((doc->getPageCropWidth(topPage) * dpi) / 72 + 0.5); } if (scrollX < (maxPageW - w) / 2) { scrollX = (maxPageW - w) / 2; } } w = continuousMode ? maxPageW : page->w; if (scrollX > w - drawAreaWidth) { scrollX = w - drawAreaWidth; } if (scrollX < 0) { scrollX = 0; } h = continuousMode ? totalDocH : page->h; if (scrollY > h - drawAreaHeight) { scrollY = h - drawAreaHeight; } if (scrollY < 0) { scrollY = 0; } // find topPage, and the first and last pages to be rasterized if (continuousMode) { //~ should use a binary search for (i = 2; i <= doc->getNumPages(); ++i) { if (pageY[i-1] > scrollY - drawAreaHeight / 2) { break; } } pg0 = i - 1; for (i = pg0 + 1; i <= doc->getNumPages(); ++i) { if (pageY[i-1] > scrollY) { break; } } topPage = i - 1; for (i = topPage + 1; i <= doc->getNumPages(); ++i) { if (pageY[i-1] > scrollY + drawAreaHeight / 2) { break; } } midPage = i - 1; for (i = midPage + 1; i <= doc->getNumPages(); ++i) { if (pageY[i-1] > scrollY + drawAreaHeight + drawAreaHeight / 2) { break; } } pg1 = i - 1; // delete pages that are no longer needed and insert new pages // objects that are needed while (pages->getLength() > 0 && ((PDFCorePage *)pages->get(0))->page < pg0) { delete (PDFCorePage *)pages->del(0); } i = pages->getLength() - 1; while (i > 0 && ((PDFCorePage *)pages->get(i))->page > pg1) { delete (PDFCorePage *)pages->del(i--); } j = pages->getLength() > 0 ? ((PDFCorePage *)pages->get(0))->page - 1 : pg1; for (i = pg0; i <= j; ++i) { rot = rotate + doc->getPageRotate(i); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } addPage(i, rot); } j = ((PDFCorePage *)pages->get(pages->getLength() - 1))->page; for (i = j + 1; i <= pg1; ++i) { rot = rotate + doc->getPageRotate(i); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } addPage(i, rot); } } else { pg0 = pg1 = topPage; } // delete tiles that are no longer needed for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); j = 0; while (j < page->tiles->getLength()) { tile = (PDFCoreTile *)page->tiles->get(j); if (continuousMode) { y0 = pageY[page->page - 1] + tile->yMin; y1 = pageY[page->page - 1] + tile->yMax; } else { y0 = tile->yMin; y1 = tile->yMax; } if (tile->xMax < scrollX - drawAreaWidth / 2 || tile->xMin > scrollX + drawAreaWidth + drawAreaWidth / 2 || y1 < scrollY - drawAreaHeight / 2 || y0 > scrollY + drawAreaHeight + drawAreaHeight / 2) { delete (PDFCoreTile *)page->tiles->del(j); } else { ++j; } } } // update page positions for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); page->xDest = -scrollX; if (continuousMode) { page->yDest = pageY[page->page - 1] - scrollY; } else { page->yDest = -scrollY; } if (continuousMode) { if (page->w < maxPageW) { page->xDest += (maxPageW - page->w) / 2; } if (maxPageW < drawAreaWidth) { page->xDest += (drawAreaWidth - maxPageW) / 2; } } else if (page->w < drawAreaWidth) { page->xDest += (drawAreaWidth - page->w) / 2; } if (continuousMode && totalDocH < drawAreaHeight) { page->yDest += (drawAreaHeight - totalDocH) / 2; } else if (!continuousMode && page->h < drawAreaHeight) { page->yDest += (drawAreaHeight - page->h) / 2; } } // rasterize any new tiles for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); x0 = page->xDest; x1 = x0 + page->w - 1; if (x0 < -drawAreaWidth / 2) { x0 = -drawAreaWidth / 2; } if (x1 > drawAreaWidth + drawAreaWidth / 2) { x1 = drawAreaWidth + drawAreaWidth / 2; } x0 = ((x0 - page->xDest) / page->tileW) * page->tileW; x1 = ((x1 - page->xDest) / page->tileW) * page->tileW; y0 = page->yDest; y1 = y0 + page->h - 1; if (y0 < -drawAreaHeight / 2) { y0 = -drawAreaHeight / 2; } if (y1 > drawAreaHeight + drawAreaHeight / 2) { y1 = drawAreaHeight + drawAreaHeight / 2; } y0 = ((y0 - page->yDest) / page->tileH) * page->tileH; y1 = ((y1 - page->yDest) / page->tileH) * page->tileH; for (y = y0; y <= y1; y += page->tileH) { for (x = x0; x <= x1; x += page->tileW) { needTile(page, x, y); } } } // update tile positions for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); for (j = 0; j < page->tiles->getLength(); ++j) { tile = (PDFCoreTile *)page->tiles->get(j); tile->xDest = tile->xMin - scrollX; if (continuousMode) { tile->yDest = tile->yMin + pageY[page->page - 1] - scrollY; } else { tile->yDest = tile->yMin - scrollY; } if (continuousMode) { if (page->w < maxPageW) { tile->xDest += (maxPageW - page->w) / 2; } if (maxPageW < drawAreaWidth) { tile->xDest += (drawAreaWidth - maxPageW) / 2; } } else if (page->w < drawAreaWidth) { tile->xDest += (drawAreaWidth - page->w) / 2; } if (continuousMode && totalDocH < drawAreaHeight) { tile->yDest += (drawAreaHeight - totalDocH) / 2; } else if (!continuousMode && page->h < drawAreaHeight) { tile->yDest += (drawAreaHeight - page->h) / 2; } } } // redraw the selection if (selectULX != selectLRX && selectULY != selectLRY) { xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY, new SplashSolidColor(selectXorColor)); } // redraw the window redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, needUpdate); updateScrollbars(); // add to history if (addToHist) { if (++historyCur == pdfHistorySize) { historyCur = 0; } hist = &history[historyCur]; if (hist->fileName) { #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; } historyFLen = 0; } } void PDFCore::addPage(int pg, int rot) { PDFCorePage *page; int w, h, t, tileW, tileH, i; w = (int)((doc->getPageCropWidth(pg) * dpi) / 72 + 0.5); h = (int)((doc->getPageCropHeight(pg) * dpi) / 72 + 0.5); if (rot == 90 || rot == 270) { t = w; w = h; h = t; } tileW = 2 * drawAreaWidth; if (tileW < 1500) { tileW = 1500; } if (tileW > w) { // tileW can't be zero -- we end up with div-by-zero problems tileW = w ? w : 1; } tileH = 2 * drawAreaHeight; if (tileH < 1500) { tileH = 1500; } if (tileH > h) { // tileH can't be zero -- we end up with div-by-zero problems tileH = h ? h : 1; } page = new PDFCorePage(pg, w, h, tileW, tileH); for (i = 0; i < pages->getLength() && pg > ((PDFCorePage *)pages->get(i))->page; ++i) ; pages->insert(i, page); } void PDFCore::needTile(PDFCorePage *page, int x, int y) { PDFCoreTile *tile; TextOutputControl textOutCtrl; TextOutputDev *textOut; int xDest, yDest, sliceW, sliceH; int i; for (i = 0; i < page->tiles->getLength(); ++i) { tile = (PDFCoreTile *)page->tiles->get(i); if (x == tile->xMin && y == tile->yMin) { return; } } setBusyCursor(gTrue); sliceW = page->tileW; if (x + sliceW > page->w) { sliceW = page->w - x; } sliceH = page->tileH; if (y + sliceH > page->h) { sliceH = page->h - y; } xDest = x - scrollX; if (continuousMode) { yDest = y + pageY[page->page - 1] - scrollY; } else { yDest = y - scrollY; } if (continuousMode) { if (page->w < maxPageW) { xDest += (maxPageW - page->w) / 2; } if (maxPageW < drawAreaWidth) { xDest += (drawAreaWidth - maxPageW) / 2; } } else if (page->w < drawAreaWidth) { xDest += (drawAreaWidth - page->w) / 2; } if (continuousMode && totalDocH < drawAreaHeight) { yDest += (drawAreaHeight - totalDocH) / 2; } else if (!continuousMode && page->h < drawAreaHeight) { yDest += (drawAreaHeight - page->h) / 2; } curTile = tile = newTile(xDest, yDest); curPage = page; tile->xMin = x; tile->yMin = y; tile->xMax = x + sliceW; tile->yMax = y + sliceH; tile->edges = 0; if (tile->xMin == 0) { tile->edges |= pdfCoreTileLeftEdge; } if (tile->xMax == page->w) { tile->edges |= pdfCoreTileRightEdge; } if (continuousMode) { if (tile->yMin == 0) { tile->edges |= pdfCoreTileTopSpace; if (page->page == 1) { tile->edges |= pdfCoreTileTopEdge; } } if (tile->yMax == page->h) { tile->edges |= pdfCoreTileBottomSpace; if (page->page == doc->getNumPages()) { tile->edges |= pdfCoreTileBottomEdge; } } } else { if (tile->yMin == 0) { tile->edges |= pdfCoreTileTopEdge; } if (tile->yMax == page->h) { tile->edges |= pdfCoreTileBottomEdge; } } doc->displayPageSlice(out, page->page, dpi, dpi, rotate, gFalse, gTrue, gFalse, x, y, sliceW, sliceH); tile->bitmap = out->takeBitmap(); memcpy(tile->ctm, out->getDefCTM(), 6 * sizeof(double)); memcpy(tile->ictm, out->getDefICTM(), 6 * sizeof(double)); if (!page->links) { page->links = doc->getLinks(page->page); } if (!page->text) { 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(); delete textOut; } } page->tiles->append(tile); curTile = NULL; curPage = NULL; setBusyCursor(gFalse); } GBool PDFCore::gotoNextPage(int inc, GBool top) { int pg, scrollYA; if (!doc || doc->getNumPages() == 0 || topPage >= doc->getNumPages()) { return gFalse; } if ((pg = topPage + inc) > doc->getNumPages()) { pg = doc->getNumPages(); } if (continuousMode) { scrollYA = -1; } else if (top) { scrollYA = 0; } else { scrollYA = scrollY; } update(pg, scrollX, scrollYA, zoom, rotate, gFalse, gTrue, gTrue); return gTrue; } GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) { int pg, scrollYA; if (!doc || doc->getNumPages() == 0 || topPage <= 1) { return gFalse; } if ((pg = topPage - dec) < 1) { pg = 1; } if (continuousMode) { scrollYA = -1; } else if (top) { scrollYA = 0; } else if (bottom) { scrollYA = ((PDFCorePage *)pages->get(0))->h - drawAreaHeight; if (scrollYA < 0) { scrollYA = 0; } } else { scrollYA = scrollY; } update(pg, scrollX, scrollYA, zoom, rotate, gFalse, gTrue, gTrue); return gTrue; } GBool PDFCore::gotoNamedDestination(GString *dest) { LinkDest *d; if (!doc) { return gFalse; } if (!(d = doc->findDest(dest))) { return gFalse; } displayDest(d, zoom, rotate, gTrue); delete d; return gTrue; } GBool PDFCore::goForward() { int pg; if (historyFLen == 0) { return gFalse; } if (++historyCur == pdfHistorySize) { historyCur = 0; } --historyFLen; ++historyBLen; if (!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); return gTrue; } GBool PDFCore::goBackward() { int pg; if (historyBLen <= 1) { return gFalse; } if (--historyCur < 0) { historyCur = pdfHistorySize - 1; } --historyBLen; ++historyFLen; 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); return gTrue; } void PDFCore::scrollLeft(int nCols) { scrollTo(scrollX - nCols, scrollY); } void PDFCore::scrollRight(int nCols) { scrollTo(scrollX + nCols, scrollY); } void PDFCore::scrollUp(int nLines) { scrollTo(scrollX, scrollY - nLines); } void PDFCore::scrollUpPrevPage(int nLines) { if (!continuousMode && scrollY == 0) { gotoPrevPage(1, gFalse, gTrue); } else { scrollTo(scrollX, scrollY - nLines); } } void PDFCore::scrollDown(int nLines) { scrollTo(scrollX, scrollY + nLines); } void PDFCore::scrollDownNextPage(int nLines) { if (!continuousMode && scrollY >= ((PDFCorePage *)pages->get(0))->h - drawAreaHeight) { gotoNextPage(1, gTrue); } else { scrollTo(scrollX, scrollY + nLines); } } void PDFCore::scrollPageUp() { if (!continuousMode && scrollY == 0) { gotoPrevPage(1, gFalse, gTrue); } else { scrollTo(scrollX, scrollY - drawAreaHeight); } } void PDFCore::scrollPageDown() { if (!continuousMode && pages->getLength() > 0 && scrollY >= ((PDFCorePage *)pages->get(0))->h - drawAreaHeight) { gotoNextPage(1, gTrue); } else { scrollTo(scrollX, scrollY + drawAreaHeight); } } void PDFCore::scrollTo(int x, int y) { update(topPage, x, y < 0 ? 0 : y, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::scrollToLeftEdge() { update(topPage, 0, scrollY, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::scrollToRightEdge() { PDFCorePage *page; page = (PDFCorePage *)pages->get(0); update(topPage, page->w - drawAreaWidth, scrollY, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::scrollToTopEdge() { int y; y = continuousMode ? pageY[topPage - 1] : 0; update(topPage, scrollX, y, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::scrollToBottomEdge() { PDFCorePage *page; int y, i; for (i = pages->getLength() - 1; i > 0; --i) { page = (PDFCorePage *)pages->get(i); if (page->yDest < drawAreaHeight) { break; } } page = (PDFCorePage *)pages->get(i); if (continuousMode) { y = pageY[page->page - 1] + page->h - drawAreaHeight; } else { y = page->h - drawAreaHeight; } update(topPage, scrollX, y, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::scrollToTopLeft() { int y; y = continuousMode ? pageY[topPage - 1] : 0; update(topPage, 0, y, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::scrollToBottomRight() { PDFCorePage *page; int x, y, i; for (i = pages->getLength() - 1; i > 0; --i) { page = (PDFCorePage *)pages->get(i); if (page->yDest < drawAreaHeight) { break; } } page = (PDFCorePage *)pages->get(i); x = page->w - drawAreaWidth; if (continuousMode) { y = pageY[page->page - 1] + page->h - drawAreaHeight; } else { y = page->h - drawAreaHeight; } update(topPage, x, y, zoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::zoomToRect(int pg, double ulx, double uly, double lrx, double lry) { int x0, y0, x1, y1, u, sx, sy; double rx, ry, newZoom, t; PDFCorePage *p; cvtUserToDev(pg, ulx, uly, &x0, &y0); cvtUserToDev(pg, lrx, lry, &x1, &y1); if (x0 > x1) { u = x0; x0 = x1; x1 = u; } if (y0 > y1) { u = y0; y0 = y1; y1 = u; } rx = (double)drawAreaWidth / (double)(x1 - x0); ry = (double)drawAreaHeight / (double)(y1 - y0); if (rx < ry) { newZoom = rx * (dpi / (0.01 * 72)); sx = (int)(rx * x0); t = (drawAreaHeight * (x1 - x0)) / drawAreaWidth; sy = (int)(rx * (y0 + y1 - t) / 2); if (continuousMode) { if ((p = findPage(pg)) && p->w < maxPageW) { sx += (int)(0.5 * rx * (maxPageW - p->w)); } u = (pg - 1) * continuousModePageSpacing; sy += (int)(rx * (pageY[pg - 1] - u)) + u; } } else { newZoom = ry * (dpi / (0.01 * 72)); t = (drawAreaWidth * (y1 - y0)) / drawAreaHeight; sx = (int)(ry * (x0 + x1 - t) / 2); sy = (int)(ry * y0); if (continuousMode) { if ((p = findPage(pg)) && p->w < maxPageW) { sx += (int)(0.5 * rx * (maxPageW - p->w)); } u = (pg - 1) * continuousModePageSpacing; sy += (int)(ry * (pageY[pg - 1] - u)) + u; } } update(pg, sx, sy, newZoom, rotate, gFalse, gFalse, gFalse); } void PDFCore::zoomCentered(double zoomA) { int sx, sy, rot, hAdjust, vAdjust, i; double dpi1, dpi2, pageW, pageH; PDFCorePage *page; if (zoomA == zoomPage) { if (continuousMode) { pageW = (rotate == 90 || rotate == 270) ? maxUnscaledPageH : maxUnscaledPageW; pageH = (rotate == 90 || rotate == 270) ? maxUnscaledPageW : maxUnscaledPageH; dpi1 = 72.0 * (double)drawAreaWidth / pageW; dpi2 = 72.0 * (double)(drawAreaHeight - continuousModePageSpacing) / pageH; if (dpi2 < dpi1) { dpi1 = dpi2; } } else { // in single-page mode, sx=sy=0 -- so dpi1 is irrelevant dpi1 = dpi; } sx = 0; } else if (zoomA == zoomWidth) { if (continuousMode) { pageW = (rotate == 90 || rotate == 270) ? maxUnscaledPageH : maxUnscaledPageW; } else { rot = rotate + doc->getPageRotate(topPage); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } pageW = (rot == 90 || rot == 270) ? doc->getPageCropHeight(topPage) : doc->getPageCropWidth(topPage); } dpi1 = 72.0 * (double)drawAreaWidth / pageW; sx = 0; } else if (zoomA <= 0) { return; } else { dpi1 = 72.0 * zoomA / 100.0; if ((page = (PDFCorePage *)pages->get(0)) && page->xDest > 0) { hAdjust = page->xDest; } else { hAdjust = 0; } sx = (int)((scrollX - hAdjust + drawAreaWidth / 2) * (dpi1 / dpi)) - drawAreaWidth / 2; if (sx < 0) { sx = 0; } } if (continuousMode) { // we can't just multiply scrollY by dpi1/dpi -- the rounding // errors add up (because the pageY values are integers) -- so // we compute the pageY values at the new zoom level instead sy = 0; for (i = 1; i < topPage; ++i) { rot = rotate + doc->getPageRotate(i); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } if (rot == 90 || rot == 270) { sy += (int)((doc->getPageCropWidth(i) * dpi1) / 72 + 0.5); } else { sy += (int)((doc->getPageCropHeight(i) * dpi1) / 72 + 0.5); } } vAdjust = (topPage - 1) * continuousModePageSpacing; sy = sy + (int)((scrollY - pageY[topPage - 1] + drawAreaHeight / 2) * (dpi1 / dpi)) + vAdjust - drawAreaHeight / 2; } else { sy = (int)((scrollY + drawAreaHeight / 2) * (dpi1 / dpi)) - drawAreaHeight / 2; } update(topPage, sx, sy, zoomA, rotate, gFalse, gFalse, gFalse); } // Zoom so that the current page(s) fill the window width. Maintain // the vertical center. void PDFCore::zoomToCurrentWidth() { double w, maxW, dpi1; int sx, sy, vAdjust, rot, i; // compute the maximum page width of visible pages rot = rotate + doc->getPageRotate(topPage); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } if (rot == 90 || rot == 270) { maxW = doc->getPageCropHeight(topPage); } else { maxW = doc->getPageCropWidth(topPage); } if (continuousMode) { for (i = topPage + 1; i < doc->getNumPages() && pageY[i-1] < scrollY + drawAreaHeight; ++i) { rot = rotate + doc->getPageRotate(i); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } if (rot == 90 || rot == 270) { w = doc->getPageCropHeight(i); } else { w = doc->getPageCropWidth(i); } if (w > maxW) { maxW = w; } } } // compute the resolution dpi1 = (drawAreaWidth / maxW) * 72; // compute the horizontal scroll position if (continuousMode) { sx = ((int)(maxPageW * dpi1 / dpi) - drawAreaWidth) / 2; } else { sx = 0; } // compute the vertical scroll position if (continuousMode) { // we can't just multiply scrollY by dpi1/dpi -- the rounding // errors add up (because the pageY values are integers) -- so // we compute the pageY values at the new zoom level instead sy = 0; for (i = 1; i < topPage; ++i) { rot = rotate + doc->getPageRotate(i); if (rot >= 360) { rot -= 360; } else if (rot < 0) { rot += 360; } if (rot == 90 || rot == 270) { sy += (int)((doc->getPageCropWidth(i) * dpi1) / 72 + 0.5); } else { sy += (int)((doc->getPageCropHeight(i) * dpi1) / 72 + 0.5); } } vAdjust = (topPage - 1) * continuousModePageSpacing; sy = sy + (int)((scrollY - pageY[topPage - 1] + drawAreaHeight / 2) * (dpi1 / dpi)) + vAdjust - drawAreaHeight / 2; } else { sy = (int)((scrollY + drawAreaHeight / 2) * (dpi1 / dpi)) - drawAreaHeight / 2; } update(topPage, sx, sy, (dpi1 * 100) / 72, rotate, gFalse, gFalse, gFalse); } void PDFCore::setContinuousMode(GBool cm) { if (continuousMode != cm) { continuousMode = cm; update(topPage, scrollX, -1, zoom, rotate, gTrue, gFalse, gTrue); } } void PDFCore::setSelectionColor(SplashColor color) { splashColorCopy(selectXorColor, color); splashColorXor(selectXorColor, paperColor); } void PDFCore::setSelection(int newSelectPage, int newSelectULX, int newSelectULY, int newSelectLRX, int newSelectLRY) { int x0, y0, x1, y1, py; GBool haveSel, newHaveSel; GBool needRedraw, needScroll; GBool moveLeft, moveRight, moveTop, moveBottom; PDFCorePage *page; haveSel = selectULX != selectLRX && selectULY != selectLRY; newHaveSel = newSelectULX != newSelectLRX && newSelectULY != newSelectLRY; // erase old selection on off-screen bitmap needRedraw = gFalse; if (haveSel) { xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY, new SplashSolidColor(selectXorColor)); needRedraw = gTrue; } // draw new selection on off-screen bitmap if (newHaveSel) { xorRectangle(newSelectPage, newSelectULX, newSelectULY, newSelectLRX, newSelectLRY, new SplashSolidColor(selectXorColor)); needRedraw = gTrue; } // check which edges moved if (!haveSel || newSelectPage != selectPage) { moveLeft = moveTop = moveRight = moveBottom = gTrue; } else { moveLeft = newSelectULX != selectULX; moveTop = newSelectULY != selectULY; moveRight = newSelectLRX != selectLRX; moveBottom = newSelectLRY != selectLRY; } // redraw currently visible part of bitmap if (needRedraw) { if (!haveSel) { page = findPage(newSelectPage); x0 = newSelectULX; y0 = newSelectULY; x1 = newSelectLRX; y1 = newSelectLRY; redrawWindow(page->xDest + x0, page->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, gFalse); } else if (!newHaveSel) { if ((page = findPage(selectPage))) { x0 = selectULX; y0 = selectULY; x1 = selectLRX; y1 = selectLRY; redrawWindow(page->xDest + x0, page->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, gFalse); } } else { page = findPage(newSelectPage); if (moveLeft) { x0 = newSelectULX < selectULX ? newSelectULX : selectULX; y0 = newSelectULY < selectULY ? newSelectULY : selectULY; x1 = newSelectULX > selectULX ? newSelectULX : selectULX; y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY; redrawWindow(page->xDest + x0, page->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, gFalse); } if (moveRight) { x0 = newSelectLRX < selectLRX ? newSelectLRX : selectLRX; y0 = newSelectULY < selectULY ? newSelectULY : selectULY; x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX; y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY; redrawWindow(page->xDest + x0, page->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, gFalse); } if (moveTop) { x0 = newSelectULX < selectULX ? newSelectULX : selectULX; y0 = newSelectULY < selectULY ? newSelectULY : selectULY; x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX; y1 = newSelectULY > selectULY ? newSelectULY : selectULY; redrawWindow(page->xDest + x0, page->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, gFalse); } if (moveBottom) { x0 = newSelectULX < selectULX ? newSelectULX : selectULX; y0 = newSelectLRY < selectLRY ? newSelectLRY : selectLRY; x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX; y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY; redrawWindow(page->xDest + x0, page->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, gFalse); } } } // switch to new selection coords selectPage = newSelectPage; selectULX = newSelectULX; selectULY = newSelectULY; selectLRX = newSelectLRX; selectLRY = newSelectLRY; // scroll if necessary if (newHaveSel) { page = findPage(selectPage); needScroll = gFalse; x0 = scrollX; y0 = scrollY; if (moveLeft && page->xDest + selectULX < 0) { x0 += page->xDest + selectULX; needScroll = gTrue; } else if (moveRight && page->xDest + selectLRX >= drawAreaWidth) { x0 += page->xDest + selectLRX - drawAreaWidth; needScroll = gTrue; } else if (moveLeft && page->xDest + selectULX >= drawAreaWidth) { x0 += page->xDest + selectULX - drawAreaWidth; needScroll = gTrue; } else if (moveRight && page->xDest + selectLRX < 0) { x0 += page->xDest + selectLRX; needScroll = gTrue; } py = continuousMode ? pageY[selectPage - 1] : 0; if (moveTop && py + selectULY < y0) { y0 = py + selectULY; needScroll = gTrue; } else if (moveBottom && py + selectLRY >= y0 + drawAreaHeight) { y0 = py + selectLRY - drawAreaHeight; needScroll = gTrue; } else if (moveTop && py + selectULY >= y0 + drawAreaHeight) { y0 = py + selectULY - drawAreaHeight; needScroll = gTrue; } else if (moveBottom && py + selectLRY < y0) { y0 = py + selectLRY; needScroll = gTrue; } if (needScroll) { scrollTo(x0, y0); } } } void PDFCore::moveSelection(int pg, int x, int y) { int newSelectULX, newSelectULY, newSelectLRX, newSelectLRY; // don't allow selections to span multiple pages if (pg != selectPage) { return; } // move appropriate edges of selection if (lastDragLeft) { if (x < selectLRX) { newSelectULX = x; newSelectLRX = selectLRX; } else { newSelectULX = selectLRX; newSelectLRX = x; lastDragLeft = gFalse; } } else { if (x > selectULX) { newSelectULX = selectULX; newSelectLRX = x; } else { newSelectULX = x; newSelectLRX = selectULX; lastDragLeft = gTrue; } } if (lastDragTop) { if (y < selectLRY) { newSelectULY = y; newSelectLRY = selectLRY; } else { newSelectULY = selectLRY; newSelectLRY = y; lastDragTop = gFalse; } } else { if (y > selectULY) { newSelectULY = selectULY; newSelectLRY = y; } else { newSelectULY = y; newSelectLRY = selectULY; lastDragTop = gTrue; } } // redraw the selection setSelection(selectPage, newSelectULX, newSelectULY, newSelectLRX, newSelectLRY); } void PDFCore::xorRectangle(int pg, int x0, int y0, int x1, int y1, SplashPattern *pattern, PDFCoreTile *oneTile) { Splash *splash; SplashPath *path; PDFCorePage *page; PDFCoreTile *tile; SplashCoord xx0, yy0, xx1, yy1; int xi, yi, wi, hi; int i; if ((page = findPage(pg))) { for (i = 0; i < page->tiles->getLength(); ++i) { tile = (PDFCoreTile *)page->tiles->get(i); if (!oneTile || tile == oneTile) { splash = new Splash(tile->bitmap, gFalse); splash->setFillPattern(pattern->copy()); xx0 = (SplashCoord)(x0 - tile->xMin); yy0 = (SplashCoord)(y0 - tile->yMin); xx1 = (SplashCoord)(x1 - tile->xMin); yy1 = (SplashCoord)(y1 - tile->yMin); path = new SplashPath(); path->moveTo(xx0, yy0); path->lineTo(xx1, yy0); path->lineTo(xx1, yy1); path->lineTo(xx0, yy1); path->close(); splash->xorFill(path, gTrue); delete path; delete splash; xi = x0 - tile->xMin; wi = x1 - x0; if (xi < 0) { wi += xi; xi = 0; } if (xi + wi > tile->bitmap->getWidth()) { wi = tile->bitmap->getWidth() - xi; } yi = y0 - tile->yMin; hi = y1 - y0; if (yi < 0) { hi += yi; yi = 0; } if (yi + hi > tile->bitmap->getHeight()) { hi = tile->bitmap->getHeight() - yi; } updateTileData(tile, xi, yi, wi, hi, gTrue); } } } delete pattern; } GBool PDFCore::getSelection(int *pg, double *ulx, double *uly, double *lrx, double *lry) { if (selectULX == selectLRX || selectULY == selectLRY) { return gFalse; } *pg = selectPage; cvtDevToUser(selectPage, selectULX, selectULY, ulx, uly); cvtDevToUser(selectPage, selectLRX, selectLRY, lrx, lry); return gTrue; } GString *PDFCore::extractText(int pg, double xMin, double yMin, double xMax, double yMax) { PDFCorePage *page; TextOutputControl textOutCtrl; TextOutputDev *textOut; int x0, y0, x1, y1, t; GString *s; if (!doc->okToCopy()) { return NULL; } if ((page = findPage(pg))) { cvtUserToDev(pg, xMin, yMin, &x0, &y0); cvtUserToDev(pg, xMax, yMax, &x1, &y1); if (x0 > x1) { t = x0; x0 = x1; x1 = t; } if (y0 > y1) { t = y0; y0 = y1; y1 = t; } s = page->text->getText(x0, y0, x1, y1); } else { 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); textOut->cvtUserToDev(xMax, yMax, &x1, &y1); if (x0 > x1) { t = x0; x0 = x1; x1 = t; } if (y0 > y1) { t = y0; y0 = y1; y1 = t; } s = textOut->getText(x0, y0, x1, y1); } else { s = new GString(); } delete textOut; } return s; } GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly) { Unicode *u; int len, i; GBool ret; // convert to Unicode len = (int)strlen(s); u = (Unicode *)gmallocn(len, sizeof(Unicode)); for (i = 0; i < len; ++i) { u[i] = (Unicode)(s[i] & 0xff); } ret = findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly); gfree(u); return ret; } 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; int pg; GBool startAtTop, startAtLast, stopAtLast; // check for zero-length string if (len == 0) { return gFalse; } setBusyCursor(gTrue); // search current page starting at previous result, current // selection, or top/bottom of page startAtTop = startAtLast = gFalse; xMin = yMin = xMax = yMax = 0; pg = topPage; if (next) { startAtLast = gTrue; } else if (selectULX != selectLRX && selectULY != selectLRY) { pg = selectPage; if (backward) { xMin = selectULX - 1; yMin = selectULY - 1; } else { xMin = selectULX + 1; yMin = selectULY + 1; } } else { startAtTop = gTrue; } if (!(page = findPage(pg))) { displayPage(pg, zoom, rotate, gTrue, gFalse); page = findPage(pg); } if (page->text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { goto found; } if (!onePageOnly) { // search following/previous pages textOutCtrl.mode = textOutPhysLayout; textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse); if (!textOut->isOk()) { delete textOut; goto notFound; } for (pg = backward ? pg - 1 : pg + 1; backward ? pg >= 1 : pg <= doc->getNumPages(); pg += backward ? -1 : 1) { doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse); if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { delete textOut; goto foundPage; } } // search previous/following pages for (pg = backward ? doc->getNumPages() : 1; backward ? pg > topPage : pg < topPage; pg += backward ? -1 : 1) { doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse); if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { delete textOut; goto foundPage; } } delete textOut; } // search current page ending at previous result, current selection, // or bottom/top of page if (!startAtTop) { xMin = yMin = xMax = yMax = 0; if (next) { stopAtLast = gTrue; } else { stopAtLast = gFalse; xMax = selectLRX; yMax = selectLRY; } if (page->text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { goto found; } } // not found notFound: setBusyCursor(gFalse); return gFalse; // found on a different page foundPage: update(pg, scrollX, continuousMode ? -1 : 0, zoom, rotate, gFalse, gTrue, gTrue); page = findPage(pg); if (!page->text->findText(u, len, gTrue, gTrue, gFalse, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { // this can happen if coalescing is bad goto notFound; } // found: change the selection found: setSelection(pg, (int)floor(xMin), (int)floor(yMin), (int)ceil(xMax), (int)ceil(yMax)); setBusyCursor(gFalse); return gTrue; } GBool PDFCore::cvtWindowToUser(int xw, int yw, int *pg, double *xu, double *yu) { PDFCorePage *page; PDFCoreTile *tile; int i; for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); if (xw >= page->xDest && xw < page->xDest + page->w && yw >= page->yDest && yw < page->yDest + page->h) { if (page->tiles->getLength() == 0) { break; } tile = (PDFCoreTile *)page->tiles->get(0); *pg = page->page; xw -= tile->xDest; yw -= tile->yDest; *xu = tile->ictm[0] * xw + tile->ictm[2] * yw + tile->ictm[4]; *yu = tile->ictm[1] * xw + tile->ictm[3] * yw + tile->ictm[5]; return gTrue; } } *pg = 0; *xu = *yu = 0; return gFalse; } GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) { PDFCorePage *page; int i; for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); if (xw >= page->xDest && xw < page->xDest + page->w && yw >= page->yDest && yw < page->yDest + page->h) { *pg = page->page; *xd = xw - page->xDest; *yd = yw - page->yDest; return gTrue; } } *pg = 0; *xd = *yd = 0; return gFalse; } void PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) { PDFCorePage *page; PDFCoreTile *tile; if ((page = findPage(pg)) && page->tiles->getLength() > 0) { tile = (PDFCoreTile *)page->tiles->get(0); } else if (curTile && curPage->page == pg) { tile = curTile; } else { tile = NULL; } if (tile) { *xw = tile->xDest + (int)(tile->ctm[0] * xu + tile->ctm[2] * yu + tile->ctm[4] + 0.5); *yw = tile->yDest + (int)(tile->ctm[1] * xu + tile->ctm[3] * yu + tile->ctm[5] + 0.5); } else { // this should never happen *xw = *yw = 0; } } void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) { PDFCorePage *page; PDFCoreTile *tile; double ctm[6]; if ((page = findPage(pg)) && page->tiles->getLength() > 0) { tile = (PDFCoreTile *)page->tiles->get(0); } else if (curTile && curPage->page == pg) { tile = curTile; } else { tile = NULL; } if (tile) { *xd = (int)(tile->xMin + tile->ctm[0] * xu + tile->ctm[2] * yu + tile->ctm[4] + 0.5); *yd = (int)(tile->yMin + tile->ctm[1] * xu + tile->ctm[3] * yu + tile->ctm[5] + 0.5); } else { doc->getCatalog()->getPage(pg)->getDefaultCTM(ctm, dpi, dpi, rotate, gFalse, out->upsideDown()); *xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5); *yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5); } } void PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) { PDFCorePage *page; if ((page = findPage(pg))) { *xw = page->xDest + xd; *yw = page->yDest + yd; } else { // this should never happen *xw = *yw = 0; } } void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) { PDFCorePage *page; PDFCoreTile *tile; if ((page = findPage(pg)) && page->tiles->getLength() > 0) { tile = (PDFCoreTile *)page->tiles->get(0); } else if (curTile && curPage->page == pg) { tile = curTile; } else { tile = NULL; } if (tile) { xd -= tile->xMin; yd -= tile->yMin; *xu = tile->ictm[0] * xd + tile->ictm[2] * yd + tile->ictm[4]; *yu = tile->ictm[1] * xd + tile->ictm[3] * yd + tile->ictm[5]; } else { // this should never happen *xu = *yu = 0; } } void PDFCore::setReverseVideo(GBool reverseVideoA) { out->setReverseVideo(reverseVideoA); update(topPage, scrollX, scrollY, zoom, rotate, gTrue, gFalse, gFalse); } LinkAction *PDFCore::findLink(int pg, double x, double y) { PDFCorePage *page; if ((page = findPage(pg))) { return page->links ? page->links->find(x, y) : (LinkAction *)NULL; } return NULL; } PDFCorePage *PDFCore::findPage(int pg) { PDFCorePage *page; int i; for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); if (page->page == pg) { return page; } } return NULL; } void PDFCore::redrawCbk(void *data, int x0, int y0, int x1, int y1, GBool composited) { PDFCore *core = (PDFCore *)data; core->curTile->bitmap = core->out->getBitmap(); // the default CTM is set by the Gfx constructor; tile->ctm is // needed by the coordinate conversion functions (which may be // called during redraw) memcpy(core->curTile->ctm, core->out->getDefCTM(), 6 * sizeof(double)); memcpy(core->curTile->ictm, core->out->getDefICTM(), 6 * sizeof(double)); // the bitmap created by Gfx and SplashOutputDev can be a slightly // different size due to rounding errors if (x1 >= core->curTile->xMax - core->curTile->xMin) { x1 = core->curTile->xMax - core->curTile->xMin - 1; } if (y1 >= core->curTile->yMax - core->curTile->yMin) { y1 = core->curTile->yMax - core->curTile->yMin - 1; } core->clippedRedrawRect(core->curTile, x0, y0, core->curTile->xDest + x0, core->curTile->yDest + y0, x1 - x0 + 1, y1 - y0 + 1, 0, 0, core->drawAreaWidth, core->drawAreaHeight, gTrue, composited); } void PDFCore::redrawWindow(int x, int y, int width, int height, GBool needUpdate) { PDFCorePage *page; PDFCoreTile *tile; int xDest, yDest, w, i, j; if (pages->getLength() == 0) { redrawRect(NULL, 0, 0, x, y, width, height, gTrue); return; } for (i = 0; i < pages->getLength(); ++i) { page = (PDFCorePage *)pages->get(i); for (j = 0; j < page->tiles->getLength(); ++j) { tile = (PDFCoreTile *)page->tiles->get(j); if (tile->edges & pdfCoreTileTopEdge) { if (tile->edges & pdfCoreTileLeftEdge) { xDest = 0; } else { xDest = tile->xDest; } if (tile->edges & pdfCoreTileRightEdge) { w = drawAreaWidth - xDest; } else { w = tile->xDest + (tile->xMax - tile->xMin) - xDest; } clippedRedrawRect(NULL, 0, 0, xDest, 0, w, tile->yDest, x, y, width, height, gFalse); } if (tile->edges & pdfCoreTileBottomEdge) { if (tile->edges & pdfCoreTileLeftEdge) { xDest = 0; } else { xDest = tile->xDest; } if (tile->edges & pdfCoreTileRightEdge) { w = drawAreaWidth - xDest; } else { w = tile->xDest + (tile->xMax - tile->xMin) - xDest; } yDest = tile->yDest + (tile->yMax - tile->yMin); clippedRedrawRect(NULL, 0, 0, xDest, yDest, w, drawAreaHeight - yDest, x, y, width, height, gFalse); } else if ((tile->edges & pdfCoreTileBottomSpace) && i+1 < pages->getLength()) { if (tile->edges & pdfCoreTileLeftEdge) { xDest = 0; } else { xDest = tile->xDest; } if (tile->edges & pdfCoreTileRightEdge) { w = drawAreaWidth - xDest; } else { w = tile->xDest + (tile->xMax - tile->xMin) - xDest; } yDest = tile->yDest + (tile->yMax - tile->yMin); clippedRedrawRect(NULL, 0, 0, xDest, yDest, w, ((PDFCorePage *)pages->get(i+1))->yDest - yDest, x, y, width, height, gFalse); } if (tile->edges & pdfCoreTileLeftEdge) { clippedRedrawRect(NULL, 0, 0, 0, tile->yDest, tile->xDest, tile->yMax - tile->yMin, x, y, width, height, gFalse); } if (tile->edges & pdfCoreTileRightEdge) { xDest = tile->xDest + (tile->xMax - tile->xMin); clippedRedrawRect(NULL, 0, 0, xDest, tile->yDest, drawAreaWidth - xDest, tile->yMax - tile->yMin, x, y, width, height, gFalse); } clippedRedrawRect(tile, 0, 0, tile->xDest, tile->yDest, tile->bitmap->getWidth(), tile->bitmap->getHeight(), x, y, width, height, needUpdate); } } } PDFCoreTile *PDFCore::newTile(int xDestA, int yDestA) { return new PDFCoreTile(xDestA, yDestA); } void PDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc, int width, int height, GBool composited) { } void PDFCore::clippedRedrawRect(PDFCoreTile *tile, int xSrc, int ySrc, int xDest, int yDest, int width, int height, int xClip, int yClip, int wClip, int hClip, GBool needUpdate, GBool composited) { if (tile && needUpdate) { updateTileData(tile, xSrc, ySrc, width, height, composited); } if (xDest < xClip) { xSrc += xClip - xDest; width -= xClip - xDest; xDest = xClip; } if (xDest + width > xClip + wClip) { width = xClip + wClip - xDest; } if (yDest < yClip) { ySrc += yClip - yDest; height -= yClip - yDest; yDest = yClip; } if (yDest + height > yClip + hClip) { height = yClip + hClip - yDest; } if (width > 0 && height > 0) { redrawRect(tile, xSrc, ySrc, xDest, yDest, width, height, composited); } } xpdf-3.04/xpdf/Zoox.cc0000644000076400007640000004547312341430012014125 0ustar dereknderekn//======================================================================== // // 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("= '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("", 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; } xpdf-3.04/xpdf/TextOutputDev.cc0000644000076400007640000034476212341430012015775 0ustar dereknderekn//======================================================================== // // TextOutputDev.cc // // Copyright 1997-2014 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #ifdef _WIN32 #include // for O_BINARY #include // for setmode #endif #include "gmem.h" #include "GString.h" #include "GList.h" #include "config.h" #include "Error.h" #include "GlobalParams.h" #include "UnicodeMap.h" #include "UnicodeTypeTable.h" #include "GfxState.h" #include "Link.h" #include "TextOutputDev.h" //------------------------------------------------------------------------ // parameters //------------------------------------------------------------------------ // 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 // shadows) which is to be discarded. #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 // Max horizontal distance between edge of word and start of underline // (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); } 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 //------------------------------------------------------------------------ class TextUnderline { public: TextUnderline(double x0A, double y0A, double x1A, double y1A) { x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; horiz = y0 == y1; } ~TextUnderline() {} double x0, y0, x1, y1; GBool horiz; }; //------------------------------------------------------------------------ // TextLink //------------------------------------------------------------------------ class TextLink { public: TextLink(double xMinA, double yMinA, double xMaxA, double yMaxA, GString *uriA) { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; uri = uriA; } ~TextLink(); 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 (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; 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 (fontName) { delete fontName; } } GBool TextFontInfo::matches(GfxState *state) { Ref *id; if (!state->getFont()) { return gFalse; } id = state->getFont()->getID(); return id->num == fontID.num && id->gen == fontID.gen; } //------------------------------------------------------------------------ // TextWord //------------------------------------------------------------------------ // 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; 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; } switch (rot) { case 0: default: edge[i] = ch->xMin; if (i == len - 1) { edge[len] = ch->xMax; } break; case 1: edge[i] = ch->yMin; if (i == len - 1) { edge[len] = ch->yMax; } break; case 2: edge[i] = ch->xMax; if (i == len - 1) { edge[len] = ch->xMin; } break; case 3: edge[i] = ch->yMax; if (i == len - 1) { edge[len] = ch->yMin; } break; } } 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() { gfree(text); gfree(edge); gfree(charPos); } // 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: default: edge[len] = ch->xMin; edge[len+1] = ch->xMax; break; case 1: edge[len] = ch->yMin; edge[len+1] = ch->yMax; break; case 2: edge[len] = ch->xMax; edge[len+1] = ch->xMin; break; case 3: edge[len] = ch->yMax; edge[len+1] = ch->yMin; break; } ++len; } int TextWord::cmpYX(const void *p1, const void *p2) { const TextWord *word1 = *(const TextWord **)p1; const TextWord *word2 = *(const TextWord **)p2; double cmp; if ((cmp = word1->yMin - word2->yMin) == 0) { cmp = word1->xMin - word2->xMin; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } 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; UnicodeMap *uMap; char buf[8]; int n, i; s = new GString(); if (!(uMap = globalParams->getTextEncoding())) { return s; } for (i = 0; i < len; ++i) { n = uMap->mapUnicode(text[i], buf, sizeof(buf)); s->append(buf, n); } uMap->decRefCnt(); return s; } void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) { if (charIdx < 0 || charIdx >= len) { return; } switch (rot) { case 0: *xMinA = edge[charIdx]; *xMaxA = edge[charIdx + 1]; *yMinA = yMin; *yMaxA = yMax; break; case 1: *xMinA = xMin; *xMaxA = xMax; *yMinA = edge[charIdx]; *yMaxA = edge[charIdx + 1]; break; case 2: *xMinA = edge[charIdx + 1]; *xMaxA = edge[charIdx]; *yMinA = yMin; *yMaxA = yMax; break; case 3: *xMinA = xMin; *xMaxA = xMax; *yMinA = edge[charIdx + 1]; *yMaxA = edge[charIdx]; break; } } 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; } } GString *TextWord::getLinkURI() { return link ? link->uri : (GString *)NULL; } //------------------------------------------------------------------------ // TextLine //------------------------------------------------------------------------ TextLine::TextLine(GList *wordsA, double xMinA, double yMinA, double xMaxA, double yMaxA, double fontSizeA) { TextWord *word; int i, j, k; words = wordsA; rot = 0; xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; fontSize = fontSizeA; px = 0; pw = 0; // build the text len = 0; for (i = 0; i < words->getLength(); ++i) { word = (TextWord *)words->get(i); len += word->len; if (word->spaceAfter) { ++len; } } text = (Unicode *)gmallocn(len, sizeof(Unicode)); edge = (double *)gmallocn(len + 1, sizeof(double)); 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)'-'; } TextLine::~TextLine() { deleteGList(words, TextWord); gfree(text); gfree(edge); } double TextLine::getBaseline() { TextWord *word0; word0 = (TextWord *)words->get(0); switch (rot) { case 0: default: return yMax + fontSize * word0->font->descent; case 1: return xMin - fontSize * word0->font->descent; case 2: return yMin - fontSize * word0->font->descent; case 3: return xMax + fontSize * word0->font->descent; } } //------------------------------------------------------------------------ // TextParagraph //------------------------------------------------------------------------ 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; } } } TextParagraph::~TextParagraph() { deleteGList(lines, TextLine); } //------------------------------------------------------------------------ // TextColumn //------------------------------------------------------------------------ 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; } } int TextColumn::cmpY(const void *p1, const void *p2) { const TextColumn *col1 = *(const TextColumn **)p1; const TextColumn *col2 = *(const TextColumn **)p2; if (col1->yMin < col2->yMin) { return -1; } else if (col1->yMin > col2->yMin) { return 1; } else { return 0; } } int TextColumn::cmpPX(const void *p1, const void *p2) { const TextColumn *col1 = *(const TextColumn **)p1; const TextColumn *col2 = *(const TextColumn **)p2; if (col1->px < col2->px) { return -1; } else if (col1->px > col2->px) { return 1; } else { return 0; } } //------------------------------------------------------------------------ // TextWordList //------------------------------------------------------------------------ TextWordList::TextWordList(GList *wordsA) { words = wordsA; } TextWordList::~TextWordList() { deleteGList(words, TextWord); } int TextWordList::getLength() { return words->getLength(); } TextWord *TextWordList::get(int idx) { if (idx < 0 || idx >= words->getLength()) { return NULL; } return (TextWord *)words->get(idx); } //------------------------------------------------------------------------ // TextPage //------------------------------------------------------------------------ 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; chars = new GList(); fonts = new GList(); underlines = new GList(); links = new GList(); findCols = NULL; findLR = gTrue; lastFindXMin = lastFindYMin = 0; haveLastFind = gFalse; } TextPage::~TextPage() { clear(); deleteGList(chars, TextChar); deleteGList(fonts, TextFontInfo); deleteGList(underlines, TextUnderline); deleteGList(links, TextLink); if (findCols) { deleteGList(findCols, TextColumn); } } void TextPage::startPage(GfxState *state) { clear(); if (state) { pageWidth = state->getPageWidth(); pageHeight = state->getPageHeight(); } else { pageWidth = pageHeight = 0; } } 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; } 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; } } 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; // 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; } // subtract char and word spacing from the dx,dy values sp = state->getCharSpace(); if (c == (CharCode)0x20) { sp += state->getWordSpace(); } state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2); dx -= dx2; dy -= dy2; state->transformDelta(dx, dy, &w1, &h1); // throw away chars that aren't inside the page bounds // (and also do a sanity check on the character size) state->transform(x, y, &x1, &y1); if (x1 + w1 < 0 || x1 > pageWidth || y1 + h1 < 0 || y1 > pageHeight || w1 > pageWidth || h1 > pageHeight) { charPos += nBytes; return; } // check the tiny chars limit if (!globalParams->getTextKeepTinyChars() && fabs(w1) < 3 && fabs(h1) < 3) { if (++nTinyChars > 50000) { charPos += nBytes; return; } } // 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; } 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: default: xMin = x2; xMax = x2 + w1; yMin = y2 - ascent; yMax = y2 - descent; break; case 1: xMin = x2 + descent; xMax = x2 + ascent; yMin = y2; yMax = y2 + h1; break; case 2: xMin = x2 + w1; xMax = x2; yMin = y2 + descent; yMax = y2 + ascent; break; case 3: xMin = x2 - ascent; xMax = x2 - descent; yMin = y2 + h1; yMax = y2; break; } if ((state->getRender() & 3) == 1) { state->getStrokeRGB(&rgb); } else { state->getFillRGB(&rgb); } if (rtl) { j = uLen - 1 - i; } else { j = i; } chars->append(new TextChar(u[j], charPos, nBytes, xMin, yMin, xMax, yMax, curRot, clipped, state->getRender() == 3, curFont, curFontSize, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b))); } } charPos += nBytes; } void TextPage::incCharCount(int nChars) { charPos += nChars; } void TextPage::beginActualText(GfxState *state, Unicode *u, int uLen) { if (actualText) { gfree(actualText); } actualText = (Unicode *)gmallocn(uLen, sizeof(Unicode)); memcpy(actualText, u, uLen * sizeof(Unicode)); actualTextLen = uLen; actualTextNBytes = 0; } void TextPage::endActualText(GfxState *state) { Unicode *u; 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; } void TextPage::addUnderline(double x0, double y0, double x1, double y1) { underlines->append(new TextUnderline(x0, y0, x1, y1)); } void TextPage::addLink(double xMin, double yMin, double xMax, double yMax, Link *link) { GString *uri; if (link && link->getAction() && link->getAction()->getKind() == actionURI) { uri = ((LinkURI *)link->getAction())->getURI()->copy(); links->append(new TextLink(xMin, yMin, xMax, yMax, uri)); } } //------------------------------------------------------------------------ // TextPage: output //------------------------------------------------------------------------ void TextPage::write(void *outputStream, TextOutputFunc outputFunc) { UnicodeMap *uMap; char space[8], eol[16], eop[8]; int spaceLen, eolLen, eopLen; GBool pageBreaks; // get the output encoding if (!(uMap = globalParams->getTextEncoding())) { return; } spaceLen = uMap->mapUnicode(0x20, space, sizeof(space)); eolLen = 0; // make gcc happy switch (globalParams->getTextEOL()) { case eolUnix: eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol)); break; case eolDOS: eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol)); eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen); break; case eolMac: eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol)); break; } eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop)); pageBreaks = globalParams->getTextPageBreaks(); switch (control.mode) { case textOutReadingOrder: writeReadingOrder(outputStream, outputFunc, uMap, space, spaceLen, eol, eolLen); break; case textOutPhysLayout: case textOutTableLayout: writePhysLayout(outputStream, outputFunc, uMap, space, spaceLen, eol, eolLen); break; case textOutLinePrinter: writeLinePrinter(outputStream, outputFunc, uMap, space, spaceLen, eol, eolLen); break; case textOutRawOrder: writeRaw(outputStream, outputFunc, uMap, space, spaceLen, eol, eolLen); break; } // end of page if (pageBreaks) { (*outputFunc)(outputStream, eop, eopLen); } uMap->decRefCnt(); } void TextPage::writeReadingOrder(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen) { TextBlock *tree; TextColumn *col; TextParagraph *par; TextLine *line; GList *columns; GBool primaryLR; GString *s; int colIdx, parIdx, lineIdx, rot, n; rot = rotateChars(chars); primaryLR = checkPrimaryLR(chars); tree = splitChars(chars); #if 0 //~debug dumpTree(tree); #endif if (!tree) { // no text unrotateChars(chars, rot); return; } columns = buildColumns(tree); delete tree; unrotateChars(chars, rot); if (control.html) { rotateUnderlinesAndLinks(rot); generateUnderlinesAndLinks(columns); } #if 0 //~debug dumpColumns(columns); #endif 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); } (*outputFunc)(outputStream, eol, eolLen); } deleteGList(columns, TextColumn); } GList *TextPage::makeColumns() { TextBlock *tree; GList *columns; tree = splitChars(chars); if (!tree) { // no text return new GList(); } columns = buildColumns(tree); delete tree; if (control.html) { generateUnderlinesAndLinks(columns); } return columns; } // 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; #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 TextPage::writeLinePrinter(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen) { TextChar *ch, *ch2; GList *line; GString *s; char buf[8]; double pitch, lineSpacing, delta; double yMin0, yShift, xMin0, xShift; double y, x; int rot, n, i, j, k; rot = rotateChars(chars); chars->sort(&TextChar::cmpX); removeDuplicates(chars, 0); chars->sort(&TextChar::cmpY); // get character pitch if (control.fixedPitch > 0) { pitch = control.fixedPitch; } else { // compute (approximate) character pitch pitch = pageWidth; for (i = 0; i < chars->getLength(); ++i) { ch = (TextChar *)chars->get(i); for (j = i + 1; j < chars->getLength(); ++j) { ch2 = (TextChar *)chars->get(j); if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) < ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin) && ch->yMin + ascentAdjustFactor * (ch->yMax - ch->yMin) < ch2->yMax - descentAdjustFactor * (ch2->yMax - ch2->yMin)) { delta = fabs(ch2->xMin - ch->xMin); if (delta > 0 && delta < pitch) { pitch = delta; } } } } } // get line spacing if (control.fixedLineSpacing > 0) { lineSpacing = control.fixedLineSpacing; } else { // compute (approximate) line spacing lineSpacing = pageHeight; i = 0; while (i < chars->getLength()) { ch = (TextChar *)chars->get(i); // look for the first char that does not (substantially) // vertically overlap this one delta = 0; for (++i; delta == 0 && i < chars->getLength(); ++i) { ch2 = (TextChar *)chars->get(i); if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) > ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin)) { delta = ch2->yMin - ch->yMin; } } if (delta > 0 && delta < lineSpacing) { lineSpacing = delta; } } } // shift the grid to avoid problems with floating point accuracy -- // for fixed line spacing, this avoids problems with // dropping/inserting blank lines if (chars->getLength()) { yMin0 = ((TextChar *)chars->get(0))->yMin; yShift = yMin0 - (int)(yMin0 / lineSpacing + 0.5) * lineSpacing - 0.5 * lineSpacing; } else { yShift = 0; } // for each line... i = 0; j = chars->getLength() - 1; for (y = yShift; y < pageHeight; y += lineSpacing) { // get the characters in this line line = new GList; while (i < chars->getLength() && ((TextChar *)chars->get(i))->yMin < y + lineSpacing) { line->append(chars->get(i++)); } line->sort(&TextChar::cmpX); // shift the grid to avoid problems with floating point accuracy // -- for fixed char spacing, this avoids problems with // dropping/inserting spaces if (line->getLength()) { xMin0 = ((TextChar *)line->get(0))->xMin; xShift = xMin0 - (int)(xMin0 / pitch + 0.5) * pitch - 0.5 * pitch; } else { xShift = 0; } // write the line s = new GString(); x = xShift; k = 0; while (k < line->getLength()) { ch = (TextChar *)line->get(k); if (ch->xMin < x + pitch) { n = uMap->mapUnicode(ch->c, buf, sizeof(buf)); s->append(buf, n); ++k; } else { s->append(space, spaceLen); n = spaceLen; } x += (uMap->isUnicode() ? 1 : n) * pitch; } s->append(eol, eolLen); (*outputFunc)(outputStream, s->getCString(), s->getLength()); delete s; delete line; } unrotateChars(chars, rot); } void TextPage::writeRaw(void *outputStream, TextOutputFunc outputFunc, UnicodeMap *uMap, char *space, int spaceLen, char *eol, int eolLen) { TextChar *ch, *ch2; GString *s; char buf[8]; int n, i; s = new GString(); for (i = 0; i < chars->getLength(); ++i) { // process one char ch = (TextChar *)chars->get(i); n = uMap->mapUnicode(ch->c, buf, sizeof(buf)); s->append(buf, n); // check for space or eol if (i+1 < chars->getLength()) { ch2 = (TextChar *)chars->get(i+1); if (ch2->rot != ch->rot) { s->append(eol, eolLen); } else { switch (ch->rot) { case 0: default: if (fabs(ch2->yMin - ch->yMin) > rawModeLineDelta * ch->fontSize || ch2->xMin - ch->xMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); } else if (ch2->xMin - ch->xMax > rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; case 1: if (fabs(ch->xMax - ch2->xMax) > rawModeLineDelta * ch->fontSize || ch2->yMin - ch->yMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); } else if (ch2->yMin - ch->yMax > rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; case 2: if (fabs(ch->yMax - ch2->yMax) > rawModeLineDelta * ch->fontSize || ch->xMin - ch2->xMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); } else if (ch->xMin - ch2->xMax > rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; case 3: if (fabs(ch2->xMin - ch->xMin) > rawModeLineDelta * ch->fontSize || ch->yMin - ch2->yMax < -rawModeCharOverlap * ch->fontSize) { s->append(eol, eolLen); } else if (ch->yMin - ch2->yMax > rawModeWordSpacing * ch->fontSize) { s->append(space, spaceLen); } break; } } } else { s->append(eol, eolLen); } if (s->getLength() > 1000) { (*outputFunc)(outputStream, s->getCString(), s->getLength()); s->clear(); } } if (s->getLength() > 0) { (*outputFunc)(outputStream, s->getCString(), s->getLength()); } delete s; } void TextPage::encodeFragment(Unicode *text, int len, UnicodeMap *uMap, GBool primaryLR, GString *s) { char lre[8], rle[8], popdf[8], buf[8]; int lreLen, rleLen, popdfLen, n; int i, j, k; if (uMap->isUnicode()) { lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre)); rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle)); popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf)); if (primaryLR) { i = 0; while (i < len) { // output a left-to-right section for (j = i; j < len && !unicodeTypeR(text[j]); ++j) ; for (k = i; k < j; ++k) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } i = j; // output a right-to-left section for (j = i; j < len && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j])); ++j) ; if (j > i) { s->append(rle, rleLen); for (k = j - 1; k >= i; --k) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } s->append(popdf, popdfLen); i = j; } } } else { // Note: This code treats numeric characters (European and // Arabic/Indic) as left-to-right, which isn't strictly correct // (incurs extra LRE/POPDF pairs), but does produce correct // visual formatting. s->append(rle, rleLen); i = len - 1; while (i >= 0) { // output a right-to-left section for (j = i; j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j])); --j) ; for (k = i; k > j; --k) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } i = j; // output a left-to-right section for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) ; if (j < i) { s->append(lre, lreLen); for (k = j + 1; k <= i; ++k) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } s->append(popdf, popdfLen); i = j; } } s->append(popdf, popdfLen); } } else { for (i = 0; i < len; ++i) { n = uMap->mapUnicode(text[i], buf, sizeof(buf)); s->append(buf, n); } } } //------------------------------------------------------------------------ // TextPage: layout analysis //------------------------------------------------------------------------ // Determine primary (most common) rotation value. Rotate all chars // to that primary rotation. int TextPage::rotateChars(GList *charsA) { TextChar *ch; int nChars[4]; double xMin, yMin, xMax, yMax, t; int rot, i; // determine primary rotation nChars[0] = nChars[1] = nChars[2] = nChars[3] = 0; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); ++nChars[ch->rot]; } rot = 0; for (i = 1; i < 4; ++i) { if (nChars[i] > nChars[rot]) { rot = i; } } // rotate switch (rot) { case 0: default: break; case 1: for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); xMin = ch->yMin; xMax = ch->yMax; yMin = pageWidth - ch->xMax; yMax = pageWidth - ch->xMin; ch->xMin = xMin; ch->xMax = xMax; ch->yMin = yMin; ch->yMax = yMax; ch->rot = (ch->rot + 3) & 3; } t = pageWidth; pageWidth = pageHeight; pageHeight = t; break; case 2: for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); xMin = pageWidth - ch->xMax; xMax = pageWidth - ch->xMin; yMin = pageHeight - ch->yMax; yMax = pageHeight - ch->yMin; ch->xMin = xMin; ch->xMax = xMax; ch->yMin = yMin; ch->yMax = yMax; ch->rot = (ch->rot + 2) & 3; } break; case 3: for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); xMin = pageHeight - ch->yMax; xMax = pageHeight - ch->yMin; yMin = ch->xMin; yMax = ch->xMax; ch->xMin = xMin; ch->xMax = xMax; ch->yMin = yMin; ch->yMax = yMax; ch->rot = (ch->rot + 1) & 3; } t = pageWidth; pageWidth = pageHeight; pageHeight = t; break; } return rot; } // Rotate the TextUnderlines and TextLinks to match the transform // performed by rotateChars(). void TextPage::rotateUnderlinesAndLinks(int rot) { TextUnderline *underline; TextLink *link; double xMin, yMin, xMax, yMax; int i; switch (rot) { case 0: default: break; case 1: for (i = 0; i < underlines->getLength(); ++i) { underline = (TextUnderline *)underlines->get(i); xMin = underline->y0; xMax = underline->y1; yMin = pageWidth - underline->x1; yMax = pageWidth - underline->x0; underline->x0 = xMin; underline->x1 = xMax; underline->y0 = yMin; underline->y1 = yMax; underline->horiz = !underline->horiz; } for (i = 0; i < links->getLength(); ++i) { link = (TextLink *)links->get(i); xMin = link->yMin; xMax = link->yMax; yMin = pageWidth - link->xMax; yMax = pageWidth - link->xMin; link->xMin = xMin; link->xMax = xMax; link->yMin = yMin; link->yMax = yMax; } break; case 2: for (i = 0; i < underlines->getLength(); ++i) { underline = (TextUnderline *)underlines->get(i); xMin = pageWidth - underline->x1; xMax = pageWidth - underline->x0; yMin = pageHeight - underline->y1; yMax = pageHeight - underline->y0; underline->x0 = xMin; underline->x1 = xMax; underline->y0 = yMin; underline->y1 = yMax; } for (i = 0; i < links->getLength(); ++i) { link = (TextLink *)links->get(i); xMin = pageWidth - link->xMax; xMax = pageWidth - link->xMin; yMin = pageHeight - link->yMax; yMax = pageHeight - link->yMin; link->xMin = xMin; link->xMax = xMax; link->yMin = yMin; link->yMax = yMax; } break; case 3: for (i = 0; i < underlines->getLength(); ++i) { underline = (TextUnderline *)underlines->get(i); xMin = pageHeight - underline->y1; xMax = pageHeight - underline->y0; yMin = underline->x0; yMax = underline->x1; underline->x0 = xMin; underline->x1 = xMax; underline->y0 = yMin; underline->y1 = yMax; underline->horiz = !underline->horiz; } for (i = 0; i < links->getLength(); ++i) { link = (TextLink *)links->get(i); xMin = pageHeight - link->yMax; xMax = pageHeight - link->yMin; yMin = link->xMin; yMax = link->xMax; link->xMin = xMin; link->xMax = xMax; link->yMin = yMin; link->yMax = yMax; } break; } } // Undo the coordinate transform performed by rotateChars(). void TextPage::unrotateChars(GList *charsA, int rot) { TextChar *ch; double xMin, yMin, xMax, yMax, t; int i; switch (rot) { case 0: default: // no transform break; case 1: t = pageWidth; pageWidth = pageHeight; pageHeight = t; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); xMin = pageWidth - ch->yMax; xMax = pageWidth - ch->yMin; yMin = ch->xMin; yMax = ch->xMax; ch->xMin = xMin; ch->xMax = xMax; ch->yMin = yMin; ch->yMax = yMax; ch->rot = (ch->rot + 1) & 3; } break; case 2: for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); xMin = pageWidth - ch->xMax; xMax = pageWidth - ch->xMin; yMin = pageHeight - ch->yMax; yMax = pageHeight - ch->yMin; ch->xMin = xMin; ch->xMax = xMax; ch->yMin = yMin; ch->yMax = yMax; ch->rot = (ch->rot + 2) & 3; } break; case 3: t = pageWidth; pageWidth = pageHeight; pageHeight = t; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); xMin = ch->yMin; xMax = ch->yMax; yMin = pageHeight - ch->xMax; yMax = pageHeight - ch->xMin; ch->xMin = xMin; ch->xMax = xMax; ch->yMin = yMin; ch->yMax = yMax; ch->rot = (ch->rot + 3) & 3; } break; } } // Undo the coordinate transform performed by rotateChars(). void TextPage::unrotateColumns(GList *columns, int rot) { TextColumn *col; TextParagraph *par; TextLine *line; TextWord *word; double xMin, yMin, xMax, yMax, t; int colIdx, parIdx, lineIdx, wordIdx, i; switch (rot) { case 0: default: // no transform break; case 1: t = pageWidth; pageWidth = pageHeight; pageHeight = t; for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) { col = (TextColumn *)columns->get(colIdx); xMin = pageWidth - col->yMax; xMax = pageWidth - col->yMin; yMin = col->xMin; yMax = col->xMax; col->xMin = xMin; col->xMax = xMax; col->yMin = yMin; col->yMax = yMax; for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) { par = (TextParagraph *)col->paragraphs->get(parIdx); xMin = pageWidth - par->yMax; xMax = pageWidth - par->yMin; yMin = par->xMin; yMax = par->xMax; par->xMin = xMin; par->xMax = xMax; par->yMin = yMin; par->yMax = yMax; for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { line = (TextLine *)par->lines->get(lineIdx); xMin = pageWidth - line->yMax; xMax = pageWidth - line->yMin; yMin = line->xMin; yMax = line->xMax; line->xMin = xMin; line->xMax = xMax; line->yMin = yMin; line->yMax = yMax; line->rot = (line->rot + 1) & 3; for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { word = (TextWord *)line->words->get(wordIdx); xMin = pageWidth - word->yMax; xMax = pageWidth - word->yMin; yMin = word->xMin; yMax = word->xMax; word->xMin = xMin; word->xMax = xMax; word->yMin = yMin; word->yMax = yMax; word->rot = (word->rot + 1) & 3; } } } } break; case 2: for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) { col = (TextColumn *)columns->get(colIdx); xMin = pageWidth - col->xMax; xMax = pageWidth - col->xMin; yMin = pageHeight - col->yMax; yMax = pageHeight - col->yMin; col->xMin = xMin; col->xMax = xMax; col->yMin = yMin; col->yMax = yMax; for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) { par = (TextParagraph *)col->paragraphs->get(parIdx); xMin = pageWidth - par->xMax; xMax = pageWidth - par->xMin; yMin = pageHeight - par->yMax; yMax = pageHeight - par->yMin; par->xMin = xMin; par->xMax = xMax; par->yMin = yMin; par->yMax = yMax; for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { line = (TextLine *)par->lines->get(lineIdx); xMin = pageWidth - line->xMax; xMax = pageWidth - line->xMin; yMin = pageHeight - line->yMax; yMax = pageHeight - line->yMin; line->xMin = xMin; line->xMax = xMax; line->yMin = yMin; line->yMax = yMax; line->rot = (line->rot + 2) & 3; for (i = 0; i <= line->len; ++i) { line->edge[i] = pageWidth - line->edge[i]; } for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { word = (TextWord *)line->words->get(wordIdx); xMin = pageWidth - word->xMax; xMax = pageWidth - word->xMin; yMin = pageHeight - word->yMax; yMax = pageHeight - word->yMin; word->xMin = xMin; word->xMax = xMax; word->yMin = yMin; word->yMax = yMax; word->rot = (word->rot + 2) & 3; for (i = 0; i <= word->len; ++i) { word->edge[i] = pageWidth - word->edge[i]; } } } } } break; case 3: t = pageWidth; pageWidth = pageHeight; pageHeight = t; for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) { col = (TextColumn *)columns->get(colIdx); xMin = col->yMin; xMax = col->yMax; yMin = pageHeight - col->xMax; yMax = pageHeight - col->xMin; col->xMin = xMin; col->xMax = xMax; col->yMin = yMin; col->yMax = yMax; for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) { par = (TextParagraph *)col->paragraphs->get(parIdx); xMin = par->yMin; xMax = par->yMax; yMin = pageHeight - par->xMax; yMax = pageHeight - par->xMin; par->xMin = xMin; par->xMax = xMax; par->yMin = yMin; par->yMax = yMax; for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { line = (TextLine *)par->lines->get(lineIdx); xMin = line->yMin; xMax = line->yMax; yMin = pageHeight - line->xMax; yMax = pageHeight - line->xMin; line->xMin = xMin; line->xMax = xMax; line->yMin = yMin; line->yMax = yMax; line->rot = (line->rot + 3) & 3; for (i = 0; i <= line->len; ++i) { line->edge[i] = pageHeight - line->edge[i]; } for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { word = (TextWord *)line->words->get(wordIdx); xMin = word->yMin; xMax = word->yMax; yMin = pageHeight - word->xMax; yMax = pageHeight - word->xMin; word->xMin = xMin; word->xMax = xMax; word->yMin = yMin; word->yMax = yMax; word->rot = (word->rot + 3) & 3; for (i = 0; i <= word->len; ++i) { word->edge[i] = pageHeight - word->edge[i]; } } } } } break; } } void TextPage::unrotateWords(GList *words, int rot) { TextWord *word; double xMin, yMin, xMax, yMax; int i, j; switch (rot) { case 0: default: // no transform break; case 1: for (i = 0; i < words->getLength(); ++i) { word = (TextWord *)words->get(i); xMin = pageWidth - word->yMax; xMax = pageWidth - word->yMin; yMin = word->xMin; yMax = word->xMax; word->xMin = xMin; word->xMax = xMax; word->yMin = yMin; word->yMax = yMax; word->rot = (word->rot + 1) & 3; } break; case 2: for (i = 0; i < words->getLength(); ++i) { word = (TextWord *)words->get(i); xMin = pageWidth - word->xMax; xMax = pageWidth - word->xMin; yMin = pageHeight - word->yMax; yMax = pageHeight - word->yMin; word->xMin = xMin; word->xMax = xMax; word->yMin = yMin; word->yMax = yMax; word->rot = (word->rot + 2) & 3; for (j = 0; j <= word->len; ++j) { word->edge[j] = pageWidth - word->edge[j]; } } break; case 3: for (i = 0; i < words->getLength(); ++i) { word = (TextWord *)words->get(i); xMin = word->yMin; xMax = word->yMax; yMin = pageHeight - word->xMax; yMax = pageHeight - word->xMin; word->xMin = xMin; word->xMax = xMax; word->yMin = yMin; word->yMax = yMax; word->rot = (word->rot + 3) & 3; for (j = 0; j <= word->len; ++j) { word->edge[j] = pageHeight - word->edge[j]; } } break; } } // 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; 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; 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; } } } } 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; } } } } } // 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; } // if the page contains no (unclipped) text, just leave an empty // column list if (!tree[0]) { delete clippedChars; return NULL; } // if the main tree is not a multicolumn node, insert one so that // rotated text has somewhere to go if (tree[0]->tag != blkTagMulticolumn) { blk = new TextBlock(blkHorizSplit, 0); blk->addChild(tree[0]); blk->tag = blkTagMulticolumn; tree[0] = blk; } // merge non-primary-rotation text into the primary-rotation tree for (rot = 1; rot < 4; ++rot) { if (tree[rot]) { insertIntoTree(tree[rot], tree[0]); tree[rot] = NULL; } } if (clippedChars->getLength()) { insertClippedChars(clippedChars, tree[0]); } delete clippedChars; #if 0 //~debug dumpTree(tree[0]); #endif return tree[0]; } // Generate a tree of TextBlocks, marked as columns, lines, and words. TextBlock *TextPage::split(GList *charsA, int rot) { TextBlock *blk; GList *chars2, *chars3; int *horizProfile, *vertProfile; double xMin, yMin, xMax, yMax; int xMinI, yMinI, xMaxI, yMaxI; int xMinI2, yMinI2, xMaxI2, yMaxI2; TextChar *ch; double minFontSize, avgFontSize, splitPrecision; double nLines, vertGapThreshold, ascentAdjust, descentAdjust, minChunk; int horizGapSize, vertGapSize; double horizGapSize2, vertGapSize2; int minHorizChunkWidth, minVertChunkWidth, nHorizGaps, nVertGaps; double largeCharSize; int nLargeChars; GBool doHorizSplit, doVertSplit, smallSplit; int i, x, y, prev, start; //----- compute bbox, min font size, average font size, and // split precision for this block xMin = yMin = xMax = yMax = 0; // make gcc happy minFontSize = avgFontSize = 0; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); if (i == 0 || ch->xMin < xMin) { xMin = ch->xMin; } if (i == 0 || ch->yMin < yMin) { yMin = ch->yMin; } if (i == 0 || ch->xMax > xMax) { xMax = ch->xMax; } if (i == 0 || ch->yMax > yMax) { yMax = ch->yMax; } avgFontSize += ch->fontSize; if (i == 0 || ch->fontSize < minFontSize) { minFontSize = ch->fontSize; } } avgFontSize /= charsA->getLength(); splitPrecision = splitPrecisionMul * minFontSize; if (splitPrecision < minSplitPrecision) { splitPrecision = minSplitPrecision; } //----- compute the horizontal and vertical profiles // add some slack to the array bounds to avoid floating point // precision problems xMinI = (int)floor(xMin / splitPrecision) - 1; yMinI = (int)floor(yMin / splitPrecision) - 1; xMaxI = (int)floor(xMax / splitPrecision) + 1; yMaxI = (int)floor(yMax / splitPrecision) + 1; horizProfile = (int *)gmallocn(yMaxI - yMinI + 1, sizeof(int)); vertProfile = (int *)gmallocn(xMaxI - xMinI + 1, sizeof(int)); memset(horizProfile, 0, (yMaxI - yMinI + 1) * sizeof(int)); memset(vertProfile, 0, (xMaxI - xMinI + 1) * sizeof(int)); for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); // yMinI2 and yMaxI2 are adjusted to allow for slightly overlapping lines switch (rot) { case 0: default: xMinI2 = (int)floor(ch->xMin / splitPrecision); xMaxI2 = (int)floor(ch->xMax / splitPrecision); ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin); yMinI2 = (int)floor((ch->yMin + ascentAdjust) / splitPrecision); descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin); yMaxI2 = (int)floor((ch->yMax - descentAdjust) / splitPrecision); break; case 1: descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin); xMinI2 = (int)floor((ch->xMin + descentAdjust) / splitPrecision); ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin); xMaxI2 = (int)floor((ch->xMax - ascentAdjust) / splitPrecision); yMinI2 = (int)floor(ch->yMin / splitPrecision); yMaxI2 = (int)floor(ch->yMax / splitPrecision); break; case 2: xMinI2 = (int)floor(ch->xMin / splitPrecision); xMaxI2 = (int)floor(ch->xMax / splitPrecision); descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin); yMinI2 = (int)floor((ch->yMin + descentAdjust) / splitPrecision); ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin); yMaxI2 = (int)floor((ch->yMax - ascentAdjust) / splitPrecision); break; case 3: ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin); xMinI2 = (int)floor((ch->xMin + ascentAdjust) / splitPrecision); descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin); xMaxI2 = (int)floor((ch->xMax - descentAdjust) / splitPrecision); yMinI2 = (int)floor(ch->yMin / splitPrecision); yMaxI2 = (int)floor(ch->yMax / splitPrecision); break; } for (y = yMinI2; y <= yMaxI2; ++y) { ++horizProfile[y - yMinI]; } for (x = xMinI2; x <= xMaxI2; ++x) { ++vertProfile[x - xMinI]; } } //----- find the largest gaps in the horizontal and vertical profiles horizGapSize = 0; for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ; for (y = start; y < yMaxI; ++y) { if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) { start = y; } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) { if (y - start > horizGapSize) { horizGapSize = y - start; } } } vertGapSize = 0; for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ; for (x = start; x < xMaxI; ++x) { if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) { start = x; } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) { if (x - start > vertGapSize) { vertGapSize = x - start; } } } horizGapSize2 = horizGapSize - splitGapSlack * avgFontSize / splitPrecision; if (horizGapSize2 < 0.99) { horizGapSize2 = 0.99; } vertGapSize2 = vertGapSize - splitGapSlack * avgFontSize / splitPrecision; if (vertGapSize2 < 0.99) { vertGapSize2 = 0.99; } //----- count horiz/vert gaps equivalent to largest gaps minHorizChunkWidth = yMaxI - yMinI; nHorizGaps = 0; for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ; prev = start - 1; for (y = start; y < yMaxI; ++y) { if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) { start = y; } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) { if (y - start > horizGapSize2) { ++nHorizGaps; if (start - prev < minHorizChunkWidth) { minHorizChunkWidth = start - prev; } prev = y; } } } minVertChunkWidth = xMaxI - xMinI; nVertGaps = 0; for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ; prev = start - 1; for (x = start; x < xMaxI; ++x) { if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) { start = x; } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) { if (x - start > vertGapSize2) { ++nVertGaps; if (start - prev < minVertChunkWidth) { minVertChunkWidth = start - prev; } prev = x; } } } //----- compute splitting parameters // approximation of number of lines in block if (fabs(avgFontSize) < 0.001) { nLines = 1; } else if (rot & 1) { nLines = (xMax - xMin) / avgFontSize; } else { nLines = (yMax - yMin) / avgFontSize; } // compute the minimum allowed vertical gap size // (this is a horizontal gap threshold for rot=1,3 if (control.mode == textOutTableLayout) { vertGapThreshold = vertGapThresholdTableMax + vertGapThresholdTableSlope * nLines; if (vertGapThreshold < vertGapThresholdTableMin) { vertGapThreshold = vertGapThresholdTableMin; } } else { vertGapThreshold = vertGapThresholdMax + vertGapThresholdSlope * nLines; if (vertGapThreshold < vertGapThresholdMin) { vertGapThreshold = vertGapThresholdMin; } } vertGapThreshold = vertGapThreshold * avgFontSize / splitPrecision; // compute the minimum allowed chunk width if (control.mode == textOutTableLayout) { minChunk = 0; } else { minChunk = vertSplitChunkThreshold * avgFontSize / splitPrecision; } // look for large chars // -- this kludge (multiply by 256, convert to int, divide by 256.0) // prevents floating point stability issues on x86 with gcc, where // largeCharSize could otherwise have slightly different values // here and where it's used below to do the large char partition // (because it gets truncated from 80 to 64 bits when spilled) largeCharSize = (int)(largeCharThreshold * avgFontSize * 256) / 256.0; nLargeChars = 0; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); if (ch->fontSize > largeCharSize) { ++nLargeChars; } } // figure out which type of split to do doHorizSplit = doVertSplit = gFalse; smallSplit = gFalse; if (rot & 1) { if (nHorizGaps > 0 && (horizGapSize > vertGapSize || control.mode == textOutTableLayout) && horizGapSize > vertGapThreshold && minHorizChunkWidth > minChunk) { doHorizSplit = gTrue; } else if (nVertGaps > 0) { doVertSplit = gTrue; } else if (nLargeChars == 0 && nHorizGaps > 0) { doHorizSplit = gTrue; smallSplit = gTrue; } } else { if (nVertGaps > 0 && (vertGapSize > horizGapSize || control.mode == textOutTableLayout) && vertGapSize > vertGapThreshold && minVertChunkWidth > minChunk) { doVertSplit = gTrue; } else if (nHorizGaps > 0) { doHorizSplit = gTrue; } else if (nLargeChars == 0 && nVertGaps > 0) { doVertSplit = gTrue; smallSplit = gTrue; } } //----- 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)); } } gfree(horizProfile); gfree(vertProfile); tagBlock(blk); return blk; } // Return the subset of chars inside a rectangle. GList *TextPage::getChars(GList *charsA, double xMin, double yMin, double xMax, double yMax) { GList *ret; TextChar *ch; double x, y; int i; ret = new GList(); for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); // because of {ascent,descent}AdjustFactor, the y coords (or x // coords for rot 1,3) for the gaps will be a little bit tight -- // so we use the center of the character here x = 0.5 * (ch->xMin + ch->xMax); y = 0.5 * (ch->yMin + ch->yMax); if (x > xMin && x < xMax && y > yMin && y < yMax) { ret->append(ch); } } return ret; } // Decide whether this block is a line, column, or multiple columns: // - all leaf nodes are lines // - horiz split nodes whose children are lines or columns are columns // - other horiz split nodes are multiple columns // - vert split nodes, with small gaps, whose children are lines are lines // - other vert split nodes are multiple columns // (for rot=1,3: the horiz and vert splits are swapped) // In table layout mode: // - all leaf nodes are lines // - vert split nodes, with small gaps, whose children are lines are lines // - everything else is multiple columns void TextPage::tagBlock(TextBlock *blk) { TextBlock *child; int i; if (control.mode == textOutTableLayout) { if (blk->type == blkLeaf) { blk->tag = blkTagLine; } else if (blk->type == ((blk->rot & 1) ? blkHorizSplit : blkVertSplit) && blk->smallSplit) { blk->tag = blkTagLine; for (i = 0; i < blk->children->getLength(); ++i) { child = (TextBlock *)blk->children->get(i); if (child->tag != blkTagLine) { blk->tag = blkTagMulticolumn; break; } } } else { blk->tag = blkTagMulticolumn; } return; } if (blk->type == blkLeaf) { blk->tag = blkTagLine; } else { if (blk->type == ((blk->rot & 1) ? blkVertSplit : blkHorizSplit)) { blk->tag = blkTagColumn; for (i = 0; i < blk->children->getLength(); ++i) { child = (TextBlock *)blk->children->get(i); if (child->tag != blkTagColumn && child->tag != blkTagLine) { blk->tag = blkTagMulticolumn; break; } } } else { if (blk->smallSplit) { blk->tag = blkTagLine; for (i = 0; i < blk->children->getLength(); ++i) { child = (TextBlock *)blk->children->get(i); if (child->tag != blkTagLine) { blk->tag = blkTagMulticolumn; break; } } } else { blk->tag = blkTagMulticolumn; } } } } // 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; } if (i > 0) { ch = (TextChar *)largeChars->get(i-1); minOverlap = 0.5 * (ch->fontSize < ch2->fontSize ? ch->fontSize : ch2->fontSize); if (ch->yMax - ch2->yMin < minOverlap || ch2->yMax - ch->yMin < minOverlap) { singleLine = gFalse; break; } } } if (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); } } } // Find the first leaf (in depth-first order) in blk, and prepend a // list of large chars. void TextPage::insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk) { TextChar *ch; int i; if (blk->type == blkLeaf) { for (i = largeChars->getLength() - 1; i >= 0; --i) { ch = (TextChar *)largeChars->get(i); blk->prependChild(ch); } } else { insertLargeCharsInFirstLeaf(largeChars, (TextBlock *)blk->children->get(0)); blk->updateBounds(0); } } // Find the leaf in where large char belongs, and prepend // it. void TextPage::insertLargeCharInLeaf(TextChar *ch, TextBlock *blk) { TextBlock *child; double y; int i; //~ this currently works only for characters in the primary rotation //~ this currently just looks down the left edge of blk //~ -- it could be extended to do more // estimate the baseline of ch y = ch->yMin + 0.75 * (ch->yMax - ch->yMin); if (blk->type == blkLeaf) { blk->prependChild(ch); } else if (blk->type == blkHorizSplit) { for (i = 0; i < blk->children->getLength(); ++i) { child = (TextBlock *)blk->children->get(i); if (y < child->yMax || i == blk->children->getLength() - 1) { insertLargeCharInLeaf(ch, child); blk->updateBounds(i); break; } } } else { insertLargeCharInLeaf(ch, (TextBlock *)blk->children->get(0)); blk->updateBounds(0); } } // Merge blk (rot != 0) into primaryTree (rot == 0). void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree) { TextBlock *child; // we insert a whole column at a time - so call insertIntoTree // recursively until we get to a column (or line) if (blk->tag == blkTagMulticolumn) { while (blk->children->getLength()) { child = (TextBlock *)blk->children->del(0); insertIntoTree(child, primaryTree); } delete blk; } else { insertColumnIntoTree(blk, primaryTree); } } // Insert a column (as an atomic subtree) into tree. // Requirement: tree is not a leaf node. void TextPage::insertColumnIntoTree(TextBlock *column, TextBlock *tree) { TextBlock *child; int i; for (i = 0; i < tree->children->getLength(); ++i) { child = (TextBlock *)tree->children->get(i); if (child->tag == blkTagMulticolumn && column->xMin >= child->xMin && column->yMin >= child->yMin && column->xMax <= child->xMax && column->yMax <= child->yMax) { insertColumnIntoTree(column, child); tree->tag = blkTagMulticolumn; return; } } if (tree->type == blkVertSplit) { if (tree->rot == 1 || tree->rot == 2) { for (i = 0; i < tree->children->getLength(); ++i) { child = (TextBlock *)tree->children->get(i); if (column->xMax > 0.5 * (child->xMin + child->xMax)) { break; } } } else { for (i = 0; i < tree->children->getLength(); ++i) { child = (TextBlock *)tree->children->get(i); if (column->xMin < 0.5 * (child->xMin + child->xMax)) { break; } } } } else if (tree->type == blkHorizSplit) { if (tree->rot >= 2) { for (i = 0; i < tree->children->getLength(); ++i) { child = (TextBlock *)tree->children->get(i); if (column->yMax > 0.5 * (child->yMin + child->yMax)) { break; } } } else { for (i = 0; i < tree->children->getLength(); ++i) { child = (TextBlock *)tree->children->get(i); if (column->yMin < 0.5 * (child->yMin + child->yMax)) { break; } } } } else { // this should never happen return; } tree->children->insert(i, column); tree->tag = blkTagMulticolumn; } // Insert clipped characters back into the TextBlock tree. void TextPage::insertClippedChars(GList *clippedChars, TextBlock *tree) { TextChar *ch, *ch2; TextBlock *leaf; double y; int i; //~ this currently works only for characters in the primary rotation clippedChars->sort(TextChar::cmpX); while (clippedChars->getLength()) { ch = (TextChar *)clippedChars->del(0); if (ch->rot != 0) { continue; } if (!(leaf = findClippedCharLeaf(ch, tree))) { continue; } leaf->addChild(ch); i = 0; while (i < clippedChars->getLength()) { ch2 = (TextChar *)clippedChars->get(i); if (ch2->xMin > ch->xMax + clippedTextMaxWordSpace * ch->fontSize) { break; } y = 0.5 * (ch2->yMin + ch2->yMax); if (y > leaf->yMin && y < leaf->yMax) { ch2 = (TextChar *)clippedChars->del(i); leaf->addChild(ch2); ch = ch2; } else { ++i; } } } } // Find the leaf in 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; //~ this currently works only for characters in the primary rotation y = 0.5 * (ch->yMin + ch->yMax); if (tree->type == blkLeaf) { if (tree->rot == 0) { if (y > tree->yMin && y < tree->yMax && ch->xMin <= tree->xMax + clippedTextMaxWordSpace * ch->fontSize) { return tree; } } } else { for (i = 0; i < tree->children->getLength(); ++i) { child = (TextBlock *)tree->children->get(i); if ((ret = findClippedCharLeaf(ch, child))) { return ret; } } } return NULL; } // Convert the tree of TextBlocks into a list of TextColumns. GList *TextPage::buildColumns(TextBlock *tree) { GList *columns; columns = new GList(); buildColumns2(tree, columns); return columns; } void TextPage::buildColumns2(TextBlock *blk, GList *columns) { TextColumn *col; int i; 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; } } TextColumn *TextPage::buildColumn(TextBlock *blk) { GList *lines, *parLines; GList *paragraphs; TextLine *line0, *line1; double spaceThresh, indent0, indent1, fontSize0, fontSize1; int i; lines = new GList(); buildLines(blk, lines); spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines); //~ could look for bulleted lists here: look for the case where //~ all out-dented lines start with the same char // build the paragraphs paragraphs = new GList(); i = 0; while (i < lines->getLength()) { // get the first line of the paragraph parLines = new GList(); line0 = (TextLine *)lines->get(i); parLines->append(line0); ++i; if (i < lines->getLength()) { line1 = (TextLine *)lines->get(i); indent0 = getLineIndent(line0, blk); indent1 = getLineIndent(line1, blk); fontSize0 = line0->fontSize; fontSize1 = line1->fontSize; // inverted indent if (indent1 - indent0 > minParagraphIndent * fontSize0 && fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta && getLineSpacing(line0, line1) <= spaceThresh) { parLines->append(line1); indent0 = indent1; for (++i; i < lines->getLength(); ++i) { line1 = (TextLine *)lines->get(i); indent1 = getLineIndent(line1, blk); fontSize1 = line1->fontSize; if (indent0 - indent1 > minParagraphIndent * fontSize0) { break; } if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) { break; } if (getLineSpacing((TextLine *)lines->get(i - 1), line1) > spaceThresh) { break; } parLines->append(line1); } // drop cap } else if (fontSize0 > largeCharThreshold * fontSize1 && indent1 - indent0 > minParagraphIndent * fontSize1 && getLineSpacing(line0, line1) < 0) { parLines->append(line1); fontSize0 = fontSize1; for (++i; i < lines->getLength(); ++i) { line1 = (TextLine *)lines->get(i); indent1 = getLineIndent(line1, blk); if (indent1 - indent0 <= minParagraphIndent * fontSize0) { break; } if (getLineSpacing((TextLine *)lines->get(i - 1), line1) > spaceThresh) { break; } parLines->append(line1); } 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); } // regular indent or no indent } else if (fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta && getLineSpacing(line0, line1) <= spaceThresh) { parLines->append(line1); indent0 = indent1; for (++i; i < lines->getLength(); ++i) { line1 = (TextLine *)lines->get(i); indent1 = getLineIndent(line1, blk); fontSize1 = line1->fontSize; if (indent1 - indent0 > minParagraphIndent * fontSize0) { break; } if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) { break; } if (getLineSpacing((TextLine *)lines->get(i - 1), line1) > spaceThresh) { break; } parLines->append(line1); } } } paragraphs->append(new TextParagraph(parLines)); } delete lines; return new TextColumn(paragraphs, blk->xMin, blk->yMin, blk->xMax, blk->yMax); } double TextPage::getLineIndent(TextLine *line, TextBlock *blk) { double indent; switch (line->rot) { case 0: default: indent = line->xMin - blk->xMin; break; case 1: indent = line->yMin - blk->yMin; break; case 2: indent = blk->xMax - line->xMax; break; case 3: indent = blk->yMax - line->yMax; break; } return indent; } // Compute average line spacing in column. double TextPage::getAverageLineSpacing(GList *lines) { double avg, sp; int n, i; avg = 0; n = 0; for (i = 1; i < lines->getLength(); ++i) { sp = getLineSpacing((TextLine *)lines->get(i - 1), (TextLine *)lines->get(i)); if (sp > 0) { avg += sp; ++n; } } if (n > 0) { avg /= n; } return avg; } // Compute the space between two lines. double TextPage::getLineSpacing(TextLine *line0, TextLine *line1) { double sp; switch (line0->rot) { case 0: default: sp = line1->yMin - line0->yMax; break; case 1: sp = line0->xMin - line1->xMax; break; case 2: sp = line0->yMin - line1->yMin; break; case 3: sp = line1->xMin - line1->xMax; break; } return sp; } void TextPage::buildLines(TextBlock *blk, GList *lines) { TextLine *line; int i; switch (blk->tag) { case blkTagLine: line = buildLine(blk); if (blk->rot == 1 || blk->rot == 2) { lines->insert(0, line); } else { lines->append(line); } break; case blkTagColumn: case blkTagMulticolumn: // multicolumn should never happen here for (i = 0; i < blk->children->getLength(); ++i) { buildLines((TextBlock *)blk->children->get(i), lines); } break; } } TextLine *TextPage::buildLine(TextBlock *blk) { GList *charsA; GList *words; TextChar *ch, *ch2; TextWord *word; double wordSp, lineFontSize, sp; GBool spaceAfter, spaceAfter2; int i, j; 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; } } delete charsA; return new TextLine(words, blk->xMin, blk->yMin, blk->xMax, blk->yMax, lineFontSize); } void TextPage::getLineChars(TextBlock *blk, GList *charsA) { int i; if (blk->type == blkLeaf) { charsA->append(blk->children); } else { for (i = 0; i < blk->children->getLength(); ++i) { getLineChars((TextBlock *)blk->children->get(i), charsA); } } } // Compute the inter-word spacing threshold for a line of chars. // Spaces greater than this threshold will be considered inter-word // spaces. double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) { TextChar *ch, *ch2; double avgFontSize, minSp, maxSp, sp; int i; avgFontSize = 0; minSp = maxSp = 0; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); avgFontSize += ch->fontSize; if (i < charsA->getLength() - 1) { ch2 = (TextChar *)charsA->get(i+1); sp = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax); if (i == 0 || sp < minSp) { minSp = sp; } if (sp > maxSp) { maxSp = sp; } } } 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; } 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; } } col->ph += par->lines->getLength(); } col->ph += col->paragraphs->getLength() - 1; } uMap->decRefCnt(); } void TextPage::computeLinePhysWidth(TextLine *line, UnicodeMap *uMap) { char buf[8]; int n, i; if (uMap->isUnicode()) { line->pw = line->len; } else { line->pw = 0; for (i = 0; i < line->len; ++i) { n = uMap->mapUnicode(line->text[i], buf, sizeof(buf)); line->pw += n; } } } // Assign physical x and y coordinates for each TextColumn. Returns // the text height (max physical y + 1). int TextPage::assignColumnPhysPositions(GList *columns) { TextColumn *col, *col2; double slack, xOverlap, yOverlap; int ph, i, j; if (control.mode == textOutTableLayout) { slack = tableCellOverlapSlack; } else { slack = 0; } // assign x positions columns->sort(&TextColumn::cmpX); for (i = 0; i < columns->getLength(); ++i) { col = (TextColumn *)columns->get(i); if (control.fixedPitch) { col->px = (int)(col->xMin / control.fixedPitch); } else { 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; } } } } } } // assign y positions ph = 0; columns->sort(&TextColumn::cmpY); for (i = 0; i < columns->getLength(); ++i) { col = (TextColumn *)columns->get(i); col->py = 0; for (j = 0; j < i; ++j) { col2 = (TextColumn *)columns->get(j); yOverlap = col2->yMax - col->yMin; if (yOverlap < slack * (col2->yMax - col2->yMin)) { if (col2->py + col2->ph + 1 > col->py) { col->py = col2->py + col2->ph + 1; } } else { xOverlap = (col->xMax < col2->xMax ? col->xMax : col2->xMax) - (col->xMin > col2->xMin ? col->xMin : col2->xMin); if (xOverlap > 0 && yOverlap < xOverlap) { if (col2->py + col2->ph > col->py) { col->py = col2->py + col2->ph; } } else { if (col2->py > col->py) { col->py = col2->py; } } } } if (col->py + col->ph > ph) { ph = col->py + col->ph; } } return ph; } void TextPage::generateUnderlinesAndLinks(GList *columns) { TextColumn *col; TextParagraph *par; TextLine *line; TextWord *word; TextUnderline *underline; TextLink *link; double base, uSlack, ubSlack, hSlack; int colIdx, parIdx, lineIdx, wordIdx, i; for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) { col = (TextColumn *)columns->get(colIdx); for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) { par = (TextParagraph *)col->paragraphs->get(parIdx); for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) { line = (TextLine *)par->lines->get(lineIdx); for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) { word = (TextWord *)line->words->get(wordIdx); base = word->getBaseline(); uSlack = underlineSlack * word->fontSize; ubSlack = underlineBaselineSlack * word->fontSize; hSlack = hyperlinkSlack * word->fontSize; //----- handle underlining for (i = 0; i < underlines->getLength(); ++i) { underline = (TextUnderline *)underlines->get(i); if (underline->horiz) { if (word->rot == 0 || word->rot == 2) { if (fabs(underline->y0 - base) < ubSlack && underline->x0 < word->xMin + uSlack && word->xMax - uSlack < underline->x1) { word->underlined = gTrue; } } } else { if (word->rot == 1 || word->rot == 3) { if (fabs(underline->x0 - base) < ubSlack && underline->y0 < word->yMin + uSlack && word->yMax - uSlack < underline->y1) { word->underlined = gTrue; } } } } //----- handle links for (i = 0; i < links->getLength(); ++i) { link = (TextLink *)links->get(i); if (link->xMin < word->xMin + hSlack && word->xMax - hSlack < link->xMax && link->yMin < word->yMin + hSlack && word->yMax - hSlack < link->yMax) { word->link = link; } } } } } } } //------------------------------------------------------------------------ // TextPage: access //------------------------------------------------------------------------ GBool TextPage::findText(Unicode *s, int len, GBool startAtTop, GBool stopAtBottom, GBool startAtLast, GBool stopAtLast, GBool caseSensitive, GBool backward, GBool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax) { TextBlock *tree; TextColumn *column; TextParagraph *par; TextLine *line; Unicode *s2, *txt; Unicode *p; 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; //~ need to handle right-to-left text 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 if (!caseSensitive) { s2 = (Unicode *)gmallocn(len, sizeof(Unicode)); for (i = 0; i < len; ++i) { s2[i] = unicodeToUpper(s[i]); } } else { s2 = s; } txt = NULL; txtSize = 0; xStart = yStart = xStop = yStop = 0; if (startAtLast && haveLastFind) { xStart = lastFindXMin; yStart = lastFindYMin; } else if (!startAtTop) { xStart = *xMin; yStart = *yMin; } if (stopAtLast && haveLastFind) { xStop = lastFindXMin; yStop = lastFindYMin; } else if (!stopAtBottom) { xStop = *xMax; yStop = *yMax; } found = gFalse; xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy for (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; } // check: is the column below the bottom limit? if (!stopAtBottom && (backward ? column->yMax < yStop : column->yMin > yStop)) { continue; } 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; } // check: is the paragraph below the bottom limit? if (!stopAtBottom && (backward ? par->yMax < yStop : par->yMin > yStop)) { continue; } 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; } // check: is the line below the bottom limit? if (!stopAtBottom && (backward ? line->yMax < 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 || !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 (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) { UnicodeMap *uMap; char space[8], eol[16]; int spaceLen, eolLen; GList *chars2; GString **out; int *outLen; TextColumn *col; TextParagraph *par; TextLine *line; TextChar *ch; GBool primaryLR; TextBlock *tree; GList *columns; GString *ret; double xx, yy; int rot, colIdx, parIdx, lineIdx, ph, y, i; // get the output encoding if (!(uMap = globalParams->getTextEncoding())) { return NULL; } 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; } // 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 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; } } } ret = new GString(); for (i = 0; i < ph; ++i) { if (out[i]) { ret->append(out[i]); delete out[i]; } if (ph > 1) { ret->append(eol, eolLen); } } gfree(out); gfree(outLen); deleteGList(columns, TextColumn); uMap->decRefCnt(); return ret; } 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; //~ this doesn't correctly handle ranges split across multiple lines //~ (the highlighted region is the bounding box of all the parts of //~ the range) xMin2 = yMin2 = xMax2 = yMax2 = 0; first = gTrue; for (i = 0; i < chars->getLength(); ++i) { ch = (TextChar *)chars->get(i); if (ch->charPos >= pos && ch->charPos < pos + length) { if (first || ch->xMin < xMin2) { xMin2 = ch->xMin; } if (first || ch->yMin < yMin2) { yMin2 = ch->yMin; } if (first || ch->xMax > xMax2) { xMax2 = ch->xMax; } if (first || ch->yMax > yMax2) { yMax2 = ch->yMax; } first = gFalse; } } if (first) { return gFalse; } *xMin = xMin2; *yMin = yMin2; *xMax = xMax2; *yMax = yMax2; return gTrue; } 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()); } } } } 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; } // this has to be done after sorting with cmpYX unrotateColumns(columns, rot); unrotateWords(words, rot); deleteGList(columns, TextColumn); return new TextWordList(words); } //------------------------------------------------------------------------ // TextPage: debug //------------------------------------------------------------------------ #if 0 //~debug void TextPage::dumpChars(GList *charsA) { TextChar *ch; int i; for (i = 0; i < charsA->getLength(); ++i) { ch = (TextChar *)charsA->get(i); printf("char: U+%04x '%c' xMin=%g yMin=%g xMax=%g yMax=%g fontSize=%g rot=%d\n", ch->c, ch->c & 0xff, ch->xMin, ch->yMin, ch->xMax, ch->yMax, ch->fontSize, ch->rot); } } void TextPage::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 < tree->children->getLength(); ++i) { dumpTree((TextBlock *)tree->children->get(i), indent + 2); } } } 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 //~debug //------------------------------------------------------------------------ // TextOutputDev //------------------------------------------------------------------------ static void outputToFile(void *stream, const char *text, int len) { fwrite(text, 1, len, (FILE *)stream); } TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA, GBool append) { text = NULL; control = *controlA; ok = gTrue; // open file needClose = gFalse; if (fileName) { if (!strcmp(fileName, "-")) { outputStream = stdout; #ifdef WIN32 // keep DOS from munging the end-of-line characters setmode(fileno(stdout), O_BINARY); #endif } else if ((outputStream = fopen(fileName, append ? "ab" : "wb"))) { needClose = gTrue; } else { error(errIO, -1, "Couldn't open text file '{0:s}'", fileName); ok = gFalse; return; } outputFunc = &outputToFile; } else { outputStream = NULL; } // set up text object text = new TextPage(&control); } TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream, TextOutputControl *controlA) { outputFunc = func; outputStream = stream; needClose = gFalse; control = *controlA; text = new TextPage(&control); ok = gTrue; } TextOutputDev::~TextOutputDev() { if (needClose) { fclose((FILE *)outputStream); } if (text) { delete text; } } void TextOutputDev::startPage(int pageNum, GfxState *state) { text->startPage(state); } void TextOutputDev::endPage() { if (outputStream) { text->write(outputStream, outputFunc); } } void TextOutputDev::restoreState(GfxState *state) { text->updateFont(state); } void TextOutputDev::updateFont(GfxState *state) { text->updateFont(state); } void TextOutputDev::beginString(GfxState *state, GString *s) { } void TextOutputDev::endString(GfxState *state) { } void TextOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode c, int nBytes, Unicode *u, int uLen) { text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen); } void TextOutputDev::incCharCount(int nChars) { text->incCharCount(nChars); } void TextOutputDev::beginActualText(GfxState *state, Unicode *u, int uLen) { text->beginActualText(state, u, uLen); } void TextOutputDev::endActualText(GfxState *state) { text->endActualText(state); } void TextOutputDev::stroke(GfxState *state) { GfxPath *path; GfxSubpath *subpath; double x[2], y[2]; if (!control.html) { return; } path = state->getPath(); if (path->getNumSubpaths() != 1) { return; } subpath = path->getSubpath(0); if (subpath->getNumPoints() != 2) { return; } state->transform(subpath->getX(0), subpath->getY(0), &x[0], &y[0]); state->transform(subpath->getX(1), subpath->getY(1), &x[1], &y[1]); // look for a vertical or horizontal line if (x[0] == x[1] || y[0] == y[1]) { text->addUnderline(x[0], y[0], x[1], y[1]); } } void TextOutputDev::fill(GfxState *state) { GfxPath *path; GfxSubpath *subpath; double x[5], y[5]; double rx0, ry0, rx1, ry1, t; int i; if (!control.html) { return; } path = state->getPath(); if (path->getNumSubpaths() != 1) { return; } subpath = path->getSubpath(0); if (subpath->getNumPoints() != 5) { return; } for (i = 0; i < 5; ++i) { if (subpath->getCurve(i)) { return; } state->transform(subpath->getX(i), subpath->getY(i), &x[i], &y[i]); } // look for a rectangle if (x[0] == x[1] && y[1] == y[2] && x[2] == x[3] && y[3] == y[4] && x[0] == x[4] && y[0] == y[4]) { rx0 = x[0]; ry0 = y[0]; rx1 = x[2]; ry1 = y[1]; } else if (y[0] == y[1] && x[1] == x[2] && y[2] == y[3] && x[3] == x[4] && x[0] == x[4] && y[0] == y[4]) { rx0 = x[0]; ry0 = y[0]; rx1 = x[1]; ry1 = y[2]; } else { return; } if (rx1 < rx0) { t = rx0; rx0 = rx1; rx1 = t; } if (ry1 < ry0) { t = ry0; ry0 = ry1; ry1 = t; } // skinny horizontal rectangle if (ry1 - ry0 < rx1 - rx0) { if (ry1 - ry0 < maxUnderlineWidth) { ry0 = 0.5 * (ry0 + ry1); text->addUnderline(rx0, ry0, rx1, ry0); } // skinny vertical rectangle } else { if (rx1 - rx0 < maxUnderlineWidth) { rx0 = 0.5 * (rx0 + rx1); text->addUnderline(rx0, ry0, rx0, ry1); } } } void TextOutputDev::eoFill(GfxState *state) { if (!control.html) { return; } fill(state); } void TextOutputDev::processLink(Link *link) { double x1, y1, x2, y2; int xMin, yMin, xMax, yMax, x, y; if (!control.html) { return; } link->getRect(&x1, &y1, &x2, &y2); cvtUserToDev(x1, y1, &x, &y); xMin = xMax = x; yMin = yMax = y; cvtUserToDev(x1, y2, &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } cvtUserToDev(x2, y1, &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } cvtUserToDev(x2, y2, &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } text->addLink(xMin, yMin, xMax, yMax, link); } GBool TextOutputDev::findText(Unicode *s, int len, GBool startAtTop, GBool stopAtBottom, GBool startAtLast, GBool stopAtLast, GBool caseSensitive, GBool backward, GBool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax) { return text->findText(s, len, startAtTop, stopAtBottom, startAtLast, stopAtLast, caseSensitive, backward, wholeWord, xMin, yMin, xMax, yMax); } GString *TextOutputDev::getText(double xMin, double yMin, double xMax, double yMax) { return text->getText(xMin, yMin, xMax, yMax); } GBool TextOutputDev::findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) { return text->findCharRange(pos, length, xMin, yMin, xMax, yMax); } TextWordList *TextOutputDev::makeWordList() { return text->makeWordList(); } TextPage *TextOutputDev::takeText() { TextPage *ret; ret = text; text = new TextPage(&control); return ret; } xpdf-3.04/xpdf/JBIG2Stream.cc0000644000076400007640000031452112341430012015130 0ustar dereknderekn//======================================================================== // // JBIG2Stream.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "GList.h" #include "Error.h" #include "JArithmeticDecoder.h" #include "JBIG2Stream.h" //~ share these tables #include "Stream-CCITT.h" //------------------------------------------------------------------------ static int contextSize[4] = { 16, 13, 10, 10 }; static int refContextSize[2] = { 13, 10 }; //------------------------------------------------------------------------ // JBIG2HuffmanTable //------------------------------------------------------------------------ #define jbig2HuffmanLOW 0xfffffffd #define jbig2HuffmanOOB 0xfffffffe #define jbig2HuffmanEOT 0xffffffff struct JBIG2HuffmanTable { int val; Guint prefixLen; Guint rangeLen; // can also be LOW, OOB, or EOT Guint prefix; }; JBIG2HuffmanTable huffTableA[] = { { 0, 1, 4, 0x000 }, { 16, 2, 8, 0x002 }, { 272, 3, 16, 0x006 }, { 65808, 3, 32, 0x007 }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableB[] = { { 0, 1, 0, 0x000 }, { 1, 2, 0, 0x002 }, { 2, 3, 0, 0x006 }, { 3, 4, 3, 0x00e }, { 11, 5, 6, 0x01e }, { 75, 6, 32, 0x03e }, { 0, 6, jbig2HuffmanOOB, 0x03f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableC[] = { { 0, 1, 0, 0x000 }, { 1, 2, 0, 0x002 }, { 2, 3, 0, 0x006 }, { 3, 4, 3, 0x00e }, { 11, 5, 6, 0x01e }, { 0, 6, jbig2HuffmanOOB, 0x03e }, { 75, 7, 32, 0x0fe }, { -256, 8, 8, 0x0fe }, { -257, 8, jbig2HuffmanLOW, 0x0ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableD[] = { { 1, 1, 0, 0x000 }, { 2, 2, 0, 0x002 }, { 3, 3, 0, 0x006 }, { 4, 4, 3, 0x00e }, { 12, 5, 6, 0x01e }, { 76, 5, 32, 0x01f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableE[] = { { 1, 1, 0, 0x000 }, { 2, 2, 0, 0x002 }, { 3, 3, 0, 0x006 }, { 4, 4, 3, 0x00e }, { 12, 5, 6, 0x01e }, { 76, 6, 32, 0x03e }, { -255, 7, 8, 0x07e }, { -256, 7, jbig2HuffmanLOW, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableF[] = { { 0, 2, 7, 0x000 }, { 128, 3, 7, 0x002 }, { 256, 3, 8, 0x003 }, { -1024, 4, 9, 0x008 }, { -512, 4, 8, 0x009 }, { -256, 4, 7, 0x00a }, { -32, 4, 5, 0x00b }, { 512, 4, 9, 0x00c }, { 1024, 4, 10, 0x00d }, { -2048, 5, 10, 0x01c }, { -128, 5, 6, 0x01d }, { -64, 5, 5, 0x01e }, { -2049, 6, jbig2HuffmanLOW, 0x03e }, { 2048, 6, 32, 0x03f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableG[] = { { -512, 3, 8, 0x000 }, { 256, 3, 8, 0x001 }, { 512, 3, 9, 0x002 }, { 1024, 3, 10, 0x003 }, { -1024, 4, 9, 0x008 }, { -256, 4, 7, 0x009 }, { -32, 4, 5, 0x00a }, { 0, 4, 5, 0x00b }, { 128, 4, 7, 0x00c }, { -128, 5, 6, 0x01a }, { -64, 5, 5, 0x01b }, { 32, 5, 5, 0x01c }, { 64, 5, 6, 0x01d }, { -1025, 5, jbig2HuffmanLOW, 0x01e }, { 2048, 5, 32, 0x01f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableH[] = { { 0, 2, 1, 0x000 }, { 0, 2, jbig2HuffmanOOB, 0x001 }, { 4, 3, 4, 0x004 }, { -1, 4, 0, 0x00a }, { 22, 4, 4, 0x00b }, { 38, 4, 5, 0x00c }, { 2, 5, 0, 0x01a }, { 70, 5, 6, 0x01b }, { 134, 5, 7, 0x01c }, { 3, 6, 0, 0x03a }, { 20, 6, 1, 0x03b }, { 262, 6, 7, 0x03c }, { 646, 6, 10, 0x03d }, { -2, 7, 0, 0x07c }, { 390, 7, 8, 0x07d }, { -15, 8, 3, 0x0fc }, { -5, 8, 1, 0x0fd }, { -7, 9, 1, 0x1fc }, { -3, 9, 0, 0x1fd }, { -16, 9, jbig2HuffmanLOW, 0x1fe }, { 1670, 9, 32, 0x1ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableI[] = { { 0, 2, jbig2HuffmanOOB, 0x000 }, { -1, 3, 1, 0x002 }, { 1, 3, 1, 0x003 }, { 7, 3, 5, 0x004 }, { -3, 4, 1, 0x00a }, { 43, 4, 5, 0x00b }, { 75, 4, 6, 0x00c }, { 3, 5, 1, 0x01a }, { 139, 5, 7, 0x01b }, { 267, 5, 8, 0x01c }, { 5, 6, 1, 0x03a }, { 39, 6, 2, 0x03b }, { 523, 6, 8, 0x03c }, { 1291, 6, 11, 0x03d }, { -5, 7, 1, 0x07c }, { 779, 7, 9, 0x07d }, { -31, 8, 4, 0x0fc }, { -11, 8, 2, 0x0fd }, { -15, 9, 2, 0x1fc }, { -7, 9, 1, 0x1fd }, { -32, 9, jbig2HuffmanLOW, 0x1fe }, { 3339, 9, 32, 0x1ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableJ[] = { { -2, 2, 2, 0x000 }, { 6, 2, 6, 0x001 }, { 0, 2, jbig2HuffmanOOB, 0x002 }, { -3, 5, 0, 0x018 }, { 2, 5, 0, 0x019 }, { 70, 5, 5, 0x01a }, { 3, 6, 0, 0x036 }, { 102, 6, 5, 0x037 }, { 134, 6, 6, 0x038 }, { 198, 6, 7, 0x039 }, { 326, 6, 8, 0x03a }, { 582, 6, 9, 0x03b }, { 1094, 6, 10, 0x03c }, { -21, 7, 4, 0x07a }, { -4, 7, 0, 0x07b }, { 4, 7, 0, 0x07c }, { 2118, 7, 11, 0x07d }, { -5, 8, 0, 0x0fc }, { 5, 8, 0, 0x0fd }, { -22, 8, jbig2HuffmanLOW, 0x0fe }, { 4166, 8, 32, 0x0ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableK[] = { { 1, 1, 0, 0x000 }, { 2, 2, 1, 0x002 }, { 4, 4, 0, 0x00c }, { 5, 4, 1, 0x00d }, { 7, 5, 1, 0x01c }, { 9, 5, 2, 0x01d }, { 13, 6, 2, 0x03c }, { 17, 7, 2, 0x07a }, { 21, 7, 3, 0x07b }, { 29, 7, 4, 0x07c }, { 45, 7, 5, 0x07d }, { 77, 7, 6, 0x07e }, { 141, 7, 32, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableL[] = { { 1, 1, 0, 0x000 }, { 2, 2, 0, 0x002 }, { 3, 3, 1, 0x006 }, { 5, 5, 0, 0x01c }, { 6, 5, 1, 0x01d }, { 8, 6, 1, 0x03c }, { 10, 7, 0, 0x07a }, { 11, 7, 1, 0x07b }, { 13, 7, 2, 0x07c }, { 17, 7, 3, 0x07d }, { 25, 7, 4, 0x07e }, { 41, 8, 5, 0x0fe }, { 73, 8, 32, 0x0ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableM[] = { { 1, 1, 0, 0x000 }, { 2, 3, 0, 0x004 }, { 7, 3, 3, 0x005 }, { 3, 4, 0, 0x00c }, { 5, 4, 1, 0x00d }, { 4, 5, 0, 0x01c }, { 15, 6, 1, 0x03a }, { 17, 6, 2, 0x03b }, { 21, 6, 3, 0x03c }, { 29, 6, 4, 0x03d }, { 45, 6, 5, 0x03e }, { 77, 7, 6, 0x07e }, { 141, 7, 32, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableN[] = { { 0, 1, 0, 0x000 }, { -2, 3, 0, 0x004 }, { -1, 3, 0, 0x005 }, { 1, 3, 0, 0x006 }, { 2, 3, 0, 0x007 }, { 0, 0, jbig2HuffmanEOT, 0 } }; JBIG2HuffmanTable huffTableO[] = { { 0, 1, 0, 0x000 }, { -1, 3, 0, 0x004 }, { 1, 3, 0, 0x005 }, { -2, 4, 0, 0x00c }, { 2, 4, 0, 0x00d }, { -4, 5, 1, 0x01c }, { 3, 5, 1, 0x01d }, { -8, 6, 2, 0x03c }, { 5, 6, 2, 0x03d }, { -24, 7, 4, 0x07c }, { 9, 7, 4, 0x07d }, { -25, 7, jbig2HuffmanLOW, 0x07e }, { 25, 7, 32, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; //------------------------------------------------------------------------ // JBIG2HuffmanDecoder //------------------------------------------------------------------------ class JBIG2HuffmanDecoder { public: JBIG2HuffmanDecoder(); ~JBIG2HuffmanDecoder(); void setStream(Stream *strA) { str = strA; } void reset(); // Returns false for OOB, otherwise sets * and returns true. GBool decodeInt(int *x, JBIG2HuffmanTable *table); Guint readBits(Guint n); Guint readBit(); // Sort the table by prefix length and assign prefix values. void buildTable(JBIG2HuffmanTable *table, Guint len); void resetByteCounter() { byteCounter = 0; } Guint getByteCounter() { return byteCounter; } private: Stream *str; Guint buf; Guint bufLen; Guint byteCounter; }; JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() { str = NULL; byteCounter = 0; reset(); } JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() { } void JBIG2HuffmanDecoder::reset() { buf = 0; bufLen = 0; } //~ optimize this GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) { Guint i, len, prefix; i = 0; len = 0; prefix = 0; while (table[i].rangeLen != jbig2HuffmanEOT) { while (len < table[i].prefixLen) { prefix = (prefix << 1) | readBit(); ++len; } if (prefix == table[i].prefix) { if (table[i].rangeLen == jbig2HuffmanOOB) { return gFalse; } if (table[i].rangeLen == jbig2HuffmanLOW) { *x = table[i].val - readBits(32); } else if (table[i].rangeLen > 0) { *x = table[i].val + readBits(table[i].rangeLen); } else { *x = table[i].val; } return gTrue; } ++i; } return gFalse; } Guint JBIG2HuffmanDecoder::readBits(Guint n) { Guint x, mask, nLeft; mask = (n == 32) ? 0xffffffff : ((1 << n) - 1); if (bufLen >= n) { x = (buf >> (bufLen - n)) & mask; bufLen -= n; } else { x = buf & ((1 << bufLen) - 1); nLeft = n - bufLen; bufLen = 0; while (nLeft >= 8) { x = (x << 8) | (str->getChar() & 0xff); ++byteCounter; nLeft -= 8; } if (nLeft > 0) { buf = str->getChar(); ++byteCounter; bufLen = 8 - nLeft; x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1)); } } return x; } Guint JBIG2HuffmanDecoder::readBit() { if (bufLen == 0) { buf = str->getChar(); ++byteCounter; bufLen = 8; } --bufLen; return (buf >> bufLen) & 1; } void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) { Guint i, j, k, prefix; JBIG2HuffmanTable tab; // stable selection sort: // - entries with prefixLen > 0, in ascending prefixLen order // - entry with prefixLen = 0, rangeLen = EOT // - all other entries with prefixLen = 0 // (on entry, table[len] has prefixLen = 0, rangeLen = EOT) for (i = 0; i < len; ++i) { for (j = i; j < len && table[j].prefixLen == 0; ++j) ; if (j == len) { break; } for (k = j + 1; k < len; ++k) { if (table[k].prefixLen > 0 && table[k].prefixLen < table[j].prefixLen) { j = k; } } if (j != i) { tab = table[j]; for (k = j; k > i; --k) { table[k] = table[k - 1]; } table[i] = tab; } } table[i] = table[len]; // assign prefixes if (table[0].rangeLen != jbig2HuffmanEOT) { i = 0; prefix = 0; table[i++].prefix = prefix++; for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) { prefix <<= table[i].prefixLen - table[i-1].prefixLen; table[i].prefix = prefix++; } } } //------------------------------------------------------------------------ // JBIG2MMRDecoder //------------------------------------------------------------------------ class JBIG2MMRDecoder { public: JBIG2MMRDecoder(); ~JBIG2MMRDecoder(); void setStream(Stream *strA) { str = strA; } void reset(); int get2DCode(); int getBlackCode(); int getWhiteCode(); Guint get24Bits(); void resetByteCounter() { byteCounter = 0; } Guint getByteCounter() { return byteCounter; } void skipTo(Guint length); private: Stream *str; Guint buf; Guint bufLen; Guint nBytesRead; Guint byteCounter; }; JBIG2MMRDecoder::JBIG2MMRDecoder() { str = NULL; byteCounter = 0; reset(); } JBIG2MMRDecoder::~JBIG2MMRDecoder() { } void JBIG2MMRDecoder::reset() { buf = 0; bufLen = 0; nBytesRead = 0; } int JBIG2MMRDecoder::get2DCode() { CCITTCode *p; if (bufLen == 0) { buf = str->getChar() & 0xff; bufLen = 8; ++nBytesRead; ++byteCounter; p = &twoDimTab1[(buf >> 1) & 0x7f]; } else if (bufLen == 8) { p = &twoDimTab1[(buf >> 1) & 0x7f]; } else { p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f]; if (p->bits < 0 || p->bits > (int)bufLen) { buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f]; } } if (p->bits < 0) { error(errSyntaxError, str->getPos(), "Bad two dim code in JBIG2 MMR stream"); return EOF; } bufLen -= p->bits; return p->n; } int JBIG2MMRDecoder::getWhiteCode() { CCITTCode *p; Guint code; if (bufLen == 0) { buf = str->getChar() & 0xff; bufLen = 8; ++nBytesRead; ++byteCounter; } while (1) { if (bufLen >= 11 && ((buf >> (bufLen - 7)) & 0x7f) == 0) { if (bufLen <= 12) { code = buf << (12 - bufLen); } else { code = buf >> (bufLen - 12); } p = &whiteTab1[code & 0x1f]; } else { if (bufLen <= 9) { code = buf << (9 - bufLen); } else { code = buf >> (bufLen - 9); } p = &whiteTab2[code & 0x1ff]; } if (p->bits > 0 && p->bits <= (int)bufLen) { bufLen -= p->bits; return p->n; } if (bufLen >= 12) { break; } buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; } error(errSyntaxError, str->getPos(), "Bad white code in JBIG2 MMR stream"); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop --bufLen; return 1; } int JBIG2MMRDecoder::getBlackCode() { CCITTCode *p; Guint code; if (bufLen == 0) { buf = str->getChar() & 0xff; bufLen = 8; ++nBytesRead; ++byteCounter; } while (1) { if (bufLen >= 10 && ((buf >> (bufLen - 6)) & 0x3f) == 0) { if (bufLen <= 13) { code = buf << (13 - bufLen); } else { code = buf >> (bufLen - 13); } p = &blackTab1[code & 0x7f]; } else if (bufLen >= 7 && ((buf >> (bufLen - 4)) & 0x0f) == 0 && ((buf >> (bufLen - 6)) & 0x03) != 0) { if (bufLen <= 12) { code = buf << (12 - bufLen); } else { code = buf >> (bufLen - 12); } p = &blackTab2[(code & 0xff) - 64]; } else { if (bufLen <= 6) { code = buf << (6 - bufLen); } else { code = buf >> (bufLen - 6); } p = &blackTab3[code & 0x3f]; } if (p->bits > 0 && p->bits <= (int)bufLen) { bufLen -= p->bits; return p->n; } if (bufLen >= 13) { break; } buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; } error(errSyntaxError, str->getPos(), "Bad black code in JBIG2 MMR stream"); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop --bufLen; return 1; } Guint JBIG2MMRDecoder::get24Bits() { while (bufLen < 24) { buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; } return (buf >> (bufLen - 24)) & 0xffffff; } void JBIG2MMRDecoder::skipTo(Guint length) { int n; n = str->discardChars(length - nBytesRead); nBytesRead += n; byteCounter += n; } //------------------------------------------------------------------------ // JBIG2Segment //------------------------------------------------------------------------ enum JBIG2SegmentType { jbig2SegBitmap, jbig2SegSymbolDict, jbig2SegPatternDict, jbig2SegCodeTable }; class JBIG2Segment { public: JBIG2Segment(Guint segNumA) { segNum = segNumA; } virtual ~JBIG2Segment() {} void setSegNum(Guint segNumA) { segNum = segNumA; } Guint getSegNum() { return segNum; } virtual JBIG2SegmentType getType() = 0; private: Guint segNum; }; //------------------------------------------------------------------------ // JBIG2Bitmap //------------------------------------------------------------------------ struct JBIG2BitmapPtr { Guchar *p; int shift; int x; }; class JBIG2Bitmap: public JBIG2Segment { public: JBIG2Bitmap(Guint segNumA, int wA, int hA); virtual ~JBIG2Bitmap(); virtual JBIG2SegmentType getType() { return jbig2SegBitmap; } JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); } JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA); void expand(int newH, Guint pixel); void clearToZero(); void clearToOne(); int getWidth() { return w; } int getHeight() { return h; } int getLineSize() { return line; } int getPixel(int x, int y) { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 : (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; } void setPixel(int x, int y) { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); } void clearPixel(int x, int y) { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); } void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr); int nextPixel(JBIG2BitmapPtr *ptr); void duplicateRow(int yDest, int ySrc); void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp); Guchar *getDataPtr() { return data; } int getDataSize() { return h * line; } private: JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap); int w, h, line; Guchar *data; }; JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA): JBIG2Segment(segNumA) { w = wA; h = hA; line = (wA + 7) >> 3; if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) { // force a call to gmalloc(-1), which will throw an exception h = -1; line = 2; } // need to allocate one extra guard byte for use in combine() data = (Guchar *)gmalloc(h * line + 1); data[h * line] = 0; } JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap): JBIG2Segment(segNumA) { w = bitmap->w; h = bitmap->h; line = bitmap->line; if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) { // force a call to gmalloc(-1), which will throw an exception h = -1; line = 2; } // need to allocate one extra guard byte for use in combine() data = (Guchar *)gmalloc(h * line + 1); memcpy(data, bitmap->data, h * line); data[h * line] = 0; } JBIG2Bitmap::~JBIG2Bitmap() { gfree(data); } //~ optimize this JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) { JBIG2Bitmap *slice; Guint xx, yy; slice = new JBIG2Bitmap(0, wA, hA); slice->clearToZero(); for (yy = 0; yy < hA; ++yy) { for (xx = 0; xx < wA; ++xx) { if (getPixel(x + xx, y + yy)) { slice->setPixel(xx, yy); } } } return slice; } void JBIG2Bitmap::expand(int newH, Guint pixel) { if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) { return; } // need to allocate one extra guard byte for use in combine() data = (Guchar *)grealloc(data, newH * line + 1); if (pixel) { memset(data + h * line, 0xff, (newH - h) * line); } else { memset(data + h * line, 0x00, (newH - h) * line); } h = newH; data[h * line] = 0; } void JBIG2Bitmap::clearToZero() { memset(data, 0, h * line); } void JBIG2Bitmap::clearToOne() { memset(data, 0xff, h * line); } inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) { if (y < 0 || y >= h || x >= w) { ptr->p = NULL; ptr->shift = 0; // make gcc happy ptr->x = 0; // make gcc happy } else if (x < 0) { ptr->p = &data[y * line]; ptr->shift = 7; ptr->x = x; } else { ptr->p = &data[y * line + (x >> 3)]; ptr->shift = 7 - (x & 7); ptr->x = x; } } inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) { int pix; if (!ptr->p) { pix = 0; } else if (ptr->x < 0) { ++ptr->x; pix = 0; } else { pix = (*ptr->p >> ptr->shift) & 1; if (++ptr->x == w) { ptr->p = NULL; } else if (ptr->shift == 0) { ++ptr->p; ptr->shift = 7; } else { --ptr->shift; } } return pix; } void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) { memcpy(data + yDest * line, data + ySrc * line, line); } void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp) { int x0, x1, y0, y1, xx, yy; Guchar *srcPtr, *destPtr; Guint src0, src1, src, dest, s1, s2, m1, m2, m3; GBool oneByte; // check for the pathological case where y = -2^31 if (y < -0x7fffffff) { return; } if (y < 0) { y0 = -y; } else { y0 = 0; } if (y + bitmap->h > h) { y1 = h - y; } else { y1 = bitmap->h; } if (y0 >= y1) { return; } if (x >= 0) { x0 = x & ~7; } else { x0 = 0; } x1 = x + bitmap->w; if (x1 > w) { x1 = w; } if (x0 >= x1) { return; } s1 = x & 7; s2 = 8 - s1; m1 = 0xff >> (x1 & 7); m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7)); m3 = (0xff >> s1) & m2; oneByte = x0 == ((x1 - 1) & ~7); for (yy = y0; yy < y1; ++yy) { // one byte per line -- need to mask both left and right side if (oneByte) { if (x >= 0) { destPtr = data + (y + yy) * line + (x >> 3); srcPtr = bitmap->data + yy * bitmap->line; dest = *destPtr; src1 = *srcPtr; switch (combOp) { case 0: // or dest |= (src1 >> s1) & m2; break; case 1: // and dest &= ((0xff00 | src1) >> s1) | m1; break; case 2: // xor dest ^= (src1 >> s1) & m2; break; case 3: // xnor dest ^= ((src1 ^ 0xff) >> s1) & m2; break; case 4: // replace dest = (dest & ~m3) | ((src1 >> s1) & m3); break; } *destPtr = dest; } else { destPtr = data + (y + yy) * line; srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); dest = *destPtr; src1 = *srcPtr; switch (combOp) { case 0: // or dest |= src1 & m2; break; case 1: // and dest &= src1 | m1; break; case 2: // xor dest ^= src1 & m2; break; case 3: // xnor dest ^= (src1 ^ 0xff) & m2; break; case 4: // replace dest = (src1 & m2) | (dest & m1); break; } *destPtr = dest; } // multiple bytes per line -- need to mask left side of left-most // byte and right side of right-most byte } else { // left-most byte if (x >= 0) { destPtr = data + (y + yy) * line + (x >> 3); srcPtr = bitmap->data + yy * bitmap->line; src1 = *srcPtr++; dest = *destPtr; switch (combOp) { case 0: // or dest |= src1 >> s1; break; case 1: // and dest &= (0xff00 | src1) >> s1; break; case 2: // xor dest ^= src1 >> s1; break; case 3: // xnor dest ^= (src1 ^ 0xff) >> s1; break; case 4: // replace dest = (dest & (0xff << s2)) | (src1 >> s1); break; } *destPtr++ = dest; xx = x0 + 8; } else { destPtr = data + (y + yy) * line; srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); src1 = *srcPtr++; xx = x0; } // middle bytes for (; xx < x1 - 8; xx += 8) { dest = *destPtr; src0 = src1; src1 = *srcPtr++; src = (((src0 << 8) | src1) >> s1) & 0xff; switch (combOp) { case 0: // or dest |= src; break; case 1: // and dest &= src; break; case 2: // xor dest ^= src; break; case 3: // xnor dest ^= src ^ 0xff; break; case 4: // replace dest = src; break; } *destPtr++ = dest; } // right-most byte // note: this last byte (src1) may not actually be used, depending // on the values of s1, m1, and m2 - and in fact, it may be off // the edge of the source bitmap, which means we need to allocate // one extra guard byte at the end of each bitmap dest = *destPtr; src0 = src1; src1 = *srcPtr++; src = (((src0 << 8) | src1) >> s1) & 0xff; switch (combOp) { case 0: // or dest |= src & m2; break; case 1: // and dest &= src | m1; break; case 2: // xor dest ^= src & m2; break; case 3: // xnor dest ^= (src ^ 0xff) & m2; break; case 4: // replace dest = (src & m2) | (dest & m1); break; } *destPtr = dest; } } } //------------------------------------------------------------------------ // JBIG2SymbolDict //------------------------------------------------------------------------ class JBIG2SymbolDict: public JBIG2Segment { public: JBIG2SymbolDict(Guint segNumA, Guint sizeA); virtual ~JBIG2SymbolDict(); virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; } Guint getSize() { return size; } void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; } JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; } void setGenericRegionStats(JArithmeticDecoderStats *stats) { genericRegionStats = stats; } void setRefinementRegionStats(JArithmeticDecoderStats *stats) { refinementRegionStats = stats; } JArithmeticDecoderStats *getGenericRegionStats() { return genericRegionStats; } JArithmeticDecoderStats *getRefinementRegionStats() { return refinementRegionStats; } private: Guint size; JBIG2Bitmap **bitmaps; JArithmeticDecoderStats *genericRegionStats; JArithmeticDecoderStats *refinementRegionStats; }; JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA): JBIG2Segment(segNumA) { Guint i; size = sizeA; bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *)); for (i = 0; i < size; ++i) { bitmaps[i] = NULL; } genericRegionStats = NULL; refinementRegionStats = NULL; } JBIG2SymbolDict::~JBIG2SymbolDict() { Guint i; for (i = 0; i < size; ++i) { if (bitmaps[i]) { delete bitmaps[i]; } } gfree(bitmaps); if (genericRegionStats) { delete genericRegionStats; } if (refinementRegionStats) { delete refinementRegionStats; } } //------------------------------------------------------------------------ // JBIG2PatternDict //------------------------------------------------------------------------ class JBIG2PatternDict: public JBIG2Segment { public: JBIG2PatternDict(Guint segNumA, Guint sizeA); virtual ~JBIG2PatternDict(); virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; } Guint getSize() { return size; } void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; } JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; } private: Guint size; JBIG2Bitmap **bitmaps; }; JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA): JBIG2Segment(segNumA) { size = sizeA; bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *)); } JBIG2PatternDict::~JBIG2PatternDict() { Guint i; for (i = 0; i < size; ++i) { delete bitmaps[i]; } gfree(bitmaps); } //------------------------------------------------------------------------ // JBIG2CodeTable //------------------------------------------------------------------------ class JBIG2CodeTable: public JBIG2Segment { public: JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA); virtual ~JBIG2CodeTable(); virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; } JBIG2HuffmanTable *getHuffTable() { return table; } private: JBIG2HuffmanTable *table; }; JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA): JBIG2Segment(segNumA) { table = tableA; } JBIG2CodeTable::~JBIG2CodeTable() { gfree(table); } //------------------------------------------------------------------------ // JBIG2Stream //------------------------------------------------------------------------ JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStreamA): FilterStream(strA) { pageBitmap = NULL; arithDecoder = new JArithmeticDecoder(); genericRegionStats = new JArithmeticDecoderStats(1 << 1); refinementRegionStats = new JArithmeticDecoderStats(1 << 1); iadhStats = new JArithmeticDecoderStats(1 << 9); iadwStats = new JArithmeticDecoderStats(1 << 9); iaexStats = new JArithmeticDecoderStats(1 << 9); iaaiStats = new JArithmeticDecoderStats(1 << 9); iadtStats = new JArithmeticDecoderStats(1 << 9); iaitStats = new JArithmeticDecoderStats(1 << 9); iafsStats = new JArithmeticDecoderStats(1 << 9); iadsStats = new JArithmeticDecoderStats(1 << 9); iardxStats = new JArithmeticDecoderStats(1 << 9); iardyStats = new JArithmeticDecoderStats(1 << 9); iardwStats = new JArithmeticDecoderStats(1 << 9); iardhStats = new JArithmeticDecoderStats(1 << 9); iariStats = new JArithmeticDecoderStats(1 << 9); iaidStats = new JArithmeticDecoderStats(1 << 1); huffDecoder = new JBIG2HuffmanDecoder(); mmrDecoder = new JBIG2MMRDecoder(); globalsStreamA->copy(&globalsStream); segments = globalSegments = NULL; curStr = NULL; dataPtr = dataEnd = NULL; } JBIG2Stream::~JBIG2Stream() { close(); globalsStream.free(); delete arithDecoder; delete genericRegionStats; delete refinementRegionStats; delete iadhStats; delete iadwStats; delete iaexStats; delete iaaiStats; delete iadtStats; delete iaitStats; delete iafsStats; delete iadsStats; delete iardxStats; delete iardyStats; delete iardwStats; delete iardhStats; delete iariStats; delete iaidStats; delete huffDecoder; delete mmrDecoder; delete str; } void JBIG2Stream::reset() { // read the globals stream globalSegments = new GList(); if (globalsStream.isStream()) { segments = globalSegments; curStr = globalsStream.getStream(); curStr->reset(); arithDecoder->setStream(curStr); huffDecoder->setStream(curStr); mmrDecoder->setStream(curStr); readSegments(); curStr->close(); } // read the main stream segments = new GList(); curStr = str; curStr->reset(); arithDecoder->setStream(curStr); huffDecoder->setStream(curStr); mmrDecoder->setStream(curStr); readSegments(); if (pageBitmap) { dataPtr = pageBitmap->getDataPtr(); dataEnd = dataPtr + pageBitmap->getDataSize(); } else { dataPtr = dataEnd = NULL; } } void JBIG2Stream::close() { if (pageBitmap) { delete pageBitmap; pageBitmap = NULL; } if (segments) { deleteGList(segments, JBIG2Segment); segments = NULL; } if (globalSegments) { deleteGList(globalSegments, JBIG2Segment); globalSegments = NULL; } dataPtr = dataEnd = NULL; FilterStream::close(); } int JBIG2Stream::getChar() { if (dataPtr && dataPtr < dataEnd) { return (*dataPtr++ ^ 0xff) & 0xff; } return EOF; } int JBIG2Stream::lookChar() { if (dataPtr && dataPtr < dataEnd) { return (*dataPtr ^ 0xff) & 0xff; } return EOF; } int JBIG2Stream::getBlock(char *blk, int size) { int n, i; if (size <= 0) { return 0; } if (dataEnd - dataPtr < size) { n = (int)(dataEnd - dataPtr); } else { n = size; } for (i = 0; i < n; ++i) { blk[i] = *dataPtr++ ^ 0xff; } return n; } GString *JBIG2Stream::getPSFilter(int psLevel, const char *indent) { return NULL; } GBool JBIG2Stream::isBinary(GBool last) { return str->isBinary(gTrue); } void JBIG2Stream::readSegments() { Guint segNum, segFlags, segType, page, segLength; Guint refFlags, nRefSegs; Guint *refSegs; int c1, c2, c3; Guint i; while (readULong(&segNum)) { // segment header flags if (!readUByte(&segFlags)) { goto eofError1; } segType = segFlags & 0x3f; // referred-to segment count and retention flags if (!readUByte(&refFlags)) { goto eofError1; } nRefSegs = refFlags >> 5; if (nRefSegs == 7) { if ((c1 = curStr->getChar()) == EOF || (c2 = curStr->getChar()) == EOF || (c3 = curStr->getChar()) == EOF) { goto eofError1; } refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3; nRefSegs = refFlags & 0x1fffffff; i = (nRefSegs + 9) >> 3; if (curStr->discardChars(i) != i) { goto eofError1; } } // referred-to segment numbers refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint)); if (segNum <= 256) { for (i = 0; i < nRefSegs; ++i) { if (!readUByte(&refSegs[i])) { goto eofError2; } } } else if (segNum <= 65536) { for (i = 0; i < nRefSegs; ++i) { if (!readUWord(&refSegs[i])) { goto eofError2; } } } else { for (i = 0; i < nRefSegs; ++i) { if (!readULong(&refSegs[i])) { goto eofError2; } } } // segment page association if (segFlags & 0x40) { if (!readULong(&page)) { goto eofError2; } } else { if (!readUByte(&page)) { goto eofError2; } } // segment data length if (!readULong(&segLength)) { goto eofError2; } // check for missing page information segment if (!pageBitmap && ((segType >= 4 && segType <= 7) || (segType >= 20 && segType <= 43))) { error(errSyntaxError, getPos(), "First JBIG2 segment associated with a page must be a page information segment"); goto syntaxError; } // read the segment data arithDecoder->resetByteCounter(); huffDecoder->resetByteCounter(); mmrDecoder->resetByteCounter(); byteCounter = 0; switch (segType) { case 0: if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) { goto syntaxError; } break; case 4: readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs); break; case 6: readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs); break; case 7: readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs); break; case 16: readPatternDictSeg(segNum, segLength); break; case 20: readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs); break; case 22: readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs); break; case 23: readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs); break; case 36: readGenericRegionSeg(segNum, gFalse, gFalse, segLength); break; case 38: readGenericRegionSeg(segNum, gTrue, gFalse, segLength); break; case 39: readGenericRegionSeg(segNum, gTrue, gTrue, segLength); break; case 40: readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs); break; case 42: readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs); break; case 43: readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs); break; case 48: readPageInfoSeg(segLength); break; case 50: readEndOfStripeSeg(segLength); break; case 52: readProfilesSeg(segLength); break; case 53: readCodeTableSeg(segNum, segLength); break; case 62: readExtensionSeg(segLength); break; default: error(errSyntaxError, getPos(), "Unknown segment type in JBIG2 stream"); if (curStr->discardChars(segLength) != segLength) { goto eofError2; } break; } // skip any unused data at the end of the segment // (except for immediate generic region segments which have // 0xffffffff = unspecified length) if (!(segType == 38 && segLength == 0xffffffff)) { byteCounter += arithDecoder->getByteCounter(); byteCounter += huffDecoder->getByteCounter(); byteCounter += mmrDecoder->getByteCounter(); // do a sanity check on byteCounter vs segLength -- if there is // a problem, abort the decode if (byteCounter > segLength || segLength - byteCounter > 65536) { error(errSyntaxError, getPos(), "Invalid segment length in JBIG2 stream"); gfree(refSegs); break; } byteCounter += curStr->discardChars(segLength - byteCounter); } gfree(refSegs); } return; syntaxError: gfree(refSegs); return; eofError2: gfree(refSegs); eofError1: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length, Guint *refSegs, Guint nRefSegs) { JBIG2SymbolDict *symbolDict; JBIG2HuffmanTable *huffDHTable, *huffDWTable; JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable; JBIG2Segment *seg; GList *codeTables; JBIG2SymbolDict *inputSymbolDict; Guint flags, sdTemplate, sdrTemplate, huff, refAgg; Guint huffDH, huffDW, huffBMSize, huffAggInst; Guint contextUsed, contextRetained; int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2]; Guint numExSyms, numNewSyms, numInputSyms, symCodeLen; JBIG2Bitmap **bitmaps; JBIG2Bitmap *collBitmap, *refBitmap; Guint *symWidths; Guint symHeight, symWidth, totalWidth, x, symID; int dh, dw, refAggNum, refDX, refDY, bmSize; GBool ex; int run, cnt; Guint i, j, k; symWidths = NULL; // symbol dictionary flags if (!readUWord(&flags)) { goto eofError; } sdTemplate = (flags >> 10) & 3; sdrTemplate = (flags >> 12) & 1; huff = flags & 1; refAgg = (flags >> 1) & 1; huffDH = (flags >> 2) & 3; huffDW = (flags >> 4) & 3; huffBMSize = (flags >> 6) & 1; huffAggInst = (flags >> 7) & 1; contextUsed = (flags >> 8) & 1; contextRetained = (flags >> 9) & 1; // symbol dictionary AT flags if (!huff) { if (sdTemplate == 0) { if (!readByte(&sdATX[0]) || !readByte(&sdATY[0]) || !readByte(&sdATX[1]) || !readByte(&sdATY[1]) || !readByte(&sdATX[2]) || !readByte(&sdATY[2]) || !readByte(&sdATX[3]) || !readByte(&sdATY[3])) { goto eofError; } } else { if (!readByte(&sdATX[0]) || !readByte(&sdATY[0])) { goto eofError; } } } // symbol dictionary refinement AT flags if (refAgg && !sdrTemplate) { if (!readByte(&sdrATX[0]) || !readByte(&sdrATY[0]) || !readByte(&sdrATX[1]) || !readByte(&sdrATY[1])) { goto eofError; } } // SDNUMEXSYMS and SDNUMNEWSYMS if (!readULong(&numExSyms) || !readULong(&numNewSyms)) { goto eofError; } // get referenced segments: input symbol dictionaries and code tables codeTables = new GList(); numInputSyms = 0; for (i = 0; i < nRefSegs; ++i) { if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { j = ((JBIG2SymbolDict *)seg)->getSize(); if (numInputSyms > UINT_MAX - j) { error(errSyntaxError, getPos(), "Too many input symbols in JBIG2 symbol dictionary"); delete codeTables; goto eofError; } numInputSyms += j; } else if (seg->getType() == jbig2SegCodeTable) { codeTables->append(seg); } } } if (numInputSyms > UINT_MAX - numNewSyms) { error(errSyntaxError, getPos(), "Too many input symbols in JBIG2 symbol dictionary"); delete codeTables; goto eofError; } // compute symbol code length i = numInputSyms + numNewSyms; if (i <= 1) { symCodeLen = huff ? 1 : 0; } else { --i; symCodeLen = 0; // i = floor((numSyms-1) / 2^symCodeLen) while (i > 0) { ++symCodeLen; i >>= 1; } } // get the input symbol bitmaps bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms, sizeof(JBIG2Bitmap *)); for (i = 0; i < numInputSyms + numNewSyms; ++i) { bitmaps[i] = NULL; } k = 0; inputSymbolDict = NULL; for (i = 0; i < nRefSegs; ++i) { if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { inputSymbolDict = (JBIG2SymbolDict *)seg; for (j = 0; j < inputSymbolDict->getSize(); ++j) { bitmaps[k++] = inputSymbolDict->getBitmap(j); } } } } // get the Huffman tables huffDHTable = huffDWTable = NULL; // make gcc happy huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy i = 0; if (huff) { if (huffDH == 0) { huffDHTable = huffTableD; } else if (huffDH == 1) { huffDHTable = huffTableE; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffDW == 0) { huffDWTable = huffTableB; } else if (huffDW == 1) { huffDWTable = huffTableC; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffBMSize == 0) { huffBMSizeTable = huffTableA; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffBMSizeTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffAggInst == 0) { huffAggInstTable = huffTableA; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffAggInstTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } } delete codeTables; // set up the Huffman decoder if (huff) { huffDecoder->reset(); // set up the arithmetic decoder } else { if (contextUsed && inputSymbolDict) { resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats()); } else { resetGenericStats(sdTemplate, NULL); } resetIntStats(symCodeLen); arithDecoder->start(); } // set up the arithmetic decoder for refinement/aggregation if (refAgg) { if (contextUsed && inputSymbolDict) { resetRefinementStats(sdrTemplate, inputSymbolDict->getRefinementRegionStats()); } else { resetRefinementStats(sdrTemplate, NULL); } } // allocate symbol widths storage if (huff && !refAgg) { symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint)); } symHeight = 0; i = 0; while (i < numNewSyms) { // read the height class delta height if (huff) { huffDecoder->decodeInt(&dh, huffDHTable); } else { arithDecoder->decodeInt(&dh, iadhStats); } if (dh < 0 && (Guint)-dh >= symHeight) { error(errSyntaxError, getPos(), "Bad delta-height value in JBIG2 symbol dictionary"); goto syntaxError; } symHeight += dh; symWidth = 0; totalWidth = 0; j = i; // read the symbols in this height class while (1) { // read the delta width if (huff) { if (!huffDecoder->decodeInt(&dw, huffDWTable)) { break; } } else { if (!arithDecoder->decodeInt(&dw, iadwStats)) { break; } } if (dw < 0 && (Guint)-dw >= symWidth) { error(errSyntaxError, getPos(), "Bad delta-height value in JBIG2 symbol dictionary"); goto syntaxError; } symWidth += dw; if (i >= numNewSyms) { error(errSyntaxError, getPos(), "Too many symbols in JBIG2 symbol dictionary"); goto syntaxError; } // using a collective bitmap, so don't read a bitmap here if (huff && !refAgg) { symWidths[i] = symWidth; totalWidth += symWidth; // refinement/aggregate coding } else if (refAgg) { if (huff) { if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) { break; } } else { if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) { break; } } #if 0 //~ This special case was added about a year before the final draft //~ of the JBIG2 spec was released. I have encountered some old //~ JBIG2 images that predate it. if (0) { #else if (refAggNum == 1) { #endif if (huff) { symID = huffDecoder->readBits(symCodeLen); huffDecoder->decodeInt(&refDX, huffTableO); huffDecoder->decodeInt(&refDY, huffTableO); huffDecoder->decodeInt(&bmSize, huffTableA); huffDecoder->reset(); arithDecoder->start(); } else { symID = arithDecoder->decodeIAID(symCodeLen, iaidStats); arithDecoder->decodeInt(&refDX, iardxStats); arithDecoder->decodeInt(&refDY, iardyStats); } if (symID >= numInputSyms + i) { error(errSyntaxError, getPos(), "Invalid symbol ID in JBIG2 symbol dictionary"); goto syntaxError; } refBitmap = bitmaps[symID]; bitmaps[numInputSyms + i] = readGenericRefinementRegion(symWidth, symHeight, sdrTemplate, gFalse, refBitmap, refDX, refDY, sdrATX, sdrATY); //~ do we need to use the bmSize value here (in Huffman mode)? } else { bitmaps[numInputSyms + i] = readTextRegion(huff, gTrue, symWidth, symHeight, refAggNum, 0, numInputSyms + i, NULL, symCodeLen, bitmaps, 0, 0, 0, 1, 0, huffTableF, huffTableH, huffTableK, huffTableO, huffTableO, huffTableO, huffTableO, huffTableA, sdrTemplate, sdrATX, sdrATY); } // non-ref/agg coding } else { bitmaps[numInputSyms + i] = readGenericBitmap(gFalse, symWidth, symHeight, sdTemplate, gFalse, gFalse, NULL, sdATX, sdATY, 0); } ++i; } // read the collective bitmap if (huff && !refAgg) { huffDecoder->decodeInt(&bmSize, huffBMSizeTable); huffDecoder->reset(); if (bmSize == 0) { collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight); bmSize = symHeight * ((totalWidth + 7) >> 3); byteCounter += curStr->getBlock((char *)collBitmap->getDataPtr(), bmSize); } else { collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight, 0, gFalse, gFalse, NULL, NULL, NULL, bmSize); } x = 0; for (; j < i; ++j) { bitmaps[numInputSyms + j] = collBitmap->getSlice(x, 0, symWidths[j], symHeight); x += symWidths[j]; } delete collBitmap; } } // create the symbol dict object symbolDict = new JBIG2SymbolDict(segNum, numExSyms); // exported symbol list i = j = 0; ex = gFalse; while (i < numInputSyms + numNewSyms) { if (huff) { huffDecoder->decodeInt(&run, huffTableA); } else { arithDecoder->decodeInt(&run, iaexStats); } if (i + run > numInputSyms + numNewSyms || (ex && j + run > numExSyms)) { error(errSyntaxError, getPos(), "Too many exported symbols in JBIG2 symbol dictionary"); delete symbolDict; goto syntaxError; } if (ex) { for (cnt = 0; cnt < run; ++cnt) { symbolDict->setBitmap(j++, bitmaps[i++]->copy()); } } else { i += run; } ex = !ex; } if (j != numExSyms) { error(errSyntaxError, getPos(), "Too few symbols in JBIG2 symbol dictionary"); delete symbolDict; goto syntaxError; } for (i = 0; i < numNewSyms; ++i) { delete bitmaps[numInputSyms + i]; } gfree(bitmaps); if (symWidths) { gfree(symWidths); } // save the arithmetic decoder stats if (!huff && contextRetained) { symbolDict->setGenericRegionStats(genericRegionStats->copy()); if (refAgg) { symbolDict->setRefinementRegionStats(refinementRegionStats->copy()); } } // store the new symbol dict segments->append(symbolDict); return gTrue; codeTableError: error(errSyntaxError, getPos(), "Missing code table in JBIG2 symbol dictionary"); delete codeTables; syntaxError: for (i = 0; i < numNewSyms; ++i) { if (bitmaps[numInputSyms + i]) { delete bitmaps[numInputSyms + i]; } } gfree(bitmaps); if (symWidths) { gfree(symWidths); } return gFalse; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); return gFalse; } void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length, Guint *refSegs, Guint nRefSegs) { JBIG2Bitmap *bitmap; JBIG2HuffmanTable runLengthTab[36]; JBIG2HuffmanTable *symCodeTab; JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable; JBIG2HuffmanTable *huffRDWTable, *huffRDHTable; JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable; JBIG2Segment *seg; GList *codeTables; JBIG2SymbolDict *symbolDict; JBIG2Bitmap **syms; Guint w, h, x, y, segInfoFlags, extCombOp; Guint flags, huff, refine, logStrips, refCorner, transposed; Guint combOp, defPixel, templ; int sOffset; Guint huffFlags, huffFS, huffDS, huffDT; Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize; Guint numInstances, numSyms, symCodeLen; int atx[2], aty[2]; Guint i, k, kk; int j; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the text region header if (!readUWord(&flags)) { goto eofError; } huff = flags & 1; refine = (flags >> 1) & 1; logStrips = (flags >> 2) & 3; refCorner = (flags >> 4) & 3; transposed = (flags >> 6) & 1; combOp = (flags >> 7) & 3; defPixel = (flags >> 9) & 1; sOffset = (flags >> 10) & 0x1f; if (sOffset & 0x10) { sOffset |= -1 - 0x0f; } templ = (flags >> 15) & 1; huffFS = huffDS = huffDT = 0; // make gcc happy huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy if (huff) { if (!readUWord(&huffFlags)) { goto eofError; } huffFS = huffFlags & 3; huffDS = (huffFlags >> 2) & 3; huffDT = (huffFlags >> 4) & 3; huffRDW = (huffFlags >> 6) & 3; huffRDH = (huffFlags >> 8) & 3; huffRDX = (huffFlags >> 10) & 3; huffRDY = (huffFlags >> 12) & 3; huffRSize = (huffFlags >> 14) & 1; } if (refine && templ == 0) { if (!readByte(&atx[0]) || !readByte(&aty[0]) || !readByte(&atx[1]) || !readByte(&aty[1])) { goto eofError; } } if (!readULong(&numInstances)) { goto eofError; } // get symbol dictionaries and tables codeTables = new GList(); numSyms = 0; for (i = 0; i < nRefSegs; ++i) { if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { numSyms += ((JBIG2SymbolDict *)seg)->getSize(); } else if (seg->getType() == jbig2SegCodeTable) { codeTables->append(seg); } } else { error(errSyntaxError, getPos(), "Invalid segment reference in JBIG2 text region"); delete codeTables; return; } } i = numSyms; if (i <= 1) { symCodeLen = huff ? 1 : 0; } else { --i; symCodeLen = 0; // i = floor((numSyms-1) / 2^symCodeLen) while (i > 0) { ++symCodeLen; i >>= 1; } } // get the symbol bitmaps syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *)); kk = 0; for (i = 0; i < nRefSegs; ++i) { if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { symbolDict = (JBIG2SymbolDict *)seg; for (k = 0; k < symbolDict->getSize(); ++k) { syms[kk++] = symbolDict->getBitmap(k); } } } } // get the Huffman tables huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy huffRDWTable = huffRDHTable = NULL; // make gcc happy huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy i = 0; if (huff) { if (huffFS == 0) { huffFSTable = huffTableF; } else if (huffFS == 1) { huffFSTable = huffTableG; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffDS == 0) { huffDSTable = huffTableH; } else if (huffDS == 1) { huffDSTable = huffTableI; } else if (huffDS == 2) { huffDSTable = huffTableJ; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffDT == 0) { huffDTTable = huffTableK; } else if (huffDT == 1) { huffDTTable = huffTableL; } else if (huffDT == 2) { huffDTTable = huffTableM; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffRDW == 0) { huffRDWTable = huffTableN; } else if (huffRDW == 1) { huffRDWTable = huffTableO; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffRDH == 0) { huffRDHTable = huffTableN; } else if (huffRDH == 1) { huffRDHTable = huffTableO; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffRDX == 0) { huffRDXTable = huffTableN; } else if (huffRDX == 1) { huffRDXTable = huffTableO; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffRDY == 0) { huffRDYTable = huffTableN; } else if (huffRDY == 1) { huffRDYTable = huffTableO; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } if (huffRSize == 0) { huffRSizeTable = huffTableA; } else { if (i >= (Guint)codeTables->getLength()) { goto codeTableError; } huffRSizeTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable(); } } delete codeTables; // symbol ID Huffman decoding table if (huff) { huffDecoder->reset(); for (i = 0; i < 32; ++i) { runLengthTab[i].val = i; runLengthTab[i].prefixLen = huffDecoder->readBits(4); runLengthTab[i].rangeLen = 0; } runLengthTab[32].val = 0x103; runLengthTab[32].prefixLen = huffDecoder->readBits(4); runLengthTab[32].rangeLen = 2; runLengthTab[33].val = 0x203; runLengthTab[33].prefixLen = huffDecoder->readBits(4); runLengthTab[33].rangeLen = 3; runLengthTab[34].val = 0x20b; runLengthTab[34].prefixLen = huffDecoder->readBits(4); runLengthTab[34].rangeLen = 7; runLengthTab[35].prefixLen = 0; runLengthTab[35].rangeLen = jbig2HuffmanEOT; huffDecoder->buildTable(runLengthTab, 35); symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1, sizeof(JBIG2HuffmanTable)); for (i = 0; i < numSyms; ++i) { symCodeTab[i].val = i; symCodeTab[i].rangeLen = 0; } i = 0; while (i < numSyms) { huffDecoder->decodeInt(&j, runLengthTab); if (j > 0x200) { for (j -= 0x200; j && i < numSyms; --j) { symCodeTab[i++].prefixLen = 0; } } else if (j > 0x100) { for (j -= 0x100; j && i < numSyms; --j) { symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen; ++i; } } else { symCodeTab[i++].prefixLen = j; } } symCodeTab[numSyms].prefixLen = 0; symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT; huffDecoder->buildTable(symCodeTab, numSyms); huffDecoder->reset(); // set up the arithmetic decoder } else { symCodeTab = NULL; resetIntStats(symCodeLen); arithDecoder->start(); } if (refine) { resetRefinementStats(templ, NULL); } bitmap = readTextRegion(huff, refine, w, h, numInstances, logStrips, numSyms, symCodeTab, symCodeLen, syms, defPixel, combOp, transposed, refCorner, sOffset, huffFSTable, huffDSTable, huffDTTable, huffRDWTable, huffRDHTable, huffRDXTable, huffRDYTable, huffRSizeTable, templ, atx, aty); gfree(syms); // combine the region bitmap into the page bitmap if (imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } pageBitmap->combine(bitmap, x, y, extCombOp); delete bitmap; // store the region bitmap } else { bitmap->setSegNum(segNum); segments->append(bitmap); } // clean up the Huffman decoder if (huff) { gfree(symCodeTab); } return; codeTableError: error(errSyntaxError, getPos(), "Missing code table in JBIG2 text region"); gfree(codeTables); delete syms; return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); return; } JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine, int w, int h, Guint numInstances, Guint logStrips, int numSyms, JBIG2HuffmanTable *symCodeTab, Guint symCodeLen, JBIG2Bitmap **syms, Guint defPixel, Guint combOp, Guint transposed, Guint refCorner, int sOffset, JBIG2HuffmanTable *huffFSTable, JBIG2HuffmanTable *huffDSTable, JBIG2HuffmanTable *huffDTTable, JBIG2HuffmanTable *huffRDWTable, JBIG2HuffmanTable *huffRDHTable, JBIG2HuffmanTable *huffRDXTable, JBIG2HuffmanTable *huffRDYTable, JBIG2HuffmanTable *huffRSizeTable, Guint templ, int *atx, int *aty) { JBIG2Bitmap *bitmap; JBIG2Bitmap *symbolBitmap; Guint strips; int t, dt, tt, s, ds, sFirst, j; int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize; Guint symID, inst, bw, bh; strips = 1 << logStrips; // allocate the bitmap bitmap = new JBIG2Bitmap(0, w, h); if (defPixel) { bitmap->clearToOne(); } else { bitmap->clearToZero(); } // decode initial T value if (huff) { huffDecoder->decodeInt(&t, huffDTTable); } else { arithDecoder->decodeInt(&t, iadtStats); } t *= -(int)strips; inst = 0; sFirst = 0; while (inst < numInstances) { // decode delta-T if (huff) { huffDecoder->decodeInt(&dt, huffDTTable); } else { arithDecoder->decodeInt(&dt, iadtStats); } t += dt * strips; // first S value if (huff) { huffDecoder->decodeInt(&ds, huffFSTable); } else { arithDecoder->decodeInt(&ds, iafsStats); } sFirst += ds; s = sFirst; // read the instances // (this loop test is here to avoid an infinite loop with damaged // JBIG2 streams where the normal loop exit doesn't get triggered) while (inst < numInstances) { // T value if (strips == 1) { dt = 0; } else if (huff) { dt = huffDecoder->readBits(logStrips); } else { arithDecoder->decodeInt(&dt, iaitStats); } tt = t + dt; // symbol ID if (huff) { if (symCodeTab) { huffDecoder->decodeInt(&j, symCodeTab); symID = (Guint)j; } else { symID = huffDecoder->readBits(symCodeLen); } } else { symID = arithDecoder->decodeIAID(symCodeLen, iaidStats); } if (symID >= (Guint)numSyms) { error(errSyntaxError, getPos(), "Invalid symbol number in JBIG2 text region"); } else { // get the symbol bitmap symbolBitmap = NULL; if (refine) { if (huff) { ri = (int)huffDecoder->readBit(); } else { arithDecoder->decodeInt(&ri, iariStats); } } else { ri = 0; } if (ri) { if (huff) { huffDecoder->decodeInt(&rdw, huffRDWTable); huffDecoder->decodeInt(&rdh, huffRDHTable); huffDecoder->decodeInt(&rdx, huffRDXTable); huffDecoder->decodeInt(&rdy, huffRDYTable); huffDecoder->decodeInt(&bmSize, huffRSizeTable); huffDecoder->reset(); arithDecoder->start(); } else { arithDecoder->decodeInt(&rdw, iardwStats); arithDecoder->decodeInt(&rdh, iardhStats); arithDecoder->decodeInt(&rdx, iardxStats); arithDecoder->decodeInt(&rdy, iardyStats); } refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx; refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy; symbolBitmap = readGenericRefinementRegion(rdw + syms[symID]->getWidth(), rdh + syms[symID]->getHeight(), templ, gFalse, syms[symID], refDX, refDY, atx, aty); //~ do we need to use the bmSize value here (in Huffman mode)? } else { symbolBitmap = syms[symID]; } // combine the symbol bitmap into the region bitmap //~ something is wrong here - refCorner shouldn't degenerate into //~ two cases bw = symbolBitmap->getWidth() - 1; bh = symbolBitmap->getHeight() - 1; if (transposed) { switch (refCorner) { case 0: // bottom left bitmap->combine(symbolBitmap, tt, s, combOp); break; case 1: // top left bitmap->combine(symbolBitmap, tt, s, combOp); break; case 2: // bottom right bitmap->combine(symbolBitmap, tt - bw, s, combOp); break; case 3: // top right bitmap->combine(symbolBitmap, tt - bw, s, combOp); break; } s += bh; } else { switch (refCorner) { case 0: // bottom left bitmap->combine(symbolBitmap, s, tt - bh, combOp); break; case 1: // top left bitmap->combine(symbolBitmap, s, tt, combOp); break; case 2: // bottom right bitmap->combine(symbolBitmap, s, tt - bh, combOp); break; case 3: // top right bitmap->combine(symbolBitmap, s, tt, combOp); break; } s += bw; } if (ri) { delete symbolBitmap; } } // next instance ++inst; // next S value if (huff) { if (!huffDecoder->decodeInt(&ds, huffDSTable)) { break; } } else { if (!arithDecoder->decodeInt(&ds, iadsStats)) { break; } } s += sOffset + ds; } } return bitmap; } void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) { JBIG2PatternDict *patternDict; JBIG2Bitmap *bitmap; Guint flags, patternW, patternH, grayMax, templ, mmr; int atx[4], aty[4]; Guint i, x; // halftone dictionary flags, pattern width and height, max gray value if (!readUByte(&flags) || !readUByte(&patternW) || !readUByte(&patternH) || !readULong(&grayMax)) { goto eofError; } templ = (flags >> 1) & 3; mmr = flags & 1; // set up the arithmetic decoder if (!mmr) { resetGenericStats(templ, NULL); arithDecoder->start(); } // read the bitmap atx[0] = -(int)patternW; aty[0] = 0; atx[1] = -3; aty[1] = -1; atx[2] = 2; aty[2] = -2; atx[3] = -2; aty[3] = -2; bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH, templ, gFalse, gFalse, NULL, atx, aty, length - 7); // create the pattern dict object patternDict = new JBIG2PatternDict(segNum, grayMax + 1); // split up the bitmap x = 0; for (i = 0; i <= grayMax; ++i) { patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH)); x += patternW; } // free memory delete bitmap; // store the new pattern dict segments->append(patternDict); return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length, Guint *refSegs, Guint nRefSegs) { JBIG2Bitmap *bitmap; JBIG2Segment *seg; JBIG2PatternDict *patternDict; JBIG2Bitmap *skipBitmap; Guint *grayImg; JBIG2Bitmap *grayBitmap; JBIG2Bitmap *patternBitmap; Guint w, h, x, y, segInfoFlags, extCombOp; Guint flags, mmr, templ, enableSkip, combOp; Guint gridW, gridH, stepX, stepY, patW, patH; int atx[4], aty[4]; int gridX, gridY, xx, yy, bit, j; Guint bpp, m, n, i; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the halftone region header if (!readUByte(&flags)) { goto eofError; } mmr = flags & 1; templ = (flags >> 1) & 3; enableSkip = (flags >> 3) & 1; combOp = (flags >> 4) & 7; if (!readULong(&gridW) || !readULong(&gridH) || !readLong(&gridX) || !readLong(&gridY) || !readUWord(&stepX) || !readUWord(&stepY)) { goto eofError; } if (w == 0 || h == 0 || w >= INT_MAX / h) { error(errSyntaxError, getPos(), "Bad bitmap size in JBIG2 halftone segment"); return; } if (gridH == 0 || gridW >= INT_MAX / gridH) { error(errSyntaxError, getPos(), "Bad grid size in JBIG2 halftone segment"); return; } // get pattern dictionary if (nRefSegs != 1) { error(errSyntaxError, getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment"); return; } if (!(seg = findSegment(refSegs[0])) || seg->getType() != jbig2SegPatternDict) { error(errSyntaxError, getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment"); return; } patternDict = (JBIG2PatternDict *)seg; i = patternDict->getSize(); if (i <= 1) { bpp = 0; } else { --i; bpp = 0; // i = floor((size-1) / 2^bpp) while (i > 0) { ++bpp; i >>= 1; } } patW = patternDict->getBitmap(0)->getWidth(); patH = patternDict->getBitmap(0)->getHeight(); // set up the arithmetic decoder if (!mmr) { resetGenericStats(templ, NULL); arithDecoder->start(); } // allocate the bitmap bitmap = new JBIG2Bitmap(segNum, w, h); if (flags & 0x80) { // HDEFPIXEL bitmap->clearToOne(); } else { bitmap->clearToZero(); } // compute the skip bitmap skipBitmap = NULL; if (enableSkip) { skipBitmap = new JBIG2Bitmap(0, gridW, gridH); skipBitmap->clearToZero(); for (m = 0; m < gridH; ++m) { for (n = 0; n < gridW; ++n) { xx = gridX + m * stepY + n * stepX; yy = gridY + m * stepX - n * stepY; if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w || ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) { skipBitmap->setPixel(n, m); } } } } // read the gray-scale image grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint)); memset(grayImg, 0, gridW * gridH * sizeof(Guint)); atx[0] = templ <= 1 ? 3 : 2; aty[0] = -1; atx[1] = -3; aty[1] = -1; atx[2] = 2; aty[2] = -2; atx[3] = -2; aty[3] = -2; for (j = bpp - 1; j >= 0; --j) { grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse, enableSkip, skipBitmap, atx, aty, -1); i = 0; for (m = 0; m < gridH; ++m) { for (n = 0; n < gridW; ++n) { bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1); grayImg[i] = (grayImg[i] << 1) | bit; ++i; } } delete grayBitmap; } // decode the image i = 0; for (m = 0; m < gridH; ++m) { xx = gridX + m * stepY; yy = gridY + m * stepX; for (n = 0; n < gridW; ++n) { if (!(enableSkip && skipBitmap->getPixel(n, m))) { patternBitmap = patternDict->getBitmap(grayImg[i]); bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp); } xx += stepX; yy -= stepY; ++i; } } gfree(grayImg); if (skipBitmap) { delete skipBitmap; } // combine the region bitmap into the page bitmap if (imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } pageBitmap->combine(bitmap, x, y, extCombOp); delete bitmap; // store the region bitmap } else { segments->append(bitmap); } return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length) { JBIG2Bitmap *bitmap; Guint w, h, x, y, segInfoFlags, extCombOp, rowCount; Guint flags, mmr, templ, tpgdOn; int atx[4], aty[4]; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the generic region segment header if (!readUByte(&flags)) { goto eofError; } mmr = flags & 1; templ = (flags >> 1) & 3; tpgdOn = (flags >> 3) & 1; // AT flags if (!mmr) { if (templ == 0) { if (!readByte(&atx[0]) || !readByte(&aty[0]) || !readByte(&atx[1]) || !readByte(&aty[1]) || !readByte(&atx[2]) || !readByte(&aty[2]) || !readByte(&atx[3]) || !readByte(&aty[3])) { goto eofError; } } else { if (!readByte(&atx[0]) || !readByte(&aty[0])) { goto eofError; } } } // set up the arithmetic decoder if (!mmr) { resetGenericStats(templ, NULL); arithDecoder->start(); } // read the bitmap bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse, NULL, atx, aty, mmr ? length - 18 : 0); // combine the region bitmap into the page bitmap if (imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } pageBitmap->combine(bitmap, x, y, extCombOp); delete bitmap; // store the region bitmap } else { bitmap->setSegNum(segNum); segments->append(bitmap); } // immediate generic segments can have an unspecified length, in // which case, a row count is stored at the end of the segment if (imm && length == 0xffffffff) { readULong(&rowCount); } return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } inline void JBIG2Stream::mmrAddPixels(int a1, int blackPixels, int *codingLine, int *a0i, int w) { if (a1 > codingLine[*a0i]) { if (a1 > w) { error(errSyntaxError, getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1); a1 = w; } if ((*a0i & 1) ^ blackPixels) { ++*a0i; } codingLine[*a0i] = a1; } } inline void JBIG2Stream::mmrAddPixelsNeg(int a1, int blackPixels, int *codingLine, int *a0i, int w) { if (a1 > codingLine[*a0i]) { if (a1 > w) { error(errSyntaxError, getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1); a1 = w; } if ((*a0i & 1) ^ blackPixels) { ++*a0i; } codingLine[*a0i] = a1; } else if (a1 < codingLine[*a0i]) { if (a1 < 0) { error(errSyntaxError, getPos(), "Invalid JBIG2 MMR code"); a1 = 0; } while (*a0i > 0 && a1 <= codingLine[*a0i - 1]) { --*a0i; } codingLine[*a0i] = a1; } } JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h, int templ, GBool tpgdOn, GBool useSkip, JBIG2Bitmap *skip, int *atx, int *aty, int mmrDataLength) { JBIG2Bitmap *bitmap; GBool ltp; Guint ltpCX, cx, cx0, cx1, cx2; int *refLine, *codingLine; int code1, code2, code3; Guchar *p0, *p1, *p2, *pp; Guchar *atP0, *atP1, *atP2, *atP3; Guint buf0, buf1, buf2; Guint atBuf0, atBuf1, atBuf2, atBuf3; int atShift0, atShift1, atShift2, atShift3; Guchar mask; int x, y, x0, x1, a0i, b1i, blackPixels, pix, i; bitmap = new JBIG2Bitmap(0, w, h); bitmap->clearToZero(); //----- MMR decode if (mmr) { mmrDecoder->reset(); if (w > INT_MAX - 2) { error(errSyntaxError, getPos(), "Bad width in JBIG2 generic bitmap"); // force a call to gmalloc(-1), which will throw an exception w = -3; } // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = w // ---> max codingLine size = w + 1 // refLine has one extra guard entry at the end // ---> max refLine size = w + 2 codingLine = (int *)gmallocn(w + 1, sizeof(int)); refLine = (int *)gmallocn(w + 2, sizeof(int)); codingLine[0] = w; for (y = 0; y < h; ++y) { // copy coding line to ref line for (i = 0; codingLine[i] < w; ++i) { refLine[i] = codingLine[i]; } refLine[i++] = w; refLine[i] = w; // decode a line codingLine[0] = 0; a0i = 0; b1i = 0; blackPixels = 0; // invariant: // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] <= w // exception at left edge: // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible // exception at right edge: // refLine[b1i] = refLine[b1i+1] = w is possible while (codingLine[a0i] < w) { code1 = mmrDecoder->get2DCode(); switch (code1) { case twoDimPass: mmrAddPixels(refLine[b1i + 1], blackPixels, codingLine, &a0i, w); if (refLine[b1i + 1] < w) { b1i += 2; } break; case twoDimHoriz: code1 = code2 = 0; if (blackPixels) { do { code1 += code3 = mmrDecoder->getBlackCode(); } while (code3 >= 64); do { code2 += code3 = mmrDecoder->getWhiteCode(); } while (code3 >= 64); } else { do { code1 += code3 = mmrDecoder->getWhiteCode(); } while (code3 >= 64); do { code2 += code3 = mmrDecoder->getBlackCode(); } while (code3 >= 64); } mmrAddPixels(codingLine[a0i] + code1, blackPixels, codingLine, &a0i, w); if (codingLine[a0i] < w) { mmrAddPixels(codingLine[a0i] + code2, blackPixels ^ 1, codingLine, &a0i, w); } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } break; case twoDimVertR3: mmrAddPixels(refLine[b1i] + 3, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertR2: mmrAddPixels(refLine[b1i] + 2, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertR1: mmrAddPixels(refLine[b1i] + 1, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVert0: mmrAddPixels(refLine[b1i], blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertL3: mmrAddPixelsNeg(refLine[b1i] - 3, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertL2: mmrAddPixelsNeg(refLine[b1i] - 2, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertL1: mmrAddPixelsNeg(refLine[b1i] - 1, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case EOF: mmrAddPixels(w, 0, codingLine, &a0i, w); break; default: error(errSyntaxError, getPos(), "Illegal code in JBIG2 MMR bitmap data"); mmrAddPixels(w, 0, codingLine, &a0i, w); break; } } // convert the run lengths to a bitmap line i = 0; while (1) { for (x = codingLine[i]; x < codingLine[i+1]; ++x) { bitmap->setPixel(x, y); } if (codingLine[i+1] >= w || codingLine[i+2] >= w) { break; } i += 2; } } if (mmrDataLength >= 0) { mmrDecoder->skipTo(mmrDataLength); } else { if (mmrDecoder->get24Bits() != 0x001001) { error(errSyntaxError, getPos(), "Missing EOFB in JBIG2 MMR bitmap data"); } } gfree(refLine); gfree(codingLine); //----- arithmetic decode } else { // set up the typical row context ltpCX = 0; // make gcc happy if (tpgdOn) { switch (templ) { case 0: ltpCX = 0x3953; // 001 11001 0101 0011 break; case 1: ltpCX = 0x079a; // 0011 11001 101 0 break; case 2: ltpCX = 0x0e3; // 001 1100 01 1 break; case 3: ltpCX = 0x18b; // 01100 0101 1 break; } } ltp = 0; cx = cx0 = cx1 = cx2 = 0; // make gcc happy for (y = 0; y < h; ++y) { // check for a "typical" (duplicate) row if (tpgdOn) { if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) { ltp = !ltp; } if (ltp) { if (y > 0) { bitmap->duplicateRow(y, y-1); } continue; } } switch (templ) { case 0: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; if (y >= 2) { p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize(); buf0 = *p0++ << 8; } else { p0 = NULL; buf0 = 0; } } else { p1 = p0 = NULL; buf1 = buf0 = 0; } if (atx[0] >= -8 && atx[0] <= 8 && atx[1] >= -8 && atx[1] <= 8 && atx[2] >= -8 && atx[2] <= 8 && atx[3] >= -8 && atx[3] <= 8) { // set up the adaptive context if (y + aty[0] >= 0) { atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = NULL; atBuf0 = 0; } atShift0 = 15 - atx[0]; if (y + aty[1] >= 0) { atP1 = bitmap->getDataPtr() + (y + aty[1]) * bitmap->getLineSize(); atBuf1 = *atP1++ << 8; } else { atP1 = NULL; atBuf1 = 0; } atShift1 = 15 - atx[1]; if (y + aty[2] >= 0) { atP2 = bitmap->getDataPtr() + (y + aty[2]) * bitmap->getLineSize(); atBuf2 = *atP2++ << 8; } else { atP2 = NULL; atBuf2 = 0; } atShift2 = 15 - atx[2]; if (y + aty[3] >= 0) { atP3 = bitmap->getDataPtr() + (y + aty[3]) * bitmap->getLineSize(); atBuf3 = *atP3++ << 8; } else { atP3 = NULL; atBuf3 = 0; } atShift3 = 15 - atx[3]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } if (atP1) { atBuf1 |= *atP1++; } if (atP2) { atBuf2 |= *atP2++; } if (atP3) { atBuf3 |= *atP3++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) | (((atBuf0 >> atShift0) & 1) << 3) | (((atBuf1 >> atShift1) & 1) << 2) | (((atBuf2 >> atShift2) & 1) << 1) | ((atBuf3 >> atShift3) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } if (aty[1] == 0) { atBuf1 |= 0x8000; } if (aty[2] == 0) { atBuf2 |= 0x8000; } if (aty[3] == 0) { atBuf3 |= 0x8000; } } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; atBuf1 <<= 1; atBuf2 <<= 1; atBuf3 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) | (bitmap->getPixel(x + atx[0], y + aty[0]) << 3) | (bitmap->getPixel(x + atx[1], y + aty[1]) << 2) | (bitmap->getPixel(x + atx[2], y + aty[2]) << 1) | bitmap->getPixel(x + atx[3], y + aty[3]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; } } } break; case 1: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; if (y >= 2) { p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize(); buf0 = *p0++ << 8; } else { p0 = NULL; buf0 = 0; } } else { p1 = p0 = NULL; buf1 = buf0 = 0; } if (atx[0] >= -8 && atx[0] <= 8) { // set up the adaptive context if (y + aty[0] >= 0) { atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = NULL; atBuf0 = 0; } atShift0 = 15 - atx[0]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 13) & 0x0f; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x07; cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) | ((atBuf0 >> atShift0) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 13) & 0x0f; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x07; cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) | bitmap->getPixel(x + atx[0], y + aty[0]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; } } } break; case 2: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; if (y >= 2) { p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize(); buf0 = *p0++ << 8; } else { p0 = NULL; buf0 = 0; } } else { p1 = p0 = NULL; buf1 = buf0 = 0; } if (atx[0] >= -8 && atx[0] <= 8) { // set up the adaptive context if (y + aty[0] >= 0) { atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = NULL; atBuf0 = 0; } atShift0 = 15 - atx[0]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 14) & 0x0f; cx2 = (buf2 >> 16) & 0x03; cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) | ((atBuf0 >> atShift0) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 14) & 0x0f; cx2 = (buf2 >> 16) & 0x03; cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) | bitmap->getPixel(x + atx[0], y + aty[0]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; } } } break; case 3: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; } else { p1 = NULL; buf1 = 0; } if (atx[0] >= -8 && atx[0] <= 8) { // set up the adaptive context if (y + aty[0] >= 0) { atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = NULL; atBuf0 = 0; } atShift0 = 15 - atx[0]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx1 = (buf1 >> 14) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx1 << 5) | (cx2 << 1) | ((atBuf0 >> atShift0) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } } } // update the context buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx1 = (buf1 >> 14) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx1 << 5) | (cx2 << 1) | bitmap->getPixel(x + atx[0], y + aty[0]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf1 <<= 1; buf2 <<= 1; } } } break; } } } return bitmap; } void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm, GBool lossless, Guint length, Guint *refSegs, Guint nRefSegs) { JBIG2Bitmap *bitmap, *refBitmap; Guint w, h, x, y, segInfoFlags, extCombOp; Guint flags, templ, tpgrOn; int atx[2], aty[2]; JBIG2Segment *seg; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the generic refinement region segment header if (!readUByte(&flags)) { goto eofError; } templ = flags & 1; tpgrOn = (flags >> 1) & 1; // AT flags if (!templ) { if (!readByte(&atx[0]) || !readByte(&aty[0]) || !readByte(&atx[1]) || !readByte(&aty[1])) { goto eofError; } } // resize the page bitmap if needed if (nRefSegs == 0 || imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } } // get referenced bitmap if (nRefSegs > 1) { error(errSyntaxError, getPos(), "Bad reference in JBIG2 generic refinement segment"); return; } if (nRefSegs == 1) { if (!(seg = findSegment(refSegs[0])) || seg->getType() != jbig2SegBitmap) { error(errSyntaxError, getPos(), "Bad bitmap reference in JBIG2 generic refinement segment"); return; } refBitmap = (JBIG2Bitmap *)seg; } else { refBitmap = pageBitmap->getSlice(x, y, w, h); } // set up the arithmetic decoder resetRefinementStats(templ, NULL); arithDecoder->start(); // read bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn, refBitmap, 0, 0, atx, aty); // combine the region bitmap into the page bitmap if (imm) { pageBitmap->combine(bitmap, x, y, extCombOp); delete bitmap; // store the region bitmap } else { bitmap->setSegNum(segNum); segments->append(bitmap); } // delete the referenced bitmap if (nRefSegs == 1) { discardSegment(refSegs[0]); } else { delete refBitmap; } return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h, int templ, GBool tpgrOn, JBIG2Bitmap *refBitmap, int refDX, int refDY, int *atx, int *aty) { JBIG2Bitmap *bitmap; GBool ltp; Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2; JBIG2BitmapPtr cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6; JBIG2BitmapPtr tpgrCXPtr0, tpgrCXPtr1, tpgrCXPtr2; int x, y, pix; bitmap = new JBIG2Bitmap(0, w, h); bitmap->clearToZero(); // set up the typical row context if (templ) { ltpCX = 0x008; } else { ltpCX = 0x0010; } ltp = 0; for (y = 0; y < h; ++y) { if (templ) { // set up the context bitmap->getPixelPtr(0, y-1, &cxPtr0); cx0 = bitmap->nextPixel(&cxPtr0); bitmap->getPixelPtr(-1, y, &cxPtr1); refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2); refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3); cx3 = refBitmap->nextPixel(&cxPtr3); cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3); refBitmap->getPixelPtr(-refDX, y+1-refDY, &cxPtr4); cx4 = refBitmap->nextPixel(&cxPtr4); // set up the typical prediction context tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy if (tpgrOn) { refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0); tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1); tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2); tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); } else { tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = NULL; // make gcc happy tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0; tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0; } for (x = 0; x < w; ++x) { // update the context cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7; cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7; cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3; if (tpgrOn) { // update the typical predictor context tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7; tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7; tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7; // check for a "typical" pixel if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) { ltp = !ltp; } if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) { bitmap->clearPixel(x, y); continue; } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) { bitmap->setPixel(x, y); continue; } } // build the context cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) | (refBitmap->nextPixel(&cxPtr2) << 5) | (cx3 << 2) | cx4; // decode the pixel if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) { bitmap->setPixel(x, y); } } } else { // set up the context bitmap->getPixelPtr(0, y-1, &cxPtr0); cx0 = bitmap->nextPixel(&cxPtr0); bitmap->getPixelPtr(-1, y, &cxPtr1); refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2); cx2 = refBitmap->nextPixel(&cxPtr2); refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3); cx3 = refBitmap->nextPixel(&cxPtr3); cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3); refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &cxPtr4); cx4 = refBitmap->nextPixel(&cxPtr4); cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4); bitmap->getPixelPtr(atx[0], y+aty[0], &cxPtr5); refBitmap->getPixelPtr(atx[1]-refDX, y+aty[1]-refDY, &cxPtr6); // set up the typical prediction context tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy if (tpgrOn) { refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0); tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1); tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2); tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); } else { tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = NULL; // make gcc happy tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0; tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0; } for (x = 0; x < w; ++x) { // update the context cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3; cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3; cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7; cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7; if (tpgrOn) { // update the typical predictor context tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7; tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7; tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7; // check for a "typical" pixel if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) { ltp = !ltp; } if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) { bitmap->clearPixel(x, y); continue; } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) { bitmap->setPixel(x, y); continue; } } // build the context cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) | (cx2 << 8) | (cx3 << 5) | (cx4 << 2) | (bitmap->nextPixel(&cxPtr5) << 1) | refBitmap->nextPixel(&cxPtr6); // decode the pixel if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) { bitmap->setPixel(x, y); } } } } return bitmap; } void JBIG2Stream::readPageInfoSeg(Guint length) { Guint xRes, yRes, flags, striping; if (!readULong(&pageW) || !readULong(&pageH) || !readULong(&xRes) || !readULong(&yRes) || !readUByte(&flags) || !readUWord(&striping)) { goto eofError; } pageDefPixel = (flags >> 2) & 1; defCombOp = (flags >> 3) & 3; // allocate the page bitmap if (pageH == 0xffffffff) { curPageH = striping & 0x7fff; } else { curPageH = pageH; } pageBitmap = new JBIG2Bitmap(0, pageW, curPageH); // default pixel value if (pageDefPixel) { pageBitmap->clearToOne(); } else { pageBitmap->clearToZero(); } return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readEndOfStripeSeg(Guint length) { // skip the segment byteCounter += curStr->discardChars(length); } void JBIG2Stream::readProfilesSeg(Guint length) { // skip the segment byteCounter += curStr->discardChars(length); } void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) { JBIG2HuffmanTable *huffTab; Guint flags, oob, prefixBits, rangeBits; int lowVal, highVal, val; Guint huffTabSize, i; if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) { goto eofError; } oob = flags & 1; prefixBits = ((flags >> 1) & 7) + 1; rangeBits = ((flags >> 4) & 7) + 1; huffDecoder->reset(); huffTabSize = 8; huffTab = (JBIG2HuffmanTable *) gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable)); i = 0; val = lowVal; while (val < highVal) { if (i == huffTabSize) { huffTabSize *= 2; huffTab = (JBIG2HuffmanTable *) greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable)); } huffTab[i].val = val; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = huffDecoder->readBits(rangeBits); val += 1 << huffTab[i].rangeLen; ++i; } if (i + oob + 3 > huffTabSize) { huffTabSize = i + oob + 3; huffTab = (JBIG2HuffmanTable *) greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable)); } huffTab[i].val = lowVal - 1; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = jbig2HuffmanLOW; ++i; huffTab[i].val = highVal; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = 32; ++i; if (oob) { huffTab[i].val = 0; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = jbig2HuffmanOOB; ++i; } huffTab[i].val = 0; huffTab[i].prefixLen = 0; huffTab[i].rangeLen = jbig2HuffmanEOT; huffDecoder->buildTable(huffTab, i); // create and store the new table segment segments->append(new JBIG2CodeTable(segNum, huffTab)); return; eofError: error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readExtensionSeg(Guint length) { // skip the segment byteCounter += curStr->discardChars(length); } JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) { JBIG2Segment *seg; int i; for (i = 0; i < globalSegments->getLength(); ++i) { seg = (JBIG2Segment *)globalSegments->get(i); if (seg->getSegNum() == segNum) { return seg; } } for (i = 0; i < segments->getLength(); ++i) { seg = (JBIG2Segment *)segments->get(i); if (seg->getSegNum() == segNum) { return seg; } } return NULL; } void JBIG2Stream::discardSegment(Guint segNum) { JBIG2Segment *seg; int i; for (i = 0; i < globalSegments->getLength(); ++i) { seg = (JBIG2Segment *)globalSegments->get(i); if (seg->getSegNum() == segNum) { globalSegments->del(i); return; } } for (i = 0; i < segments->getLength(); ++i) { seg = (JBIG2Segment *)segments->get(i); if (seg->getSegNum() == segNum) { segments->del(i); return; } } } void JBIG2Stream::resetGenericStats(Guint templ, JArithmeticDecoderStats *prevStats) { int size; size = contextSize[templ]; if (prevStats && prevStats->getContextSize() == size) { if (genericRegionStats->getContextSize() == size) { genericRegionStats->copyFrom(prevStats); } else { delete genericRegionStats; genericRegionStats = prevStats->copy(); } } else { if (genericRegionStats->getContextSize() == size) { genericRegionStats->reset(); } else { delete genericRegionStats; genericRegionStats = new JArithmeticDecoderStats(1 << size); } } } void JBIG2Stream::resetRefinementStats(Guint templ, JArithmeticDecoderStats *prevStats) { int size; size = refContextSize[templ]; if (prevStats && prevStats->getContextSize() == size) { if (refinementRegionStats->getContextSize() == size) { refinementRegionStats->copyFrom(prevStats); } else { delete refinementRegionStats; refinementRegionStats = prevStats->copy(); } } else { if (refinementRegionStats->getContextSize() == size) { refinementRegionStats->reset(); } else { delete refinementRegionStats; refinementRegionStats = new JArithmeticDecoderStats(1 << size); } } } void JBIG2Stream::resetIntStats(int symCodeLen) { iadhStats->reset(); iadwStats->reset(); iaexStats->reset(); iaaiStats->reset(); iadtStats->reset(); iaitStats->reset(); iafsStats->reset(); iadsStats->reset(); iardxStats->reset(); iardyStats->reset(); iardwStats->reset(); iardhStats->reset(); iariStats->reset(); if (iaidStats->getContextSize() == 1 << (symCodeLen + 1)) { iaidStats->reset(); } else { delete iaidStats; iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1)); } } GBool JBIG2Stream::readUByte(Guint *x) { int c0; if ((c0 = curStr->getChar()) == EOF) { return gFalse; } ++byteCounter; *x = (Guint)c0; return gTrue; } GBool JBIG2Stream::readByte(int *x) { int c0; if ((c0 = curStr->getChar()) == EOF) { return gFalse; } ++byteCounter; *x = c0; if (c0 & 0x80) { *x |= -1 - 0xff; } return gTrue; } GBool JBIG2Stream::readUWord(Guint *x) { int c0, c1; if ((c0 = curStr->getChar()) == EOF || (c1 = curStr->getChar()) == EOF) { return gFalse; } byteCounter += 2; *x = (Guint)((c0 << 8) | c1); return gTrue; } GBool JBIG2Stream::readULong(Guint *x) { int c0, c1, c2, c3; if ((c0 = curStr->getChar()) == EOF || (c1 = curStr->getChar()) == EOF || (c2 = curStr->getChar()) == EOF || (c3 = curStr->getChar()) == EOF) { return gFalse; } byteCounter += 4; *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); return gTrue; } GBool JBIG2Stream::readLong(int *x) { int c0, c1, c2, c3; if ((c0 = curStr->getChar()) == EOF || (c1 = curStr->getChar()) == EOF || (c2 = curStr->getChar()) == EOF || (c3 = curStr->getChar()) == EOF) { return gFalse; } byteCounter += 4; *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); if (c0 & 0x80) { *x |= -1 - (int)0xffffffff; } return gTrue; } xpdf-3.04/xpdf/BuiltinFont.h0000644000076400007640000000204212341430012015246 0ustar dereknderekn//======================================================================== // // BuiltinFont.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef BUILTINFONT_H #define BUILTINFONT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" struct BuiltinFont; class BuiltinFontWidths; //------------------------------------------------------------------------ struct BuiltinFont { const char *name; const char **defaultBaseEnc; short ascent; short descent; short bbox[4]; BuiltinFontWidths *widths; }; //------------------------------------------------------------------------ struct BuiltinFontWidth { const char *name; Gushort width; BuiltinFontWidth *next; }; class BuiltinFontWidths { public: BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA); ~BuiltinFontWidths(); GBool getWidth(const char *name, Gushort *width); private: int hash(const char *name); BuiltinFontWidth **tab; int size; }; #endif xpdf-3.04/xpdf/about.xbm0000644000076400007640000000042312341430012014463 0ustar dereknderekn#define about_width 10 #define about_height 15 static unsigned char about_bits[] = { 0x78, 0x00, 0xfc, 0x00, 0xce, 0x01, 0x86, 0x01, 0x80, 0x01, 0x80, 0x01, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00}; xpdf-3.04/xpdf/AcroForm.cc0000644000076400007640000014265012341430012014671 0ustar dereknderekn//======================================================================== // // AcroForm.cc // // Copyright 2012 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "GString.h" #include "GList.h" #include "Error.h" #include "Object.h" #include "PDFDoc.h" #include "TextString.h" #include "Gfx.h" #include "GfxFont.h" #include "OptionalContent.h" #include "Annot.h" #include "Lexer.h" #include "AcroForm.h" //------------------------------------------------------------------------ #define acroFormFlagReadOnly (1 << 0) // all #define acroFormFlagRequired (1 << 1) // all #define acroFormFlagNoExport (1 << 2) // all #define acroFormFlagMultiline (1 << 12) // text #define acroFormFlagPassword (1 << 13) // text #define acroFormFlagNoToggleToOff (1 << 14) // button #define acroFormFlagRadio (1 << 15) // button #define acroFormFlagPushbutton (1 << 16) // button #define acroFormFlagCombo (1 << 17) // choice #define acroFormFlagEdit (1 << 18) // choice #define acroFormFlagSort (1 << 19) // choice #define acroFormFlagFileSelect (1 << 20) // text #define acroFormFlagMultiSelect (1 << 21) // choice #define acroFormFlagDoNotSpellCheck (1 << 22) // text, choice #define acroFormFlagDoNotScroll (1 << 23) // text #define acroFormFlagComb (1 << 24) // text #define acroFormFlagRadiosInUnison (1 << 25) // button #define acroFormFlagRichText (1 << 25) // text #define acroFormFlagCommitOnSelChange (1 << 26) // choice #define acroFormQuadLeft 0 #define acroFormQuadCenter 1 #define acroFormQuadRight 2 #define annotFlagHidden 0x0002 #define annotFlagPrint 0x0004 #define annotFlagNoView 0x0020 // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle 0.55228475 //------------------------------------------------------------------------ // map an annotation ref to a page number class AcroFormAnnotPage { public: AcroFormAnnotPage(int annotNumA, int annotGenA, int pageNumA) { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; } int annotNum; int annotGen; int pageNum; }; //------------------------------------------------------------------------ // AcroForm //------------------------------------------------------------------------ AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) { AcroForm *acroForm; Object fieldsObj, obj1, obj2; int i; acroForm = new AcroForm(docA, acroFormObjA); if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) { acroForm->needAppearances = obj1.getBool(); } obj1.free(); acroForm->buildAnnotPageList(catalog); if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) { if (!obj1.isNull()) { error(errSyntaxError, -1, "AcroForm Fields entry is wrong type"); } obj1.free(); delete acroForm; return NULL; } for (i = 0; i < obj1.arrayGetLength(); ++i) { obj1.arrayGetNF(i, &obj2); acroForm->scanField(&obj2); obj2.free(); } obj1.free(); return acroForm; } AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA): Form(docA) { acroFormObjA->copy(&acroFormObj); needAppearances = gFalse; annotPages = new GList(); fields = new GList(); } AcroForm::~AcroForm() { acroFormObj.free(); deleteGList(annotPages, AcroFormAnnotPage); deleteGList(fields, AcroFormField); } void AcroForm::buildAnnotPageList(Catalog *catalog) { Object annotsObj, annotObj; int pageNum, i; for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) { if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) { for (i = 0; i < annotsObj.arrayGetLength(); ++i) { if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) { annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(), annotObj.getRefGen(), pageNum)); } annotObj.free(); } } annotsObj.free(); } //~ sort the list } int AcroForm::lookupAnnotPage(Object *annotRef) { AcroFormAnnotPage *annotPage; int num, gen, i; if (!annotRef->isRef()) { return 0; } num = annotRef->getRefNum(); gen = annotRef->getRefGen(); //~ use bin search for (i = 0; i < annotPages->getLength(); ++i) { annotPage = (AcroFormAnnotPage *)annotPages->get(i); if (annotPage->annotNum == num && annotPage->annotGen == gen) { return annotPage->pageNum; } } return 0; } void AcroForm::scanField(Object *fieldRef) { AcroFormField *field; Object fieldObj, kidsObj, kidRef, kidObj, subtypeObj; GBool isTerminal; int i; fieldRef->fetch(doc->getXRef(), &fieldObj); if (!fieldObj.isDict()) { error(errSyntaxError, -1, "AcroForm field object is wrong type"); fieldObj.free(); return; } // if this field has a Kids array, and all of the kids have a Parent // reference (i.e., they're all form fields, not widget // annotations), then this is a non-terminal field, and we need to // scan the kids isTerminal = gTrue; if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) { isTerminal = gFalse; for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) { kidsObj.arrayGet(i, &kidObj); if (kidObj.isDict()) { if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) { isTerminal = gTrue; } subtypeObj.free(); } kidObj.free(); } if (!isTerminal) { for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) { kidsObj.arrayGetNF(i, &kidRef); scanField(&kidRef); kidRef.free(); } } } kidsObj.free(); if (isTerminal) { if ((field = AcroFormField::load(this, fieldRef))) { fields->append(field); } } fieldObj.free(); } void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) { int i; for (i = 0; i < fields->getLength(); ++i) { ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing); } } int AcroForm::getNumFields() { return fields->getLength(); } FormField *AcroForm::getField(int idx) { return (AcroFormField *)fields->get(idx); } //------------------------------------------------------------------------ // AcroFormField //------------------------------------------------------------------------ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) { GString *typeStr; TextString *nameA; Guint flagsA; GBool haveFlags; Object fieldObjA, parentObj, parentObj2, obj1, obj2; AcroFormFieldType typeA; AcroFormField *field; fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA); //----- get field info if (fieldObjA.dictLookup("T", &obj1)->isString()) { nameA = new TextString(obj1.getString()); } else { nameA = new TextString(); } obj1.free(); if (fieldObjA.dictLookup("FT", &obj1)->isName()) { typeStr = new GString(obj1.getName()); } else { typeStr = NULL; } obj1.free(); if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) { flagsA = (Guint)obj1.getInt(); haveFlags = gTrue; } else { flagsA = 0; haveFlags = gFalse; } obj1.free(); //----- get info from parent non-terminal fields fieldObjA.dictLookup("Parent", &parentObj); while (parentObj.isDict()) { if (parentObj.dictLookup("T", &obj1)->isString()) { if (nameA->getLength()) { nameA->insert(0, (Unicode)'.'); } nameA->insert(0, obj1.getString()); } obj1.free(); if (!typeStr) { if (parentObj.dictLookup("FT", &obj1)->isName()) { typeStr = new GString(obj1.getName()); } obj1.free(); } if (!haveFlags) { if (parentObj.dictLookup("Ff", &obj1)->isInt()) { flagsA = (Guint)obj1.getInt(); haveFlags = gTrue; } obj1.free(); } parentObj.dictLookup("Parent", &parentObj2); parentObj.free(); parentObj = parentObj2; } parentObj.free(); if (!typeStr) { error(errSyntaxError, -1, "Missing type in AcroForm field"); goto err1; } else if (!typeStr->cmp("Btn")) { if (flagsA & acroFormFlagPushbutton) { typeA = acroFormFieldPushbutton; } else if (flagsA & acroFormFlagRadio) { typeA = acroFormFieldRadioButton; } else { typeA = acroFormFieldCheckbox; } } else if (!typeStr->cmp("Tx")) { if (flagsA & acroFormFlagFileSelect) { typeA = acroFormFieldFileSelect; } else if (flagsA & acroFormFlagMultiline) { typeA = acroFormFieldMultilineText; } else { typeA = acroFormFieldText; } } else if (!typeStr->cmp("Ch")) { if (flagsA & acroFormFlagCombo) { typeA = acroFormFieldComboBox; } else { typeA = acroFormFieldListBox; } } else if (!typeStr->cmp("Sig")) { typeA = acroFormFieldSignature; } else { error(errSyntaxError, -1, "Invalid type in AcroForm field"); goto err1; } delete typeStr; field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA, typeA, nameA, flagsA); fieldObjA.free(); return field; err1: delete typeStr; delete nameA; fieldObjA.free(); return NULL; } AcroFormField::AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA, AcroFormFieldType typeA, TextString *nameA, Guint flagsA) { acroForm = acroFormA; fieldRefA->copy(&fieldRef); fieldObjA->copy(&fieldObj); type = typeA; name = nameA; flags = flagsA; } AcroFormField::~AcroFormField() { fieldRef.free(); fieldObj.free(); delete name; } const char *AcroFormField::getType() { switch (type) { case acroFormFieldPushbutton: return "PushButton"; case acroFormFieldRadioButton: return "RadioButton"; case acroFormFieldCheckbox: return "Checkbox"; case acroFormFieldFileSelect: return "FileSelect"; case acroFormFieldMultilineText: return "MultilineText"; case acroFormFieldText: return "Text"; case acroFormFieldComboBox: return "ComboBox"; case acroFormFieldListBox: return "ListBox"; case acroFormFieldSignature: return "Signature"; default: return NULL; } } Unicode *AcroFormField::getName(int *length) { Unicode *u, *ret; int n; u = name->getUnicode(); n = name->getLength(); ret = (Unicode *)gmallocn(n, sizeof(Unicode)); memcpy(ret, u, n * sizeof(Unicode)); *length = n; return ret; } Unicode *AcroFormField::getValue(int *length) { Object obj1; Unicode *u; char *s; TextString *ts; int n, i; fieldLookup("V", &obj1); if (obj1.isName()) { s = obj1.getName(); n = (int)strlen(s); u = (Unicode *)gmallocn(n, sizeof(Unicode)); for (i = 0; i < n; ++i) { u[i] = s[i] & 0xff; } *length = n; return u; } else if (obj1.isString()) { ts = new TextString(obj1.getString()); n = ts->getLength(); u = (Unicode *)gmallocn(n, sizeof(Unicode)); memcpy(u, ts->getUnicode(), n * sizeof(Unicode)); *length = n; delete ts; return u; } else { return NULL; } } void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) { Object kidsObj, annotRef, annotObj; int i; // find the annotation object(s) if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) { for (i = 0; i < kidsObj.arrayGetLength(); ++i) { kidsObj.arrayGetNF(i, &annotRef); annotRef.fetch(acroForm->doc->getXRef(), &annotObj); drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj); annotObj.free(); annotRef.free(); } } else { drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj); } kidsObj.free(); } void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing, Object *annotRef, Object *annotObj) { Object obj1, obj2; double xMin, yMin, xMax, yMax, t; int annotFlags; GBool oc; if (!annotObj->isDict()) { return; } //----- get the page number // the "P" (page) field in annotations is optional, so we can't // depend on it here if (acroForm->lookupAnnotPage(annotRef) != pageNum) { return; } //----- check annotation flags if (annotObj->dictLookup("F", &obj1)->isInt()) { annotFlags = obj1.getInt(); } else { annotFlags = 0; } obj1.free(); if ((annotFlags & annotFlagHidden) || (printing && !(annotFlags & annotFlagPrint)) || (!printing && (annotFlags & annotFlagNoView))) { return; } //----- check the optional content entry annotObj->dictLookupNF("OC", &obj1); if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) { obj1.free(); return; } obj1.free(); //----- get the bounding box if (annotObj->dictLookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) { xMin = yMin = xMax = yMax = 0; if (obj1.arrayGet(0, &obj2)->isNum()) { xMin = obj2.getNum(); } obj2.free(); if (obj1.arrayGet(1, &obj2)->isNum()) { yMin = obj2.getNum(); } obj2.free(); if (obj1.arrayGet(2, &obj2)->isNum()) { xMax = obj2.getNum(); } obj2.free(); if (obj1.arrayGet(3, &obj2)->isNum()) { yMax = obj2.getNum(); } obj2.free(); if (xMin > xMax) { t = xMin; xMin = xMax; xMax = t; } if (yMin > yMax) { t = yMin; yMin = yMax; yMax = t; } } else { error(errSyntaxError, -1, "Bad bounding box for annotation"); obj1.free(); return; } obj1.free(); //----- draw it if (acroForm->needAppearances) { drawNewAppearance(gfx, annotObj->getDict(), xMin, yMin, xMax, yMax); } else { drawExistingAppearance(gfx, annotObj->getDict(), xMin, yMin, xMax, yMax); } } // Draw the existing appearance stream for a single annotation // attached to this field. void AcroFormField::drawExistingAppearance(Gfx *gfx, Dict *annot, double xMin, double yMin, double xMax, double yMax) { Object apObj, asObj, appearance, obj1; //----- get the appearance stream if (annot->lookup("AP", &apObj)->isDict()) { apObj.dictLookup("N", &obj1); if (obj1.isDict()) { if (annot->lookup("AS", &asObj)->isName()) { obj1.dictLookupNF(asObj.getName(), &appearance); } else if (obj1.dictGetLength() == 1) { obj1.dictGetValNF(0, &appearance); } else { obj1.dictLookupNF("Off", &appearance); } asObj.free(); } else { apObj.dictLookupNF("N", &appearance); } obj1.free(); } apObj.free(); //----- draw it if (!appearance.isNone()) { gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax); appearance.free(); } } // Regenerate the appearnce for this field, and draw it. void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot, double xMin, double yMin, double xMax, double yMax) { Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj; Object obj1, obj2, obj3; Dict *mkDict; MemStream *appearStream; GfxFontDict *fontDict; GBool hasCaption; double dx, dy, r; GString *caption, *da; GString **text; GBool *selection; AnnotBorderType borderType; double borderWidth; double *borderDash; GString *appearanceState; int borderDashLength, rot, quadding, comb, nOptions, topIdx, i, j; appearBuf = new GString(); // get the appearance characteristics (MK) dictionary if (annot->lookup("MK", &mkObj)->isDict()) { mkDict = mkObj.getDict(); } else { mkDict = NULL; } // draw the background if (mkDict) { if (mkDict->lookup("BG", &obj1)->isArray() && obj1.arrayGetLength() > 0) { setColor(obj1.getArray(), gTrue, 0); appearBuf->appendf("0 0 {0:.4f} {1:.4f} re f\n", xMax - xMin, yMax - yMin); } obj1.free(); } // get the field type fieldLookup("FT", &ftObj); // draw the border borderType = annotBorderSolid; borderWidth = 1; borderDash = NULL; borderDashLength = 0; if (annot->lookup("BS", &obj1)->isDict()) { if (obj1.dictLookup("S", &obj2)->isName()) { if (obj2.isName("S")) { borderType = annotBorderSolid; } else if (obj2.isName("D")) { borderType = annotBorderDashed; } else if (obj2.isName("B")) { borderType = annotBorderBeveled; } else if (obj2.isName("I")) { borderType = annotBorderInset; } else if (obj2.isName("U")) { borderType = annotBorderUnderlined; } } obj2.free(); if (obj1.dictLookup("W", &obj2)->isNum()) { borderWidth = obj2.getNum(); } obj2.free(); if (obj1.dictLookup("D", &obj2)->isArray()) { borderDashLength = obj2.arrayGetLength(); borderDash = (double *)gmallocn(borderDashLength, sizeof(double)); for (i = 0; i < borderDashLength; ++i) { if (obj2.arrayGet(i, &obj3)->isNum()) { borderDash[i] = obj3.getNum(); } else { borderDash[i] = 1; } obj3.free(); } } obj2.free(); } else { obj1.free(); if (annot->lookup("Border", &obj1)->isArray()) { if (obj1.arrayGetLength() >= 3) { if (obj1.arrayGet(2, &obj2)->isNum()) { borderWidth = obj2.getNum(); } obj2.free(); if (obj1.arrayGetLength() >= 4) { if (obj1.arrayGet(3, &obj2)->isArray()) { borderType = annotBorderDashed; borderDashLength = obj2.arrayGetLength(); borderDash = (double *)gmallocn(borderDashLength, sizeof(double)); for (i = 0; i < borderDashLength; ++i) { if (obj2.arrayGet(i, &obj3)->isNum()) { borderDash[i] = obj3.getNum(); } else { borderDash[i] = 1; } obj3.free(); } } else { // Adobe draws no border at all if the last element is of // the wrong type. borderWidth = 0; } obj2.free(); } } } } obj1.free(); if (mkDict) { if (borderWidth > 0) { mkDict->lookup("BC", &obj1); if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) { mkDict->lookup("BG", &obj1); } if (obj1.isArray() && obj1.arrayGetLength() > 0) { dx = xMax - xMin; dy = yMax - yMin; // radio buttons with no caption have a round border hasCaption = mkDict->lookup("CA", &obj2)->isString(); obj2.free(); if (ftObj.isName("Btn") && (flags & acroFormFlagRadio) && !hasCaption) { r = 0.5 * (dx < dy ? dx : dy); switch (borderType) { case annotBorderDashed: appearBuf->append("["); for (i = 0; i < borderDashLength; ++i) { appearBuf->appendf(" {0:.4f}", borderDash[i]); } appearBuf->append("] 0 d\n"); // fall through to the solid case case annotBorderSolid: case annotBorderUnderlined: appearBuf->appendf("{0:.4f} w\n", borderWidth); setColor(obj1.getArray(), gFalse, 0); drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * borderWidth, "s"); break; case annotBorderBeveled: case annotBorderInset: appearBuf->appendf("{0:.4f} w\n", 0.5 * borderWidth); setColor(obj1.getArray(), gFalse, 0); drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * borderWidth, "s"); setColor(obj1.getArray(), gFalse, borderType == annotBorderBeveled ? 1 : -1); drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth); setColor(obj1.getArray(), gFalse, borderType == annotBorderBeveled ? -1 : 1); drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth); break; } } else { switch (borderType) { case annotBorderDashed: appearBuf->append("["); for (i = 0; i < borderDashLength; ++i) { appearBuf->appendf(" {0:.4f}", borderDash[i]); } appearBuf->append("] 0 d\n"); // fall through to the solid case case annotBorderSolid: appearBuf->appendf("{0:.4f} w\n", borderWidth); setColor(obj1.getArray(), gFalse, 0); appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n", 0.5 * borderWidth, dx - borderWidth, dy - borderWidth); break; case annotBorderBeveled: case annotBorderInset: setColor(obj1.getArray(), gTrue, borderType == annotBorderBeveled ? 1 : -1); appearBuf->append("0 0 m\n"); appearBuf->appendf("0 {0:.4f} l\n", dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx - borderWidth, dy - borderWidth); appearBuf->appendf("{0:.4f} {1:.4f} l\n", borderWidth, dy - borderWidth); appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth); appearBuf->append("f\n"); setColor(obj1.getArray(), gTrue, borderType == annotBorderBeveled ? -1 : 1); appearBuf->append("0 0 m\n"); appearBuf->appendf("{0:.4f} 0 l\n", dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx - borderWidth, dy - borderWidth); appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx - borderWidth, borderWidth); appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth); appearBuf->append("f\n"); break; case annotBorderUnderlined: appearBuf->appendf("{0:.4f} w\n", borderWidth); setColor(obj1.getArray(), gFalse, 0); appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx); break; } // clip to the inside of the border appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n", borderWidth, dx - 2 * borderWidth, dy - 2 * borderWidth); } } obj1.free(); } } gfree(borderDash); // get the resource dictionary fieldLookup("DR", &drObj); // build the font dictionary if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) { fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict()); } else { fontDict = NULL; } obj1.free(); // get the default appearance string if (fieldLookup("DA", &obj1)->isString()) { da = obj1.getString()->copy(); } else { da = NULL; } obj1.free(); // get the rotation value rot = 0; if (mkDict) { if (mkDict->lookup("R", &obj1)->isInt()) { rot = obj1.getInt(); } obj1.free(); } // get the appearance state annot->lookup("AP", &apObj); annot->lookup("AS", &asObj); appearanceState = NULL; if (asObj.isName()) { appearanceState = new GString(asObj.getName()); } else if (apObj.isDict()) { apObj.dictLookup("N", &obj1); if (obj1.isDict() && obj1.dictGetLength() == 1) { appearanceState = new GString(obj1.dictGetKey(0)); } obj1.free(); } if (!appearanceState) { appearanceState = new GString("Off"); } asObj.free(); apObj.free(); // draw the field contents if (ftObj.isName("Btn")) { caption = NULL; if (mkDict) { if (mkDict->lookup("CA", &obj1)->isString()) { caption = obj1.getString()->copy(); } obj1.free(); } // radio button if (flags & acroFormFlagRadio) { //~ Acrobat doesn't draw a caption if there is no AP dict (?) if (fieldLookup("V", &obj1) ->isName(appearanceState->getCString())) { if (caption) { drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter, gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth); } else { if (mkDict) { if (mkDict->lookup("BC", &obj2)->isArray() && obj2.arrayGetLength() > 0) { dx = xMax - xMin; dy = yMax - yMin; setColor(obj2.getArray(), gTrue, 0); drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), "f"); } obj2.free(); } } } obj1.free(); // pushbutton } else if (flags & acroFormFlagPushbutton) { if (caption) { drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter, gFalse, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth); } // checkbox } else { fieldLookup("V", &obj1); if (obj1.isName() && !obj1.isName("Off")) { if (!caption) { caption = new GString("3"); // ZapfDingbats checkmark } drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter, gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth); } obj1.free(); } if (caption) { delete caption; } } else if (ftObj.isName("Tx")) { //~ value strings can be Unicode if (!fieldLookup("V", &obj1)->isString()) { obj1.free(); fieldLookup("DV", &obj1); } if (obj1.isString()) { if (fieldLookup("Q", &obj2)->isInt()) { quadding = obj2.getInt(); } else { quadding = acroFormQuadLeft; } obj2.free(); comb = 0; if (flags & acroFormFlagComb) { if (fieldLookup("MaxLen", &obj2)->isInt()) { comb = obj2.getInt(); } obj2.free(); } drawText(obj1.getString(), da, fontDict, flags & acroFormFlagMultiline, comb, quadding, gTrue, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth); } obj1.free(); } else if (ftObj.isName("Ch")) { //~ value/option strings can be Unicode if (fieldLookup("Q", &obj1)->isInt()) { quadding = obj1.getInt(); } else { quadding = acroFormQuadLeft; } obj1.free(); // combo box if (flags & acroFormFlagCombo) { if (fieldLookup("V", &obj1)->isString()) { drawText(obj1.getString(), da, fontDict, gFalse, 0, quadding, gTrue, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth); //~ Acrobat draws a popup icon on the right side } obj1.free(); // list box } else { if (fieldObj.dictLookup("Opt", &obj1)->isArray()) { nOptions = obj1.arrayGetLength(); // get the option text text = (GString **)gmallocn(nOptions, sizeof(GString *)); for (i = 0; i < nOptions; ++i) { text[i] = NULL; obj1.arrayGet(i, &obj2); if (obj2.isString()) { text[i] = obj2.getString()->copy(); } else if (obj2.isArray() && obj2.arrayGetLength() == 2) { if (obj2.arrayGet(1, &obj3)->isString()) { text[i] = obj3.getString()->copy(); } obj3.free(); } obj2.free(); if (!text[i]) { text[i] = new GString(); } } // get the selected option(s) selection = (GBool *)gmallocn(nOptions, sizeof(GBool)); //~ need to use the I field in addition to the V field fieldLookup("V", &obj2); for (i = 0; i < nOptions; ++i) { selection[i] = gFalse; if (obj2.isString()) { if (!obj2.getString()->cmp(text[i])) { selection[i] = gTrue; } } else if (obj2.isArray()) { for (j = 0; j < obj2.arrayGetLength(); ++j) { if (obj2.arrayGet(j, &obj3)->isString() && !obj3.getString()->cmp(text[i])) { selection[i] = gTrue; } obj3.free(); } } } obj2.free(); // get the top index if (fieldObj.dictLookup("TI", &obj2)->isInt()) { topIdx = obj2.getInt(); } else { topIdx = 0; } obj2.free(); // draw the text drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding, xMin, yMin, xMax, yMax, borderWidth); for (i = 0; i < nOptions; ++i) { delete text[i]; } gfree(text); gfree(selection); } obj1.free(); } } else if (ftObj.isName("Sig")) { //~unimp } else { error(errSyntaxError, -1, "Unknown field type"); } delete appearanceState; if (da) { delete da; } // build the appearance stream dictionary appearDict.initDict(acroForm->doc->getXRef()); appearDict.dictAdd(copyString("Length"), obj1.initInt(appearBuf->getLength())); appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form")); obj1.initArray(acroForm->doc->getXRef()); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(xMax - xMin)); obj1.arrayAdd(obj2.initReal(yMax - yMin)); appearDict.dictAdd(copyString("BBox"), &obj1); // set the resource dictionary if (drObj.isDict()) { appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1)); } drObj.free(); // build the appearance stream appearStream = new MemStream(appearBuf->getCString(), 0, appearBuf->getLength(), &appearDict); appearance.initStream(appearStream); // draw it gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax); appearance.free(); delete appearBuf; appearBuf = NULL; if (fontDict) { delete fontDict; } ftObj.free(); mkObj.free(); } // Set the current fill or stroke color, based on (which should // have 1, 3, or 4 elements). If is +1, color is brightened; // if is -1, color is darkened; otherwise color is not // modified. void AcroFormField::setColor(Array *a, GBool fill, int adjust) { Object obj1; double color[4]; int nComps, i; nComps = a->getLength(); if (nComps > 4) { nComps = 4; } for (i = 0; i < nComps && i < 4; ++i) { if (a->get(i, &obj1)->isNum()) { color[i] = obj1.getNum(); } else { color[i] = 0; } obj1.free(); } if (nComps == 4) { adjust = -adjust; } if (adjust > 0) { for (i = 0; i < nComps; ++i) { color[i] = 0.5 * color[i] + 0.5; } } else if (adjust < 0) { for (i = 0; i < nComps; ++i) { color[i] = 0.5 * color[i]; } } if (nComps == 4) { appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n", color[0], color[1], color[2], color[3], fill ? 'k' : 'K'); } else if (nComps == 3) { appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n", color[0], color[1], color[2], fill ? "rg" : "RG"); } else { appearBuf->appendf("{0:.2f} {1:c}\n", color[0], fill ? 'g' : 'G'); } } // Draw the variable text or caption for a field. void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict, GBool multiline, int comb, int quadding, GBool txField, GBool forceZapfDingbats, int rot, double xMin, double yMin, double xMax, double yMax, double border) { GString *text2; GList *daToks; GString *tok; GfxFont *font; double dx, dy; double fontSize, fontSize2, x, xPrev, y, w, w2, wMax; int tfPos, tmPos, i, j, k, c; //~ if there is no MK entry, this should use the existing content stream, //~ and only replace the marked content portion of it //~ (this is only relevant for Tx fields) // check for a Unicode string //~ this currently drops all non-Latin1 characters if (text->getLength() >= 2 && text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') { text2 = new GString(); for (i = 2; i+1 < text->getLength(); i += 2) { c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff); if (c <= 0xff) { text2->append((char)c); } else { text2->append('?'); } } } else { text2 = text; } // parse the default appearance string tfPos = tmPos = -1; if (da) { daToks = new GList(); i = 0; while (i < da->getLength()) { while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) { ++i; } if (i < da->getLength()) { for (j = i + 1; j < da->getLength() && !Lexer::isSpace(da->getChar(j)); ++j) ; daToks->append(new GString(da, i, j - i)); i = j; } } for (i = 2; i < daToks->getLength(); ++i) { if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) { tfPos = i - 2; } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) { tmPos = i - 6; } } } else { daToks = NULL; } // force ZapfDingbats //~ this should create the font if needed (?) if (forceZapfDingbats) { if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos); if (tok->cmp("/ZaDb")) { tok->clear(); tok->append("/ZaDb"); } } } // get the font and font size font = NULL; fontSize = 0; if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos); if (tok->getLength() >= 1 && tok->getChar(0) == '/') { if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) { error(errSyntaxError, -1, "Unknown font in field's DA string"); } } else { error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string"); } tok = (GString *)daToks->get(tfPos + 1); fontSize = atof(tok->getCString()); } else { error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string"); } // setup if (txField) { appearBuf->append("/Tx BMC\n"); } appearBuf->append("q\n"); if (rot == 90) { appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin); dx = yMax - yMin; dy = xMax - xMin; } else if (rot == 180) { appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", xMax - xMin, yMax - yMin); dx = xMax - yMax; dy = yMax - yMin; } else if (rot == 270) { appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin); dx = yMax - yMin; dy = xMax - xMin; } else { // assume rot == 0 dx = xMax - xMin; dy = yMax - yMin; } appearBuf->append("BT\n"); // multi-line text if (multiline) { // note: the comb flag is ignored in multiline mode wMax = dx - 2 * border - 4; // compute font autosize if (fontSize == 0) { for (fontSize = 20; fontSize > 1; --fontSize) { y = dy - 3; w2 = 0; i = 0; while (i < text2->getLength()) { getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k); if (w > w2) { w2 = w; } i = k; y -= fontSize; } // approximate the descender for the last line if (y >= 0.33 * fontSize) { break; } } if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); tok->appendf("{0:.2f}", fontSize); } } // starting y coordinate // (note: each line of text starts with a Td operator that moves // down a line) y = dy - 3; // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); tok->append('0'); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); tok->appendf("{0:.4f}", y); } // write the DA string if (daToks) { for (i = 0; i < daToks->getLength(); ++i) { appearBuf->append((GString *)daToks->get(i))->append(' '); } } // write the font matrix (if not part of the DA string) if (tmPos < 0) { appearBuf->appendf("1 0 0 1 0 {0:.4f} Tm\n", y); } // write a series of lines of text i = 0; xPrev = 0; while (i < text2->getLength()) { getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k); // compute text start position switch (quadding) { case acroFormQuadLeft: default: x = border + 2; break; case acroFormQuadCenter: x = (dx - w) / 2; break; case acroFormQuadRight: x = dx - border - 2 - w; break; } // draw the line appearBuf->appendf("{0:.4f} {1:.4f} Td\n", x - xPrev, -fontSize); appearBuf->append('('); for (; i < j; ++i) { c = text2->getChar(i) & 0xff; if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append(c); } else if (c < 0x20 || c >= 0x80) { appearBuf->appendf("\\{0:03o}", c); } else { appearBuf->append(c); } } appearBuf->append(") Tj\n"); // next line i = k; xPrev = x; } // single-line text } else { //~ replace newlines with spaces? - what does Acrobat do? // comb formatting if (comb > 0) { // compute comb spacing w = (dx - 2 * border) / comb; // compute font autosize if (fontSize == 0) { fontSize = dy - 2 * border; if (w < fontSize) { fontSize = w; } fontSize = floor(fontSize); if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); tok->appendf("{0:.4f}", fontSize); } } // compute text start position switch (quadding) { case acroFormQuadLeft: default: x = border + 2; break; case acroFormQuadCenter: x = border + 2 + 0.5 * (comb - text2->getLength()) * w; break; case acroFormQuadRight: x = border + 2 + (comb - text2->getLength()) * w; break; } y = 0.5 * dy - 0.4 * fontSize; // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); tok->appendf("{0:.4f}", x); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); tok->appendf("{0:.4f}", y); } // write the DA string if (daToks) { for (i = 0; i < daToks->getLength(); ++i) { appearBuf->append((GString *)daToks->get(i))->append(' '); } } // write the font matrix (if not part of the DA string) if (tmPos < 0) { appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y); } // write the text string //~ this should center (instead of left-justify) each character within //~ its comb cell for (i = 0; i < text2->getLength(); ++i) { if (i > 0) { appearBuf->appendf("{0:.4f} 0 Td\n", w); } appearBuf->append('('); c = text2->getChar(i) & 0xff; if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append(c); } else if (c < 0x20 || c >= 0x80) { appearBuf->appendf("{0:.4f} 0 Td\n", w); } else { appearBuf->append(c); } appearBuf->append(") Tj\n"); } // regular (non-comb) formatting } else { // compute string width if (font && !font->isCIDFont()) { w = 0; for (i = 0; i < text2->getLength(); ++i) { w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i)); } } else { // otherwise, make a crude estimate w = text2->getLength() * 0.5; } // compute font autosize if (fontSize == 0) { fontSize = dy - 2 * border; fontSize2 = (dx - 4 - 2 * border) / w; if (fontSize2 < fontSize) { fontSize = fontSize2; } fontSize = floor(fontSize); if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); tok->appendf("{0:.4f}", fontSize); } } // compute text start position w *= fontSize; switch (quadding) { case acroFormQuadLeft: default: x = border + 2; break; case acroFormQuadCenter: x = (dx - w) / 2; break; case acroFormQuadRight: x = dx - border - 2 - w; break; } y = 0.5 * dy - 0.4 * fontSize; // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); tok->appendf("{0:.4f}", x); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); tok->appendf("{0:.4f}", y); } // write the DA string if (daToks) { for (i = 0; i < daToks->getLength(); ++i) { appearBuf->append((GString *)daToks->get(i))->append(' '); } } // write the font matrix (if not part of the DA string) if (tmPos < 0) { appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y); } // write the text string appearBuf->append('('); for (i = 0; i < text2->getLength(); ++i) { c = text2->getChar(i) & 0xff; if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append(c); } else if (c < 0x20 || c >= 0x80) { appearBuf->appendf("\\{0:03o}", c); } else { appearBuf->append(c); } } appearBuf->append(") Tj\n"); } } // cleanup appearBuf->append("ET\n"); appearBuf->append("Q\n"); if (txField) { appearBuf->append("EMC\n"); } if (daToks) { deleteGList(daToks, GString); } if (text2 != text) { delete text2; } } // Draw the variable text or caption for a field. void AcroFormField::drawListBox(GString **text, GBool *selection, int nOptions, int topIdx, GString *da, GfxFontDict *fontDict, GBool quadding, double xMin, double yMin, double xMax, double yMax, double border) { GList *daToks; GString *tok; GfxFont *font; double fontSize, fontSize2, x, y, w, wMax; int tfPos, tmPos, i, j, c; //~ if there is no MK entry, this should use the existing content stream, //~ and only replace the marked content portion of it //~ (this is only relevant for Tx fields) // parse the default appearance string tfPos = tmPos = -1; if (da) { daToks = new GList(); i = 0; while (i < da->getLength()) { while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) { ++i; } if (i < da->getLength()) { for (j = i + 1; j < da->getLength() && !Lexer::isSpace(da->getChar(j)); ++j) ; daToks->append(new GString(da, i, j - i)); i = j; } } for (i = 2; i < daToks->getLength(); ++i) { if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) { tfPos = i - 2; } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) { tmPos = i - 6; } } } else { daToks = NULL; } // get the font and font size font = NULL; fontSize = 0; if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos); if (tok->getLength() >= 1 && tok->getChar(0) == '/') { if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) { error(errSyntaxError, -1, "Unknown font in field's DA string"); } } else { error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string"); } tok = (GString *)daToks->get(tfPos + 1); fontSize = atof(tok->getCString()); } else { error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string"); } // compute font autosize if (fontSize == 0) { wMax = 0; for (i = 0; i < nOptions; ++i) { if (font && !font->isCIDFont()) { w = 0; for (j = 0; j < text[i]->getLength(); ++j) { w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j)); } } else { // otherwise, make a crude estimate w = text[i]->getLength() * 0.5; } if (w > wMax) { wMax = w; } } fontSize = yMax - yMin - 2 * border; fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax; if (fontSize2 < fontSize) { fontSize = fontSize2; } fontSize = floor(fontSize); if (tfPos >= 0) { tok = (GString *)daToks->get(tfPos + 1); tok->clear(); tok->appendf("{0:.4f}", fontSize); } } // draw the text y = yMax - yMin - 1.1 * fontSize; for (i = topIdx; i < nOptions; ++i) { // setup appearBuf->append("q\n"); // draw the background if selected if (selection[i]) { appearBuf->append("0 g f\n"); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n", border, y - 0.2 * fontSize, xMax - xMin - 2 * border, 1.1 * fontSize); } // setup appearBuf->append("BT\n"); // compute string width if (font && !font->isCIDFont()) { w = 0; for (j = 0; j < text[i]->getLength(); ++j) { w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j)); } } else { // otherwise, make a crude estimate w = text[i]->getLength() * 0.5; } // compute text start position w *= fontSize; switch (quadding) { case acroFormQuadLeft: default: x = border + 2; break; case acroFormQuadCenter: x = (xMax - xMin - w) / 2; break; case acroFormQuadRight: x = xMax - xMin - border - 2 - w; break; } // set the font matrix if (tmPos >= 0) { tok = (GString *)daToks->get(tmPos + 4); tok->clear(); tok->appendf("{0:.4f}", x); tok = (GString *)daToks->get(tmPos + 5); tok->clear(); tok->appendf("{0:.4f}", y); } // write the DA string if (daToks) { for (j = 0; j < daToks->getLength(); ++j) { appearBuf->append((GString *)daToks->get(j))->append(' '); } } // write the font matrix (if not part of the DA string) if (tmPos < 0) { appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y); } // change the text color if selected if (selection[i]) { appearBuf->append("1 g\n"); } // write the text string appearBuf->append('('); for (j = 0; j < text[i]->getLength(); ++j) { c = text[i]->getChar(j) & 0xff; if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append(c); } else if (c < 0x20 || c >= 0x80) { appearBuf->appendf("\\{0:03o}", c); } else { appearBuf->append(c); } } appearBuf->append(") Tj\n"); // cleanup appearBuf->append("ET\n"); appearBuf->append("Q\n"); // next line y -= 1.1 * fontSize; } if (daToks) { deleteGList(daToks, GString); } } // Figure out how much text will fit on the next line. Returns: // *end = one past the last character to be included // *width = width of the characters start .. end-1 // *next = index of first character on the following line void AcroFormField::getNextLine(GString *text, int start, GfxFont *font, double fontSize, double wMax, int *end, double *width, int *next) { double w, dw; int j, k, c; // figure out how much text will fit on the line //~ what does Adobe do with tabs? w = 0; for (j = start; j < text->getLength() && w <= wMax; ++j) { c = text->getChar(j) & 0xff; if (c == 0x0a || c == 0x0d) { break; } if (font && !font->isCIDFont()) { dw = ((Gfx8BitFont *)font)->getWidth(c) * fontSize; } else { // otherwise, make a crude estimate dw = 0.5 * fontSize; } w += dw; } if (w > wMax) { for (k = j; k > start && text->getChar(k-1) != ' '; --k) ; for (; k > start && text->getChar(k-1) == ' '; --k) ; if (k > start) { j = k; } if (j == start) { // handle the pathological case where the first character is // too wide to fit on the line all by itself j = start + 1; } } *end = j; // compute the width w = 0; for (k = start; k < j; ++k) { if (font && !font->isCIDFont()) { dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize; } else { // otherwise, make a crude estimate dw = 0.5 * fontSize; } w += dw; } *width = w; // next line while (j < text->getLength() && text->getChar(j) == ' ') { ++j; } if (j < text->getLength() && text->getChar(j) == 0x0d) { ++j; } if (j < text->getLength() && text->getChar(j) == 0x0a) { ++j; } *next = j; } // Draw an (approximate) circle of radius centered at (, ). // is used to draw the circle ("f", "s", or "b"). void AcroFormField::drawCircle(double cx, double cy, double r, const char *cmd) { appearBuf->appendf("{0:.4f} {1:.4f} m\n", cx + r, cy); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + r, cy + bezierCircle * r, cx + bezierCircle * r, cy + r, cx, cy + r); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - bezierCircle * r, cy + r, cx - r, cy + bezierCircle * r, cx - r, cy); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - r, cy - bezierCircle * r, cx - bezierCircle * r, cy - r, cx, cy - r); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + bezierCircle * r, cy - r, cx + r, cy - bezierCircle * r, cx + r, cy); appearBuf->appendf("{0:s}\n", cmd); } // Draw the top-left half of an (approximate) circle of radius // centered at (, ). void AcroFormField::drawCircleTopLeft(double cx, double cy, double r) { double r2; r2 = r / sqrt(2.0); appearBuf->appendf("{0:.4f} {1:.4f} m\n", cx + r2, cy + r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - r2, cy + r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx - (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx - r2, cy - r2); appearBuf->append("S\n"); } // Draw the bottom-right half of an (approximate) circle of radius // centered at (, ). void AcroFormField::drawCircleBottomRight(double cx, double cy, double r) { double r2; r2 = r / sqrt(2.0); appearBuf->appendf("{0:.4f} {1:.4f} m\n", cx - r2, cy - r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + r2, cy - r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx + (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx + r2, cy + r2); appearBuf->append("S\n"); } Object *AcroFormField::getResources(Object *res) { Object kidsObj, annotObj, obj1; int i; if (acroForm->needAppearances) { fieldLookup("DR", res); } else { res->initArray(acroForm->doc->getXRef()); // find the annotation object(s) if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) { for (i = 0; i < kidsObj.arrayGetLength(); ++i) { kidsObj.arrayGet(i, &annotObj); if (annotObj.isDict()) { if (getAnnotResources(annotObj.getDict(), &obj1)->isDict()) { res->arrayAdd(&obj1); } else { obj1.free(); } } annotObj.free(); } } else { if (getAnnotResources(fieldObj.getDict(), &obj1)->isDict()) { res->arrayAdd(&obj1); } else { obj1.free(); } } kidsObj.free(); } return res; } Object *AcroFormField::getAnnotResources(Dict *annot, Object *res) { Object apObj, asObj, appearance, obj1; // get the appearance stream if (annot->lookup("AP", &apObj)->isDict()) { apObj.dictLookup("N", &obj1); if (obj1.isDict()) { if (annot->lookup("AS", &asObj)->isName()) { obj1.dictLookup(asObj.getName(), &appearance); } else if (obj1.dictGetLength() == 1) { obj1.dictGetVal(0, &appearance); } else { obj1.dictLookup("Off", &appearance); } asObj.free(); } else { obj1.copy(&appearance); } obj1.free(); } apObj.free(); if (appearance.isStream()) { appearance.streamGetDict()->lookup("Resources", res); } else { res->initNull(); } appearance.free(); return res; } // Look up an inheritable field dictionary entry. Object *AcroFormField::fieldLookup(const char *key, Object *obj) { return fieldLookup(fieldObj.getDict(), key, obj); } Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) { Object parent; if (!dict->lookup(key, obj)->isNull()) { return obj; } obj->free(); if (dict->lookup("Parent", &parent)->isDict()) { fieldLookup(parent.getDict(), key, obj); } else { // some fields don't specify a parent, so we check the AcroForm // dictionary just in case acroForm->acroFormObj.dictLookup(key, obj); } parent.free(); return obj; } xpdf-3.04/xpdf/CompactFontTables.h0000644000076400007640000002027312341430012016367 0ustar dereknderekn//======================================================================== // // CompactFontTables.h // // Copyright 1999-2003 Glyph & Cog, LLC // //======================================================================== #ifndef COMPACTFONTINFO_H #define COMPACTFONTINFO_H static char *type1CStdStrings[391] = { ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold" }; static Gushort type1CISOAdobeCharset[229] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228 }; static Gushort type1CExpertCharset[166] = { 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378 }; static Gushort type1CExpertSubsetCharset[87] = { 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346 }; #endif xpdf-3.04/xpdf/Form.cc0000644000076400007640000000320712341430012014056 0ustar dereknderekn//======================================================================== // // Form.cc // // Copyright 2012 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "PDFDoc.h" #include "AcroForm.h" #include "XFAForm.h" #include "Form.h" //------------------------------------------------------------------------ // Form //------------------------------------------------------------------------ Form *Form::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj) { Form *form; Object xfaObj, catDict, needsRenderingObj; if (!acroFormObj->isDict()) { error(errSyntaxError, -1, "AcroForm object is wrong type"); return NULL; } //~ temporary: create an XFAForm only for XFAF, not for dynamic XFA acroFormObj->dictLookup("XFA", &xfaObj); docA->getXRef()->getCatalog(&catDict); catDict.dictLookup("NeedsRendering", &needsRenderingObj); catDict.free(); if (globalParams->getEnableXFA() && !xfaObj.isNull() && !(needsRenderingObj.isBool() && needsRenderingObj.getBool())) { form = XFAForm::load(docA, acroFormObj, &xfaObj); } else { form = AcroForm::load(docA, catalog, acroFormObj); } xfaObj.free(); needsRenderingObj.free(); return form; } Form::Form(PDFDoc *docA) { doc = docA; } Form::~Form() { } //------------------------------------------------------------------------ // FormField //------------------------------------------------------------------------ FormField::FormField() { } FormField::~FormField() { } xpdf-3.04/xpdf/Annot.cc0000644000076400007640000007314512341430012014242 0ustar dereknderekn//======================================================================== // // Annot.cc // // Copyright 2000-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "GList.h" #include "Error.h" #include "Object.h" #include "Catalog.h" #include "Gfx.h" #include "GfxFont.h" #include "Lexer.h" #include "PDFDoc.h" #include "OptionalContent.h" #include "Form.h" #include "Annot.h" // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ #define annotFlagHidden 0x0002 #define annotFlagPrint 0x0004 #define annotFlagNoView 0x0020 // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle 0.55228475 #define lineEndSize1 6 #define lineEndSize2 10 #define lineArrowAngle (M_PI / 6) //------------------------------------------------------------------------ // AnnotBorderStyle //------------------------------------------------------------------------ AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA, double *dashA, int dashLengthA, double *colorA, int nColorCompsA) { type = typeA; width = widthA; dash = dashA; dashLength = dashLengthA; color[0] = colorA[0]; color[1] = colorA[1]; color[2] = colorA[2]; color[3] = colorA[3]; nColorComps = nColorCompsA; } AnnotBorderStyle::~AnnotBorderStyle() { if (dash) { gfree(dash); } } //------------------------------------------------------------------------ // Annot //------------------------------------------------------------------------ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) { Object apObj, asObj, obj1, obj2, obj3; AnnotBorderType borderType; double borderWidth; double *borderDash; int borderDashLength; double borderColor[4]; int nBorderColorComps; double t; int i; ok = gTrue; doc = docA; xref = doc->getXRef(); ref = *refA; type = NULL; appearanceState = NULL; appearBuf = NULL; borderStyle = NULL; //----- parse the type if (dict->lookup("Subtype", &obj1)->isName()) { type = new GString(obj1.getName()); } obj1.free(); //----- parse the rectangle if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) { xMin = yMin = xMax = yMax = 0; if (obj1.arrayGet(0, &obj2)->isNum()) { xMin = obj2.getNum(); } obj2.free(); if (obj1.arrayGet(1, &obj2)->isNum()) { yMin = obj2.getNum(); } obj2.free(); if (obj1.arrayGet(2, &obj2)->isNum()) { xMax = obj2.getNum(); } obj2.free(); if (obj1.arrayGet(3, &obj2)->isNum()) { yMax = obj2.getNum(); } obj2.free(); if (xMin > xMax) { t = xMin; xMin = xMax; xMax = t; } if (yMin > yMax) { t = yMin; yMin = yMax; yMax = t; } } else { error(errSyntaxError, -1, "Bad bounding box for annotation"); ok = gFalse; } obj1.free(); //----- parse the flags if (dict->lookup("F", &obj1)->isInt()) { flags = obj1.getInt(); } else { flags = 0; } obj1.free(); //----- parse the border style borderType = annotBorderSolid; borderWidth = 1; borderDash = NULL; borderDashLength = 0; nBorderColorComps = 3; borderColor[0] = 0; borderColor[1] = 0; borderColor[2] = 1; borderColor[3] = 0; if (dict->lookup("BS", &obj1)->isDict()) { if (obj1.dictLookup("S", &obj2)->isName()) { if (obj2.isName("S")) { borderType = annotBorderSolid; } else if (obj2.isName("D")) { borderType = annotBorderDashed; } else if (obj2.isName("B")) { borderType = annotBorderBeveled; } else if (obj2.isName("I")) { borderType = annotBorderInset; } else if (obj2.isName("U")) { borderType = annotBorderUnderlined; } } obj2.free(); if (obj1.dictLookup("W", &obj2)->isNum()) { borderWidth = obj2.getNum(); } obj2.free(); if (obj1.dictLookup("D", &obj2)->isArray()) { borderDashLength = obj2.arrayGetLength(); borderDash = (double *)gmallocn(borderDashLength, sizeof(double)); for (i = 0; i < borderDashLength; ++i) { if (obj2.arrayGet(i, &obj3)->isNum()) { borderDash[i] = obj3.getNum(); } else { borderDash[i] = 1; } obj3.free(); } } obj2.free(); } else { obj1.free(); if (dict->lookup("Border", &obj1)->isArray()) { if (obj1.arrayGetLength() >= 3) { if (obj1.arrayGet(2, &obj2)->isNum()) { borderWidth = obj2.getNum(); } obj2.free(); if (obj1.arrayGetLength() >= 4) { if (obj1.arrayGet(3, &obj2)->isArray()) { borderType = annotBorderDashed; borderDashLength = obj2.arrayGetLength(); borderDash = (double *)gmallocn(borderDashLength, sizeof(double)); for (i = 0; i < borderDashLength; ++i) { if (obj2.arrayGet(i, &obj3)->isNum()) { borderDash[i] = obj3.getNum(); } else { borderDash[i] = 1; } obj3.free(); } } else { // Adobe draws no border at all if the last element is of // the wrong type. borderWidth = 0; } obj2.free(); } } else { // an empty Border array also means "no border" borderWidth = 0; } } } obj1.free(); if (dict->lookup("C", &obj1)->isArray() && (obj1.arrayGetLength() == 1 || obj1.arrayGetLength() == 3 || obj1.arrayGetLength() == 4)) { nBorderColorComps = obj1.arrayGetLength(); for (i = 0; i < nBorderColorComps; ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { borderColor[i] = obj2.getNum(); } else { borderColor[i] = 0; } obj2.free(); } } obj1.free(); borderStyle = new AnnotBorderStyle(borderType, borderWidth, borderDash, borderDashLength, borderColor, nBorderColorComps); //----- get the appearance state dict->lookup("AP", &apObj); dict->lookup("AS", &asObj); if (asObj.isName()) { appearanceState = new GString(asObj.getName()); } else if (apObj.isDict()) { apObj.dictLookup("N", &obj1); if (obj1.isDict() && obj1.dictGetLength() == 1) { appearanceState = new GString(obj1.dictGetKey(0)); } obj1.free(); } if (!appearanceState) { appearanceState = new GString("Off"); } asObj.free(); //----- get the annotation appearance if (apObj.isDict()) { apObj.dictLookup("N", &obj1); apObj.dictLookupNF("N", &obj2); if (obj1.isDict()) { if (obj1.dictLookupNF(appearanceState->getCString(), &obj3)->isRef()) { obj3.copy(&appearance); } obj3.free(); } else if (obj2.isRef()) { obj2.copy(&appearance); } obj1.free(); obj2.free(); } apObj.free(); //----- get the optional content entry dict->lookupNF("OC", &ocObj); } Annot::~Annot() { if (type) { delete type; } if (appearanceState) { delete appearanceState; } appearance.free(); if (appearBuf) { delete appearBuf; } if (borderStyle) { delete borderStyle; } ocObj.free(); } void Annot::generateAnnotAppearance() { Object obj; appearance.fetch(doc->getXRef(), &obj); if (!obj.isStream()) { if (type) { if (!type->cmp("Line")) { generateLineAppearance(); } else if (!type->cmp("PolyLine")) { generatePolyLineAppearance(); } else if (!type->cmp("Polygon")) { generatePolygonAppearance(); } } } obj.free(); } //~ this doesn't draw the caption void Annot::generateLineAppearance() { Object annotObj, gfxStateDict, appearDict, obj1, obj2; MemStream *appearStream; double x1, y1, x2, y2, dx, dy, len, w; double lx1, ly1, lx2, ly2; double tx1, ty1, tx2, ty2; double ax1, ay1, ax2, ay2; double bx1, by1, bx2, by2; double leaderLen, leaderExtLen, leaderOffLen; AnnotLineEndType lineEnd1, lineEnd2; GBool fill; if (!getObject(&annotObj)->isDict()) { annotObj.free(); return; } appearBuf = new GString(); //----- check for transparency if (annotObj.dictLookup("CA", &obj1)->isNum()) { gfxStateDict.initDict(doc->getXRef()); gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2)); appearBuf->append("/GS1 gs\n"); } obj1.free(); //----- set line style, colors setLineStyle(borderStyle, &w); setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps()); fill = gFalse; if (annotObj.dictLookup("IC", &obj1)->isArray()) { if (setFillColor(&obj1)) { fill = gTrue; } } obj1.free(); //----- get line properties if (annotObj.dictLookup("L", &obj1)->isArray() && obj1.arrayGetLength() == 4) { if (obj1.arrayGet(0, &obj2)->isNum()) { x1 = obj2.getNum(); } else { obj2.free(); obj1.free(); goto err1; } obj2.free(); if (obj1.arrayGet(1, &obj2)->isNum()) { y1 = obj2.getNum(); } else { obj2.free(); obj1.free(); goto err1; } obj2.free(); if (obj1.arrayGet(2, &obj2)->isNum()) { x2 = obj2.getNum(); } else { obj2.free(); obj1.free(); goto err1; } obj2.free(); if (obj1.arrayGet(3, &obj2)->isNum()) { y2 = obj2.getNum(); } else { obj2.free(); obj1.free(); goto err1; } obj2.free(); } else { obj1.free(); goto err1; } obj1.free(); lineEnd1 = lineEnd2 = annotLineEndNone; if (annotObj.dictLookup("LE", &obj1)->isArray() && obj1.arrayGetLength() == 2) { lineEnd1 = parseLineEndType(obj1.arrayGet(0, &obj2)); obj2.free(); lineEnd2 = parseLineEndType(obj1.arrayGet(1, &obj2)); obj2.free(); } obj1.free(); if (annotObj.dictLookup("LL", &obj1)->isNum()) { leaderLen = obj1.getNum(); } else { leaderLen = 0; } obj1.free(); if (annotObj.dictLookup("LLE", &obj1)->isNum()) { leaderExtLen = obj1.getNum(); } else { leaderExtLen = 0; } obj1.free(); if (annotObj.dictLookup("LLO", &obj1)->isNum()) { leaderOffLen = obj1.getNum(); } else { leaderOffLen = 0; } obj1.free(); //----- compute positions x1 -= xMin; y1 -= yMin; x2 -= xMin; y2 -= yMin; dx = x2 - x1; dy = y2 - y1; len = sqrt(dx*dx + dy*dy); if (len > 0) { dx /= len; dy /= len; } if (leaderLen != 0) { ax1 = x1 + leaderOffLen * dy; ay1 = y1 - leaderOffLen * dx; lx1 = ax1 + leaderLen * dy; ly1 = ay1 - leaderLen * dx; bx1 = lx1 + leaderExtLen * dy; by1 = ly1 - leaderExtLen * dx; ax2 = x2 + leaderOffLen * dy; ay2 = y2 - leaderOffLen * dx; lx2 = ax2 + leaderLen * dy; ly2 = ay2 - leaderLen * dx; bx2 = lx2 + leaderExtLen * dy; by2 = ly2 - leaderExtLen * dx; } else { lx1 = x1; ly1 = y1; lx2 = x2; ly2 = y2; ax1 = ay1 = ax2 = ay2 = 0; // make gcc happy bx1 = by1 = bx2 = by2 = 0; } adjustLineEndpoint(lineEnd1, lx1, ly1, dx, dy, w, &tx1, &ty1); adjustLineEndpoint(lineEnd2, lx2, ly2, -dx, -dy, w, &tx2, &ty2); //----- draw leaders if (leaderLen != 0) { appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n", ax1, ay1, bx1, by1); appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n", ax2, ay2 , bx2, by2); } //----- draw the line appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n", tx1, ty1, tx2, ty2); appearBuf->append("S\n"); //----- draw the arrows if (borderStyle->getType() == annotBorderDashed) { appearBuf->append("[] 0 d\n"); } drawLineArrow(lineEnd1, lx1, ly1, dx, dy, w, fill); drawLineArrow(lineEnd2, lx2, ly2, -dx, -dy, w, fill); //----- build the appearance stream dictionary appearDict.initDict(doc->getXRef()); appearDict.dictAdd(copyString("Length"), obj1.initInt(appearBuf->getLength())); appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form")); obj1.initArray(doc->getXRef()); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(xMax - xMin)); obj1.arrayAdd(obj2.initReal(yMax - yMin)); appearDict.dictAdd(copyString("BBox"), &obj1); if (gfxStateDict.isDict()) { obj1.initDict(doc->getXRef()); obj2.initDict(doc->getXRef()); obj2.dictAdd(copyString("GS1"), &gfxStateDict); obj1.dictAdd(copyString("ExtGState"), &obj2); appearDict.dictAdd(copyString("Resources"), &obj1); } //----- build the appearance stream appearStream = new MemStream(appearBuf->getCString(), 0, appearBuf->getLength(), &appearDict); appearance.free(); appearance.initStream(appearStream); err1: annotObj.free(); } //~ this doesn't handle line ends (arrows) void Annot::generatePolyLineAppearance() { Object annotObj, gfxStateDict, appearDict, obj1, obj2; MemStream *appearStream; double x1, y1, w; int i; if (!getObject(&annotObj)->isDict()) { annotObj.free(); return; } appearBuf = new GString(); //----- check for transparency if (annotObj.dictLookup("CA", &obj1)->isNum()) { gfxStateDict.initDict(doc->getXRef()); gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2)); appearBuf->append("/GS1 gs\n"); } obj1.free(); //----- set line style, colors setLineStyle(borderStyle, &w); setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps()); // fill = gFalse; // if (annotObj.dictLookup("IC", &obj1)->isArray()) { // if (setFillColor(&obj1)) { // fill = gTrue; // } // } // obj1.free(); //----- draw line if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) { obj1.free(); goto err1; } for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) { if (!obj1.arrayGet(i, &obj2)->isNum()) { obj2.free(); obj1.free(); goto err1; } x1 = obj2.getNum(); obj2.free(); if (!obj1.arrayGet(i+1, &obj2)->isNum()) { obj2.free(); obj1.free(); goto err1; } y1 = obj2.getNum(); obj2.free(); x1 -= xMin; y1 -= yMin; if (i == 0) { appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1); } else { appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1); } } appearBuf->append("S\n"); obj1.free(); //----- build the appearance stream dictionary appearDict.initDict(doc->getXRef()); appearDict.dictAdd(copyString("Length"), obj1.initInt(appearBuf->getLength())); appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form")); obj1.initArray(doc->getXRef()); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(xMax - xMin)); obj1.arrayAdd(obj2.initReal(yMax - yMin)); appearDict.dictAdd(copyString("BBox"), &obj1); if (gfxStateDict.isDict()) { obj1.initDict(doc->getXRef()); obj2.initDict(doc->getXRef()); obj2.dictAdd(copyString("GS1"), &gfxStateDict); obj1.dictAdd(copyString("ExtGState"), &obj2); appearDict.dictAdd(copyString("Resources"), &obj1); } //----- build the appearance stream appearStream = new MemStream(appearBuf->getCString(), 0, appearBuf->getLength(), &appearDict); appearance.free(); appearance.initStream(appearStream); err1: annotObj.free(); } void Annot::generatePolygonAppearance() { Object annotObj, gfxStateDict, appearDict, obj1, obj2; MemStream *appearStream; double x1, y1; int i; if (!getObject(&annotObj)->isDict()) { annotObj.free(); return; } appearBuf = new GString(); //----- check for transparency if (annotObj.dictLookup("CA", &obj1)->isNum()) { gfxStateDict.initDict(doc->getXRef()); gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2)); appearBuf->append("/GS1 gs\n"); } obj1.free(); //----- set fill color if (!annotObj.dictLookup("IC", &obj1)->isArray() || !setFillColor(&obj1)) { obj1.free(); goto err1; } obj1.free(); //----- fill polygon if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) { obj1.free(); goto err1; } for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) { if (!obj1.arrayGet(i, &obj2)->isNum()) { obj2.free(); obj1.free(); goto err1; } x1 = obj2.getNum(); obj2.free(); if (!obj1.arrayGet(i+1, &obj2)->isNum()) { obj2.free(); obj1.free(); goto err1; } y1 = obj2.getNum(); obj2.free(); x1 -= xMin; y1 -= yMin; if (i == 0) { appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1); } else { appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1); } } appearBuf->append("f\n"); obj1.free(); //----- build the appearance stream dictionary appearDict.initDict(doc->getXRef()); appearDict.dictAdd(copyString("Length"), obj1.initInt(appearBuf->getLength())); appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form")); obj1.initArray(doc->getXRef()); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(0)); obj1.arrayAdd(obj2.initReal(xMax - xMin)); obj1.arrayAdd(obj2.initReal(yMax - yMin)); appearDict.dictAdd(copyString("BBox"), &obj1); if (gfxStateDict.isDict()) { obj1.initDict(doc->getXRef()); obj2.initDict(doc->getXRef()); obj2.dictAdd(copyString("GS1"), &gfxStateDict); obj1.dictAdd(copyString("ExtGState"), &obj2); appearDict.dictAdd(copyString("Resources"), &obj1); } //----- build the appearance stream appearStream = new MemStream(appearBuf->getCString(), 0, appearBuf->getLength(), &appearDict); appearance.free(); appearance.initStream(appearStream); err1: annotObj.free(); } void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) { double *dash; double w; int dashLength, i; if ((w = borderStyle->getWidth()) <= 0) { w = 0.1; } *lineWidth = w; appearBuf->appendf("{0:.4f} w\n", w); // this treats beveled/inset/underline as solid if (borderStyle->getType() == annotBorderDashed) { borderStyle->getDash(&dash, &dashLength); appearBuf->append("["); for (i = 0; i < dashLength; ++i) { appearBuf->appendf(" {0:.4f}", dash[i]); } appearBuf->append("] 0 d\n"); } appearBuf->append("0 j\n0 J\n"); } void Annot::setStrokeColor(double *color, int nComps) { switch (nComps) { case 0: appearBuf->append("0 G\n"); break; case 1: appearBuf->appendf("{0:.2f} G\n", color[0]); break; case 3: appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} RG\n", color[0], color[1], color[2]); break; case 4: appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} K\n", color[0], color[1], color[2], color[3]); break; } } GBool Annot::setFillColor(Object *colorObj) { Object obj; double color[4]; int i; if (!colorObj->isArray()) { return gFalse; } for (i = 0; i < colorObj->arrayGetLength(); ++i) { if (colorObj->arrayGet(i, &obj)->isNum()) { color[i] = obj.getNum(); } else { color[i] = 0; } obj.free(); } switch (colorObj->arrayGetLength()) { case 1: appearBuf->appendf("{0:.2f} g\n", color[0]); return gTrue; case 3: appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} rg\n", color[0], color[1], color[2]); return gTrue; case 4: appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.3f} k\n", color[0], color[1], color[2], color[3]); return gTrue; } return gFalse; } AnnotLineEndType Annot::parseLineEndType(Object *obj) { if (obj->isName("None")) { return annotLineEndNone; } else if (obj->isName("Square")) { return annotLineEndSquare; } else if (obj->isName("Circle")) { return annotLineEndCircle; } else if (obj->isName("Diamond")) { return annotLineEndDiamond; } else if (obj->isName("OpenArrow")) { return annotLineEndOpenArrow; } else if (obj->isName("ClosedArrow")) { return annotLineEndClosedArrow; } else if (obj->isName("Butt")) { return annotLineEndButt; } else if (obj->isName("ROpenArrow")) { return annotLineEndROpenArrow; } else if (obj->isName("RClosedArrow")) { return annotLineEndRClosedArrow; } else if (obj->isName("Slash")) { return annotLineEndSlash; } else { return annotLineEndNone; } } void Annot::adjustLineEndpoint(AnnotLineEndType lineEnd, double x, double y, double dx, double dy, double w, double *tx, double *ty) { switch (lineEnd) { case annotLineEndNone: w = 0; break; case annotLineEndSquare: w *= lineEndSize1; break; case annotLineEndCircle: w *= lineEndSize1; break; case annotLineEndDiamond: w *= lineEndSize1; break; case annotLineEndOpenArrow: w = 0; break; case annotLineEndClosedArrow: w *= lineEndSize2 * cos(lineArrowAngle); break; case annotLineEndButt: w = 0; break; case annotLineEndROpenArrow: w *= lineEndSize2 * cos(lineArrowAngle); break; case annotLineEndRClosedArrow: w *= lineEndSize2 * cos(lineArrowAngle); break; case annotLineEndSlash: w = 0; break; } *tx = x + w * dx; *ty = y + w * dy; } void Annot::drawLineArrow(AnnotLineEndType lineEnd, double x, double y, double dx, double dy, double w, GBool fill) { switch (lineEnd) { case annotLineEndNone: break; case annotLineEndSquare: w *= lineEndSize1; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + w*dx + 0.5*w*dy, y + w*dy - 0.5*w*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + 0.5*w*dy, y - 0.5*w*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x - 0.5*w*dy, y + 0.5*w*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + w*dx - 0.5*w*dy, y + w*dy + 0.5*w*dx); appearBuf->append(fill ? "b\n" : "s\n"); break; case annotLineEndCircle: w *= lineEndSize1; drawCircle(x + 0.5*w*dx, y + 0.5*w*dy, 0.5*w, fill ? "b" : "s"); break; case annotLineEndDiamond: w *= lineEndSize1; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x, y); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + 0.5*w*dx - 0.5*w*dy, y + 0.5*w*dy + 0.5*w*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + w*dx, y + w*dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + 0.5*w*dx + 0.5*w*dy, y + 0.5*w*dy - 0.5*w*dx); appearBuf->append(fill ? "b\n" : "s\n"); break; case annotLineEndOpenArrow: w *= lineEndSize2; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy, y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy, y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx); appearBuf->append("S\n"); break; case annotLineEndClosedArrow: w *= lineEndSize2; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy, y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy, y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx); appearBuf->append(fill ? "b\n" : "s\n"); break; case annotLineEndButt: w *= lineEndSize1; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + 0.5*w*dy, y - 0.5*w*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x - 0.5*w*dy, y + 0.5*w*dx); appearBuf->append("S\n"); break; case annotLineEndROpenArrow: w *= lineEndSize2; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + w*sin(lineArrowAngle)*dy, y - w*sin(lineArrowAngle)*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + w*cos(lineArrowAngle)*dx, y + w*cos(lineArrowAngle)*dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x - w*sin(lineArrowAngle)*dy, y + w*sin(lineArrowAngle)*dx); appearBuf->append("S\n"); break; case annotLineEndRClosedArrow: w *= lineEndSize2; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + w*sin(lineArrowAngle)*dy, y - w*sin(lineArrowAngle)*dx); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x + w*cos(lineArrowAngle)*dx, y + w*cos(lineArrowAngle)*dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x - w*sin(lineArrowAngle)*dy, y + w*sin(lineArrowAngle)*dx); appearBuf->append(fill ? "b\n" : "s\n"); break; case annotLineEndSlash: w *= lineEndSize1; appearBuf->appendf("{0:.4f} {1:.4f} m\n", x + 0.5*w*cos(lineArrowAngle)*dy - 0.5*w*sin(lineArrowAngle)*dx, y - 0.5*w*cos(lineArrowAngle)*dx - 0.5*w*sin(lineArrowAngle)*dy); appearBuf->appendf("{0:.4f} {1:.4f} l\n", x - 0.5*w*cos(lineArrowAngle)*dy + 0.5*w*sin(lineArrowAngle)*dx, y + 0.5*w*cos(lineArrowAngle)*dx + 0.5*w*sin(lineArrowAngle)*dy); appearBuf->append("S\n"); break; } } // Draw an (approximate) circle of radius centered at (, ). // is used to draw the circle ("f", "s", or "b"). void Annot::drawCircle(double cx, double cy, double r, const char *cmd) { appearBuf->appendf("{0:.4f} {1:.4f} m\n", cx + r, cy); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + r, cy + bezierCircle * r, cx + bezierCircle * r, cy + r, cx, cy + r); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - bezierCircle * r, cy + r, cx - r, cy + bezierCircle * r, cx - r, cy); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - r, cy - bezierCircle * r, cx - bezierCircle * r, cy - r, cx, cy - r); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + bezierCircle * r, cy - r, cx + r, cy - bezierCircle * r, cx + r, cy); appearBuf->appendf("{0:s}\n", cmd); } // Draw the top-left half of an (approximate) circle of radius // centered at (, ). void Annot::drawCircleTopLeft(double cx, double cy, double r) { double r2; r2 = r / sqrt(2.0); appearBuf->appendf("{0:.4f} {1:.4f} m\n", cx + r2, cy + r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - r2, cy + r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx - (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx - r2, cy - r2); appearBuf->append("S\n"); } // Draw the bottom-right half of an (approximate) circle of radius // centered at (, ). void Annot::drawCircleBottomRight(double cx, double cy, double r) { double r2; r2 = r / sqrt(2.0); appearBuf->appendf("{0:.4f} {1:.4f} m\n", cx - r2, cy - r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx - (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + r2, cy - r2); appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n", cx + (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx + (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx + r2, cy + r2); appearBuf->append("S\n"); } void Annot::draw(Gfx *gfx, GBool printing) { GBool oc, isLink; // check the flags if ((flags & annotFlagHidden) || (printing && !(flags & annotFlagPrint)) || (!printing && (flags & annotFlagNoView))) { return; } // check the optional content entry if (doc->getOptionalContent()->evalOCObject(&ocObj, &oc) && !oc) { return; } // draw the appearance stream isLink = type && !type->cmp("Link"); gfx->drawAnnot(&appearance, isLink ? borderStyle : (AnnotBorderStyle *)NULL, xMin, yMin, xMax, yMax); } Object *Annot::getObject(Object *obj) { if (ref.num >= 0) { xref->fetch(ref.num, ref.gen, obj); } else { obj->initNull(); } return obj; } //------------------------------------------------------------------------ // Annots //------------------------------------------------------------------------ Annots::Annots(PDFDoc *docA, Object *annotsObj) { Annot *annot; Object obj1, obj2; Ref ref; GBool drawWidgetAnnots; int size; int i; doc = docA; annots = NULL; size = 0; nAnnots = 0; if (annotsObj->isArray()) { // Kludge: some PDF files define an empty AcroForm, but still // include Widget-type annotations -- in that case, we want to // draw the widgets (since the form code won't). This really // ought to look for Widget-type annotations that are not included // in any form field. drawWidgetAnnots = !doc->getCatalog()->getForm() || doc->getCatalog()->getForm()->getNumFields() == 0; for (i = 0; i < annotsObj->arrayGetLength(); ++i) { if (annotsObj->arrayGetNF(i, &obj1)->isRef()) { ref = obj1.getRef(); obj1.free(); annotsObj->arrayGet(i, &obj1); } else { ref.num = ref.gen = -1; } if (obj1.isDict()) { if (drawWidgetAnnots || !obj1.dictLookup("Subtype", &obj2)->isName("Widget")) { annot = new Annot(doc, obj1.getDict(), &ref); if (annot->isOk()) { if (nAnnots >= size) { size += 16; annots = (Annot **)greallocn(annots, size, sizeof(Annot *)); } annots[nAnnots++] = annot; } else { delete annot; } } obj2.free(); } obj1.free(); } } } Annots::~Annots() { int i; for (i = 0; i < nAnnots; ++i) { delete annots[i]; } gfree(annots); } void Annots::generateAnnotAppearances() { int i; for (i = 0; i < nAnnots; ++i) { annots[i]->generateAnnotAppearance(); } } Annot *Annots::findAnnot(Ref *ref) { int i; for (i = 0; i < nAnnots; ++i) { if (annots[i]->match(ref)) { return annots[i]; } } return NULL; } xpdf-3.04/xpdf/BuiltinFontTables.cc0000644000076400007640000070010412341430012016543 0ustar dereknderekn//======================================================================== // // BuiltinFontTables.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #include #include "FontEncodingTables.h" #include "BuiltinFontTables.h" static BuiltinFontWidth courierWidthsTab[] = { { "Ntilde", 600, NULL }, { "rcaron", 600, NULL }, { "kcommaaccent", 600, NULL }, { "Ncommaaccent", 600, NULL }, { "Zacute", 600, NULL }, { "comma", 600, NULL }, { "cedilla", 600, NULL }, { "plusminus", 600, NULL }, { "circumflex", 600, NULL }, { "dotaccent", 600, NULL }, { "edotaccent", 600, NULL }, { "asciitilde", 600, NULL }, { "colon", 600, NULL }, { "onehalf", 600, NULL }, { "dollar", 600, NULL }, { "Lcaron", 600, NULL }, { "ntilde", 600, NULL }, { "Aogonek", 600, NULL }, { "ncommaaccent", 600, NULL }, { "minus", 600, NULL }, { "Iogonek", 600, NULL }, { "zacute", 600, NULL }, { "yen", 600, NULL }, { "space", 600, NULL }, { "Omacron", 600, NULL }, { "questiondown", 600, NULL }, { "emdash", 600, NULL }, { "Agrave", 600, NULL }, { "three", 600, NULL }, { "numbersign", 600, NULL }, { "lcaron", 600, NULL }, { "A", 600, NULL }, { "B", 600, NULL }, { "C", 600, NULL }, { "aogonek", 600, NULL }, { "D", 600, NULL }, { "E", 600, NULL }, { "onequarter", 600, NULL }, { "F", 600, NULL }, { "G", 600, NULL }, { "H", 600, NULL }, { "I", 600, NULL }, { "J", 600, NULL }, { "K", 600, NULL }, { "iogonek", 600, NULL }, { "L", 600, NULL }, { "backslash", 600, NULL }, { "periodcentered", 600, NULL }, { "M", 600, NULL }, { "N", 600, NULL }, { "omacron", 600, NULL }, { "Tcommaaccent", 600, NULL }, { "O", 600, NULL }, { "P", 600, NULL }, { "Q", 600, NULL }, { "Uhungarumlaut", 600, NULL }, { "R", 600, NULL }, { "Aacute", 600, NULL }, { "caron", 600, NULL }, { "S", 600, NULL }, { "T", 600, NULL }, { "U", 600, NULL }, { "agrave", 600, NULL }, { "V", 600, NULL }, { "W", 600, NULL }, { "equal", 600, NULL }, { "question", 600, NULL }, { "X", 600, NULL }, { "Y", 600, NULL }, { "Z", 600, NULL }, { "four", 600, NULL }, { "a", 600, NULL }, { "Gcommaaccent", 600, NULL }, { "b", 600, NULL }, { "c", 600, NULL }, { "d", 600, NULL }, { "e", 600, NULL }, { "f", 600, NULL }, { "g", 600, NULL }, { "bullet", 600, NULL }, { "h", 600, NULL }, { "i", 600, NULL }, { "Oslash", 600, NULL }, { "dagger", 600, NULL }, { "j", 600, NULL }, { "k", 600, NULL }, { "l", 600, NULL }, { "m", 600, NULL }, { "n", 600, NULL }, { "tcommaaccent", 600, NULL }, { "o", 600, NULL }, { "ordfeminine", 600, NULL }, { "ring", 600, NULL }, { "p", 600, NULL }, { "q", 600, NULL }, { "uhungarumlaut", 600, NULL }, { "r", 600, NULL }, { "twosuperior", 600, NULL }, { "aacute", 600, NULL }, { "s", 600, NULL }, { "OE", 600, NULL }, { "t", 600, NULL }, { "divide", 600, NULL }, { "u", 600, NULL }, { "Ccaron", 600, NULL }, { "v", 600, NULL }, { "w", 600, NULL }, { "x", 600, NULL }, { "y", 600, NULL }, { "z", 600, NULL }, { "Gbreve", 600, NULL }, { "commaaccent", 600, NULL }, { "hungarumlaut", 600, NULL }, { "Idotaccent", 600, NULL }, { "Nacute", 600, NULL }, { "quotedbl", 600, NULL }, { "gcommaaccent", 600, NULL }, { "mu", 600, NULL }, { "greaterequal", 600, NULL }, { "Scaron", 600, NULL }, { "Lslash", 600, NULL }, { "semicolon", 600, NULL }, { "oslash", 600, NULL }, { "lessequal", 600, NULL }, { "lozenge", 600, NULL }, { "parenright", 600, NULL }, { "ccaron", 600, NULL }, { "Ecircumflex", 600, NULL }, { "gbreve", 600, NULL }, { "trademark", 600, NULL }, { "daggerdbl", 600, NULL }, { "nacute", 600, NULL }, { "macron", 600, NULL }, { "Otilde", 600, NULL }, { "Emacron", 600, NULL }, { "ellipsis", 600, NULL }, { "scaron", 600, NULL }, { "AE", 600, NULL }, { "Ucircumflex", 600, NULL }, { "lslash", 600, NULL }, { "quotedblleft", 600, NULL }, { "hyphen", 600, NULL }, { "guilsinglright", 600, NULL }, { "quotesingle", 600, NULL }, { "eight", 600, NULL }, { "exclamdown", 600, NULL }, { "endash", 600, NULL }, { "oe", 600, NULL }, { "Abreve", 600, NULL }, { "Umacron", 600, NULL }, { "ecircumflex", 600, NULL }, { "Adieresis", 600, NULL }, { "copyright", 600, NULL }, { "Egrave", 600, NULL }, { "slash", 600, NULL }, { "Edieresis", 600, NULL }, { "otilde", 600, NULL }, { "Idieresis", 600, NULL }, { "parenleft", 600, NULL }, { "one", 600, NULL }, { "emacron", 600, NULL }, { "Odieresis", 600, NULL }, { "ucircumflex", 600, NULL }, { "bracketleft", 600, NULL }, { "Ugrave", 600, NULL }, { "quoteright", 600, NULL }, { "Udieresis", 600, NULL }, { "perthousand", 600, NULL }, { "Ydieresis", 600, NULL }, { "umacron", 600, NULL }, { "abreve", 600, NULL }, { "Eacute", 600, NULL }, { "adieresis", 600, NULL }, { "egrave", 600, NULL }, { "edieresis", 600, NULL }, { "idieresis", 600, NULL }, { "Eth", 600, NULL }, { "ae", 600, NULL }, { "asterisk", 600, NULL }, { "odieresis", 600, NULL }, { "Uacute", 600, NULL }, { "ugrave", 600, NULL }, { "five", 600, NULL }, { "nine", 600, NULL }, { "udieresis", 600, NULL }, { "Zcaron", 600, NULL }, { "Scommaaccent", 600, NULL }, { "threequarters", 600, NULL }, { "guillemotright", 600, NULL }, { "Ccedilla", 600, NULL }, { "ydieresis", 600, NULL }, { "tilde", 600, NULL }, { "at", 600, NULL }, { "eacute", 600, NULL }, { "underscore", 600, NULL }, { "Euro", 600, NULL }, { "Dcroat", 600, NULL }, { "zero", 600, NULL }, { "multiply", 600, NULL }, { "eth", 600, NULL }, { "Scedilla", 600, NULL }, { "Racute", 600, NULL }, { "Ograve", 600, NULL }, { "partialdiff", 600, NULL }, { "uacute", 600, NULL }, { "braceleft", 600, NULL }, { "Thorn", 600, NULL }, { "zcaron", 600, NULL }, { "scommaaccent", 600, NULL }, { "ccedilla", 600, NULL }, { "Dcaron", 600, NULL }, { "dcroat", 600, NULL }, { "scedilla", 600, NULL }, { "Oacute", 600, NULL }, { "Ocircumflex", 600, NULL }, { "ogonek", 600, NULL }, { "ograve", 600, NULL }, { "racute", 600, NULL }, { "Tcaron", 600, NULL }, { "Eogonek", 600, NULL }, { "thorn", 600, NULL }, { "degree", 600, NULL }, { "registered", 600, NULL }, { "radical", 600, NULL }, { "Aring", 600, NULL }, { "percent", 600, NULL }, { "six", 600, NULL }, { "paragraph", 600, NULL }, { "dcaron", 600, NULL }, { "Uogonek", 600, NULL }, { "two", 600, NULL }, { "summation", 600, NULL }, { "Igrave", 600, NULL }, { "Lacute", 600, NULL }, { "ocircumflex", 600, NULL }, { "oacute", 600, NULL }, { "Uring", 600, NULL }, { "Lcommaaccent", 600, NULL }, { "tcaron", 600, NULL }, { "eogonek", 600, NULL }, { "Delta", 600, NULL }, { "Ohungarumlaut", 600, NULL }, { "asciicircum", 600, NULL }, { "aring", 600, NULL }, { "grave", 600, NULL }, { "uogonek", 600, NULL }, { "bracketright", 600, NULL }, { "ampersand", 600, NULL }, { "Iacute", 600, NULL }, { "lacute", 600, NULL }, { "igrave", 600, NULL }, { "Ncaron", 600, NULL }, { "plus", 600, NULL }, { "uring", 600, NULL }, { "quotesinglbase", 600, NULL }, { "lcommaaccent", 600, NULL }, { "Yacute", 600, NULL }, { "ohungarumlaut", 600, NULL }, { "threesuperior", 600, NULL }, { "acute", 600, NULL }, { "section", 600, NULL }, { "dieresis", 600, NULL }, { "quotedblbase", 600, NULL }, { "iacute", 600, NULL }, { "ncaron", 600, NULL }, { "florin", 600, NULL }, { "yacute", 600, NULL }, { "Rcommaaccent", 600, NULL }, { "fi", 600, NULL }, { "fl", 600, NULL }, { "Acircumflex", 600, NULL }, { "Cacute", 600, NULL }, { "Icircumflex", 600, NULL }, { "guillemotleft", 600, NULL }, { "germandbls", 600, NULL }, { "seven", 600, NULL }, { "Amacron", 600, NULL }, { "Sacute", 600, NULL }, { "ordmasculine", 600, NULL }, { "dotlessi", 600, NULL }, { "sterling", 600, NULL }, { "notequal", 600, NULL }, { "Imacron", 600, NULL }, { "rcommaaccent", 600, NULL }, { "Zdotaccent", 600, NULL }, { "acircumflex", 600, NULL }, { "cacute", 600, NULL }, { "Ecaron", 600, NULL }, { "braceright", 600, NULL }, { "icircumflex", 600, NULL }, { "quotedblright", 600, NULL }, { "amacron", 600, NULL }, { "sacute", 600, NULL }, { "imacron", 600, NULL }, { "cent", 600, NULL }, { "currency", 600, NULL }, { "logicalnot", 600, NULL }, { "zdotaccent", 600, NULL }, { "Atilde", 600, NULL }, { "breve", 600, NULL }, { "bar", 600, NULL }, { "fraction", 600, NULL }, { "less", 600, NULL }, { "ecaron", 600, NULL }, { "guilsinglleft", 600, NULL }, { "exclam", 600, NULL }, { "period", 600, NULL }, { "Rcaron", 600, NULL }, { "Kcommaaccent", 600, NULL }, { "greater", 600, NULL }, { "atilde", 600, NULL }, { "brokenbar", 600, NULL }, { "quoteleft", 600, NULL }, { "Edotaccent", 600, NULL }, { "onesuperior", 600, NULL } }; static BuiltinFontWidth courierBoldWidthsTab[] = { { "Ntilde", 600, NULL }, { "rcaron", 600, NULL }, { "kcommaaccent", 600, NULL }, { "Ncommaaccent", 600, NULL }, { "Zacute", 600, NULL }, { "comma", 600, NULL }, { "cedilla", 600, NULL }, { "plusminus", 600, NULL }, { "circumflex", 600, NULL }, { "dotaccent", 600, NULL }, { "edotaccent", 600, NULL }, { "asciitilde", 600, NULL }, { "colon", 600, NULL }, { "onehalf", 600, NULL }, { "dollar", 600, NULL }, { "Lcaron", 600, NULL }, { "ntilde", 600, NULL }, { "Aogonek", 600, NULL }, { "ncommaaccent", 600, NULL }, { "minus", 600, NULL }, { "Iogonek", 600, NULL }, { "zacute", 600, NULL }, { "yen", 600, NULL }, { "space", 600, NULL }, { "Omacron", 600, NULL }, { "questiondown", 600, NULL }, { "emdash", 600, NULL }, { "Agrave", 600, NULL }, { "three", 600, NULL }, { "numbersign", 600, NULL }, { "lcaron", 600, NULL }, { "A", 600, NULL }, { "B", 600, NULL }, { "C", 600, NULL }, { "aogonek", 600, NULL }, { "D", 600, NULL }, { "E", 600, NULL }, { "onequarter", 600, NULL }, { "F", 600, NULL }, { "G", 600, NULL }, { "H", 600, NULL }, { "I", 600, NULL }, { "J", 600, NULL }, { "K", 600, NULL }, { "iogonek", 600, NULL }, { "backslash", 600, NULL }, { "L", 600, NULL }, { "periodcentered", 600, NULL }, { "M", 600, NULL }, { "N", 600, NULL }, { "omacron", 600, NULL }, { "Tcommaaccent", 600, NULL }, { "O", 600, NULL }, { "P", 600, NULL }, { "Q", 600, NULL }, { "Uhungarumlaut", 600, NULL }, { "R", 600, NULL }, { "Aacute", 600, NULL }, { "caron", 600, NULL }, { "S", 600, NULL }, { "T", 600, NULL }, { "U", 600, NULL }, { "agrave", 600, NULL }, { "V", 600, NULL }, { "W", 600, NULL }, { "X", 600, NULL }, { "question", 600, NULL }, { "equal", 600, NULL }, { "Y", 600, NULL }, { "Z", 600, NULL }, { "four", 600, NULL }, { "a", 600, NULL }, { "Gcommaaccent", 600, NULL }, { "b", 600, NULL }, { "c", 600, NULL }, { "d", 600, NULL }, { "e", 600, NULL }, { "f", 600, NULL }, { "g", 600, NULL }, { "bullet", 600, NULL }, { "h", 600, NULL }, { "i", 600, NULL }, { "Oslash", 600, NULL }, { "dagger", 600, NULL }, { "j", 600, NULL }, { "k", 600, NULL }, { "l", 600, NULL }, { "m", 600, NULL }, { "n", 600, NULL }, { "tcommaaccent", 600, NULL }, { "o", 600, NULL }, { "ordfeminine", 600, NULL }, { "ring", 600, NULL }, { "p", 600, NULL }, { "q", 600, NULL }, { "uhungarumlaut", 600, NULL }, { "r", 600, NULL }, { "twosuperior", 600, NULL }, { "aacute", 600, NULL }, { "s", 600, NULL }, { "OE", 600, NULL }, { "t", 600, NULL }, { "divide", 600, NULL }, { "u", 600, NULL }, { "Ccaron", 600, NULL }, { "v", 600, NULL }, { "w", 600, NULL }, { "x", 600, NULL }, { "y", 600, NULL }, { "z", 600, NULL }, { "Gbreve", 600, NULL }, { "commaaccent", 600, NULL }, { "hungarumlaut", 600, NULL }, { "Idotaccent", 600, NULL }, { "Nacute", 600, NULL }, { "quotedbl", 600, NULL }, { "gcommaaccent", 600, NULL }, { "mu", 600, NULL }, { "greaterequal", 600, NULL }, { "Scaron", 600, NULL }, { "Lslash", 600, NULL }, { "semicolon", 600, NULL }, { "oslash", 600, NULL }, { "lessequal", 600, NULL }, { "lozenge", 600, NULL }, { "parenright", 600, NULL }, { "ccaron", 600, NULL }, { "Ecircumflex", 600, NULL }, { "gbreve", 600, NULL }, { "trademark", 600, NULL }, { "daggerdbl", 600, NULL }, { "nacute", 600, NULL }, { "macron", 600, NULL }, { "Otilde", 600, NULL }, { "Emacron", 600, NULL }, { "ellipsis", 600, NULL }, { "scaron", 600, NULL }, { "AE", 600, NULL }, { "Ucircumflex", 600, NULL }, { "lslash", 600, NULL }, { "quotedblleft", 600, NULL }, { "guilsinglright", 600, NULL }, { "hyphen", 600, NULL }, { "quotesingle", 600, NULL }, { "eight", 600, NULL }, { "exclamdown", 600, NULL }, { "endash", 600, NULL }, { "oe", 600, NULL }, { "Abreve", 600, NULL }, { "Umacron", 600, NULL }, { "ecircumflex", 600, NULL }, { "Adieresis", 600, NULL }, { "copyright", 600, NULL }, { "Egrave", 600, NULL }, { "slash", 600, NULL }, { "Edieresis", 600, NULL }, { "otilde", 600, NULL }, { "Idieresis", 600, NULL }, { "parenleft", 600, NULL }, { "one", 600, NULL }, { "emacron", 600, NULL }, { "Odieresis", 600, NULL }, { "ucircumflex", 600, NULL }, { "bracketleft", 600, NULL }, { "Ugrave", 600, NULL }, { "quoteright", 600, NULL }, { "Udieresis", 600, NULL }, { "perthousand", 600, NULL }, { "Ydieresis", 600, NULL }, { "umacron", 600, NULL }, { "abreve", 600, NULL }, { "Eacute", 600, NULL }, { "adieresis", 600, NULL }, { "egrave", 600, NULL }, { "edieresis", 600, NULL }, { "idieresis", 600, NULL }, { "Eth", 600, NULL }, { "ae", 600, NULL }, { "asterisk", 600, NULL }, { "odieresis", 600, NULL }, { "Uacute", 600, NULL }, { "ugrave", 600, NULL }, { "nine", 600, NULL }, { "five", 600, NULL }, { "udieresis", 600, NULL }, { "Zcaron", 600, NULL }, { "Scommaaccent", 600, NULL }, { "threequarters", 600, NULL }, { "guillemotright", 600, NULL }, { "Ccedilla", 600, NULL }, { "ydieresis", 600, NULL }, { "tilde", 600, NULL }, { "at", 600, NULL }, { "eacute", 600, NULL }, { "underscore", 600, NULL }, { "Euro", 600, NULL }, { "Dcroat", 600, NULL }, { "multiply", 600, NULL }, { "zero", 600, NULL }, { "eth", 600, NULL }, { "Scedilla", 600, NULL }, { "Ograve", 600, NULL }, { "Racute", 600, NULL }, { "partialdiff", 600, NULL }, { "uacute", 600, NULL }, { "braceleft", 600, NULL }, { "Thorn", 600, NULL }, { "zcaron", 600, NULL }, { "scommaaccent", 600, NULL }, { "ccedilla", 600, NULL }, { "Dcaron", 600, NULL }, { "dcroat", 600, NULL }, { "Ocircumflex", 600, NULL }, { "Oacute", 600, NULL }, { "scedilla", 600, NULL }, { "ogonek", 600, NULL }, { "ograve", 600, NULL }, { "racute", 600, NULL }, { "Tcaron", 600, NULL }, { "Eogonek", 600, NULL }, { "thorn", 600, NULL }, { "degree", 600, NULL }, { "registered", 600, NULL }, { "radical", 600, NULL }, { "Aring", 600, NULL }, { "percent", 600, NULL }, { "six", 600, NULL }, { "paragraph", 600, NULL }, { "dcaron", 600, NULL }, { "Uogonek", 600, NULL }, { "two", 600, NULL }, { "summation", 600, NULL }, { "Igrave", 600, NULL }, { "Lacute", 600, NULL }, { "ocircumflex", 600, NULL }, { "oacute", 600, NULL }, { "Uring", 600, NULL }, { "Lcommaaccent", 600, NULL }, { "tcaron", 600, NULL }, { "eogonek", 600, NULL }, { "Delta", 600, NULL }, { "Ohungarumlaut", 600, NULL }, { "asciicircum", 600, NULL }, { "aring", 600, NULL }, { "grave", 600, NULL }, { "uogonek", 600, NULL }, { "bracketright", 600, NULL }, { "Iacute", 600, NULL }, { "ampersand", 600, NULL }, { "igrave", 600, NULL }, { "lacute", 600, NULL }, { "Ncaron", 600, NULL }, { "plus", 600, NULL }, { "uring", 600, NULL }, { "quotesinglbase", 600, NULL }, { "lcommaaccent", 600, NULL }, { "Yacute", 600, NULL }, { "ohungarumlaut", 600, NULL }, { "threesuperior", 600, NULL }, { "acute", 600, NULL }, { "section", 600, NULL }, { "dieresis", 600, NULL }, { "iacute", 600, NULL }, { "quotedblbase", 600, NULL }, { "ncaron", 600, NULL }, { "florin", 600, NULL }, { "yacute", 600, NULL }, { "Rcommaaccent", 600, NULL }, { "fi", 600, NULL }, { "fl", 600, NULL }, { "Acircumflex", 600, NULL }, { "Cacute", 600, NULL }, { "Icircumflex", 600, NULL }, { "guillemotleft", 600, NULL }, { "germandbls", 600, NULL }, { "Amacron", 600, NULL }, { "seven", 600, NULL }, { "Sacute", 600, NULL }, { "ordmasculine", 600, NULL }, { "dotlessi", 600, NULL }, { "sterling", 600, NULL }, { "notequal", 600, NULL }, { "Imacron", 600, NULL }, { "rcommaaccent", 600, NULL }, { "Zdotaccent", 600, NULL }, { "acircumflex", 600, NULL }, { "cacute", 600, NULL }, { "Ecaron", 600, NULL }, { "icircumflex", 600, NULL }, { "braceright", 600, NULL }, { "quotedblright", 600, NULL }, { "amacron", 600, NULL }, { "sacute", 600, NULL }, { "imacron", 600, NULL }, { "cent", 600, NULL }, { "currency", 600, NULL }, { "logicalnot", 600, NULL }, { "zdotaccent", 600, NULL }, { "Atilde", 600, NULL }, { "breve", 600, NULL }, { "bar", 600, NULL }, { "fraction", 600, NULL }, { "less", 600, NULL }, { "ecaron", 600, NULL }, { "guilsinglleft", 600, NULL }, { "exclam", 600, NULL }, { "period", 600, NULL }, { "Rcaron", 600, NULL }, { "Kcommaaccent", 600, NULL }, { "greater", 600, NULL }, { "atilde", 600, NULL }, { "brokenbar", 600, NULL }, { "quoteleft", 600, NULL }, { "Edotaccent", 600, NULL }, { "onesuperior", 600, NULL } }; static BuiltinFontWidth courierBoldObliqueWidthsTab[] = { { "Ntilde", 600, NULL }, { "rcaron", 600, NULL }, { "kcommaaccent", 600, NULL }, { "Ncommaaccent", 600, NULL }, { "Zacute", 600, NULL }, { "comma", 600, NULL }, { "cedilla", 600, NULL }, { "plusminus", 600, NULL }, { "circumflex", 600, NULL }, { "dotaccent", 600, NULL }, { "edotaccent", 600, NULL }, { "asciitilde", 600, NULL }, { "colon", 600, NULL }, { "onehalf", 600, NULL }, { "dollar", 600, NULL }, { "Lcaron", 600, NULL }, { "ntilde", 600, NULL }, { "Aogonek", 600, NULL }, { "ncommaaccent", 600, NULL }, { "minus", 600, NULL }, { "Iogonek", 600, NULL }, { "zacute", 600, NULL }, { "yen", 600, NULL }, { "space", 600, NULL }, { "Omacron", 600, NULL }, { "questiondown", 600, NULL }, { "emdash", 600, NULL }, { "Agrave", 600, NULL }, { "three", 600, NULL }, { "numbersign", 600, NULL }, { "lcaron", 600, NULL }, { "A", 600, NULL }, { "B", 600, NULL }, { "C", 600, NULL }, { "aogonek", 600, NULL }, { "D", 600, NULL }, { "E", 600, NULL }, { "onequarter", 600, NULL }, { "F", 600, NULL }, { "G", 600, NULL }, { "H", 600, NULL }, { "I", 600, NULL }, { "J", 600, NULL }, { "K", 600, NULL }, { "iogonek", 600, NULL }, { "backslash", 600, NULL }, { "L", 600, NULL }, { "periodcentered", 600, NULL }, { "M", 600, NULL }, { "N", 600, NULL }, { "omacron", 600, NULL }, { "Tcommaaccent", 600, NULL }, { "O", 600, NULL }, { "P", 600, NULL }, { "Q", 600, NULL }, { "Uhungarumlaut", 600, NULL }, { "R", 600, NULL }, { "Aacute", 600, NULL }, { "caron", 600, NULL }, { "S", 600, NULL }, { "T", 600, NULL }, { "U", 600, NULL }, { "agrave", 600, NULL }, { "V", 600, NULL }, { "W", 600, NULL }, { "X", 600, NULL }, { "question", 600, NULL }, { "equal", 600, NULL }, { "Y", 600, NULL }, { "Z", 600, NULL }, { "four", 600, NULL }, { "a", 600, NULL }, { "Gcommaaccent", 600, NULL }, { "b", 600, NULL }, { "c", 600, NULL }, { "d", 600, NULL }, { "e", 600, NULL }, { "f", 600, NULL }, { "g", 600, NULL }, { "bullet", 600, NULL }, { "h", 600, NULL }, { "i", 600, NULL }, { "Oslash", 600, NULL }, { "dagger", 600, NULL }, { "j", 600, NULL }, { "k", 600, NULL }, { "l", 600, NULL }, { "m", 600, NULL }, { "n", 600, NULL }, { "tcommaaccent", 600, NULL }, { "o", 600, NULL }, { "ordfeminine", 600, NULL }, { "ring", 600, NULL }, { "p", 600, NULL }, { "q", 600, NULL }, { "uhungarumlaut", 600, NULL }, { "r", 600, NULL }, { "twosuperior", 600, NULL }, { "aacute", 600, NULL }, { "s", 600, NULL }, { "OE", 600, NULL }, { "t", 600, NULL }, { "divide", 600, NULL }, { "u", 600, NULL }, { "Ccaron", 600, NULL }, { "v", 600, NULL }, { "w", 600, NULL }, { "x", 600, NULL }, { "y", 600, NULL }, { "z", 600, NULL }, { "Gbreve", 600, NULL }, { "commaaccent", 600, NULL }, { "hungarumlaut", 600, NULL }, { "Idotaccent", 600, NULL }, { "Nacute", 600, NULL }, { "quotedbl", 600, NULL }, { "gcommaaccent", 600, NULL }, { "mu", 600, NULL }, { "greaterequal", 600, NULL }, { "Scaron", 600, NULL }, { "Lslash", 600, NULL }, { "semicolon", 600, NULL }, { "oslash", 600, NULL }, { "lessequal", 600, NULL }, { "lozenge", 600, NULL }, { "parenright", 600, NULL }, { "ccaron", 600, NULL }, { "Ecircumflex", 600, NULL }, { "gbreve", 600, NULL }, { "trademark", 600, NULL }, { "daggerdbl", 600, NULL }, { "nacute", 600, NULL }, { "macron", 600, NULL }, { "Otilde", 600, NULL }, { "Emacron", 600, NULL }, { "ellipsis", 600, NULL }, { "scaron", 600, NULL }, { "AE", 600, NULL }, { "Ucircumflex", 600, NULL }, { "lslash", 600, NULL }, { "quotedblleft", 600, NULL }, { "guilsinglright", 600, NULL }, { "hyphen", 600, NULL }, { "quotesingle", 600, NULL }, { "eight", 600, NULL }, { "exclamdown", 600, NULL }, { "endash", 600, NULL }, { "oe", 600, NULL }, { "Abreve", 600, NULL }, { "Umacron", 600, NULL }, { "ecircumflex", 600, NULL }, { "Adieresis", 600, NULL }, { "copyright", 600, NULL }, { "Egrave", 600, NULL }, { "slash", 600, NULL }, { "Edieresis", 600, NULL }, { "otilde", 600, NULL }, { "Idieresis", 600, NULL }, { "parenleft", 600, NULL }, { "one", 600, NULL }, { "emacron", 600, NULL }, { "Odieresis", 600, NULL }, { "ucircumflex", 600, NULL }, { "bracketleft", 600, NULL }, { "Ugrave", 600, NULL }, { "quoteright", 600, NULL }, { "Udieresis", 600, NULL }, { "perthousand", 600, NULL }, { "Ydieresis", 600, NULL }, { "umacron", 600, NULL }, { "abreve", 600, NULL }, { "Eacute", 600, NULL }, { "adieresis", 600, NULL }, { "egrave", 600, NULL }, { "edieresis", 600, NULL }, { "idieresis", 600, NULL }, { "Eth", 600, NULL }, { "ae", 600, NULL }, { "asterisk", 600, NULL }, { "odieresis", 600, NULL }, { "Uacute", 600, NULL }, { "ugrave", 600, NULL }, { "nine", 600, NULL }, { "five", 600, NULL }, { "udieresis", 600, NULL }, { "Zcaron", 600, NULL }, { "Scommaaccent", 600, NULL }, { "threequarters", 600, NULL }, { "guillemotright", 600, NULL }, { "Ccedilla", 600, NULL }, { "ydieresis", 600, NULL }, { "tilde", 600, NULL }, { "at", 600, NULL }, { "eacute", 600, NULL }, { "underscore", 600, NULL }, { "Euro", 600, NULL }, { "Dcroat", 600, NULL }, { "multiply", 600, NULL }, { "zero", 600, NULL }, { "eth", 600, NULL }, { "Scedilla", 600, NULL }, { "Ograve", 600, NULL }, { "Racute", 600, NULL }, { "partialdiff", 600, NULL }, { "uacute", 600, NULL }, { "braceleft", 600, NULL }, { "Thorn", 600, NULL }, { "zcaron", 600, NULL }, { "scommaaccent", 600, NULL }, { "ccedilla", 600, NULL }, { "Dcaron", 600, NULL }, { "dcroat", 600, NULL }, { "Ocircumflex", 600, NULL }, { "Oacute", 600, NULL }, { "scedilla", 600, NULL }, { "ogonek", 600, NULL }, { "ograve", 600, NULL }, { "racute", 600, NULL }, { "Tcaron", 600, NULL }, { "Eogonek", 600, NULL }, { "thorn", 600, NULL }, { "degree", 600, NULL }, { "registered", 600, NULL }, { "radical", 600, NULL }, { "Aring", 600, NULL }, { "percent", 600, NULL }, { "six", 600, NULL }, { "paragraph", 600, NULL }, { "dcaron", 600, NULL }, { "Uogonek", 600, NULL }, { "two", 600, NULL }, { "summation", 600, NULL }, { "Igrave", 600, NULL }, { "Lacute", 600, NULL }, { "ocircumflex", 600, NULL }, { "oacute", 600, NULL }, { "Uring", 600, NULL }, { "Lcommaaccent", 600, NULL }, { "tcaron", 600, NULL }, { "eogonek", 600, NULL }, { "Delta", 600, NULL }, { "Ohungarumlaut", 600, NULL }, { "asciicircum", 600, NULL }, { "aring", 600, NULL }, { "grave", 600, NULL }, { "uogonek", 600, NULL }, { "bracketright", 600, NULL }, { "Iacute", 600, NULL }, { "ampersand", 600, NULL }, { "igrave", 600, NULL }, { "lacute", 600, NULL }, { "Ncaron", 600, NULL }, { "plus", 600, NULL }, { "uring", 600, NULL }, { "quotesinglbase", 600, NULL }, { "lcommaaccent", 600, NULL }, { "Yacute", 600, NULL }, { "ohungarumlaut", 600, NULL }, { "threesuperior", 600, NULL }, { "acute", 600, NULL }, { "section", 600, NULL }, { "dieresis", 600, NULL }, { "iacute", 600, NULL }, { "quotedblbase", 600, NULL }, { "ncaron", 600, NULL }, { "florin", 600, NULL }, { "yacute", 600, NULL }, { "Rcommaaccent", 600, NULL }, { "fi", 600, NULL }, { "fl", 600, NULL }, { "Acircumflex", 600, NULL }, { "Cacute", 600, NULL }, { "Icircumflex", 600, NULL }, { "guillemotleft", 600, NULL }, { "germandbls", 600, NULL }, { "Amacron", 600, NULL }, { "seven", 600, NULL }, { "Sacute", 600, NULL }, { "ordmasculine", 600, NULL }, { "dotlessi", 600, NULL }, { "sterling", 600, NULL }, { "notequal", 600, NULL }, { "Imacron", 600, NULL }, { "rcommaaccent", 600, NULL }, { "Zdotaccent", 600, NULL }, { "acircumflex", 600, NULL }, { "cacute", 600, NULL }, { "Ecaron", 600, NULL }, { "icircumflex", 600, NULL }, { "braceright", 600, NULL }, { "quotedblright", 600, NULL }, { "amacron", 600, NULL }, { "sacute", 600, NULL }, { "imacron", 600, NULL }, { "cent", 600, NULL }, { "currency", 600, NULL }, { "logicalnot", 600, NULL }, { "zdotaccent", 600, NULL }, { "Atilde", 600, NULL }, { "breve", 600, NULL }, { "bar", 600, NULL }, { "fraction", 600, NULL }, { "less", 600, NULL }, { "ecaron", 600, NULL }, { "guilsinglleft", 600, NULL }, { "exclam", 600, NULL }, { "period", 600, NULL }, { "Rcaron", 600, NULL }, { "Kcommaaccent", 600, NULL }, { "greater", 600, NULL }, { "atilde", 600, NULL }, { "brokenbar", 600, NULL }, { "quoteleft", 600, NULL }, { "Edotaccent", 600, NULL }, { "onesuperior", 600, NULL } }; static BuiltinFontWidth courierObliqueWidthsTab[] = { { "Ntilde", 600, NULL }, { "rcaron", 600, NULL }, { "kcommaaccent", 600, NULL }, { "Ncommaaccent", 600, NULL }, { "Zacute", 600, NULL }, { "comma", 600, NULL }, { "cedilla", 600, NULL }, { "plusminus", 600, NULL }, { "circumflex", 600, NULL }, { "dotaccent", 600, NULL }, { "edotaccent", 600, NULL }, { "asciitilde", 600, NULL }, { "colon", 600, NULL }, { "onehalf", 600, NULL }, { "dollar", 600, NULL }, { "Lcaron", 600, NULL }, { "ntilde", 600, NULL }, { "Aogonek", 600, NULL }, { "ncommaaccent", 600, NULL }, { "minus", 600, NULL }, { "Iogonek", 600, NULL }, { "zacute", 600, NULL }, { "yen", 600, NULL }, { "space", 600, NULL }, { "Omacron", 600, NULL }, { "questiondown", 600, NULL }, { "emdash", 600, NULL }, { "Agrave", 600, NULL }, { "three", 600, NULL }, { "numbersign", 600, NULL }, { "lcaron", 600, NULL }, { "A", 600, NULL }, { "B", 600, NULL }, { "C", 600, NULL }, { "aogonek", 600, NULL }, { "D", 600, NULL }, { "E", 600, NULL }, { "onequarter", 600, NULL }, { "F", 600, NULL }, { "G", 600, NULL }, { "H", 600, NULL }, { "I", 600, NULL }, { "J", 600, NULL }, { "K", 600, NULL }, { "iogonek", 600, NULL }, { "backslash", 600, NULL }, { "L", 600, NULL }, { "periodcentered", 600, NULL }, { "M", 600, NULL }, { "N", 600, NULL }, { "omacron", 600, NULL }, { "Tcommaaccent", 600, NULL }, { "O", 600, NULL }, { "P", 600, NULL }, { "Q", 600, NULL }, { "Uhungarumlaut", 600, NULL }, { "R", 600, NULL }, { "Aacute", 600, NULL }, { "caron", 600, NULL }, { "S", 600, NULL }, { "T", 600, NULL }, { "U", 600, NULL }, { "agrave", 600, NULL }, { "V", 600, NULL }, { "W", 600, NULL }, { "X", 600, NULL }, { "question", 600, NULL }, { "equal", 600, NULL }, { "Y", 600, NULL }, { "Z", 600, NULL }, { "four", 600, NULL }, { "a", 600, NULL }, { "Gcommaaccent", 600, NULL }, { "b", 600, NULL }, { "c", 600, NULL }, { "d", 600, NULL }, { "e", 600, NULL }, { "f", 600, NULL }, { "g", 600, NULL }, { "bullet", 600, NULL }, { "h", 600, NULL }, { "i", 600, NULL }, { "Oslash", 600, NULL }, { "dagger", 600, NULL }, { "j", 600, NULL }, { "k", 600, NULL }, { "l", 600, NULL }, { "m", 600, NULL }, { "n", 600, NULL }, { "tcommaaccent", 600, NULL }, { "o", 600, NULL }, { "ordfeminine", 600, NULL }, { "ring", 600, NULL }, { "p", 600, NULL }, { "q", 600, NULL }, { "uhungarumlaut", 600, NULL }, { "r", 600, NULL }, { "twosuperior", 600, NULL }, { "aacute", 600, NULL }, { "s", 600, NULL }, { "OE", 600, NULL }, { "t", 600, NULL }, { "divide", 600, NULL }, { "u", 600, NULL }, { "Ccaron", 600, NULL }, { "v", 600, NULL }, { "w", 600, NULL }, { "x", 600, NULL }, { "y", 600, NULL }, { "z", 600, NULL }, { "Gbreve", 600, NULL }, { "commaaccent", 600, NULL }, { "hungarumlaut", 600, NULL }, { "Idotaccent", 600, NULL }, { "Nacute", 600, NULL }, { "quotedbl", 600, NULL }, { "gcommaaccent", 600, NULL }, { "mu", 600, NULL }, { "greaterequal", 600, NULL }, { "Scaron", 600, NULL }, { "Lslash", 600, NULL }, { "semicolon", 600, NULL }, { "oslash", 600, NULL }, { "lessequal", 600, NULL }, { "lozenge", 600, NULL }, { "parenright", 600, NULL }, { "ccaron", 600, NULL }, { "Ecircumflex", 600, NULL }, { "gbreve", 600, NULL }, { "trademark", 600, NULL }, { "daggerdbl", 600, NULL }, { "nacute", 600, NULL }, { "macron", 600, NULL }, { "Otilde", 600, NULL }, { "Emacron", 600, NULL }, { "ellipsis", 600, NULL }, { "scaron", 600, NULL }, { "AE", 600, NULL }, { "Ucircumflex", 600, NULL }, { "lslash", 600, NULL }, { "quotedblleft", 600, NULL }, { "guilsinglright", 600, NULL }, { "hyphen", 600, NULL }, { "quotesingle", 600, NULL }, { "eight", 600, NULL }, { "exclamdown", 600, NULL }, { "endash", 600, NULL }, { "oe", 600, NULL }, { "Abreve", 600, NULL }, { "Umacron", 600, NULL }, { "ecircumflex", 600, NULL }, { "Adieresis", 600, NULL }, { "copyright", 600, NULL }, { "Egrave", 600, NULL }, { "slash", 600, NULL }, { "Edieresis", 600, NULL }, { "otilde", 600, NULL }, { "Idieresis", 600, NULL }, { "parenleft", 600, NULL }, { "one", 600, NULL }, { "emacron", 600, NULL }, { "Odieresis", 600, NULL }, { "ucircumflex", 600, NULL }, { "bracketleft", 600, NULL }, { "Ugrave", 600, NULL }, { "quoteright", 600, NULL }, { "Udieresis", 600, NULL }, { "perthousand", 600, NULL }, { "Ydieresis", 600, NULL }, { "umacron", 600, NULL }, { "abreve", 600, NULL }, { "Eacute", 600, NULL }, { "adieresis", 600, NULL }, { "egrave", 600, NULL }, { "edieresis", 600, NULL }, { "idieresis", 600, NULL }, { "Eth", 600, NULL }, { "ae", 600, NULL }, { "asterisk", 600, NULL }, { "odieresis", 600, NULL }, { "Uacute", 600, NULL }, { "ugrave", 600, NULL }, { "nine", 600, NULL }, { "five", 600, NULL }, { "udieresis", 600, NULL }, { "Zcaron", 600, NULL }, { "Scommaaccent", 600, NULL }, { "threequarters", 600, NULL }, { "guillemotright", 600, NULL }, { "Ccedilla", 600, NULL }, { "ydieresis", 600, NULL }, { "tilde", 600, NULL }, { "at", 600, NULL }, { "eacute", 600, NULL }, { "underscore", 600, NULL }, { "Euro", 600, NULL }, { "Dcroat", 600, NULL }, { "multiply", 600, NULL }, { "zero", 600, NULL }, { "eth", 600, NULL }, { "Scedilla", 600, NULL }, { "Ograve", 600, NULL }, { "Racute", 600, NULL }, { "partialdiff", 600, NULL }, { "uacute", 600, NULL }, { "braceleft", 600, NULL }, { "Thorn", 600, NULL }, { "zcaron", 600, NULL }, { "scommaaccent", 600, NULL }, { "ccedilla", 600, NULL }, { "Dcaron", 600, NULL }, { "dcroat", 600, NULL }, { "Ocircumflex", 600, NULL }, { "Oacute", 600, NULL }, { "scedilla", 600, NULL }, { "ogonek", 600, NULL }, { "ograve", 600, NULL }, { "racute", 600, NULL }, { "Tcaron", 600, NULL }, { "Eogonek", 600, NULL }, { "thorn", 600, NULL }, { "degree", 600, NULL }, { "registered", 600, NULL }, { "radical", 600, NULL }, { "Aring", 600, NULL }, { "percent", 600, NULL }, { "six", 600, NULL }, { "paragraph", 600, NULL }, { "dcaron", 600, NULL }, { "Uogonek", 600, NULL }, { "two", 600, NULL }, { "summation", 600, NULL }, { "Igrave", 600, NULL }, { "Lacute", 600, NULL }, { "ocircumflex", 600, NULL }, { "oacute", 600, NULL }, { "Uring", 600, NULL }, { "Lcommaaccent", 600, NULL }, { "tcaron", 600, NULL }, { "eogonek", 600, NULL }, { "Delta", 600, NULL }, { "Ohungarumlaut", 600, NULL }, { "asciicircum", 600, NULL }, { "aring", 600, NULL }, { "grave", 600, NULL }, { "uogonek", 600, NULL }, { "bracketright", 600, NULL }, { "Iacute", 600, NULL }, { "ampersand", 600, NULL }, { "igrave", 600, NULL }, { "lacute", 600, NULL }, { "Ncaron", 600, NULL }, { "plus", 600, NULL }, { "uring", 600, NULL }, { "quotesinglbase", 600, NULL }, { "lcommaaccent", 600, NULL }, { "Yacute", 600, NULL }, { "ohungarumlaut", 600, NULL }, { "threesuperior", 600, NULL }, { "acute", 600, NULL }, { "section", 600, NULL }, { "dieresis", 600, NULL }, { "iacute", 600, NULL }, { "quotedblbase", 600, NULL }, { "ncaron", 600, NULL }, { "florin", 600, NULL }, { "yacute", 600, NULL }, { "Rcommaaccent", 600, NULL }, { "fi", 600, NULL }, { "fl", 600, NULL }, { "Acircumflex", 600, NULL }, { "Cacute", 600, NULL }, { "Icircumflex", 600, NULL }, { "guillemotleft", 600, NULL }, { "germandbls", 600, NULL }, { "Amacron", 600, NULL }, { "seven", 600, NULL }, { "Sacute", 600, NULL }, { "ordmasculine", 600, NULL }, { "dotlessi", 600, NULL }, { "sterling", 600, NULL }, { "notequal", 600, NULL }, { "Imacron", 600, NULL }, { "rcommaaccent", 600, NULL }, { "Zdotaccent", 600, NULL }, { "acircumflex", 600, NULL }, { "cacute", 600, NULL }, { "Ecaron", 600, NULL }, { "icircumflex", 600, NULL }, { "braceright", 600, NULL }, { "quotedblright", 600, NULL }, { "amacron", 600, NULL }, { "sacute", 600, NULL }, { "imacron", 600, NULL }, { "cent", 600, NULL }, { "currency", 600, NULL }, { "logicalnot", 600, NULL }, { "zdotaccent", 600, NULL }, { "Atilde", 600, NULL }, { "breve", 600, NULL }, { "bar", 600, NULL }, { "fraction", 600, NULL }, { "less", 600, NULL }, { "ecaron", 600, NULL }, { "guilsinglleft", 600, NULL }, { "exclam", 600, NULL }, { "period", 600, NULL }, { "Rcaron", 600, NULL }, { "Kcommaaccent", 600, NULL }, { "greater", 600, NULL }, { "atilde", 600, NULL }, { "brokenbar", 600, NULL }, { "quoteleft", 600, NULL }, { "Edotaccent", 600, NULL }, { "onesuperior", 600, NULL } }; static BuiltinFontWidth helveticaWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 333, NULL }, { "kcommaaccent", 500, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 611, NULL }, { "comma", 278, NULL }, { "cedilla", 333, NULL }, { "plusminus", 584, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 556, NULL }, { "asciitilde", 584, NULL }, { "colon", 278, NULL }, { "onehalf", 834, NULL }, { "dollar", 556, NULL }, { "Lcaron", 556, NULL }, { "ntilde", 556, NULL }, { "Aogonek", 667, NULL }, { "ncommaaccent", 556, NULL }, { "minus", 584, NULL }, { "Iogonek", 278, NULL }, { "zacute", 500, NULL }, { "yen", 556, NULL }, { "space", 278, NULL }, { "Omacron", 778, NULL }, { "questiondown", 611, NULL }, { "emdash", 1000, NULL }, { "Agrave", 667, NULL }, { "three", 556, NULL }, { "numbersign", 556, NULL }, { "lcaron", 299, NULL }, { "A", 667, NULL }, { "B", 667, NULL }, { "C", 722, NULL }, { "aogonek", 556, NULL }, { "D", 722, NULL }, { "E", 667, NULL }, { "onequarter", 834, NULL }, { "F", 611, NULL }, { "G", 778, NULL }, { "H", 722, NULL }, { "I", 278, NULL }, { "J", 500, NULL }, { "K", 667, NULL }, { "iogonek", 222, NULL }, { "backslash", 278, NULL }, { "L", 556, NULL }, { "periodcentered", 278, NULL }, { "M", 833, NULL }, { "N", 722, NULL }, { "omacron", 556, NULL }, { "Tcommaaccent", 611, NULL }, { "O", 778, NULL }, { "P", 667, NULL }, { "Q", 778, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 722, NULL }, { "Aacute", 667, NULL }, { "caron", 333, NULL }, { "S", 667, NULL }, { "T", 611, NULL }, { "U", 722, NULL }, { "agrave", 556, NULL }, { "V", 667, NULL }, { "W", 944, NULL }, { "X", 667, NULL }, { "question", 556, NULL }, { "equal", 584, NULL }, { "Y", 667, NULL }, { "Z", 611, NULL }, { "four", 556, NULL }, { "a", 556, NULL }, { "Gcommaaccent", 778, NULL }, { "b", 556, NULL }, { "c", 500, NULL }, { "d", 556, NULL }, { "e", 556, NULL }, { "f", 278, NULL }, { "g", 556, NULL }, { "bullet", 350, NULL }, { "h", 556, NULL }, { "i", 222, NULL }, { "Oslash", 778, NULL }, { "dagger", 556, NULL }, { "j", 222, NULL }, { "k", 500, NULL }, { "l", 222, NULL }, { "m", 833, NULL }, { "n", 556, NULL }, { "tcommaaccent", 278, NULL }, { "o", 556, NULL }, { "ordfeminine", 370, NULL }, { "ring", 333, NULL }, { "p", 556, NULL }, { "q", 556, NULL }, { "uhungarumlaut", 556, NULL }, { "r", 333, NULL }, { "twosuperior", 333, NULL }, { "aacute", 556, NULL }, { "s", 500, NULL }, { "OE", 1000, NULL }, { "t", 278, NULL }, { "divide", 584, NULL }, { "u", 556, NULL }, { "Ccaron", 722, NULL }, { "v", 500, NULL }, { "w", 722, NULL }, { "x", 500, NULL }, { "y", 500, NULL }, { "z", 500, NULL }, { "Gbreve", 778, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 278, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 355, NULL }, { "gcommaaccent", 556, NULL }, { "mu", 556, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 667, NULL }, { "Lslash", 556, NULL }, { "semicolon", 278, NULL }, { "oslash", 611, NULL }, { "lessequal", 549, NULL }, { "lozenge", 471, NULL }, { "parenright", 333, NULL }, { "ccaron", 500, NULL }, { "Ecircumflex", 667, NULL }, { "gbreve", 556, NULL }, { "trademark", 1000, NULL }, { "daggerdbl", 556, NULL }, { "nacute", 556, NULL }, { "macron", 333, NULL }, { "Otilde", 778, NULL }, { "Emacron", 667, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 500, NULL }, { "AE", 1000, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 222, NULL }, { "quotedblleft", 333, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 191, NULL }, { "eight", 556, NULL }, { "exclamdown", 333, NULL }, { "endash", 556, NULL }, { "oe", 944, NULL }, { "Abreve", 667, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 556, NULL }, { "Adieresis", 667, NULL }, { "copyright", 737, NULL }, { "Egrave", 667, NULL }, { "slash", 278, NULL }, { "Edieresis", 667, NULL }, { "otilde", 556, NULL }, { "Idieresis", 278, NULL }, { "parenleft", 333, NULL }, { "one", 556, NULL }, { "emacron", 556, NULL }, { "Odieresis", 778, NULL }, { "ucircumflex", 556, NULL }, { "bracketleft", 278, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 222, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 667, NULL }, { "umacron", 556, NULL }, { "abreve", 556, NULL }, { "Eacute", 667, NULL }, { "adieresis", 556, NULL }, { "egrave", 556, NULL }, { "edieresis", 556, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 889, NULL }, { "asterisk", 389, NULL }, { "odieresis", 556, NULL }, { "Uacute", 722, NULL }, { "ugrave", 556, NULL }, { "nine", 556, NULL }, { "five", 556, NULL }, { "udieresis", 556, NULL }, { "Zcaron", 611, NULL }, { "Scommaaccent", 667, NULL }, { "threequarters", 834, NULL }, { "guillemotright", 556, NULL }, { "Ccedilla", 722, NULL }, { "ydieresis", 500, NULL }, { "tilde", 333, NULL }, { "at", 1015, NULL }, { "eacute", 556, NULL }, { "underscore", 556, NULL }, { "Euro", 556, NULL }, { "Dcroat", 722, NULL }, { "multiply", 584, NULL }, { "zero", 556, NULL }, { "eth", 556, NULL }, { "Scedilla", 667, NULL }, { "Ograve", 778, NULL }, { "Racute", 722, NULL }, { "partialdiff", 476, NULL }, { "uacute", 556, NULL }, { "braceleft", 334, NULL }, { "Thorn", 667, NULL }, { "zcaron", 500, NULL }, { "scommaaccent", 500, NULL }, { "ccedilla", 500, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 556, NULL }, { "Ocircumflex", 778, NULL }, { "Oacute", 778, NULL }, { "scedilla", 500, NULL }, { "ogonek", 333, NULL }, { "ograve", 556, NULL }, { "racute", 333, NULL }, { "Tcaron", 611, NULL }, { "Eogonek", 667, NULL }, { "thorn", 556, NULL }, { "degree", 400, NULL }, { "registered", 737, NULL }, { "radical", 453, NULL }, { "Aring", 667, NULL }, { "percent", 889, NULL }, { "six", 556, NULL }, { "paragraph", 537, NULL }, { "dcaron", 643, NULL }, { "Uogonek", 722, NULL }, { "two", 556, NULL }, { "summation", 600, NULL }, { "Igrave", 278, NULL }, { "Lacute", 556, NULL }, { "ocircumflex", 556, NULL }, { "oacute", 556, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 556, NULL }, { "tcaron", 317, NULL }, { "eogonek", 556, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 778, NULL }, { "asciicircum", 469, NULL }, { "aring", 556, NULL }, { "grave", 333, NULL }, { "uogonek", 556, NULL }, { "bracketright", 278, NULL }, { "Iacute", 278, NULL }, { "ampersand", 667, NULL }, { "igrave", 278, NULL }, { "lacute", 222, NULL }, { "Ncaron", 722, NULL }, { "plus", 584, NULL }, { "uring", 556, NULL }, { "quotesinglbase", 222, NULL }, { "lcommaaccent", 222, NULL }, { "Yacute", 667, NULL }, { "ohungarumlaut", 556, NULL }, { "threesuperior", 333, NULL }, { "acute", 333, NULL }, { "section", 556, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 333, NULL }, { "ncaron", 556, NULL }, { "florin", 556, NULL }, { "yacute", 500, NULL }, { "Rcommaaccent", 722, NULL }, { "fi", 500, NULL }, { "fl", 500, NULL }, { "Acircumflex", 667, NULL }, { "Cacute", 722, NULL }, { "Icircumflex", 278, NULL }, { "guillemotleft", 556, NULL }, { "germandbls", 611, NULL }, { "Amacron", 667, NULL }, { "seven", 556, NULL }, { "Sacute", 667, NULL }, { "ordmasculine", 365, NULL }, { "dotlessi", 278, NULL }, { "sterling", 556, NULL }, { "notequal", 549, NULL }, { "Imacron", 278, NULL }, { "rcommaaccent", 333, NULL }, { "Zdotaccent", 611, NULL }, { "acircumflex", 556, NULL }, { "cacute", 500, NULL }, { "Ecaron", 667, NULL }, { "icircumflex", 278, NULL }, { "braceright", 334, NULL }, { "quotedblright", 333, NULL }, { "amacron", 556, NULL }, { "sacute", 500, NULL }, { "imacron", 278, NULL }, { "cent", 556, NULL }, { "currency", 556, NULL }, { "logicalnot", 584, NULL }, { "zdotaccent", 500, NULL }, { "Atilde", 667, NULL }, { "breve", 333, NULL }, { "bar", 260, NULL }, { "fraction", 167, NULL }, { "less", 584, NULL }, { "ecaron", 556, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 278, NULL }, { "period", 278, NULL }, { "Rcaron", 722, NULL }, { "Kcommaaccent", 667, NULL }, { "greater", 584, NULL }, { "atilde", 556, NULL }, { "brokenbar", 260, NULL }, { "quoteleft", 222, NULL }, { "Edotaccent", 667, NULL }, { "onesuperior", 333, NULL } }; static BuiltinFontWidth helveticaBoldWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 389, NULL }, { "kcommaaccent", 556, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 611, NULL }, { "comma", 278, NULL }, { "cedilla", 333, NULL }, { "plusminus", 584, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 556, NULL }, { "asciitilde", 584, NULL }, { "colon", 333, NULL }, { "onehalf", 834, NULL }, { "dollar", 556, NULL }, { "Lcaron", 611, NULL }, { "ntilde", 611, NULL }, { "Aogonek", 722, NULL }, { "ncommaaccent", 611, NULL }, { "minus", 584, NULL }, { "Iogonek", 278, NULL }, { "zacute", 500, NULL }, { "yen", 556, NULL }, { "space", 278, NULL }, { "Omacron", 778, NULL }, { "questiondown", 611, NULL }, { "emdash", 1000, NULL }, { "Agrave", 722, NULL }, { "three", 556, NULL }, { "numbersign", 556, NULL }, { "lcaron", 400, NULL }, { "A", 722, NULL }, { "B", 722, NULL }, { "C", 722, NULL }, { "aogonek", 556, NULL }, { "D", 722, NULL }, { "E", 667, NULL }, { "onequarter", 834, NULL }, { "F", 611, NULL }, { "G", 778, NULL }, { "H", 722, NULL }, { "I", 278, NULL }, { "J", 556, NULL }, { "K", 722, NULL }, { "iogonek", 278, NULL }, { "backslash", 278, NULL }, { "L", 611, NULL }, { "periodcentered", 278, NULL }, { "M", 833, NULL }, { "N", 722, NULL }, { "omacron", 611, NULL }, { "Tcommaaccent", 611, NULL }, { "O", 778, NULL }, { "P", 667, NULL }, { "Q", 778, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 722, NULL }, { "Aacute", 722, NULL }, { "caron", 333, NULL }, { "S", 667, NULL }, { "T", 611, NULL }, { "U", 722, NULL }, { "agrave", 556, NULL }, { "V", 667, NULL }, { "W", 944, NULL }, { "X", 667, NULL }, { "question", 611, NULL }, { "equal", 584, NULL }, { "Y", 667, NULL }, { "Z", 611, NULL }, { "four", 556, NULL }, { "a", 556, NULL }, { "Gcommaaccent", 778, NULL }, { "b", 611, NULL }, { "c", 556, NULL }, { "d", 611, NULL }, { "e", 556, NULL }, { "f", 333, NULL }, { "g", 611, NULL }, { "bullet", 350, NULL }, { "h", 611, NULL }, { "i", 278, NULL }, { "Oslash", 778, NULL }, { "dagger", 556, NULL }, { "j", 278, NULL }, { "k", 556, NULL }, { "l", 278, NULL }, { "m", 889, NULL }, { "n", 611, NULL }, { "tcommaaccent", 333, NULL }, { "o", 611, NULL }, { "ordfeminine", 370, NULL }, { "ring", 333, NULL }, { "p", 611, NULL }, { "q", 611, NULL }, { "uhungarumlaut", 611, NULL }, { "r", 389, NULL }, { "twosuperior", 333, NULL }, { "aacute", 556, NULL }, { "s", 556, NULL }, { "OE", 1000, NULL }, { "t", 333, NULL }, { "divide", 584, NULL }, { "u", 611, NULL }, { "Ccaron", 722, NULL }, { "v", 556, NULL }, { "w", 778, NULL }, { "x", 556, NULL }, { "y", 556, NULL }, { "z", 500, NULL }, { "Gbreve", 778, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 278, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 474, NULL }, { "gcommaaccent", 611, NULL }, { "mu", 611, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 667, NULL }, { "Lslash", 611, NULL }, { "semicolon", 333, NULL }, { "oslash", 611, NULL }, { "lessequal", 549, NULL }, { "lozenge", 494, NULL }, { "parenright", 333, NULL }, { "ccaron", 556, NULL }, { "Ecircumflex", 667, NULL }, { "gbreve", 611, NULL }, { "trademark", 1000, NULL }, { "daggerdbl", 556, NULL }, { "nacute", 611, NULL }, { "macron", 333, NULL }, { "Otilde", 778, NULL }, { "Emacron", 667, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 556, NULL }, { "AE", 1000, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 278, NULL }, { "quotedblleft", 500, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 238, NULL }, { "eight", 556, NULL }, { "exclamdown", 333, NULL }, { "endash", 556, NULL }, { "oe", 944, NULL }, { "Abreve", 722, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 556, NULL }, { "Adieresis", 722, NULL }, { "copyright", 737, NULL }, { "Egrave", 667, NULL }, { "slash", 278, NULL }, { "Edieresis", 667, NULL }, { "otilde", 611, NULL }, { "Idieresis", 278, NULL }, { "parenleft", 333, NULL }, { "one", 556, NULL }, { "emacron", 556, NULL }, { "Odieresis", 778, NULL }, { "ucircumflex", 611, NULL }, { "bracketleft", 333, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 278, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 667, NULL }, { "umacron", 611, NULL }, { "abreve", 556, NULL }, { "Eacute", 667, NULL }, { "adieresis", 556, NULL }, { "egrave", 556, NULL }, { "edieresis", 556, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 889, NULL }, { "asterisk", 389, NULL }, { "odieresis", 611, NULL }, { "Uacute", 722, NULL }, { "ugrave", 611, NULL }, { "nine", 556, NULL }, { "five", 556, NULL }, { "udieresis", 611, NULL }, { "Zcaron", 611, NULL }, { "Scommaaccent", 667, NULL }, { "threequarters", 834, NULL }, { "guillemotright", 556, NULL }, { "Ccedilla", 722, NULL }, { "ydieresis", 556, NULL }, { "tilde", 333, NULL }, { "dbldaggerumlaut", 556, NULL }, { "at", 975, NULL }, { "eacute", 556, NULL }, { "underscore", 556, NULL }, { "Euro", 556, NULL }, { "Dcroat", 722, NULL }, { "multiply", 584, NULL }, { "zero", 556, NULL }, { "eth", 611, NULL }, { "Scedilla", 667, NULL }, { "Ograve", 778, NULL }, { "Racute", 722, NULL }, { "partialdiff", 494, NULL }, { "uacute", 611, NULL }, { "braceleft", 389, NULL }, { "Thorn", 667, NULL }, { "zcaron", 500, NULL }, { "scommaaccent", 556, NULL }, { "ccedilla", 556, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 611, NULL }, { "Ocircumflex", 778, NULL }, { "Oacute", 778, NULL }, { "scedilla", 556, NULL }, { "ogonek", 333, NULL }, { "ograve", 611, NULL }, { "racute", 389, NULL }, { "Tcaron", 611, NULL }, { "Eogonek", 667, NULL }, { "thorn", 611, NULL }, { "degree", 400, NULL }, { "registered", 737, NULL }, { "radical", 549, NULL }, { "Aring", 722, NULL }, { "percent", 889, NULL }, { "six", 556, NULL }, { "paragraph", 556, NULL }, { "dcaron", 743, NULL }, { "Uogonek", 722, NULL }, { "two", 556, NULL }, { "summation", 600, NULL }, { "Igrave", 278, NULL }, { "Lacute", 611, NULL }, { "ocircumflex", 611, NULL }, { "oacute", 611, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 611, NULL }, { "tcaron", 389, NULL }, { "eogonek", 556, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 778, NULL }, { "asciicircum", 584, NULL }, { "aring", 556, NULL }, { "grave", 333, NULL }, { "uogonek", 611, NULL }, { "bracketright", 333, NULL }, { "Iacute", 278, NULL }, { "ampersand", 722, NULL }, { "igrave", 278, NULL }, { "lacute", 278, NULL }, { "Ncaron", 722, NULL }, { "plus", 584, NULL }, { "uring", 611, NULL }, { "quotesinglbase", 278, NULL }, { "lcommaaccent", 278, NULL }, { "Yacute", 667, NULL }, { "ohungarumlaut", 611, NULL }, { "threesuperior", 333, NULL }, { "acute", 333, NULL }, { "section", 556, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 500, NULL }, { "ncaron", 611, NULL }, { "florin", 556, NULL }, { "yacute", 556, NULL }, { "Rcommaaccent", 722, NULL }, { "fi", 611, NULL }, { "fl", 611, NULL }, { "Acircumflex", 722, NULL }, { "Cacute", 722, NULL }, { "Icircumflex", 278, NULL }, { "guillemotleft", 556, NULL }, { "germandbls", 611, NULL }, { "Amacron", 722, NULL }, { "seven", 556, NULL }, { "Sacute", 667, NULL }, { "ordmasculine", 365, NULL }, { "dotlessi", 278, NULL }, { "sterling", 556, NULL }, { "notequal", 549, NULL }, { "Imacron", 278, NULL }, { "rcommaaccent", 389, NULL }, { "Zdotaccent", 611, NULL }, { "acircumflex", 556, NULL }, { "cacute", 556, NULL }, { "Ecaron", 667, NULL }, { "icircumflex", 278, NULL }, { "braceright", 389, NULL }, { "quotedblright", 500, NULL }, { "amacron", 556, NULL }, { "sacute", 556, NULL }, { "imacron", 278, NULL }, { "cent", 556, NULL }, { "currency", 556, NULL }, { "logicalnot", 584, NULL }, { "zdotaccent", 500, NULL }, { "Atilde", 722, NULL }, { "breve", 333, NULL }, { "bar", 280, NULL }, { "fraction", 167, NULL }, { "less", 584, NULL }, { "ecaron", 556, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 333, NULL }, { "period", 278, NULL }, { "Rcaron", 722, NULL }, { "Kcommaaccent", 722, NULL }, { "greater", 584, NULL }, { "atilde", 556, NULL }, { "brokenbar", 280, NULL }, { "quoteleft", 278, NULL }, { "Edotaccent", 667, NULL }, { "onesuperior", 333, NULL } }; static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 389, NULL }, { "kcommaaccent", 556, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 611, NULL }, { "comma", 278, NULL }, { "cedilla", 333, NULL }, { "plusminus", 584, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 556, NULL }, { "asciitilde", 584, NULL }, { "colon", 333, NULL }, { "onehalf", 834, NULL }, { "dollar", 556, NULL }, { "Lcaron", 611, NULL }, { "ntilde", 611, NULL }, { "Aogonek", 722, NULL }, { "ncommaaccent", 611, NULL }, { "minus", 584, NULL }, { "Iogonek", 278, NULL }, { "zacute", 500, NULL }, { "yen", 556, NULL }, { "space", 278, NULL }, { "Omacron", 778, NULL }, { "questiondown", 611, NULL }, { "emdash", 1000, NULL }, { "Agrave", 722, NULL }, { "three", 556, NULL }, { "numbersign", 556, NULL }, { "lcaron", 400, NULL }, { "A", 722, NULL }, { "B", 722, NULL }, { "C", 722, NULL }, { "aogonek", 556, NULL }, { "D", 722, NULL }, { "E", 667, NULL }, { "onequarter", 834, NULL }, { "F", 611, NULL }, { "G", 778, NULL }, { "H", 722, NULL }, { "I", 278, NULL }, { "J", 556, NULL }, { "K", 722, NULL }, { "iogonek", 278, NULL }, { "backslash", 278, NULL }, { "L", 611, NULL }, { "periodcentered", 278, NULL }, { "M", 833, NULL }, { "N", 722, NULL }, { "omacron", 611, NULL }, { "Tcommaaccent", 611, NULL }, { "O", 778, NULL }, { "P", 667, NULL }, { "Q", 778, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 722, NULL }, { "Aacute", 722, NULL }, { "caron", 333, NULL }, { "S", 667, NULL }, { "T", 611, NULL }, { "U", 722, NULL }, { "agrave", 556, NULL }, { "V", 667, NULL }, { "W", 944, NULL }, { "X", 667, NULL }, { "question", 611, NULL }, { "equal", 584, NULL }, { "Y", 667, NULL }, { "Z", 611, NULL }, { "four", 556, NULL }, { "a", 556, NULL }, { "Gcommaaccent", 778, NULL }, { "b", 611, NULL }, { "c", 556, NULL }, { "d", 611, NULL }, { "e", 556, NULL }, { "f", 333, NULL }, { "g", 611, NULL }, { "bullet", 350, NULL }, { "h", 611, NULL }, { "i", 278, NULL }, { "Oslash", 778, NULL }, { "dagger", 556, NULL }, { "j", 278, NULL }, { "k", 556, NULL }, { "l", 278, NULL }, { "m", 889, NULL }, { "n", 611, NULL }, { "tcommaaccent", 333, NULL }, { "o", 611, NULL }, { "ordfeminine", 370, NULL }, { "ring", 333, NULL }, { "p", 611, NULL }, { "q", 611, NULL }, { "uhungarumlaut", 611, NULL }, { "r", 389, NULL }, { "twosuperior", 333, NULL }, { "aacute", 556, NULL }, { "s", 556, NULL }, { "OE", 1000, NULL }, { "t", 333, NULL }, { "divide", 584, NULL }, { "u", 611, NULL }, { "Ccaron", 722, NULL }, { "v", 556, NULL }, { "w", 778, NULL }, { "x", 556, NULL }, { "y", 556, NULL }, { "z", 500, NULL }, { "Gbreve", 778, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 278, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 474, NULL }, { "gcommaaccent", 611, NULL }, { "mu", 611, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 667, NULL }, { "Lslash", 611, NULL }, { "semicolon", 333, NULL }, { "oslash", 611, NULL }, { "lessequal", 549, NULL }, { "lozenge", 494, NULL }, { "parenright", 333, NULL }, { "ccaron", 556, NULL }, { "Ecircumflex", 667, NULL }, { "gbreve", 611, NULL }, { "trademark", 1000, NULL }, { "daggerdbl", 556, NULL }, { "nacute", 611, NULL }, { "macron", 333, NULL }, { "Otilde", 778, NULL }, { "Emacron", 667, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 556, NULL }, { "AE", 1000, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 278, NULL }, { "quotedblleft", 500, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 238, NULL }, { "eight", 556, NULL }, { "exclamdown", 333, NULL }, { "endash", 556, NULL }, { "oe", 944, NULL }, { "Abreve", 722, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 556, NULL }, { "Adieresis", 722, NULL }, { "copyright", 737, NULL }, { "Egrave", 667, NULL }, { "slash", 278, NULL }, { "Edieresis", 667, NULL }, { "otilde", 611, NULL }, { "Idieresis", 278, NULL }, { "parenleft", 333, NULL }, { "one", 556, NULL }, { "emacron", 556, NULL }, { "Odieresis", 778, NULL }, { "ucircumflex", 611, NULL }, { "bracketleft", 333, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 278, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 667, NULL }, { "umacron", 611, NULL }, { "abreve", 556, NULL }, { "Eacute", 667, NULL }, { "adieresis", 556, NULL }, { "egrave", 556, NULL }, { "edieresis", 556, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 889, NULL }, { "asterisk", 389, NULL }, { "odieresis", 611, NULL }, { "Uacute", 722, NULL }, { "ugrave", 611, NULL }, { "nine", 556, NULL }, { "five", 556, NULL }, { "udieresis", 611, NULL }, { "Zcaron", 611, NULL }, { "Scommaaccent", 667, NULL }, { "threequarters", 834, NULL }, { "guillemotright", 556, NULL }, { "Ccedilla", 722, NULL }, { "ydieresis", 556, NULL }, { "tilde", 333, NULL }, { "at", 975, NULL }, { "eacute", 556, NULL }, { "underscore", 556, NULL }, { "Euro", 556, NULL }, { "Dcroat", 722, NULL }, { "multiply", 584, NULL }, { "zero", 556, NULL }, { "eth", 611, NULL }, { "Scedilla", 667, NULL }, { "Ograve", 778, NULL }, { "Racute", 722, NULL }, { "partialdiff", 494, NULL }, { "uacute", 611, NULL }, { "braceleft", 389, NULL }, { "Thorn", 667, NULL }, { "zcaron", 500, NULL }, { "scommaaccent", 556, NULL }, { "ccedilla", 556, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 611, NULL }, { "Ocircumflex", 778, NULL }, { "Oacute", 778, NULL }, { "scedilla", 556, NULL }, { "ogonek", 333, NULL }, { "ograve", 611, NULL }, { "racute", 389, NULL }, { "Tcaron", 611, NULL }, { "Eogonek", 667, NULL }, { "thorn", 611, NULL }, { "degree", 400, NULL }, { "registered", 737, NULL }, { "radical", 549, NULL }, { "Aring", 722, NULL }, { "percent", 889, NULL }, { "six", 556, NULL }, { "paragraph", 556, NULL }, { "dcaron", 743, NULL }, { "Uogonek", 722, NULL }, { "two", 556, NULL }, { "summation", 600, NULL }, { "Igrave", 278, NULL }, { "Lacute", 611, NULL }, { "ocircumflex", 611, NULL }, { "oacute", 611, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 611, NULL }, { "tcaron", 389, NULL }, { "eogonek", 556, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 778, NULL }, { "asciicircum", 584, NULL }, { "aring", 556, NULL }, { "grave", 333, NULL }, { "uogonek", 611, NULL }, { "bracketright", 333, NULL }, { "Iacute", 278, NULL }, { "ampersand", 722, NULL }, { "igrave", 278, NULL }, { "lacute", 278, NULL }, { "Ncaron", 722, NULL }, { "plus", 584, NULL }, { "uring", 611, NULL }, { "quotesinglbase", 278, NULL }, { "lcommaaccent", 278, NULL }, { "Yacute", 667, NULL }, { "ohungarumlaut", 611, NULL }, { "threesuperior", 333, NULL }, { "acute", 333, NULL }, { "section", 556, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 500, NULL }, { "ncaron", 611, NULL }, { "florin", 556, NULL }, { "yacute", 556, NULL }, { "Rcommaaccent", 722, NULL }, { "fi", 611, NULL }, { "fl", 611, NULL }, { "Acircumflex", 722, NULL }, { "Cacute", 722, NULL }, { "Icircumflex", 278, NULL }, { "guillemotleft", 556, NULL }, { "germandbls", 611, NULL }, { "Amacron", 722, NULL }, { "seven", 556, NULL }, { "Sacute", 667, NULL }, { "ordmasculine", 365, NULL }, { "dotlessi", 278, NULL }, { "sterling", 556, NULL }, { "notequal", 549, NULL }, { "Imacron", 278, NULL }, { "rcommaaccent", 389, NULL }, { "Zdotaccent", 611, NULL }, { "acircumflex", 556, NULL }, { "cacute", 556, NULL }, { "Ecaron", 667, NULL }, { "icircumflex", 278, NULL }, { "braceright", 389, NULL }, { "quotedblright", 500, NULL }, { "amacron", 556, NULL }, { "sacute", 556, NULL }, { "imacron", 278, NULL }, { "cent", 556, NULL }, { "currency", 556, NULL }, { "logicalnot", 584, NULL }, { "zdotaccent", 500, NULL }, { "Atilde", 722, NULL }, { "breve", 333, NULL }, { "bar", 280, NULL }, { "fraction", 167, NULL }, { "less", 584, NULL }, { "ecaron", 556, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 333, NULL }, { "period", 278, NULL }, { "Rcaron", 722, NULL }, { "Kcommaaccent", 722, NULL }, { "greater", 584, NULL }, { "atilde", 556, NULL }, { "brokenbar", 280, NULL }, { "quoteleft", 278, NULL }, { "Edotaccent", 667, NULL }, { "onesuperior", 333, NULL } }; static BuiltinFontWidth helveticaObliqueWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 333, NULL }, { "kcommaaccent", 500, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 611, NULL }, { "comma", 278, NULL }, { "cedilla", 333, NULL }, { "plusminus", 584, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 556, NULL }, { "asciitilde", 584, NULL }, { "colon", 278, NULL }, { "onehalf", 834, NULL }, { "dollar", 556, NULL }, { "Lcaron", 556, NULL }, { "ntilde", 556, NULL }, { "Aogonek", 667, NULL }, { "ncommaaccent", 556, NULL }, { "minus", 584, NULL }, { "Iogonek", 278, NULL }, { "zacute", 500, NULL }, { "yen", 556, NULL }, { "space", 278, NULL }, { "Omacron", 778, NULL }, { "questiondown", 611, NULL }, { "emdash", 1000, NULL }, { "Agrave", 667, NULL }, { "three", 556, NULL }, { "numbersign", 556, NULL }, { "lcaron", 299, NULL }, { "A", 667, NULL }, { "B", 667, NULL }, { "C", 722, NULL }, { "aogonek", 556, NULL }, { "D", 722, NULL }, { "E", 667, NULL }, { "onequarter", 834, NULL }, { "F", 611, NULL }, { "G", 778, NULL }, { "H", 722, NULL }, { "I", 278, NULL }, { "J", 500, NULL }, { "K", 667, NULL }, { "iogonek", 222, NULL }, { "backslash", 278, NULL }, { "L", 556, NULL }, { "periodcentered", 278, NULL }, { "M", 833, NULL }, { "N", 722, NULL }, { "omacron", 556, NULL }, { "Tcommaaccent", 611, NULL }, { "O", 778, NULL }, { "P", 667, NULL }, { "Q", 778, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 722, NULL }, { "Aacute", 667, NULL }, { "caron", 333, NULL }, { "S", 667, NULL }, { "T", 611, NULL }, { "U", 722, NULL }, { "agrave", 556, NULL }, { "V", 667, NULL }, { "W", 944, NULL }, { "X", 667, NULL }, { "question", 556, NULL }, { "equal", 584, NULL }, { "Y", 667, NULL }, { "Z", 611, NULL }, { "four", 556, NULL }, { "a", 556, NULL }, { "Gcommaaccent", 778, NULL }, { "b", 556, NULL }, { "c", 500, NULL }, { "d", 556, NULL }, { "e", 556, NULL }, { "f", 278, NULL }, { "g", 556, NULL }, { "bullet", 350, NULL }, { "h", 556, NULL }, { "i", 222, NULL }, { "Oslash", 778, NULL }, { "dagger", 556, NULL }, { "j", 222, NULL }, { "k", 500, NULL }, { "l", 222, NULL }, { "m", 833, NULL }, { "n", 556, NULL }, { "tcommaaccent", 278, NULL }, { "o", 556, NULL }, { "ordfeminine", 370, NULL }, { "ring", 333, NULL }, { "p", 556, NULL }, { "q", 556, NULL }, { "uhungarumlaut", 556, NULL }, { "r", 333, NULL }, { "twosuperior", 333, NULL }, { "aacute", 556, NULL }, { "s", 500, NULL }, { "OE", 1000, NULL }, { "t", 278, NULL }, { "divide", 584, NULL }, { "u", 556, NULL }, { "Ccaron", 722, NULL }, { "v", 500, NULL }, { "w", 722, NULL }, { "x", 500, NULL }, { "y", 500, NULL }, { "z", 500, NULL }, { "Gbreve", 778, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 278, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 355, NULL }, { "gcommaaccent", 556, NULL }, { "mu", 556, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 667, NULL }, { "Lslash", 556, NULL }, { "semicolon", 278, NULL }, { "oslash", 611, NULL }, { "lessequal", 549, NULL }, { "lozenge", 471, NULL }, { "parenright", 333, NULL }, { "ccaron", 500, NULL }, { "Ecircumflex", 667, NULL }, { "gbreve", 556, NULL }, { "trademark", 1000, NULL }, { "daggerdbl", 556, NULL }, { "nacute", 556, NULL }, { "macron", 333, NULL }, { "Otilde", 778, NULL }, { "Emacron", 667, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 500, NULL }, { "AE", 1000, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 222, NULL }, { "quotedblleft", 333, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 191, NULL }, { "eight", 556, NULL }, { "exclamdown", 333, NULL }, { "endash", 556, NULL }, { "oe", 944, NULL }, { "Abreve", 667, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 556, NULL }, { "Adieresis", 667, NULL }, { "copyright", 737, NULL }, { "Egrave", 667, NULL }, { "slash", 278, NULL }, { "Edieresis", 667, NULL }, { "otilde", 556, NULL }, { "Idieresis", 278, NULL }, { "parenleft", 333, NULL }, { "one", 556, NULL }, { "emacron", 556, NULL }, { "Odieresis", 778, NULL }, { "ucircumflex", 556, NULL }, { "bracketleft", 278, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 222, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 667, NULL }, { "umacron", 556, NULL }, { "abreve", 556, NULL }, { "Eacute", 667, NULL }, { "adieresis", 556, NULL }, { "egrave", 556, NULL }, { "edieresis", 556, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 889, NULL }, { "asterisk", 389, NULL }, { "odieresis", 556, NULL }, { "Uacute", 722, NULL }, { "ugrave", 556, NULL }, { "nine", 556, NULL }, { "five", 556, NULL }, { "udieresis", 556, NULL }, { "Zcaron", 611, NULL }, { "Scommaaccent", 667, NULL }, { "threequarters", 834, NULL }, { "guillemotright", 556, NULL }, { "Ccedilla", 722, NULL }, { "ydieresis", 500, NULL }, { "tilde", 333, NULL }, { "at", 1015, NULL }, { "eacute", 556, NULL }, { "underscore", 556, NULL }, { "Euro", 556, NULL }, { "Dcroat", 722, NULL }, { "multiply", 584, NULL }, { "zero", 556, NULL }, { "eth", 556, NULL }, { "Scedilla", 667, NULL }, { "Ograve", 778, NULL }, { "Racute", 722, NULL }, { "partialdiff", 476, NULL }, { "uacute", 556, NULL }, { "braceleft", 334, NULL }, { "Thorn", 667, NULL }, { "zcaron", 500, NULL }, { "scommaaccent", 500, NULL }, { "ccedilla", 500, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 556, NULL }, { "Ocircumflex", 778, NULL }, { "Oacute", 778, NULL }, { "scedilla", 500, NULL }, { "ogonek", 333, NULL }, { "ograve", 556, NULL }, { "racute", 333, NULL }, { "Tcaron", 611, NULL }, { "Eogonek", 667, NULL }, { "thorn", 556, NULL }, { "degree", 400, NULL }, { "registered", 737, NULL }, { "radical", 453, NULL }, { "Aring", 667, NULL }, { "percent", 889, NULL }, { "six", 556, NULL }, { "paragraph", 537, NULL }, { "dcaron", 643, NULL }, { "Uogonek", 722, NULL }, { "two", 556, NULL }, { "summation", 600, NULL }, { "Igrave", 278, NULL }, { "Lacute", 556, NULL }, { "ocircumflex", 556, NULL }, { "oacute", 556, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 556, NULL }, { "tcaron", 317, NULL }, { "eogonek", 556, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 778, NULL }, { "asciicircum", 469, NULL }, { "aring", 556, NULL }, { "grave", 333, NULL }, { "uogonek", 556, NULL }, { "bracketright", 278, NULL }, { "Iacute", 278, NULL }, { "ampersand", 667, NULL }, { "igrave", 278, NULL }, { "lacute", 222, NULL }, { "Ncaron", 722, NULL }, { "plus", 584, NULL }, { "uring", 556, NULL }, { "quotesinglbase", 222, NULL }, { "lcommaaccent", 222, NULL }, { "Yacute", 667, NULL }, { "ohungarumlaut", 556, NULL }, { "threesuperior", 333, NULL }, { "acute", 333, NULL }, { "section", 556, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 333, NULL }, { "ncaron", 556, NULL }, { "florin", 556, NULL }, { "yacute", 500, NULL }, { "Rcommaaccent", 722, NULL }, { "fi", 500, NULL }, { "fl", 500, NULL }, { "Acircumflex", 667, NULL }, { "Cacute", 722, NULL }, { "Icircumflex", 278, NULL }, { "guillemotleft", 556, NULL }, { "germandbls", 611, NULL }, { "Amacron", 667, NULL }, { "seven", 556, NULL }, { "Sacute", 667, NULL }, { "ordmasculine", 365, NULL }, { "dotlessi", 278, NULL }, { "sterling", 556, NULL }, { "notequal", 549, NULL }, { "Imacron", 278, NULL }, { "rcommaaccent", 333, NULL }, { "Zdotaccent", 611, NULL }, { "acircumflex", 556, NULL }, { "cacute", 500, NULL }, { "Ecaron", 667, NULL }, { "icircumflex", 278, NULL }, { "braceright", 334, NULL }, { "quotedblright", 333, NULL }, { "amacron", 556, NULL }, { "sacute", 500, NULL }, { "imacron", 278, NULL }, { "cent", 556, NULL }, { "currency", 556, NULL }, { "logicalnot", 584, NULL }, { "zdotaccent", 500, NULL }, { "Atilde", 667, NULL }, { "breve", 333, NULL }, { "bar", 260, NULL }, { "fraction", 167, NULL }, { "less", 584, NULL }, { "ecaron", 556, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 278, NULL }, { "period", 278, NULL }, { "Rcaron", 722, NULL }, { "Kcommaaccent", 667, NULL }, { "greater", 584, NULL }, { "atilde", 556, NULL }, { "brokenbar", 260, NULL }, { "quoteleft", 222, NULL }, { "Edotaccent", 667, NULL }, { "onesuperior", 333, NULL } }; static BuiltinFontWidth symbolWidthsTab[] = { { "bracketleftex", 384, NULL }, { "alpha", 631, NULL }, { "union", 768, NULL }, { "infinity", 713, NULL }, { "comma", 250, NULL }, { "copyrightsans", 790, NULL }, { "plusminus", 549, NULL }, { "arrowup", 603, NULL }, { "apple", 790, NULL }, { "parenleftbt", 384, NULL }, { "notelement", 713, NULL }, { "colon", 278, NULL }, { "beta", 549, NULL }, { "braceleftbt", 494, NULL }, { "Lambda", 686, NULL }, { "Phi", 763, NULL }, { "minus", 549, NULL }, { "space", 250, NULL }, { "Sigma", 592, NULL }, { "approxequal", 549, NULL }, { "minute", 247, NULL }, { "circleplus", 768, NULL }, { "Omicron", 722, NULL }, { "three", 500, NULL }, { "numbersign", 500, NULL }, { "lambda", 549, NULL }, { "phi", 521, NULL }, { "aleph", 823, NULL }, { "Tau", 611, NULL }, { "spade", 753, NULL }, { "logicaland", 603, NULL }, { "sigma", 603, NULL }, { "propersuperset", 713, NULL }, { "omicron", 549, NULL }, { "question", 444, NULL }, { "equal", 549, NULL }, { "Epsilon", 611, NULL }, { "emptyset", 823, NULL }, { "diamond", 753, NULL }, { "four", 500, NULL }, { "Mu", 889, NULL }, { "parenlefttp", 384, NULL }, { "club", 753, NULL }, { "bullet", 460, NULL }, { "Omega", 768, NULL }, { "tau", 439, NULL }, { "Upsilon", 690, NULL }, { "bracelefttp", 494, NULL }, { "heart", 753, NULL }, { "divide", 549, NULL }, { "epsilon", 439, NULL }, { "logicalor", 603, NULL }, { "parenleftex", 384, NULL }, { "greaterequal", 549, NULL }, { "mu", 576, NULL }, { "Nu", 722, NULL }, { "therefore", 863, NULL }, { "notsubset", 713, NULL }, { "omega", 686, NULL }, { "semicolon", 278, NULL }, { "element", 713, NULL }, { "upsilon", 576, NULL }, { "existential", 549, NULL }, { "integralbt", 686, NULL }, { "lessequal", 549, NULL }, { "phi1", 603, NULL }, { "lozenge", 494, NULL }, { "trademarkserif", 890, NULL }, { "parenright", 333, NULL }, { "reflexsuperset", 713, NULL }, { "sigma1", 439, NULL }, { "nu", 521, NULL }, { "Gamma", 603, NULL }, { "angleright", 329, NULL }, { "ellipsis", 1000, NULL }, { "Rho", 556, NULL }, { "parenrightbt", 384, NULL }, { "radicalex", 500, NULL }, { "eight", 500, NULL }, { "angleleft", 329, NULL }, { "arrowdbldown", 603, NULL }, { "congruent", 549, NULL }, { "Theta", 741, NULL }, { "intersection", 768, NULL }, { "Pi", 768, NULL }, { "slash", 278, NULL }, { "registerserif", 790, NULL }, { "parenleft", 333, NULL }, { "one", 500, NULL }, { "gamma", 411, NULL }, { "bracketleft", 333, NULL }, { "rho", 549, NULL }, { "circlemultiply", 768, NULL }, { "Chi", 722, NULL }, { "theta", 521, NULL }, { "pi", 549, NULL }, { "integraltp", 686, NULL }, { "Eta", 722, NULL }, { "product", 823, NULL }, { "nine", 500, NULL }, { "five", 500, NULL }, { "propersubset", 713, NULL }, { "bracketrightbt", 384, NULL }, { "trademarksans", 786, NULL }, { "dotmath", 250, NULL }, { "integralex", 686, NULL }, { "chi", 549, NULL }, { "parenrighttp", 384, NULL }, { "eta", 603, NULL }, { "underscore", 500, NULL }, { "Euro", 750, NULL }, { "multiply", 549, NULL }, { "zero", 500, NULL }, { "partialdiff", 494, NULL }, { "angle", 768, NULL }, { "arrowdblleft", 987, NULL }, { "braceleft", 480, NULL }, { "parenrightex", 384, NULL }, { "Rfraktur", 795, NULL }, { "Zeta", 611, NULL }, { "braceex", 494, NULL }, { "arrowdblup", 603, NULL }, { "arrowdown", 603, NULL }, { "Ifraktur", 686, NULL }, { "degree", 400, NULL }, { "Iota", 333, NULL }, { "perpendicular", 658, NULL }, { "radical", 549, NULL }, { "asteriskmath", 500, NULL }, { "percent", 833, NULL }, { "zeta", 494, NULL }, { "six", 500, NULL }, { "two", 500, NULL }, { "weierstrass", 987, NULL }, { "summation", 713, NULL }, { "bracketrighttp", 384, NULL }, { "carriagereturn", 658, NULL }, { "suchthat", 439, NULL }, { "arrowvertex", 603, NULL }, { "Delta", 612, NULL }, { "iota", 329, NULL }, { "arrowhorizex", 1000, NULL }, { "bracketrightex", 384, NULL }, { "bracketright", 333, NULL }, { "ampersand", 778, NULL }, { "plus", 549, NULL }, { "proportional", 713, NULL }, { "delta", 494, NULL }, { "copyrightserif", 790, NULL }, { "bracerightmid", 494, NULL }, { "arrowleft", 987, NULL }, { "second", 411, NULL }, { "arrowdblboth", 1042, NULL }, { "florin", 500, NULL }, { "Psi", 795, NULL }, { "bracerightbt", 494, NULL }, { "bracketleftbt", 384, NULL }, { "seven", 500, NULL }, { "braceleftmid", 494, NULL }, { "notequal", 549, NULL }, { "psi", 686, NULL }, { "equivalence", 549, NULL }, { "universal", 713, NULL }, { "arrowdblright", 987, NULL }, { "braceright", 480, NULL }, { "reflexsubset", 713, NULL }, { "Xi", 645, NULL }, { "theta1", 631, NULL }, { "logicalnot", 713, NULL }, { "Kappa", 722, NULL }, { "similar", 549, NULL }, { "bar", 200, NULL }, { "fraction", 167, NULL }, { "less", 549, NULL }, { "registersans", 790, NULL }, { "omega1", 713, NULL }, { "exclam", 333, NULL }, { "Upsilon1", 620, NULL }, { "bracerighttp", 494, NULL }, { "xi", 493, NULL }, { "period", 250, NULL }, { "Alpha", 722, NULL }, { "arrowright", 987, NULL }, { "greater", 549, NULL }, { "bracketlefttp", 384, NULL }, { "kappa", 549, NULL }, { "gradient", 713, NULL }, { "integral", 274, NULL }, { "arrowboth", 1042, NULL }, { "Beta", 667, NULL } }; static BuiltinFontWidth timesBoldWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 444, NULL }, { "kcommaaccent", 556, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 667, NULL }, { "comma", 250, NULL }, { "cedilla", 333, NULL }, { "plusminus", 570, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 444, NULL }, { "asciitilde", 520, NULL }, { "colon", 333, NULL }, { "onehalf", 750, NULL }, { "dollar", 500, NULL }, { "Lcaron", 667, NULL }, { "ntilde", 556, NULL }, { "Aogonek", 722, NULL }, { "ncommaaccent", 556, NULL }, { "minus", 570, NULL }, { "Iogonek", 389, NULL }, { "zacute", 444, NULL }, { "yen", 500, NULL }, { "space", 250, NULL }, { "Omacron", 778, NULL }, { "questiondown", 500, NULL }, { "emdash", 1000, NULL }, { "Agrave", 722, NULL }, { "three", 500, NULL }, { "numbersign", 500, NULL }, { "lcaron", 394, NULL }, { "A", 722, NULL }, { "B", 667, NULL }, { "C", 722, NULL }, { "aogonek", 500, NULL }, { "D", 722, NULL }, { "E", 667, NULL }, { "onequarter", 750, NULL }, { "F", 611, NULL }, { "G", 778, NULL }, { "H", 778, NULL }, { "I", 389, NULL }, { "J", 500, NULL }, { "K", 778, NULL }, { "iogonek", 278, NULL }, { "backslash", 278, NULL }, { "L", 667, NULL }, { "periodcentered", 250, NULL }, { "M", 944, NULL }, { "N", 722, NULL }, { "omacron", 500, NULL }, { "Tcommaaccent", 667, NULL }, { "O", 778, NULL }, { "P", 611, NULL }, { "Q", 778, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 722, NULL }, { "Aacute", 722, NULL }, { "caron", 333, NULL }, { "S", 556, NULL }, { "T", 667, NULL }, { "U", 722, NULL }, { "agrave", 500, NULL }, { "V", 722, NULL }, { "W", 1000, NULL }, { "X", 722, NULL }, { "question", 500, NULL }, { "equal", 570, NULL }, { "Y", 722, NULL }, { "Z", 667, NULL }, { "four", 500, NULL }, { "a", 500, NULL }, { "Gcommaaccent", 778, NULL }, { "b", 556, NULL }, { "c", 444, NULL }, { "d", 556, NULL }, { "e", 444, NULL }, { "f", 333, NULL }, { "g", 500, NULL }, { "bullet", 350, NULL }, { "h", 556, NULL }, { "i", 278, NULL }, { "Oslash", 778, NULL }, { "dagger", 500, NULL }, { "j", 333, NULL }, { "k", 556, NULL }, { "l", 278, NULL }, { "m", 833, NULL }, { "n", 556, NULL }, { "tcommaaccent", 333, NULL }, { "o", 500, NULL }, { "ordfeminine", 300, NULL }, { "ring", 333, NULL }, { "p", 556, NULL }, { "q", 556, NULL }, { "uhungarumlaut", 556, NULL }, { "r", 444, NULL }, { "twosuperior", 300, NULL }, { "aacute", 500, NULL }, { "s", 389, NULL }, { "OE", 1000, NULL }, { "t", 333, NULL }, { "divide", 570, NULL }, { "u", 556, NULL }, { "Ccaron", 722, NULL }, { "v", 500, NULL }, { "w", 722, NULL }, { "x", 500, NULL }, { "y", 500, NULL }, { "z", 444, NULL }, { "Gbreve", 778, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 389, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 555, NULL }, { "gcommaaccent", 500, NULL }, { "mu", 556, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 556, NULL }, { "Lslash", 667, NULL }, { "semicolon", 333, NULL }, { "oslash", 500, NULL }, { "lessequal", 549, NULL }, { "lozenge", 494, NULL }, { "parenright", 333, NULL }, { "ccaron", 444, NULL }, { "Ecircumflex", 667, NULL }, { "gbreve", 500, NULL }, { "trademark", 1000, NULL }, { "daggerdbl", 500, NULL }, { "nacute", 556, NULL }, { "macron", 333, NULL }, { "Otilde", 778, NULL }, { "Emacron", 667, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 389, NULL }, { "AE", 1000, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 278, NULL }, { "quotedblleft", 500, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 278, NULL }, { "eight", 500, NULL }, { "exclamdown", 333, NULL }, { "endash", 500, NULL }, { "oe", 722, NULL }, { "Abreve", 722, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 444, NULL }, { "Adieresis", 722, NULL }, { "copyright", 747, NULL }, { "Egrave", 667, NULL }, { "slash", 278, NULL }, { "Edieresis", 667, NULL }, { "otilde", 500, NULL }, { "Idieresis", 389, NULL }, { "parenleft", 333, NULL }, { "one", 500, NULL }, { "emacron", 444, NULL }, { "Odieresis", 778, NULL }, { "ucircumflex", 556, NULL }, { "bracketleft", 333, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 333, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 722, NULL }, { "umacron", 556, NULL }, { "abreve", 500, NULL }, { "Eacute", 667, NULL }, { "adieresis", 500, NULL }, { "egrave", 444, NULL }, { "edieresis", 444, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 722, NULL }, { "asterisk", 500, NULL }, { "odieresis", 500, NULL }, { "Uacute", 722, NULL }, { "ugrave", 556, NULL }, { "nine", 500, NULL }, { "five", 500, NULL }, { "udieresis", 556, NULL }, { "Zcaron", 667, NULL }, { "Scommaaccent", 556, NULL }, { "threequarters", 750, NULL }, { "guillemotright", 500, NULL }, { "Ccedilla", 722, NULL }, { "ydieresis", 500, NULL }, { "tilde", 333, NULL }, { "at", 930, NULL }, { "eacute", 444, NULL }, { "underscore", 500, NULL }, { "Euro", 500, NULL }, { "Dcroat", 722, NULL }, { "multiply", 570, NULL }, { "zero", 500, NULL }, { "eth", 500, NULL }, { "Scedilla", 556, NULL }, { "Ograve", 778, NULL }, { "Racute", 722, NULL }, { "partialdiff", 494, NULL }, { "uacute", 556, NULL }, { "braceleft", 394, NULL }, { "Thorn", 611, NULL }, { "zcaron", 444, NULL }, { "scommaaccent", 389, NULL }, { "ccedilla", 444, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 556, NULL }, { "Ocircumflex", 778, NULL }, { "Oacute", 778, NULL }, { "scedilla", 389, NULL }, { "ogonek", 333, NULL }, { "ograve", 500, NULL }, { "racute", 444, NULL }, { "Tcaron", 667, NULL }, { "Eogonek", 667, NULL }, { "thorn", 556, NULL }, { "degree", 400, NULL }, { "registered", 747, NULL }, { "radical", 549, NULL }, { "Aring", 722, NULL }, { "percent", 1000, NULL }, { "six", 500, NULL }, { "paragraph", 540, NULL }, { "dcaron", 672, NULL }, { "Uogonek", 722, NULL }, { "two", 500, NULL }, { "summation", 600, NULL }, { "Igrave", 389, NULL }, { "Lacute", 667, NULL }, { "ocircumflex", 500, NULL }, { "oacute", 500, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 667, NULL }, { "tcaron", 416, NULL }, { "eogonek", 444, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 778, NULL }, { "asciicircum", 581, NULL }, { "aring", 500, NULL }, { "grave", 333, NULL }, { "uogonek", 556, NULL }, { "bracketright", 333, NULL }, { "Iacute", 389, NULL }, { "ampersand", 833, NULL }, { "igrave", 278, NULL }, { "lacute", 278, NULL }, { "Ncaron", 722, NULL }, { "plus", 570, NULL }, { "uring", 556, NULL }, { "quotesinglbase", 333, NULL }, { "lcommaaccent", 278, NULL }, { "Yacute", 722, NULL }, { "ohungarumlaut", 500, NULL }, { "threesuperior", 300, NULL }, { "acute", 333, NULL }, { "section", 500, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 500, NULL }, { "ncaron", 556, NULL }, { "florin", 500, NULL }, { "yacute", 500, NULL }, { "Rcommaaccent", 722, NULL }, { "fi", 556, NULL }, { "fl", 556, NULL }, { "Acircumflex", 722, NULL }, { "Cacute", 722, NULL }, { "Icircumflex", 389, NULL }, { "guillemotleft", 500, NULL }, { "germandbls", 556, NULL }, { "Amacron", 722, NULL }, { "seven", 500, NULL }, { "Sacute", 556, NULL }, { "ordmasculine", 330, NULL }, { "dotlessi", 278, NULL }, { "sterling", 500, NULL }, { "notequal", 549, NULL }, { "Imacron", 389, NULL }, { "rcommaaccent", 444, NULL }, { "Zdotaccent", 667, NULL }, { "acircumflex", 500, NULL }, { "cacute", 444, NULL }, { "Ecaron", 667, NULL }, { "icircumflex", 278, NULL }, { "braceright", 394, NULL }, { "quotedblright", 500, NULL }, { "amacron", 500, NULL }, { "sacute", 389, NULL }, { "imacron", 278, NULL }, { "cent", 500, NULL }, { "currency", 500, NULL }, { "logicalnot", 570, NULL }, { "zdotaccent", 444, NULL }, { "Atilde", 722, NULL }, { "breve", 333, NULL }, { "bar", 220, NULL }, { "fraction", 167, NULL }, { "less", 570, NULL }, { "ecaron", 444, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 333, NULL }, { "period", 250, NULL }, { "Rcaron", 722, NULL }, { "Kcommaaccent", 778, NULL }, { "greater", 570, NULL }, { "atilde", 500, NULL }, { "brokenbar", 220, NULL }, { "quoteleft", 333, NULL }, { "Edotaccent", 667, NULL }, { "onesuperior", 300, NULL } }; static BuiltinFontWidth timesBoldItalicWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 389, NULL }, { "kcommaaccent", 500, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 611, NULL }, { "comma", 250, NULL }, { "cedilla", 333, NULL }, { "plusminus", 570, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 444, NULL }, { "asciitilde", 570, NULL }, { "colon", 333, NULL }, { "onehalf", 750, NULL }, { "dollar", 500, NULL }, { "Lcaron", 611, NULL }, { "ntilde", 556, NULL }, { "Aogonek", 667, NULL }, { "ncommaaccent", 556, NULL }, { "minus", 606, NULL }, { "Iogonek", 389, NULL }, { "zacute", 389, NULL }, { "yen", 500, NULL }, { "space", 250, NULL }, { "Omacron", 722, NULL }, { "questiondown", 500, NULL }, { "emdash", 1000, NULL }, { "Agrave", 667, NULL }, { "three", 500, NULL }, { "numbersign", 500, NULL }, { "lcaron", 382, NULL }, { "A", 667, NULL }, { "B", 667, NULL }, { "C", 667, NULL }, { "aogonek", 500, NULL }, { "D", 722, NULL }, { "E", 667, NULL }, { "onequarter", 750, NULL }, { "F", 667, NULL }, { "G", 722, NULL }, { "H", 778, NULL }, { "I", 389, NULL }, { "J", 500, NULL }, { "K", 667, NULL }, { "iogonek", 278, NULL }, { "backslash", 278, NULL }, { "L", 611, NULL }, { "periodcentered", 250, NULL }, { "M", 889, NULL }, { "N", 722, NULL }, { "omacron", 500, NULL }, { "Tcommaaccent", 611, NULL }, { "O", 722, NULL }, { "P", 611, NULL }, { "Q", 722, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 667, NULL }, { "Aacute", 667, NULL }, { "caron", 333, NULL }, { "S", 556, NULL }, { "T", 611, NULL }, { "U", 722, NULL }, { "agrave", 500, NULL }, { "V", 667, NULL }, { "W", 889, NULL }, { "X", 667, NULL }, { "question", 500, NULL }, { "equal", 570, NULL }, { "Y", 611, NULL }, { "Z", 611, NULL }, { "four", 500, NULL }, { "a", 500, NULL }, { "Gcommaaccent", 722, NULL }, { "b", 500, NULL }, { "c", 444, NULL }, { "d", 500, NULL }, { "e", 444, NULL }, { "f", 333, NULL }, { "g", 500, NULL }, { "bullet", 350, NULL }, { "h", 556, NULL }, { "i", 278, NULL }, { "Oslash", 722, NULL }, { "dagger", 500, NULL }, { "j", 278, NULL }, { "k", 500, NULL }, { "l", 278, NULL }, { "m", 778, NULL }, { "n", 556, NULL }, { "tcommaaccent", 278, NULL }, { "o", 500, NULL }, { "ordfeminine", 266, NULL }, { "ring", 333, NULL }, { "p", 500, NULL }, { "q", 500, NULL }, { "uhungarumlaut", 556, NULL }, { "r", 389, NULL }, { "twosuperior", 300, NULL }, { "aacute", 500, NULL }, { "s", 389, NULL }, { "OE", 944, NULL }, { "t", 278, NULL }, { "divide", 570, NULL }, { "u", 556, NULL }, { "Ccaron", 667, NULL }, { "v", 444, NULL }, { "w", 667, NULL }, { "x", 500, NULL }, { "y", 444, NULL }, { "z", 389, NULL }, { "Gbreve", 722, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 389, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 555, NULL }, { "gcommaaccent", 500, NULL }, { "mu", 576, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 556, NULL }, { "Lslash", 611, NULL }, { "semicolon", 333, NULL }, { "oslash", 500, NULL }, { "lessequal", 549, NULL }, { "lozenge", 494, NULL }, { "parenright", 333, NULL }, { "ccaron", 444, NULL }, { "Ecircumflex", 667, NULL }, { "gbreve", 500, NULL }, { "trademark", 1000, NULL }, { "daggerdbl", 500, NULL }, { "nacute", 556, NULL }, { "macron", 333, NULL }, { "Otilde", 722, NULL }, { "Emacron", 667, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 389, NULL }, { "AE", 944, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 278, NULL }, { "quotedblleft", 500, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 278, NULL }, { "eight", 500, NULL }, { "exclamdown", 389, NULL }, { "endash", 500, NULL }, { "oe", 722, NULL }, { "Abreve", 667, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 444, NULL }, { "Adieresis", 667, NULL }, { "copyright", 747, NULL }, { "Egrave", 667, NULL }, { "slash", 278, NULL }, { "Edieresis", 667, NULL }, { "otilde", 500, NULL }, { "Idieresis", 389, NULL }, { "parenleft", 333, NULL }, { "one", 500, NULL }, { "emacron", 444, NULL }, { "Odieresis", 722, NULL }, { "ucircumflex", 556, NULL }, { "bracketleft", 333, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 333, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 611, NULL }, { "umacron", 556, NULL }, { "abreve", 500, NULL }, { "Eacute", 667, NULL }, { "adieresis", 500, NULL }, { "egrave", 444, NULL }, { "edieresis", 444, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 722, NULL }, { "asterisk", 500, NULL }, { "odieresis", 500, NULL }, { "Uacute", 722, NULL }, { "ugrave", 556, NULL }, { "nine", 500, NULL }, { "five", 500, NULL }, { "udieresis", 556, NULL }, { "Zcaron", 611, NULL }, { "Scommaaccent", 556, NULL }, { "threequarters", 750, NULL }, { "guillemotright", 500, NULL }, { "Ccedilla", 667, NULL }, { "ydieresis", 444, NULL }, { "tilde", 333, NULL }, { "at", 832, NULL }, { "eacute", 444, NULL }, { "underscore", 500, NULL }, { "Euro", 500, NULL }, { "Dcroat", 722, NULL }, { "multiply", 570, NULL }, { "zero", 500, NULL }, { "eth", 500, NULL }, { "Scedilla", 556, NULL }, { "Ograve", 722, NULL }, { "Racute", 667, NULL }, { "partialdiff", 494, NULL }, { "uacute", 556, NULL }, { "braceleft", 348, NULL }, { "Thorn", 611, NULL }, { "zcaron", 389, NULL }, { "scommaaccent", 389, NULL }, { "ccedilla", 444, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 500, NULL }, { "Ocircumflex", 722, NULL }, { "Oacute", 722, NULL }, { "scedilla", 389, NULL }, { "ogonek", 333, NULL }, { "ograve", 500, NULL }, { "racute", 389, NULL }, { "Tcaron", 611, NULL }, { "Eogonek", 667, NULL }, { "thorn", 500, NULL }, { "degree", 400, NULL }, { "registered", 747, NULL }, { "radical", 549, NULL }, { "Aring", 667, NULL }, { "percent", 833, NULL }, { "six", 500, NULL }, { "paragraph", 500, NULL }, { "dcaron", 608, NULL }, { "Uogonek", 722, NULL }, { "two", 500, NULL }, { "summation", 600, NULL }, { "Igrave", 389, NULL }, { "Lacute", 611, NULL }, { "ocircumflex", 500, NULL }, { "oacute", 500, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 611, NULL }, { "tcaron", 366, NULL }, { "eogonek", 444, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 722, NULL }, { "asciicircum", 570, NULL }, { "aring", 500, NULL }, { "grave", 333, NULL }, { "uogonek", 556, NULL }, { "bracketright", 333, NULL }, { "Iacute", 389, NULL }, { "ampersand", 778, NULL }, { "igrave", 278, NULL }, { "lacute", 278, NULL }, { "Ncaron", 722, NULL }, { "plus", 570, NULL }, { "uring", 556, NULL }, { "quotesinglbase", 333, NULL }, { "lcommaaccent", 278, NULL }, { "Yacute", 611, NULL }, { "ohungarumlaut", 500, NULL }, { "threesuperior", 300, NULL }, { "acute", 333, NULL }, { "section", 500, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 500, NULL }, { "ncaron", 556, NULL }, { "florin", 500, NULL }, { "yacute", 444, NULL }, { "Rcommaaccent", 667, NULL }, { "fi", 556, NULL }, { "fl", 556, NULL }, { "Acircumflex", 667, NULL }, { "Cacute", 667, NULL }, { "Icircumflex", 389, NULL }, { "guillemotleft", 500, NULL }, { "germandbls", 500, NULL }, { "Amacron", 667, NULL }, { "seven", 500, NULL }, { "Sacute", 556, NULL }, { "ordmasculine", 300, NULL }, { "dotlessi", 278, NULL }, { "sterling", 500, NULL }, { "notequal", 549, NULL }, { "Imacron", 389, NULL }, { "rcommaaccent", 389, NULL }, { "Zdotaccent", 611, NULL }, { "acircumflex", 500, NULL }, { "cacute", 444, NULL }, { "Ecaron", 667, NULL }, { "icircumflex", 278, NULL }, { "braceright", 348, NULL }, { "quotedblright", 500, NULL }, { "amacron", 500, NULL }, { "sacute", 389, NULL }, { "imacron", 278, NULL }, { "cent", 500, NULL }, { "currency", 500, NULL }, { "logicalnot", 606, NULL }, { "zdotaccent", 389, NULL }, { "Atilde", 667, NULL }, { "breve", 333, NULL }, { "bar", 220, NULL }, { "fraction", 167, NULL }, { "less", 570, NULL }, { "ecaron", 444, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 389, NULL }, { "period", 250, NULL }, { "Rcaron", 667, NULL }, { "Kcommaaccent", 667, NULL }, { "greater", 570, NULL }, { "atilde", 500, NULL }, { "brokenbar", 220, NULL }, { "quoteleft", 333, NULL }, { "Edotaccent", 667, NULL }, { "onesuperior", 300, NULL } }; static BuiltinFontWidth timesItalicWidthsTab[] = { { "Ntilde", 667, NULL }, { "rcaron", 389, NULL }, { "kcommaaccent", 444, NULL }, { "Ncommaaccent", 667, NULL }, { "Zacute", 556, NULL }, { "comma", 250, NULL }, { "cedilla", 333, NULL }, { "plusminus", 675, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 444, NULL }, { "asciitilde", 541, NULL }, { "colon", 333, NULL }, { "onehalf", 750, NULL }, { "dollar", 500, NULL }, { "Lcaron", 611, NULL }, { "ntilde", 500, NULL }, { "Aogonek", 611, NULL }, { "ncommaaccent", 500, NULL }, { "minus", 675, NULL }, { "Iogonek", 333, NULL }, { "zacute", 389, NULL }, { "yen", 500, NULL }, { "space", 250, NULL }, { "Omacron", 722, NULL }, { "questiondown", 500, NULL }, { "emdash", 889, NULL }, { "Agrave", 611, NULL }, { "three", 500, NULL }, { "numbersign", 500, NULL }, { "lcaron", 300, NULL }, { "A", 611, NULL }, { "B", 611, NULL }, { "C", 667, NULL }, { "aogonek", 500, NULL }, { "D", 722, NULL }, { "E", 611, NULL }, { "onequarter", 750, NULL }, { "F", 611, NULL }, { "G", 722, NULL }, { "H", 722, NULL }, { "I", 333, NULL }, { "J", 444, NULL }, { "K", 667, NULL }, { "iogonek", 278, NULL }, { "backslash", 278, NULL }, { "L", 556, NULL }, { "periodcentered", 250, NULL }, { "M", 833, NULL }, { "N", 667, NULL }, { "omacron", 500, NULL }, { "Tcommaaccent", 556, NULL }, { "O", 722, NULL }, { "P", 611, NULL }, { "Q", 722, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 611, NULL }, { "Aacute", 611, NULL }, { "caron", 333, NULL }, { "S", 500, NULL }, { "T", 556, NULL }, { "U", 722, NULL }, { "agrave", 500, NULL }, { "V", 611, NULL }, { "W", 833, NULL }, { "X", 611, NULL }, { "question", 500, NULL }, { "equal", 675, NULL }, { "Y", 556, NULL }, { "Z", 556, NULL }, { "four", 500, NULL }, { "a", 500, NULL }, { "Gcommaaccent", 722, NULL }, { "b", 500, NULL }, { "c", 444, NULL }, { "d", 500, NULL }, { "e", 444, NULL }, { "f", 278, NULL }, { "g", 500, NULL }, { "bullet", 350, NULL }, { "h", 500, NULL }, { "i", 278, NULL }, { "Oslash", 722, NULL }, { "dagger", 500, NULL }, { "j", 278, NULL }, { "k", 444, NULL }, { "l", 278, NULL }, { "m", 722, NULL }, { "n", 500, NULL }, { "tcommaaccent", 278, NULL }, { "o", 500, NULL }, { "ordfeminine", 276, NULL }, { "ring", 333, NULL }, { "p", 500, NULL }, { "q", 500, NULL }, { "uhungarumlaut", 500, NULL }, { "r", 389, NULL }, { "twosuperior", 300, NULL }, { "aacute", 500, NULL }, { "s", 389, NULL }, { "OE", 944, NULL }, { "t", 278, NULL }, { "divide", 675, NULL }, { "u", 500, NULL }, { "Ccaron", 667, NULL }, { "v", 444, NULL }, { "w", 667, NULL }, { "x", 444, NULL }, { "y", 444, NULL }, { "z", 389, NULL }, { "Gbreve", 722, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 333, NULL }, { "Nacute", 667, NULL }, { "quotedbl", 420, NULL }, { "gcommaaccent", 500, NULL }, { "mu", 500, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 500, NULL }, { "Lslash", 556, NULL }, { "semicolon", 333, NULL }, { "oslash", 500, NULL }, { "lessequal", 549, NULL }, { "lozenge", 471, NULL }, { "parenright", 333, NULL }, { "ccaron", 444, NULL }, { "Ecircumflex", 611, NULL }, { "gbreve", 500, NULL }, { "trademark", 980, NULL }, { "daggerdbl", 500, NULL }, { "nacute", 500, NULL }, { "macron", 333, NULL }, { "Otilde", 722, NULL }, { "Emacron", 611, NULL }, { "ellipsis", 889, NULL }, { "scaron", 389, NULL }, { "AE", 889, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 278, NULL }, { "quotedblleft", 556, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 214, NULL }, { "eight", 500, NULL }, { "exclamdown", 389, NULL }, { "endash", 500, NULL }, { "oe", 667, NULL }, { "Abreve", 611, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 444, NULL }, { "Adieresis", 611, NULL }, { "copyright", 760, NULL }, { "Egrave", 611, NULL }, { "slash", 278, NULL }, { "Edieresis", 611, NULL }, { "otilde", 500, NULL }, { "Idieresis", 333, NULL }, { "parenleft", 333, NULL }, { "one", 500, NULL }, { "emacron", 444, NULL }, { "Odieresis", 722, NULL }, { "ucircumflex", 500, NULL }, { "bracketleft", 389, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 333, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 556, NULL }, { "umacron", 500, NULL }, { "abreve", 500, NULL }, { "Eacute", 611, NULL }, { "adieresis", 500, NULL }, { "egrave", 444, NULL }, { "edieresis", 444, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 667, NULL }, { "asterisk", 500, NULL }, { "odieresis", 500, NULL }, { "Uacute", 722, NULL }, { "ugrave", 500, NULL }, { "nine", 500, NULL }, { "five", 500, NULL }, { "udieresis", 500, NULL }, { "Zcaron", 556, NULL }, { "Scommaaccent", 500, NULL }, { "threequarters", 750, NULL }, { "guillemotright", 500, NULL }, { "Ccedilla", 667, NULL }, { "ydieresis", 444, NULL }, { "tilde", 333, NULL }, { "at", 920, NULL }, { "eacute", 444, NULL }, { "underscore", 500, NULL }, { "Euro", 500, NULL }, { "Dcroat", 722, NULL }, { "multiply", 675, NULL }, { "zero", 500, NULL }, { "eth", 500, NULL }, { "Scedilla", 500, NULL }, { "Ograve", 722, NULL }, { "Racute", 611, NULL }, { "partialdiff", 476, NULL }, { "uacute", 500, NULL }, { "braceleft", 400, NULL }, { "Thorn", 611, NULL }, { "zcaron", 389, NULL }, { "scommaaccent", 389, NULL }, { "ccedilla", 444, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 500, NULL }, { "Ocircumflex", 722, NULL }, { "Oacute", 722, NULL }, { "scedilla", 389, NULL }, { "ogonek", 333, NULL }, { "ograve", 500, NULL }, { "racute", 389, NULL }, { "Tcaron", 556, NULL }, { "Eogonek", 611, NULL }, { "thorn", 500, NULL }, { "degree", 400, NULL }, { "registered", 760, NULL }, { "radical", 453, NULL }, { "Aring", 611, NULL }, { "percent", 833, NULL }, { "six", 500, NULL }, { "paragraph", 523, NULL }, { "dcaron", 544, NULL }, { "Uogonek", 722, NULL }, { "two", 500, NULL }, { "summation", 600, NULL }, { "Igrave", 333, NULL }, { "Lacute", 556, NULL }, { "ocircumflex", 500, NULL }, { "oacute", 500, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 556, NULL }, { "tcaron", 300, NULL }, { "eogonek", 444, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 722, NULL }, { "asciicircum", 422, NULL }, { "aring", 500, NULL }, { "grave", 333, NULL }, { "uogonek", 500, NULL }, { "bracketright", 389, NULL }, { "Iacute", 333, NULL }, { "ampersand", 778, NULL }, { "igrave", 278, NULL }, { "lacute", 278, NULL }, { "Ncaron", 667, NULL }, { "plus", 675, NULL }, { "uring", 500, NULL }, { "quotesinglbase", 333, NULL }, { "lcommaaccent", 278, NULL }, { "Yacute", 556, NULL }, { "ohungarumlaut", 500, NULL }, { "threesuperior", 300, NULL }, { "acute", 333, NULL }, { "section", 500, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 556, NULL }, { "ncaron", 500, NULL }, { "florin", 500, NULL }, { "yacute", 444, NULL }, { "Rcommaaccent", 611, NULL }, { "fi", 500, NULL }, { "fl", 500, NULL }, { "Acircumflex", 611, NULL }, { "Cacute", 667, NULL }, { "Icircumflex", 333, NULL }, { "guillemotleft", 500, NULL }, { "germandbls", 500, NULL }, { "Amacron", 611, NULL }, { "seven", 500, NULL }, { "Sacute", 500, NULL }, { "ordmasculine", 310, NULL }, { "dotlessi", 278, NULL }, { "sterling", 500, NULL }, { "notequal", 549, NULL }, { "Imacron", 333, NULL }, { "rcommaaccent", 389, NULL }, { "Zdotaccent", 556, NULL }, { "acircumflex", 500, NULL }, { "cacute", 444, NULL }, { "Ecaron", 611, NULL }, { "icircumflex", 278, NULL }, { "braceright", 400, NULL }, { "quotedblright", 556, NULL }, { "amacron", 500, NULL }, { "sacute", 389, NULL }, { "imacron", 278, NULL }, { "cent", 500, NULL }, { "currency", 500, NULL }, { "logicalnot", 675, NULL }, { "zdotaccent", 389, NULL }, { "Atilde", 611, NULL }, { "breve", 333, NULL }, { "bar", 275, NULL }, { "fraction", 167, NULL }, { "less", 675, NULL }, { "ecaron", 444, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 333, NULL }, { "period", 250, NULL }, { "Rcaron", 611, NULL }, { "Kcommaaccent", 667, NULL }, { "greater", 675, NULL }, { "atilde", 500, NULL }, { "brokenbar", 275, NULL }, { "quoteleft", 333, NULL }, { "Edotaccent", 611, NULL }, { "onesuperior", 300, NULL } }; static BuiltinFontWidth timesRomanWidthsTab[] = { { "Ntilde", 722, NULL }, { "rcaron", 333, NULL }, { "kcommaaccent", 500, NULL }, { "Ncommaaccent", 722, NULL }, { "Zacute", 611, NULL }, { "comma", 250, NULL }, { "cedilla", 333, NULL }, { "plusminus", 564, NULL }, { "circumflex", 333, NULL }, { "dotaccent", 333, NULL }, { "edotaccent", 444, NULL }, { "asciitilde", 541, NULL }, { "colon", 278, NULL }, { "onehalf", 750, NULL }, { "dollar", 500, NULL }, { "Lcaron", 611, NULL }, { "ntilde", 500, NULL }, { "Aogonek", 722, NULL }, { "ncommaaccent", 500, NULL }, { "minus", 564, NULL }, { "Iogonek", 333, NULL }, { "zacute", 444, NULL }, { "yen", 500, NULL }, { "space", 250, NULL }, { "Omacron", 722, NULL }, { "questiondown", 444, NULL }, { "emdash", 1000, NULL }, { "Agrave", 722, NULL }, { "three", 500, NULL }, { "numbersign", 500, NULL }, { "lcaron", 344, NULL }, { "A", 722, NULL }, { "B", 667, NULL }, { "C", 667, NULL }, { "aogonek", 444, NULL }, { "D", 722, NULL }, { "E", 611, NULL }, { "onequarter", 750, NULL }, { "F", 556, NULL }, { "G", 722, NULL }, { "H", 722, NULL }, { "I", 333, NULL }, { "J", 389, NULL }, { "K", 722, NULL }, { "iogonek", 278, NULL }, { "backslash", 278, NULL }, { "L", 611, NULL }, { "periodcentered", 250, NULL }, { "M", 889, NULL }, { "N", 722, NULL }, { "omacron", 500, NULL }, { "Tcommaaccent", 611, NULL }, { "O", 722, NULL }, { "P", 556, NULL }, { "Q", 722, NULL }, { "Uhungarumlaut", 722, NULL }, { "R", 667, NULL }, { "Aacute", 722, NULL }, { "caron", 333, NULL }, { "S", 556, NULL }, { "T", 611, NULL }, { "U", 722, NULL }, { "agrave", 444, NULL }, { "V", 722, NULL }, { "W", 944, NULL }, { "X", 722, NULL }, { "question", 444, NULL }, { "equal", 564, NULL }, { "Y", 722, NULL }, { "Z", 611, NULL }, { "four", 500, NULL }, { "a", 444, NULL }, { "Gcommaaccent", 722, NULL }, { "b", 500, NULL }, { "c", 444, NULL }, { "d", 500, NULL }, { "e", 444, NULL }, { "f", 333, NULL }, { "g", 500, NULL }, { "bullet", 350, NULL }, { "h", 500, NULL }, { "i", 278, NULL }, { "Oslash", 722, NULL }, { "dagger", 500, NULL }, { "j", 278, NULL }, { "k", 500, NULL }, { "l", 278, NULL }, { "m", 778, NULL }, { "n", 500, NULL }, { "tcommaaccent", 278, NULL }, { "o", 500, NULL }, { "ordfeminine", 276, NULL }, { "ring", 333, NULL }, { "p", 500, NULL }, { "q", 500, NULL }, { "uhungarumlaut", 500, NULL }, { "r", 333, NULL }, { "twosuperior", 300, NULL }, { "aacute", 444, NULL }, { "s", 389, NULL }, { "OE", 889, NULL }, { "t", 278, NULL }, { "divide", 564, NULL }, { "u", 500, NULL }, { "Ccaron", 667, NULL }, { "v", 500, NULL }, { "w", 722, NULL }, { "x", 500, NULL }, { "y", 500, NULL }, { "z", 444, NULL }, { "Gbreve", 722, NULL }, { "commaaccent", 250, NULL }, { "hungarumlaut", 333, NULL }, { "Idotaccent", 333, NULL }, { "Nacute", 722, NULL }, { "quotedbl", 408, NULL }, { "gcommaaccent", 500, NULL }, { "mu", 500, NULL }, { "greaterequal", 549, NULL }, { "Scaron", 556, NULL }, { "Lslash", 611, NULL }, { "semicolon", 278, NULL }, { "oslash", 500, NULL }, { "lessequal", 549, NULL }, { "lozenge", 471, NULL }, { "parenright", 333, NULL }, { "ccaron", 444, NULL }, { "Ecircumflex", 611, NULL }, { "gbreve", 500, NULL }, { "trademark", 980, NULL }, { "daggerdbl", 500, NULL }, { "nacute", 500, NULL }, { "macron", 333, NULL }, { "Otilde", 722, NULL }, { "Emacron", 611, NULL }, { "ellipsis", 1000, NULL }, { "scaron", 389, NULL }, { "AE", 889, NULL }, { "Ucircumflex", 722, NULL }, { "lslash", 278, NULL }, { "quotedblleft", 444, NULL }, { "guilsinglright", 333, NULL }, { "hyphen", 333, NULL }, { "quotesingle", 180, NULL }, { "eight", 500, NULL }, { "exclamdown", 333, NULL }, { "endash", 500, NULL }, { "oe", 722, NULL }, { "Abreve", 722, NULL }, { "Umacron", 722, NULL }, { "ecircumflex", 444, NULL }, { "Adieresis", 722, NULL }, { "copyright", 760, NULL }, { "Egrave", 611, NULL }, { "slash", 278, NULL }, { "Edieresis", 611, NULL }, { "otilde", 500, NULL }, { "Idieresis", 333, NULL }, { "parenleft", 333, NULL }, { "one", 500, NULL }, { "emacron", 444, NULL }, { "Odieresis", 722, NULL }, { "ucircumflex", 500, NULL }, { "bracketleft", 333, NULL }, { "Ugrave", 722, NULL }, { "quoteright", 333, NULL }, { "Udieresis", 722, NULL }, { "perthousand", 1000, NULL }, { "Ydieresis", 722, NULL }, { "umacron", 500, NULL }, { "abreve", 444, NULL }, { "Eacute", 611, NULL }, { "adieresis", 444, NULL }, { "egrave", 444, NULL }, { "edieresis", 444, NULL }, { "idieresis", 278, NULL }, { "Eth", 722, NULL }, { "ae", 667, NULL }, { "asterisk", 500, NULL }, { "odieresis", 500, NULL }, { "Uacute", 722, NULL }, { "ugrave", 500, NULL }, { "nine", 500, NULL }, { "five", 500, NULL }, { "udieresis", 500, NULL }, { "Zcaron", 611, NULL }, { "Scommaaccent", 556, NULL }, { "threequarters", 750, NULL }, { "guillemotright", 500, NULL }, { "Ccedilla", 667, NULL }, { "ydieresis", 500, NULL }, { "tilde", 333, NULL }, { "at", 921, NULL }, { "eacute", 444, NULL }, { "underscore", 500, NULL }, { "Euro", 500, NULL }, { "Dcroat", 722, NULL }, { "multiply", 564, NULL }, { "zero", 500, NULL }, { "eth", 500, NULL }, { "Scedilla", 556, NULL }, { "Ograve", 722, NULL }, { "Racute", 667, NULL }, { "partialdiff", 476, NULL }, { "uacute", 500, NULL }, { "braceleft", 480, NULL }, { "Thorn", 556, NULL }, { "zcaron", 444, NULL }, { "scommaaccent", 389, NULL }, { "ccedilla", 444, NULL }, { "Dcaron", 722, NULL }, { "dcroat", 500, NULL }, { "Ocircumflex", 722, NULL }, { "Oacute", 722, NULL }, { "scedilla", 389, NULL }, { "ogonek", 333, NULL }, { "ograve", 500, NULL }, { "racute", 333, NULL }, { "Tcaron", 611, NULL }, { "Eogonek", 611, NULL }, { "thorn", 500, NULL }, { "degree", 400, NULL }, { "registered", 760, NULL }, { "radical", 453, NULL }, { "Aring", 722, NULL }, { "percent", 833, NULL }, { "six", 500, NULL }, { "paragraph", 453, NULL }, { "dcaron", 588, NULL }, { "Uogonek", 722, NULL }, { "two", 500, NULL }, { "summation", 600, NULL }, { "Igrave", 333, NULL }, { "Lacute", 611, NULL }, { "ocircumflex", 500, NULL }, { "oacute", 500, NULL }, { "Uring", 722, NULL }, { "Lcommaaccent", 611, NULL }, { "tcaron", 326, NULL }, { "eogonek", 444, NULL }, { "Delta", 612, NULL }, { "Ohungarumlaut", 722, NULL }, { "asciicircum", 469, NULL }, { "aring", 444, NULL }, { "grave", 333, NULL }, { "uogonek", 500, NULL }, { "bracketright", 333, NULL }, { "Iacute", 333, NULL }, { "ampersand", 778, NULL }, { "igrave", 278, NULL }, { "lacute", 278, NULL }, { "Ncaron", 722, NULL }, { "plus", 564, NULL }, { "uring", 500, NULL }, { "quotesinglbase", 333, NULL }, { "lcommaaccent", 278, NULL }, { "Yacute", 722, NULL }, { "ohungarumlaut", 500, NULL }, { "threesuperior", 300, NULL }, { "acute", 333, NULL }, { "section", 500, NULL }, { "dieresis", 333, NULL }, { "iacute", 278, NULL }, { "quotedblbase", 444, NULL }, { "ncaron", 500, NULL }, { "florin", 500, NULL }, { "yacute", 500, NULL }, { "Rcommaaccent", 667, NULL }, { "fi", 556, NULL }, { "fl", 556, NULL }, { "Acircumflex", 722, NULL }, { "Cacute", 667, NULL }, { "Icircumflex", 333, NULL }, { "guillemotleft", 500, NULL }, { "germandbls", 500, NULL }, { "Amacron", 722, NULL }, { "seven", 500, NULL }, { "Sacute", 556, NULL }, { "ordmasculine", 310, NULL }, { "dotlessi", 278, NULL }, { "sterling", 500, NULL }, { "notequal", 549, NULL }, { "Imacron", 333, NULL }, { "rcommaaccent", 333, NULL }, { "Zdotaccent", 611, NULL }, { "acircumflex", 444, NULL }, { "cacute", 444, NULL }, { "Ecaron", 611, NULL }, { "icircumflex", 278, NULL }, { "braceright", 480, NULL }, { "quotedblright", 444, NULL }, { "amacron", 444, NULL }, { "sacute", 389, NULL }, { "imacron", 278, NULL }, { "cent", 500, NULL }, { "currency", 500, NULL }, { "logicalnot", 564, NULL }, { "zdotaccent", 444, NULL }, { "Atilde", 722, NULL }, { "breve", 333, NULL }, { "bar", 200, NULL }, { "fraction", 167, NULL }, { "less", 564, NULL }, { "ecaron", 444, NULL }, { "guilsinglleft", 333, NULL }, { "exclam", 333, NULL }, { "period", 250, NULL }, { "Rcaron", 667, NULL }, { "Kcommaaccent", 722, NULL }, { "greater", 564, NULL }, { "atilde", 444, NULL }, { "brokenbar", 200, NULL }, { "quoteleft", 333, NULL }, { "Edotaccent", 611, NULL }, { "onesuperior", 300, NULL } }; static BuiltinFontWidth zapfDingbatsWidthsTab[] = { { "a81", 438, NULL }, { "a82", 138, NULL }, { "a83", 277, NULL }, { "a84", 415, NULL }, { "a85", 509, NULL }, { "a86", 410, NULL }, { "a87", 234, NULL }, { "a88", 234, NULL }, { "a89", 390, NULL }, { "a140", 788, NULL }, { "a141", 788, NULL }, { "a142", 788, NULL }, { "a143", 788, NULL }, { "a144", 788, NULL }, { "a145", 788, NULL }, { "a146", 788, NULL }, { "a147", 788, NULL }, { "a148", 788, NULL }, { "a149", 788, NULL }, { "a90", 390, NULL }, { "a91", 276, NULL }, { "a92", 276, NULL }, { "space", 278, NULL }, { "a93", 317, NULL }, { "a94", 317, NULL }, { "a95", 334, NULL }, { "a96", 334, NULL }, { "a97", 392, NULL }, { "a98", 392, NULL }, { "a99", 668, NULL }, { "a150", 788, NULL }, { "a151", 788, NULL }, { "a152", 788, NULL }, { "a153", 788, NULL }, { "a154", 788, NULL }, { "a155", 788, NULL }, { "a156", 788, NULL }, { "a157", 788, NULL }, { "a158", 788, NULL }, { "a159", 788, NULL }, { "a160", 894, NULL }, { "a161", 838, NULL }, { "a162", 924, NULL }, { "a163", 1016, NULL }, { "a164", 458, NULL }, { "a165", 924, NULL }, { "a166", 918, NULL }, { "a167", 927, NULL }, { "a168", 928, NULL }, { "a169", 928, NULL }, { "a170", 834, NULL }, { "a171", 873, NULL }, { "a172", 828, NULL }, { "a173", 924, NULL }, { "a174", 917, NULL }, { "a175", 930, NULL }, { "a176", 931, NULL }, { "a177", 463, NULL }, { "a178", 883, NULL }, { "a179", 836, NULL }, { "a180", 867, NULL }, { "a181", 696, NULL }, { "a182", 874, NULL }, { "a183", 760, NULL }, { "a184", 946, NULL }, { "a185", 865, NULL }, { "a186", 967, NULL }, { "a187", 831, NULL }, { "a188", 873, NULL }, { "a189", 927, NULL }, { "a1", 974, NULL }, { "a2", 961, NULL }, { "a3", 980, NULL }, { "a4", 719, NULL }, { "a5", 789, NULL }, { "a6", 494, NULL }, { "a7", 552, NULL }, { "a8", 537, NULL }, { "a9", 577, NULL }, { "a190", 970, NULL }, { "a191", 918, NULL }, { "a192", 748, NULL }, { "a193", 836, NULL }, { "a194", 771, NULL }, { "a195", 888, NULL }, { "a196", 748, NULL }, { "a197", 771, NULL }, { "a198", 888, NULL }, { "a199", 867, NULL }, { "a10", 692, NULL }, { "a11", 960, NULL }, { "a12", 939, NULL }, { "a13", 549, NULL }, { "a14", 855, NULL }, { "a15", 911, NULL }, { "a16", 933, NULL }, { "a17", 945, NULL }, { "a18", 974, NULL }, { "a19", 755, NULL }, { "a20", 846, NULL }, { "a21", 762, NULL }, { "a22", 761, NULL }, { "a23", 571, NULL }, { "a24", 677, NULL }, { "a25", 763, NULL }, { "a26", 760, NULL }, { "a27", 759, NULL }, { "a28", 754, NULL }, { "a29", 786, NULL }, { "a30", 788, NULL }, { "a31", 788, NULL }, { "a32", 790, NULL }, { "a33", 793, NULL }, { "a34", 794, NULL }, { "a35", 816, NULL }, { "a36", 823, NULL }, { "a37", 789, NULL }, { "a38", 841, NULL }, { "a39", 823, NULL }, { "a40", 833, NULL }, { "a41", 816, NULL }, { "a42", 831, NULL }, { "a43", 923, NULL }, { "a44", 744, NULL }, { "a45", 723, NULL }, { "a46", 749, NULL }, { "a47", 790, NULL }, { "a48", 792, NULL }, { "a49", 695, NULL }, { "a100", 668, NULL }, { "a101", 732, NULL }, { "a102", 544, NULL }, { "a103", 544, NULL }, { "a104", 910, NULL }, { "a105", 911, NULL }, { "a106", 667, NULL }, { "a107", 760, NULL }, { "a108", 760, NULL }, { "a109", 626, NULL }, { "a50", 776, NULL }, { "a51", 768, NULL }, { "a52", 792, NULL }, { "a53", 759, NULL }, { "a54", 707, NULL }, { "a55", 708, NULL }, { "a56", 682, NULL }, { "a57", 701, NULL }, { "a58", 826, NULL }, { "a59", 815, NULL }, { "a110", 694, NULL }, { "a111", 595, NULL }, { "a112", 776, NULL }, { "a117", 690, NULL }, { "a118", 791, NULL }, { "a119", 790, NULL }, { "a60", 789, NULL }, { "a61", 789, NULL }, { "a62", 707, NULL }, { "a63", 687, NULL }, { "a64", 696, NULL }, { "a65", 689, NULL }, { "a66", 786, NULL }, { "a67", 787, NULL }, { "a68", 713, NULL }, { "a69", 791, NULL }, { "a200", 696, NULL }, { "a201", 874, NULL }, { "a120", 788, NULL }, { "a121", 788, NULL }, { "a202", 974, NULL }, { "a122", 788, NULL }, { "a203", 762, NULL }, { "a123", 788, NULL }, { "a204", 759, NULL }, { "a124", 788, NULL }, { "a205", 509, NULL }, { "a125", 788, NULL }, { "a206", 410, NULL }, { "a126", 788, NULL }, { "a127", 788, NULL }, { "a128", 788, NULL }, { "a129", 788, NULL }, { "a70", 785, NULL }, { "a71", 791, NULL }, { "a72", 873, NULL }, { "a73", 761, NULL }, { "a74", 762, NULL }, { "a75", 759, NULL }, { "a76", 892, NULL }, { "a77", 892, NULL }, { "a78", 788, NULL }, { "a79", 784, NULL }, { "a130", 788, NULL }, { "a131", 788, NULL }, { "a132", 788, NULL }, { "a133", 788, NULL }, { "a134", 788, NULL }, { "a135", 788, NULL }, { "a136", 788, NULL }, { "a137", 788, NULL }, { "a138", 788, NULL }, { "a139", 788, NULL } }; BuiltinFont builtinFonts[] = { { "Courier", standardEncoding, 629, -157, { -23, -250, 715, 805}, NULL }, { "Courier-Bold", standardEncoding, 629, -157, {-113, -250, 749, 801}, NULL }, { "Courier-BoldOblique", standardEncoding, 629, -157, { -57, -250, 869, 801}, NULL }, { "Courier-Oblique", standardEncoding, 629, -157, { -27, -250, 849, 805}, NULL }, { "Helvetica", standardEncoding, 718, -207, {-166, -225, 1000, 931}, NULL }, { "Helvetica-Bold", standardEncoding, 718, -207, {-170, -228, 1003, 962}, NULL }, { "Helvetica-BoldOblique", standardEncoding, 718, -207, {-174, -228, 1114, 962}, NULL }, { "Helvetica-Oblique", standardEncoding, 718, -207, {-170, -225, 1116, 931}, NULL }, { "Symbol", symbolEncoding, 1010, -293, {-180, -293, 1090, 1010}, NULL }, { "Times-Bold", standardEncoding, 683, -217, {-168, -218, 1000, 935}, NULL }, { "Times-BoldItalic", standardEncoding, 683, -217, {-200, -218, 996, 921}, NULL }, { "Times-Italic", standardEncoding, 683, -217, {-169, -217, 1010, 883}, NULL }, { "Times-Roman", standardEncoding, 683, -217, {-168, -218, 1000, 898}, NULL }, { "ZapfDingbats", zapfDingbatsEncoding, 820, -143, { -1, -143, 981, 820}, NULL } }; BuiltinFont *builtinFontSubst[] = { &builtinFonts[0], &builtinFonts[3], &builtinFonts[1], &builtinFonts[2], &builtinFonts[4], &builtinFonts[7], &builtinFonts[5], &builtinFonts[6], &builtinFonts[12], &builtinFonts[11], &builtinFonts[9], &builtinFonts[10] }; void initBuiltinFontTables() { builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 315); builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 315); builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 315); builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 315); builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 315); builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 316); builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 315); builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 315); builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 190); builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 315); builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 315); builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 315); builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 315); builtinFonts[13].widths = new BuiltinFontWidths(zapfDingbatsWidthsTab, 202); } void freeBuiltinFontTables() { int i; for (i = 0; i < 14; ++i) { delete builtinFonts[i].widths; } } xpdf-3.04/xpdf/pdfdetach.cc0000644000076400007640000001274512341430012015104 0ustar dereknderekn//======================================================================== // // pdfdetach.cc // // Copyright 2010 Glyph & Cog, LLC // //======================================================================== #include #include #include "gtypes.h" #include "gmem.h" #include "parseargs.h" #include "GlobalParams.h" #include "PDFDoc.h" #include "CharTypes.h" #include "UnicodeMap.h" #include "Error.h" #include "config.h" static GBool doList = gFalse; static int saveNum = 0; static GBool saveAll = gFalse; static char savePath[1024] = ""; static char textEncName[128] = ""; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static char cfgFileName[256] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-list", argFlag, &doList, 0, "list all embedded files"}, {"-save", argInt, &saveNum, 0, "save the specified embedded file"}, {"-saveall", argFlag, &saveAll, 0, "save all embedded files"}, {"-o", argString, savePath, sizeof(savePath), "file name for the saved embedded file"}, {"-enc", argString, textEncName, sizeof(textEncName), "output text encoding name"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; int main(int argc, char *argv[]) { GString *fileName; UnicodeMap *uMap; GString *ownerPW, *userPW; PDFDoc *doc; Unicode *name; char uBuf[8]; char path[1024]; char *p; GBool ok; int exitCode; int nFiles, nameLen, n, i, j; exitCode = 99; // parse args ok = parseArgs(argDesc, &argc, argv); if ((doList ? 1 : 0) + ((saveNum != 0) ? 1 : 0) + (saveAll ? 1 : 0) != 1) { ok = gFalse; } if (!ok || argc != 2 || printVersion || printHelp) { fprintf(stderr, "pdfdetach version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfdetach", "", argDesc); } goto err0; } fileName = new GString(argv[1]); // read config file globalParams = new GlobalParams(cfgFileName); if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } // get mapping to output encoding if (!(uMap = globalParams->getTextEncoding())) { error(errConfig, -1, "Couldn't get text encoding"); delete fileName; goto err1; } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = new GString(ownerPassword); } else { ownerPW = NULL; } if (userPassword[0] != '\001') { userPW = new GString(userPassword); } else { userPW = NULL; } doc = new PDFDoc(fileName, ownerPW, userPW); if (userPW) { delete userPW; } if (ownerPW) { delete ownerPW; } if (!doc->isOk()) { exitCode = 1; goto err2; } nFiles = doc->getNumEmbeddedFiles(); // list embedded files if (doList) { printf("%d embedded files\n", nFiles); for (i = 0; i < nFiles; ++i) { printf("%d: ", i+1); name = doc->getEmbeddedFileName(i); nameLen = doc->getEmbeddedFileNameLength(i); for (j = 0; j < nameLen; ++j) { n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf)); fwrite(uBuf, 1, n, stdout); } fputc('\n', stdout); } // save all embedded files } else if (saveAll) { for (i = 0; i < nFiles; ++i) { if (savePath[0]) { n = (int)strlen(savePath); if (n > (int)sizeof(path) - 2) { n = sizeof(path) - 2; } memcpy(path, savePath, n); path[n] = '/'; p = path + n + 1; } else { p = path; } name = doc->getEmbeddedFileName(i); nameLen = doc->getEmbeddedFileNameLength(i); for (j = 0; j < nameLen; ++j) { n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf)); if (p + n >= path + sizeof(path)) { break; } memcpy(p, uBuf, n); p += n; } *p = '\0'; if (!doc->saveEmbeddedFile(i, path)) { error(errIO, -1, "Error saving embedded file as '{0:s}'", p); exitCode = 2; goto err2; } } // save an embedded file } else { if (saveNum < 1 || saveNum > nFiles) { error(errCommandLine, -1, "Invalid file number"); goto err2; } if (savePath[0]) { p = savePath; } else { name = doc->getEmbeddedFileName(saveNum - 1); nameLen = doc->getEmbeddedFileNameLength(saveNum - 1); p = path; for (j = 0; j < nameLen; ++j) { n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf)); if (p + n >= path + sizeof(path)) { break; } memcpy(p, uBuf, n); p += n; } *p = '\0'; p = path; } if (!doc->saveEmbeddedFile(saveNum - 1, p)) { error(errIO, -1, "Error saving embedded file as '{0:s}'", p); exitCode = 2; goto err2; } } exitCode = 0; // clean up err2: uMap->decRefCnt(); delete doc; err1: delete globalParams; err0: // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); return exitCode; } xpdf-3.04/xpdf/GlobalParams.h0000644000076400007640000004273412341430012015371 0ustar dereknderekn//======================================================================== // // GlobalParams.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GLOBALPARAMS_H #define GLOBALPARAMS_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "gtypes.h" #include "CharTypes.h" #if MULTITHREADED #include "GMutex.h" #endif class GString; class GList; class GHash; class NameToCharCode; class CharCodeToUnicode; class CharCodeToUnicodeCache; class UnicodeMap; class UnicodeMapCache; class CMap; class CMapCache; struct XpdfSecurityHandler; class GlobalParams; class SysFontList; //------------------------------------------------------------------------ // The global parameters object. extern GlobalParams *globalParams; //------------------------------------------------------------------------ enum SysFontType { sysFontPFA, sysFontPFB, sysFontTTF, sysFontTTC }; //------------------------------------------------------------------------ class PSFontParam16 { public: GString *name; // PDF font name for psResidentFont16; // char collection name for psResidentFontCC int wMode; // writing mode (0=horiz, 1=vert) GString *psFontName; // PostScript font name GString *encoding; // encoding PSFontParam16(GString *nameA, int wModeA, GString *psFontNameA, GString *encodingA); ~PSFontParam16(); }; //------------------------------------------------------------------------ enum PSLevel { psLevel1, psLevel1Sep, psLevel2, psLevel2Sep, psLevel3, psLevel3Sep }; //------------------------------------------------------------------------ enum EndOfLineKind { eolUnix, // LF eolDOS, // CR+LF eolMac // CR }; //------------------------------------------------------------------------ enum ScreenType { screenUnset, screenDispersed, screenClustered, screenStochasticClustered }; //------------------------------------------------------------------------ class KeyBinding { public: int code; // 0x20 .. 0xfe = ASCII, // >=0x10000 = special keys, mouse buttons, // etc. (xpdfKeyCode* symbols) int mods; // modifiers (xpdfKeyMod* symbols, or-ed // together) int context; // context (xpdfKeyContext* symbols, or-ed // together) GList *cmds; // list of commands [GString] KeyBinding(int codeA, int modsA, int contextA, const char *cmd0); KeyBinding(int codeA, int modsA, int contextA, const char *cmd0, const char *cmd1); KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA); ~KeyBinding(); }; #define xpdfKeyCodeTab 0x1000 #define xpdfKeyCodeReturn 0x1001 #define xpdfKeyCodeEnter 0x1002 #define xpdfKeyCodeBackspace 0x1003 #define xpdfKeyCodeInsert 0x1004 #define xpdfKeyCodeDelete 0x1005 #define xpdfKeyCodeHome 0x1006 #define xpdfKeyCodeEnd 0x1007 #define xpdfKeyCodePgUp 0x1008 #define xpdfKeyCodePgDn 0x1009 #define xpdfKeyCodeLeft 0x100a #define xpdfKeyCodeRight 0x100b #define xpdfKeyCodeUp 0x100c #define xpdfKeyCodeDown 0x100d #define xpdfKeyCodeF1 0x1100 #define xpdfKeyCodeF35 0x1122 #define xpdfKeyCodeMousePress1 0x2001 #define xpdfKeyCodeMousePress2 0x2002 #define xpdfKeyCodeMousePress3 0x2003 #define xpdfKeyCodeMousePress4 0x2004 #define xpdfKeyCodeMousePress5 0x2005 #define xpdfKeyCodeMousePress6 0x2006 #define xpdfKeyCodeMousePress7 0x2007 // ... #define xpdfKeyCodeMousePress32 0x2020 #define xpdfKeyCodeMouseRelease1 0x2101 #define xpdfKeyCodeMouseRelease2 0x2102 #define xpdfKeyCodeMouseRelease3 0x2103 #define xpdfKeyCodeMouseRelease4 0x2104 #define xpdfKeyCodeMouseRelease5 0x2105 #define xpdfKeyCodeMouseRelease6 0x2106 #define xpdfKeyCodeMouseRelease7 0x2107 // ... #define xpdfKeyCodeMouseRelease32 0x2120 #define xpdfKeyModNone 0 #define xpdfKeyModShift (1 << 0) #define xpdfKeyModCtrl (1 << 1) #define xpdfKeyModAlt (1 << 2) #define xpdfKeyContextAny 0 #define xpdfKeyContextFullScreen (1 << 0) #define xpdfKeyContextWindow (2 << 0) #define xpdfKeyContextContinuous (1 << 2) #define xpdfKeyContextSinglePage (2 << 2) #define xpdfKeyContextOverLink (1 << 4) #define xpdfKeyContextOffLink (2 << 4) #define xpdfKeyContextOutline (1 << 6) #define xpdfKeyContextMainWin (2 << 6) #define xpdfKeyContextScrLockOn (1 << 8) #define xpdfKeyContextScrLockOff (2 << 8) //------------------------------------------------------------------------ class GlobalParams { public: // Initialize the global parameters by attempting to read a config // file. GlobalParams(const char *cfgFileName); ~GlobalParams(); void setBaseDir(char *dir); void setupBaseFonts(char *dir); void parseLine(char *buf, GString *fileName, int line); //----- accessors CharCode getMacRomanCharCode(char *charName); GString *getBaseDir(); Unicode mapNameToUnicode(const char *charName); UnicodeMap *getResidentUnicodeMap(GString *encodingName); FILE *getUnicodeMapFile(GString *encodingName); 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); GString *getPSFile(); int getPSPaperWidth(); int getPSPaperHeight(); void getPSImageableArea(int *llx, int *lly, int *urx, int *ury); GBool getPSDuplex(); GBool getPSCrop(); GBool getPSUseCropBoxAsPage(); GBool getPSExpandSmaller(); GBool getPSShrinkLarger(); GBool getPSCenter(); PSLevel getPSLevel(); GString *getPSResidentFont(GString *fontName); GList *getPSResidentFonts(); PSFontParam16 *getPSResidentFont16(GString *fontName, int wMode); PSFontParam16 *getPSResidentFontCC(GString *collection, int wMode); GBool getPSEmbedType1(); GBool getPSEmbedTrueType(); GBool getPSEmbedCIDPostScript(); GBool getPSEmbedCIDTrueType(); GBool getPSFontPassthrough(); GBool getPSPreload(); GBool getPSOPI(); GBool getPSASCIIHex(); GBool getPSLZW(); GBool getPSUncompressPreloadedImages(); double getPSMinLineWidth(); double getPSRasterResolution(); GBool getPSRasterMono(); int getPSRasterSliceSize(); GBool getPSAlwaysRasterize(); GString *getTextEncodingName(); EndOfLineKind getTextEOL(); GBool getTextPageBreaks(); GBool getTextKeepTinyChars(); GString *getInitialZoom(); GBool getContinuousView(); GBool getEnableFreeType(); GBool getDisableFreeTypeHinting(); GBool getAntialias(); GBool getVectorAntialias(); GBool getAntialiasPrinting(); GBool getStrokeAdjust(); ScreenType getScreenType(); int getScreenSize(); int getScreenDotRadius(); double getScreenGamma(); double getScreenBlackThreshold(); double getScreenWhiteThreshold(); double getMinLineWidth(); GBool getDrawAnnotations(); GBool getOverprintPreview() { return overprintPreview; } GString *getLaunchCommand() { return launchCommand; } GString *getURLCommand() { return urlCommand; } GString *getMovieCommand() { return movieCommand; } GBool getMapNumericCharNames(); GBool getMapUnknownCharNames(); GBool getMapExtTrueTypeFontsViaUnicode(); GBool getEnableXFA(); GList *getKeyBinding(int code, int mods, int context); GBool getPrintCommands(); GBool getErrQuiet(); CharCodeToUnicode *getCIDToUnicode(GString *collection); CharCodeToUnicode *getUnicodeToUnicode(GString *fontName); UnicodeMap *getUnicodeMap(GString *encodingName); CMap *getCMap(GString *collection, GString *cMapName); UnicodeMap *getTextEncoding(); //----- functions to set parameters void addFontFile(GString *fontName, GString *path); void setPSFile(char *file); GBool setPSPaperSize(char *size); void setPSPaperWidth(int width); void setPSPaperHeight(int height); void setPSImageableArea(int llx, int lly, int urx, int ury); void setPSDuplex(GBool duplex); void setPSCrop(GBool crop); void setPSUseCropBoxAsPage(GBool crop); void setPSExpandSmaller(GBool expand); void setPSShrinkLarger(GBool shrink); void setPSCenter(GBool center); void setPSLevel(PSLevel level); void setPSEmbedType1(GBool embed); void setPSEmbedTrueType(GBool embed); void setPSEmbedCIDPostScript(GBool embed); void setPSEmbedCIDTrueType(GBool embed); void setPSFontPassthrough(GBool passthrough); void setPSPreload(GBool preload); void setPSOPI(GBool opi); void setPSASCIIHex(GBool hex); 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 setEnableFreeType(char *s); GBool setAntialias(char *s); GBool setVectorAntialias(char *s); void setScreenType(ScreenType t); void setScreenSize(int size); void setScreenDotRadius(int r); void setScreenGamma(double gamma); void setScreenBlackThreshold(double thresh); void setScreenWhiteThreshold(double thresh); void setMapNumericCharNames(GBool map); void setMapUnknownCharNames(GBool map); void setMapExtTrueTypeFontsViaUnicode(GBool map); void setEnableXFA(GBool enable); void setPrintCommands(GBool printCommandsA); void setErrQuiet(GBool errQuietA); //----- security handlers void addSecurityHandler(XpdfSecurityHandler *handler); XpdfSecurityHandler *getSecurityHandler(char *name); private: void createDefaultKeyBindings(); void parseFile(GString *fileName, FILE *f); void parseNameToUnicode(GList *tokens, GString *fileName, int line); void parseCIDToUnicode(GList *tokens, GString *fileName, int line); void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line); void parseUnicodeMap(GList *tokens, GString *fileName, int line); void parseCMapDir(GList *tokens, GString *fileName, int line); void parseToUnicodeDir(GList *tokens, GString *fileName, int line); void parseFontFile(GList *tokens, GString *fileName, int line); void parseFontDir(GList *tokens, GString *fileName, int line); void parseFontFileCC(GList *tokens, GString *fileName, int line); void parsePSFile(GList *tokens, GString *fileName, int line); void parsePSPaperSize(GList *tokens, GString *fileName, int line); void parsePSImageableArea(GList *tokens, GString *fileName, int line); void parsePSLevel(GList *tokens, GString *fileName, int line); void parsePSResidentFont(GList *tokens, GString *fileName, int line); void parsePSResidentFont16(GList *tokens, GString *fileName, int line); void parsePSResidentFontCC(GList *tokens, GString *fileName, int line); void parseTextEncoding(GList *tokens, GString *fileName, int line); void parseTextEOL(GList *tokens, GString *fileName, int line); void parseInitialZoom(GList *tokens, GString *fileName, int line); void parseScreenType(GList *tokens, GString *fileName, int line); void parseBind(GList *tokens, GString *fileName, int line); void parseUnbind(GList *tokens, GString *fileName, int line); GBool parseKey(GString *modKeyStr, GString *contextStr, int *code, int *mods, int *context, const char *cmdName, GList *tokens, GString *fileName, int line); void parseCommand(const char *cmdName, GString **val, GList *tokens, GString *fileName, int line); void parseYesNo(const char *cmdName, GBool *flag, GList *tokens, GString *fileName, int line); GBool parseYesNo2(char *token, GBool *flag); void parseInteger(const char *cmdName, int *val, GList *tokens, GString *fileName, int line); void parseFloat(const char *cmdName, double *val, GList *tokens, GString *fileName, int line); UnicodeMap *getUnicodeMap2(GString *encodingName); #ifdef ENABLE_PLUGINS GBool loadPlugin(char *type, char *name); #endif //----- static tables NameToCharCode * // mapping from char name to macRomanReverseMap; // MacRomanEncoding index //----- user-modifiable settings GString *baseDir; // base directory - for plugins, etc. NameToCharCode * // mapping from char name to Unicode nameToUnicode; GHash *cidToUnicodes; // files for mappings from char collections // to Unicode, indexed by collection name // [GString] GHash *unicodeToUnicodes; // files for Unicode-to-Unicode mappings, // indexed by font name pattern [GString] GHash *residentUnicodeMaps; // mappings from Unicode to char codes, // indexed by encoding name [UnicodeMap] GHash *unicodeMaps; // files for mappings from Unicode to char // codes, indexed by encoding name [GString] GHash *cMapDirs; // list of CMap dirs, indexed by collection // name [GList[GString]] GList *toUnicodeDirs; // list of ToUnicode CMap dirs [GString] GHash *fontFiles; // font files: font name mapped to path // [GString] 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 int psPaperHeight; // PostScript output int psImageableLLX, // imageable area, in PostScript points, psImageableLLY, // for PostScript output psImageableURX, psImageableURY; GBool psCrop; // crop PS output to CropBox GBool 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 GBool psDuplex; // enable duplexing in PostScript? PSLevel psLevel; // PostScript level to generate GHash *psResidentFonts; // 8-bit fonts resident in printer: // PDF font name mapped to PS font name // [GString] GList *psResidentFonts16; // 16-bit fonts resident in printer: // PDF font name mapped to font info // [PSFontParam16] GList *psResidentFontsCC; // 16-bit character collection fonts // resident in printer: collection name // mapped to font info [PSFontParam16] GBool psEmbedType1; // embed Type 1 fonts? GBool psEmbedTrueType; // embed TrueType fonts? GBool psEmbedCIDPostScript; // embed CID PostScript fonts? GBool psEmbedCIDTrueType; // embed CID TrueType fonts? GBool psFontPassthrough; // pass all fonts through as-is? GBool psPreload; // preload PostScript images and forms into // 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 EndOfLineKind textEOL; // type of EOL marker to use for text // output GBool textPageBreaks; // insert end-of-page markers? GBool textKeepTinyChars; // keep all characters in text output GString *initialZoom; // initial zoom level GBool continuousView; // continuous view mode GBool enableFreeType; // FreeType enable flag GBool disableFreeTypeHinting; // FreeType hinting disable flag GBool antialias; // font anti-aliasing enable flag GBool vectorAntialias; // vector anti-aliasing enable flag GBool antialiasPrinting; // allow anti-aliasing when printing GBool strokeAdjust; // stroke adjustment enable flag ScreenType screenType; // halftone screen type int screenSize; // screen matrix size int screenDotRadius; // screen dot radius double screenGamma; // screen gamma correction double screenBlackThreshold; // screen black clamping threshold double screenWhiteThreshold; // screen white clamping threshold double minLineWidth; // minimum line width GBool drawAnnotations; // draw annotations or not GBool overprintPreview; // enable overprint preview GString *launchCommand; // command executed for 'launch' links GString *urlCommand; // command executed for URL links GString *movieCommand; // command executed for movie annotations GBool mapNumericCharNames; // map numeric char names (from font subsets)? GBool mapUnknownCharNames; // map unknown char names? 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? CharCodeToUnicodeCache *cidToUnicodeCache; CharCodeToUnicodeCache *unicodeToUnicodeCache; UnicodeMapCache *unicodeMapCache; CMapCache *cMapCache; #ifdef ENABLE_PLUGINS GList *plugins; // list of plugins [Plugin] GList *securityHandlers; // list of loaded security handlers // [XpdfSecurityHandler] #endif #if MULTITHREADED GMutex mutex; GMutex unicodeMapCacheMutex; GMutex cMapCacheMutex; #endif }; #endif xpdf-3.04/xpdf/ErrorCodes.h0000644000076400007640000000173012341430012015063 0ustar dereknderekn//======================================================================== // // ErrorCodes.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef ERRORCODES_H #define ERRORCODES_H #define errNone 0 // no error #define errOpenFile 1 // couldn't open the PDF file #define errBadCatalog 2 // couldn't read the page catalog #define errDamaged 3 // PDF file was damaged and couldn't be // repaired #define errEncrypted 4 // file was encrypted and password was // incorrect or not supplied #define errHighlightFile 5 // nonexistent or invalid highlight file #define errBadPrinter 6 // invalid printer #define errPrinting 7 // error during printing #define errPermission 8 // PDF file doesn't allow that operation #define errBadPageNum 9 // invalid page number #define errFileIO 10 // file I/O error #endif xpdf-3.04/xpdf/XPDFViewer.cc0000644000076400007640000033015612341430012015104 0ustar dereknderekn//======================================================================== // // XPDFViewer.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #ifdef HAVE_X11_XPM_H #include #endif #if defined(__sgi) && (XmVERSION <= 1) #define Object XtObject #include #undef Object #endif #include "gmem.h" #include "gfile.h" #include "GString.h" #include "GList.h" #include "Error.h" #include "GlobalParams.h" #include "PDFDoc.h" #include "Link.h" #include "ErrorCodes.h" #include "Outline.h" #include "UnicodeMap.h" #ifndef DISABLE_OUTLINE #define Object XtObject #include "XPDFTree.h" #undef Object #endif #include "XPDFApp.h" #include "XPDFViewer.h" #include "PSOutputDev.h" #include "config.h" // these macro defns conflict with xpdf's Object class #ifdef LESSTIF_VERSION #undef XtDisplay #undef XtScreen #undef XtWindow #undef XtParent #undef XtIsRealized #endif #if XmVERSION <= 1 #define XmSET True #define XmUNSET False #endif // hack around old X includes which are missing these symbols #ifndef XK_Page_Up #define XK_Page_Up 0xFF55 #endif #ifndef XK_Page_Down #define XK_Page_Down 0xFF56 #endif #ifndef XK_KP_Home #define XK_KP_Home 0xFF95 #endif #ifndef XK_KP_Left #define XK_KP_Left 0xFF96 #endif #ifndef XK_KP_Up #define XK_KP_Up 0xFF97 #endif #ifndef XK_KP_Right #define XK_KP_Right 0xFF98 #endif #ifndef XK_KP_Down #define XK_KP_Down 0xFF99 #endif #ifndef XK_KP_Prior #define XK_KP_Prior 0xFF9A #endif #ifndef XK_KP_Page_Up #define XK_KP_Page_Up 0xFF9A #endif #ifndef XK_KP_Next #define XK_KP_Next 0xFF9B #endif #ifndef XK_KP_Page_Down #define XK_KP_Page_Down 0xFF9B #endif #ifndef XK_KP_End #define XK_KP_End 0xFF9C #endif #ifndef XK_KP_Begin #define XK_KP_Begin 0xFF9D #endif #ifndef XK_KP_Insert #define XK_KP_Insert 0xFF9E #endif #ifndef XK_KP_Delete #define XK_KP_Delete 0xFF9F #endif //------------------------------------------------------------------------ // GUI includes //------------------------------------------------------------------------ #include "xpdfIcon.xpm" #include "leftArrow.xbm" #include "leftArrowDis.xbm" #include "dblLeftArrow.xbm" #include "dblLeftArrowDis.xbm" #include "rightArrow.xbm" #include "rightArrowDis.xbm" #include "dblRightArrow.xbm" #include "dblRightArrowDis.xbm" #include "backArrow.xbm" #include "backArrowDis.xbm" #include "forwardArrow.xbm" #include "forwardArrowDis.xbm" #include "find.xbm" #include "findDis.xbm" #include "print.xbm" #include "printDis.xbm" #include "about.xbm" #include "about-text.h" //------------------------------------------------------------------------ struct ZoomMenuInfo { const char *label; double zoom; }; static ZoomMenuInfo zoomMenuInfo[nZoomMenuItems] = { { "400%", 400 }, { "200%", 200 }, { "150%", 150 }, { "125%", 125 }, { "100%", 100 }, { "50%", 50 }, { "25%", 25 }, { "12.5%", 12.5 }, { "fit page", zoomPage }, { "fit width", zoomWidth } }; #define maxZoomIdx 0 #define defZoomIdx 3 #define minZoomIdx 7 #define zoomPageIdx 8 #define zoomWidthIdx 9 //------------------------------------------------------------------------ #define cmdMaxArgs 8 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 }, { "find", 0, gTrue, gFalse, &XPDFViewer::cmdFind }, { "findNext", 0, gTrue, gFalse, &XPDFViewer::cmdFindNext }, { "focusToDocWin", 0, gFalse, gFalse, &XPDFViewer::cmdFocusToDocWin }, { "focusToPageNum", 0, gFalse, gFalse, &XPDFViewer::cmdFocusToPageNum }, { "followLink", 0, gTrue, gTrue, &XPDFViewer::cmdFollowLink }, { "followLinkInNewWin", 0, gTrue, gTrue, &XPDFViewer::cmdFollowLinkInNewWin }, { "followLinkInNewWinNoSel", 0, gTrue, gTrue, &XPDFViewer::cmdFollowLinkInNewWinNoSel }, { "followLinkNoSel", 0, gTrue, gTrue, &XPDFViewer::cmdFollowLinkNoSel }, { "fullScreenMode", 0, gFalse, gFalse, &XPDFViewer::cmdFullScreenMode }, { "goBackward", 0, gFalse, gFalse, &XPDFViewer::cmdGoBackward }, { "goForward", 0, gFalse, gFalse, &XPDFViewer::cmdGoForward }, { "gotoDest", 1, gTrue, gFalse, &XPDFViewer::cmdGotoDest }, { "gotoLastPage", 0, gTrue, gFalse, &XPDFViewer::cmdGotoLastPage }, { "gotoLastPageNoScroll", 0, gTrue, gFalse, &XPDFViewer::cmdGotoLastPageNoScroll }, { "gotoPage", 1, gTrue, gFalse, &XPDFViewer::cmdGotoPage }, { "gotoPageNoScroll", 1, gTrue, gFalse, &XPDFViewer::cmdGotoPageNoScroll }, { "nextPage", 0, gTrue, gFalse, &XPDFViewer::cmdNextPage }, { "nextPageNoScroll", 0, gTrue, gFalse, &XPDFViewer::cmdNextPageNoScroll }, { "open", 0, gFalse, gFalse, &XPDFViewer::cmdOpen }, { "openFile", 1, gFalse, gFalse, &XPDFViewer::cmdOpenFile }, { "openFileAtDest", 2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtDest }, { "openFileAtDestInNewWin", 2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtDestInNewWin }, { "openFileAtPage", 2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtPage }, { "openFileAtPageInNewWin", 2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtPageInNewWin }, { "openFileInNewWin", 1, gFalse, gFalse, &XPDFViewer::cmdOpenFileInNewWin }, { "openInNewWin", 0, gFalse, gFalse, &XPDFViewer::cmdOpenInNewWin }, { "openOutline", 0, gFalse, gFalse, &XPDFViewer::cmdOpenOutline }, { "pageDown", 0, gTrue, gFalse, &XPDFViewer::cmdPageDown }, { "pageUp", 0, gTrue, gFalse, &XPDFViewer::cmdPageUp }, { "postPopupMenu", 0, gFalse, gTrue, &XPDFViewer::cmdPostPopupMenu }, { "prevPage", 0, gTrue, gFalse, &XPDFViewer::cmdPrevPage }, { "prevPageNoScroll", 0, gTrue, gFalse, &XPDFViewer::cmdPrevPageNoScroll }, { "print", 0, gTrue, gFalse, &XPDFViewer::cmdPrint }, { "quit", 0, gFalse, gFalse, &XPDFViewer::cmdQuit }, { "raise", 0, gFalse, gFalse, &XPDFViewer::cmdRaise }, { "redraw", 0, gTrue, gFalse, &XPDFViewer::cmdRedraw }, { "reload", 0, gTrue, gFalse, &XPDFViewer::cmdReload }, { "rotateCCW", 0, gTrue, gFalse, &XPDFViewer::cmdRotateCCW }, { "rotateCW", 0, gTrue, gFalse, &XPDFViewer::cmdRotateCW }, { "run", 1, gFalse, gFalse, &XPDFViewer::cmdRun }, { "scrollDown", 1, gTrue, gFalse, &XPDFViewer::cmdScrollDown }, { "scrollDownNextPage", 1, gTrue, gFalse, &XPDFViewer::cmdScrollDownNextPage }, { "scrollLeft", 1, gTrue, gFalse, &XPDFViewer::cmdScrollLeft }, { "scrollOutlineDown", 1, gTrue, gFalse, &XPDFViewer::cmdScrollOutlineDown }, { "scrollOutlineUp", 1, gTrue, gFalse, &XPDFViewer::cmdScrollOutlineUp }, { "scrollRight", 1, gTrue, gFalse, &XPDFViewer::cmdScrollRight }, { "scrollToBottomEdge", 0, gTrue, gFalse, &XPDFViewer::cmdScrollToBottomEdge }, { "scrollToBottomRight", 0, gTrue, gFalse, &XPDFViewer::cmdScrollToBottomRight }, { "scrollToLeftEdge", 0, gTrue, gFalse, &XPDFViewer::cmdScrollToLeftEdge }, { "scrollToRightEdge", 0, gTrue, gFalse, &XPDFViewer::cmdScrollToRightEdge }, { "scrollToTopEdge", 0, gTrue, gFalse, &XPDFViewer::cmdScrollToTopEdge }, { "scrollToTopLeft", 0, gTrue, gFalse, &XPDFViewer::cmdScrollToTopLeft }, { "scrollUp", 1, gTrue, gFalse, &XPDFViewer::cmdScrollUp }, { "scrollUpPrevPage", 1, gTrue, gFalse, &XPDFViewer::cmdScrollUpPrevPage }, { "setSelection", 5, gTrue, gFalse, &XPDFViewer::cmdSetSelection }, { "singlePageMode", 0, gFalse, gFalse, &XPDFViewer::cmdSinglePageMode }, { "startPan", 0, gTrue, gTrue, &XPDFViewer::cmdStartPan }, { "startSelection", 0, gTrue, gTrue, &XPDFViewer::cmdStartSelection }, { "toggleContinuousMode", 0, gFalse, gFalse, &XPDFViewer::cmdToggleContinuousMode }, { "toggleFullScreenMode", 0, gFalse, gFalse, &XPDFViewer::cmdToggleFullScreenMode }, { "toggleOutline", 0, gFalse, gFalse, &XPDFViewer::cmdToggleOutline }, { "windowMode", 0, gFalse, gFalse, &XPDFViewer::cmdWindowMode }, { "zoomFitPage", 0, gFalse, gFalse, &XPDFViewer::cmdZoomFitPage }, { "zoomFitWidth", 0, gFalse, gFalse, &XPDFViewer::cmdZoomFitWidth }, { "zoomIn", 0, gFalse, gFalse, &XPDFViewer::cmdZoomIn }, { "zoomOut", 0, gFalse, gFalse, &XPDFViewer::cmdZoomOut }, { "zoomPercent", 1, gFalse, gFalse, &XPDFViewer::cmdZoomPercent }, { "zoomToSelection", 0, gTrue, gFalse, &XPDFViewer::cmdZoomToSelection } }; #define nCmds (sizeof(cmdTab) / sizeof(XPDFViewerCmd)) //------------------------------------------------------------------------ XPDFViewer::XPDFViewer(XPDFApp *appA, GString *fileName, int pageA, GString *destName, GBool fullScreen, GString *ownerPassword, GString *userPassword) { LinkDest *dest; int pg; double z; app = appA; win = NULL; core = NULL; ok = gFalse; #ifndef DISABLE_OUTLINE outlineLabels = NULL; outlineLabelsLength = outlineLabelsSize = 0; outlinePaneWidth = 175; #endif // do Motif-specific initialization and create the window; // this also creates the core object initWindow(fullScreen); initAboutDialog(); initFindDialog(); initPrintDialog(); openDialog = NULL; saveAsDialog = NULL; dest = NULL; // make gcc happy pg = pageA; // make gcc happy if (fileName) { if (loadFile(fileName, ownerPassword, userPassword)) { getPageAndDest(pageA, destName, &pg, &dest); #ifndef DISABLE_OUTLINE if (outlineScroll != None && core->getDoc()->getOutline()->getItems() && core->getDoc()->getOutline()->getItems()->getLength() > 0) { XtVaSetValues(outlineScroll, XmNwidth, outlinePaneWidth, NULL); } #endif } else { return; } } core->resizeToPage(pg); // map the window -- we do this after calling resizeToPage to avoid // an annoying on-screen resize mapWindow(); // display the first page z = core->getZoom(); if (dest) { displayDest(dest, z, core->getRotate(), gTrue); delete dest; } else { displayPage(pg, z, core->getRotate(), gTrue, gTrue); } ok = gTrue; } XPDFViewer::XPDFViewer(XPDFApp *appA, PDFDoc *doc, int pageA, GString *destName, GBool fullScreen) { LinkDest *dest; int pg; double z; app = appA; win = NULL; core = NULL; ok = gFalse; #ifndef DISABLE_OUTLINE outlineLabels = NULL; outlineLabelsLength = outlineLabelsSize = 0; outlinePaneWidth = 175; #endif // do Motif-specific initialization and create the window; // this also creates the core object initWindow(fullScreen); initAboutDialog(); initFindDialog(); initPrintDialog(); openDialog = NULL; saveAsDialog = NULL; dest = NULL; // make gcc happy pg = pageA; // make gcc happy if (doc) { core->loadDoc(doc); getPageAndDest(pageA, destName, &pg, &dest); #ifndef DISABLE_OUTLINE if (outlineScroll != None && core->getDoc()->getOutline()->getItems() && core->getDoc()->getOutline()->getItems()->getLength() > 0) { XtVaSetValues(outlineScroll, XmNwidth, outlinePaneWidth, NULL); } #endif } core->resizeToPage(pg); // map the window -- we do this after calling resizeToPage to avoid // an annoying on-screen resize mapWindow(); // display the first page z = core->getZoom(); if (dest) { displayDest(dest, z, core->getRotate(), gTrue); delete dest; } else { displayPage(pg, z, core->getRotate(), gTrue, gTrue); } ok = gTrue; } XPDFViewer::~XPDFViewer() { delete core; if (aboutBigFont) { XmFontListFree(aboutBigFont); } if (aboutVersionFont) { XmFontListFree(aboutVersionFont); } if (aboutFixedFont) { XmFontListFree(aboutFixedFont); } closeWindow(); #ifndef DISABLE_OUTLINE if (outlineLabels) { gfree(outlineLabels); } #endif } void XPDFViewer::open(GString *fileName, int pageA, GString *destName) { LinkDest *dest; int pg; double z; if (!core->getDoc() || !core->getDoc()->getFileName() || fileName->cmp(core->getDoc()->getFileName())) { if (!loadFile(fileName, NULL, NULL)) { return; } } getPageAndDest(pageA, destName, &pg, &dest); z = core->getZoom(); if (dest) { displayDest(dest, z, core->getRotate(), gTrue); delete dest; } else { displayPage(pg, z, core->getRotate(), gTrue, gTrue); } } void XPDFViewer::clear() { char *title; XmString s; core->clear(); // set up title title = app->getTitle() ? app->getTitle()->getCString() : (char *)xpdfAppName; XtVaSetValues(win, XmNtitle, title, XmNiconName, title, NULL); if (toolBar != None) { // set up number-of-pages display s = XmStringCreateLocalized(""); XtVaSetValues(pageNumText, XmNlabelString, s, NULL); XmStringFree(s); s = XmStringCreateLocalized(" of 0"); XtVaSetValues(pageCountLabel, XmNlabelString, s, NULL); XmStringFree(s); // disable buttons XtVaSetValues(prevTenPageBtn, XmNsensitive, False, NULL); XtVaSetValues(prevPageBtn, XmNsensitive, False, NULL); XtVaSetValues(nextTenPageBtn, XmNsensitive, False, NULL); XtVaSetValues(nextPageBtn, XmNsensitive, False, NULL); } // remove the old outline #ifndef DISABLE_OUTLINE setupOutline(); #endif } //------------------------------------------------------------------------ // load / display //------------------------------------------------------------------------ GBool XPDFViewer::loadFile(GString *fileName, GString *ownerPassword, GString *userPassword) { return core->loadFile(fileName, ownerPassword, userPassword) == errNone; } void XPDFViewer::reloadFile() { int pg; if (!core->getDoc() || !core->getDoc()->getFileName()) { return; } pg = core->getPageNum(); loadFile(core->getDoc()->getFileName()); if (pg > core->getDoc()->getNumPages()) { pg = core->getDoc()->getNumPages(); } displayPage(pg, core->getZoom(), core->getRotate(), gFalse, gFalse); } void XPDFViewer::displayPage(int pageA, double zoomA, int rotateA, GBool scrollToTop, GBool addToHist) { core->displayPage(pageA, zoomA, rotateA, scrollToTop, addToHist); } void XPDFViewer::displayDest(LinkDest *dest, double zoomA, int rotateA, GBool addToHist) { core->displayDest(dest, zoomA, rotateA, addToHist); } void XPDFViewer::getPageAndDest(int pageA, GString *destName, int *pageOut, LinkDest **destOut) { Ref pageRef; // find the page number for a named destination *pageOut = pageA; *destOut = NULL; if (destName && (*destOut = core->getDoc()->findDest(destName))) { if ((*destOut)->isPageRef()) { pageRef = (*destOut)->getPageRef(); *pageOut = core->getDoc()->findPage(pageRef.num, pageRef.gen); } else { *pageOut = (*destOut)->getPageNum(); } } if (*pageOut <= 0) { *pageOut = 1; } if (*pageOut > core->getDoc()->getNumPages()) { *pageOut = core->getDoc()->getNumPages(); } } //------------------------------------------------------------------------ // hyperlinks / actions //------------------------------------------------------------------------ void XPDFViewer::doLink(int wx, int wy, GBool onlyIfNoSelection, GBool newWin) { XPDFViewer *newViewer; LinkAction *action; int pg, selPg; double xu, yu, selULX, selULY, selLRX, selLRY; if (core->getHyperlinksEnabled() && core->cvtWindowToUser(wx, wy, &pg, &xu, &yu) && !(onlyIfNoSelection && core->getSelection(&selPg, &selULX, &selULY, &selLRX, &selLRY))) { if ((action = core->findLink(pg, xu, yu))) { if (newWin && core->getDoc()->getFileName() && (action->getKind() == actionGoTo || action->getKind() == actionGoToR || (action->getKind() == actionNamed && ((LinkNamed *)action)->getName()->cmp("Quit")))) { newViewer = app->open(core->getDoc()->getFileName()); newViewer->core->doAction(action); } else { core->doAction(action); } } } } void XPDFViewer::actionCbk(void *data, char *action) { XPDFViewer *viewer = (XPDFViewer *)data; if (!strcmp(action, "Quit")) { viewer->app->quit(); } } //------------------------------------------------------------------------ // keyboard/mouse input //------------------------------------------------------------------------ void XPDFViewer::keyPressCbk(void *data, KeySym key, Guint modifiers, XEvent *event) { XPDFViewer *viewer = (XPDFViewer *)data; int keyCode; GList *cmds; int i; if (key >= 0x20 && key <= 0xfe) { keyCode = (int)key; } else if (key == XK_Tab || key == XK_KP_Tab) { keyCode = xpdfKeyCodeTab; } else if (key == XK_Return) { keyCode = xpdfKeyCodeReturn; } else if (key == XK_KP_Enter) { keyCode = xpdfKeyCodeEnter; } else if (key == XK_BackSpace) { keyCode = xpdfKeyCodeBackspace; } else if (key == XK_Insert || key == XK_KP_Insert) { keyCode = xpdfKeyCodeInsert; } else if (key == XK_Delete || key == XK_KP_Delete) { keyCode = xpdfKeyCodeDelete; } else if (key == XK_Home || key == XK_KP_Home) { keyCode = xpdfKeyCodeHome; } else if (key == XK_End || key == XK_KP_End) { keyCode = xpdfKeyCodeEnd; } else if (key == XK_Page_Up || key == XK_KP_Page_Up) { keyCode = xpdfKeyCodePgUp; } else if (key == XK_Page_Down || key == XK_KP_Page_Down) { keyCode = xpdfKeyCodePgDn; } else if (key == XK_Left || key == XK_KP_Left) { keyCode = xpdfKeyCodeLeft; } else if (key == XK_Right || key == XK_KP_Right) { keyCode = xpdfKeyCodeRight; } else if (key == XK_Up || key == XK_KP_Up) { keyCode = xpdfKeyCodeUp; } else if (key == XK_Down || key == XK_KP_Down) { keyCode = xpdfKeyCodeDown; } else if (key >= XK_F1 && key <= XK_F35) { keyCode = xpdfKeyCodeF1 + (key - XK_F1); } else { return; } if ((cmds = globalParams->getKeyBinding(keyCode, viewer->getModifiers(modifiers), viewer->getContext(modifiers)))) { for (i = 0; i < cmds->getLength(); ++i) { viewer->execCmd((GString *)cmds->get(i), event); } deleteGList(cmds, GString); } } void XPDFViewer::mouseCbk(void *data, XEvent *event) { XPDFViewer *viewer = (XPDFViewer *)data; int keyCode; GList *cmds; int i; if (event->type == ButtonPress) { if (event->xbutton.button >= 1 && event->xbutton.button <= 32) { keyCode = xpdfKeyCodeMousePress1 + event->xbutton.button - 1; } else { return; } } else if (event->type == ButtonRelease) { if (event->xbutton.button >= 1 && event->xbutton.button <= 32) { keyCode = xpdfKeyCodeMouseRelease1 + event->xbutton.button - 1; } else { return; } } else { return; } if ((cmds = globalParams->getKeyBinding(keyCode, viewer->getModifiers( event->xkey.state), viewer->getContext( event->xkey.state)))) { for (i = 0; i < cmds->getLength(); ++i) { viewer->execCmd((GString *)cmds->get(i), event); } deleteGList(cmds, GString); } } int XPDFViewer::getModifiers(Guint modifiers) { int mods; mods = 0; if (modifiers & ShiftMask) { mods |= xpdfKeyModShift; } if (modifiers & ControlMask) { mods |= xpdfKeyModCtrl; } if (modifiers & Mod1Mask) { mods |= xpdfKeyModAlt; } return mods; } int XPDFViewer::getContext(Guint modifiers) { int context; context = (core->getFullScreen() ? xpdfKeyContextFullScreen : xpdfKeyContextWindow) | (core->getContinuousMode() ? xpdfKeyContextContinuous : xpdfKeyContextSinglePage) | (core->getLinkAction() ? xpdfKeyContextOverLink : xpdfKeyContextOffLink) | ((modifiers & Mod5Mask) ? xpdfKeyContextScrLockOn : xpdfKeyContextScrLockOff); return context; } void XPDFViewer::execCmd(GString *cmd, XEvent *event) { GString *name; GString *args[cmdMaxArgs]; char *p0, *p1; int nArgs, i; int a, b, m, cmp; //----- parse the command name = NULL; nArgs = 0; for (i = 0; i < cmdMaxArgs; ++i) { args[i] = NULL; } p0 = cmd->getCString(); for (p1 = p0; *p1 && isalnum(*p1); ++p1) ; if (p1 == p0) { goto err1; } name = new GString(p0, p1 - p0); if (*p1 == '(') { while (nArgs < cmdMaxArgs) { p0 = p1 + 1; for (p1 = p0; *p1 && *p1 != ',' && *p1 != ')'; ++p1) ; args[nArgs++] = new GString(p0, p1 - p0); if (*p1 != ',') { break; } } if (*p1 != ')') { goto err1; } ++p1; } if (*p1) { goto err1; } //----- find the command a = -1; b = nCmds; // invariant: cmdTab[a].name < name < cmdTab[b].name while (b - a > 1) { m = (a + b) / 2; cmp = strcmp(cmdTab[m].name, name->getCString()); if (cmp < 0) { a = m; } else if (cmp > 0) { b = m; } else { a = b = m; } } if (cmp != 0) { goto err1; } //----- execute the command if (nArgs != cmdTab[a].nArgs || (cmdTab[a].requiresEvent && !event)) { goto err1; } if (cmdTab[a].requiresDoc && !core->getDoc()) { // don't issue an error message for this -- it happens, e.g., when // clicking in a window with no open PDF file goto err2; } (this->*cmdTab[a].func)(args, nArgs, event); //----- clean up delete name; for (i = 0; i < nArgs; ++i) { if (args[i]) { delete args[i]; } } return; err1: error(errConfig, -1, "Invalid command syntax: '{0:t}'", cmd); err2: if (name) { delete name; } for (i = 0; i < nArgs; ++i) { if (args[i]) { delete args[i]; } } } //------------------------------------------------------------------------ // command functions //------------------------------------------------------------------------ static int mouseX(XEvent *event) { switch (event->type) { case ButtonPress: case ButtonRelease: return event->xbutton.x; case KeyPress: return event->xkey.x; } return 0; } static int mouseY(XEvent *event) { switch (event->type) { case ButtonPress: case ButtonRelease: return event->xbutton.y; case KeyPress: return event->xkey.y; } return 0; } void XPDFViewer::cmdAbout(GString *args[], int nArgs, XEvent *event) { XtManageChild(aboutDialog); } void XPDFViewer::cmdCloseOutline(GString *args[], int nArgs, XEvent *event) { #ifndef DISABLE_OUTLINE Dimension w; if (outlineScroll == None) { return; } XtVaGetValues(outlineScroll, XmNwidth, &w, NULL); if (w > 1) { outlinePaneWidth = w; // this ugly kludge is apparently the only way to resize the panes // within an XmPanedWindow XtVaSetValues(outlineScroll, XmNpaneMinimum, 1, XmNpaneMaximum, 1, NULL); XtVaSetValues(outlineScroll, XmNpaneMinimum, 1, XmNpaneMaximum, 10000, NULL); } #endif } void XPDFViewer::cmdCloseWindow(GString *args[], int nArgs, XEvent *event) { app->close(this, gFalse); } void XPDFViewer::cmdCloseWindowOrQuit(GString *args[], int nArgs, XEvent *event) { app->close(this, gTrue); } void XPDFViewer::cmdContinuousMode(GString *args[], int nArgs, XEvent *event) { Widget btn; if (core->getContinuousMode()) { return; } core->setContinuousMode(gTrue); btn = XtNameToWidget(popupMenu, "continuousMode"); XtVaSetValues(btn, XmNset, XmSET, NULL); } void XPDFViewer::cmdEndPan(GString *args[], int nArgs, XEvent *event) { core->endPan(mouseX(event), mouseY(event)); } void XPDFViewer::cmdEndSelection(GString *args[], int nArgs, XEvent *event) { core->endSelection(mouseX(event), mouseY(event)); } void XPDFViewer::cmdFind(GString *args[], int nArgs, XEvent *event) { mapFindDialog(); } void XPDFViewer::cmdFindNext(GString *args[], int nArgs, XEvent *event) { doFind(gTrue); } void XPDFViewer::cmdFocusToDocWin(GString *args[], int nArgs, XEvent *event) { core->takeFocus(); } void XPDFViewer::cmdFocusToPageNum(GString *args[], int nArgs, XEvent *event) { if (toolBar != None) { XmTextFieldSetSelection(pageNumText, 0, strlen(XmTextFieldGetString(pageNumText)), XtLastTimestampProcessed(display)); XmProcessTraversal(pageNumText, XmTRAVERSE_CURRENT); } } void XPDFViewer::cmdFollowLink(GString *args[], int nArgs, XEvent *event) { doLink(mouseX(event), mouseY(event), gFalse, gFalse); } void XPDFViewer::cmdFollowLinkInNewWin(GString *args[], int nArgs, XEvent *event) { doLink(mouseX(event), mouseY(event), gFalse, gTrue); } void XPDFViewer::cmdFollowLinkInNewWinNoSel(GString *args[], int nArgs, XEvent *event) { doLink(mouseX(event), mouseY(event), gTrue, gTrue); } void XPDFViewer::cmdFollowLinkNoSel(GString *args[], int nArgs, XEvent *event) { doLink(mouseX(event), mouseY(event), gTrue, gFalse); } void XPDFViewer::cmdFullScreenMode(GString *args[], int nArgs, XEvent *event) { PDFDoc *doc; XPDFViewer *viewer; int pg; Widget btn; if (core->getFullScreen()) { return; } pg = core->getPageNum(); XtPopdown(win); doc = core->takeDoc(gFalse); viewer = app->reopen(this, doc, pg, gTrue); btn = XtNameToWidget(viewer->popupMenu, "fullScreen"); XtVaSetValues(btn, XmNset, XmSET, NULL); } void XPDFViewer::cmdGoBackward(GString *args[], int nArgs, XEvent *event) { core->goBackward(); } void XPDFViewer::cmdGoForward(GString *args[], int nArgs, XEvent *event) { core->goForward(); } void XPDFViewer::cmdGotoDest(GString *args[], int nArgs, XEvent *event) { int pg; LinkDest *dest; getPageAndDest(1, args[0], &pg, &dest); if (dest) { displayDest(dest, core->getZoom(), core->getRotate(), gTrue); delete dest; } } void XPDFViewer::cmdGotoLastPage(GString *args[], int nArgs, XEvent *event) { displayPage(core->getDoc()->getNumPages(), core->getZoom(), core->getRotate(), gTrue, gTrue); } void XPDFViewer::cmdGotoLastPageNoScroll(GString *args[], int nArgs, XEvent *event) { displayPage(core->getDoc()->getNumPages(), core->getZoom(), core->getRotate(), gFalse, gTrue); } void XPDFViewer::cmdGotoPage(GString *args[], int nArgs, XEvent *event) { int pg; pg = atoi(args[0]->getCString()); if (pg < 1 || pg > core->getDoc()->getNumPages()) { return; } displayPage(pg, core->getZoom(), core->getRotate(), gTrue, gTrue); } void XPDFViewer::cmdGotoPageNoScroll(GString *args[], int nArgs, XEvent *event) { int pg; pg = atoi(args[0]->getCString()); if (pg < 1 || pg > core->getDoc()->getNumPages()) { return; } displayPage(pg, core->getZoom(), core->getRotate(), gFalse, gTrue); } void XPDFViewer::cmdNextPage(GString *args[], int nArgs, XEvent *event) { core->gotoNextPage(1, gTrue); } void XPDFViewer::cmdNextPageNoScroll(GString *args[], int nArgs, XEvent *event) { core->gotoNextPage(1, gFalse); } void XPDFViewer::cmdOpen(GString *args[], int nArgs, XEvent *event) { mapOpenDialog(gFalse); } void XPDFViewer::cmdOpenFile(GString *args[], int nArgs, XEvent *event) { open(args[0], 1, NULL); } void XPDFViewer::cmdOpenFileAtDest(GString *args[], int nArgs, XEvent *event) { open(args[0], 1, args[1]); } void XPDFViewer::cmdOpenFileAtDestInNewWin(GString *args[], int nArgs, XEvent *event) { app->openAtDest(args[0], args[1]); } void XPDFViewer::cmdOpenFileAtPage(GString *args[], int nArgs, XEvent *event) { open(args[0], atoi(args[1]->getCString()), NULL); } void XPDFViewer::cmdOpenFileAtPageInNewWin(GString *args[], int nArgs, XEvent *event) { app->open(args[0], atoi(args[1]->getCString())); } void XPDFViewer::cmdOpenFileInNewWin(GString *args[], int nArgs, XEvent *event) { app->open(args[0]); } void XPDFViewer::cmdOpenInNewWin(GString *args[], int nArgs, XEvent *event) { mapOpenDialog(gTrue); } void XPDFViewer::cmdOpenOutline(GString *args[], int nArgs, XEvent *event) { #ifndef DISABLE_OUTLINE Dimension w; if (outlineScroll == None) { return; } XtVaGetValues(outlineScroll, XmNwidth, &w, NULL); if (w == 1) { // this ugly kludge is apparently the only way to resize the panes // within an XmPanedWindow XtVaSetValues(outlineScroll, XmNpaneMinimum, outlinePaneWidth, XmNpaneMaximum, outlinePaneWidth, NULL); XtVaSetValues(outlineScroll, XmNpaneMinimum, 1, XmNpaneMaximum, 10000, NULL); } #endif } void XPDFViewer::cmdPageDown(GString *args[], int nArgs, XEvent *event) { core->scrollPageDown(); } void XPDFViewer::cmdPageUp(GString *args[], int nArgs, XEvent *event) { core->scrollPageUp(); } void XPDFViewer::cmdPostPopupMenu(GString *args[], int nArgs, XEvent *event) { XmMenuPosition(popupMenu, event->type == ButtonPress ? &event->xbutton : (XButtonEvent *)NULL); XtManageChild(popupMenu); // this is magic (taken from DDD) - weird things happen if this // call isn't made (this is done in two different places, in hopes // of squashing this stupid bug) XtUngrabButton(core->getDrawAreaWidget(), AnyButton, AnyModifier); } void XPDFViewer::cmdPrevPage(GString *args[], int nArgs, XEvent *event) { core->gotoPrevPage(1, gTrue, gFalse); } void XPDFViewer::cmdPrevPageNoScroll(GString *args[], int nArgs, XEvent *event) { core->gotoPrevPage(1, gFalse, gFalse); } void XPDFViewer::cmdPrint(GString *args[], int nArgs, XEvent *event) { XtManageChild(printDialog); } void XPDFViewer::cmdQuit(GString *args[], int nArgs, XEvent *event) { app->quit(); } void XPDFViewer::cmdRaise(GString *args[], int nArgs, XEvent *event) { XMapRaised(display, XtWindow(win)); XFlush(display); } void XPDFViewer::cmdRedraw(GString *args[], int nArgs, XEvent *event) { displayPage(core->getPageNum(), core->getZoom(), core->getRotate(), gFalse, gFalse); } void XPDFViewer::cmdReload(GString *args[], int nArgs, XEvent *event) { reloadFile(); } void XPDFViewer::cmdRotateCCW(GString *args[], int nArgs, XEvent *event) { int r; r = core->getRotate(); r = (r == 0) ? 270 : r - 90; displayPage(core->getPageNum(), core->getZoom(), r, gTrue, gFalse); } void XPDFViewer::cmdRotateCW(GString *args[], int nArgs, XEvent *event) { int r; r = core->getRotate(); r = (r == 270) ? 0 : r + 90; displayPage(core->getPageNum(), core->getZoom(), r, gTrue, gFalse); } void XPDFViewer::cmdRun(GString *args[], int nArgs, XEvent *event) { GString *fmt, *cmd, *s; LinkAction *action; double selLRX, selLRY, selURX, selURY, mouseX, mouseY; int selPage, mousePage; GBool gotSel, gotMouse; char buf[64]; char *p; char c0, c1; int i; cmd = new GString(); fmt = args[0]; i = 0; gotSel = gotMouse = gFalse; while (i < fmt->getLength()) { c0 = fmt->getChar(i); if (c0 == '%' && i+1 < fmt->getLength()) { c1 = fmt->getChar(i+1); switch (c1) { case 'f': if (core->getDoc() && (s = core->getDoc()->getFileName())) { cmd->append(s); } break; case 'b': if (core->getDoc() && (s = core->getDoc()->getFileName())) { if ((p = strrchr(s->getCString(), '.'))) { cmd->append(s->getCString(), p - s->getCString()); } else { cmd->append(s); } } break; case 'u': if ((action = core->getLinkAction()) && action->getKind() == actionURI) { s = core->mungeURL(((LinkURI *)action)->getURI()); cmd->append(s); delete s; } break; case 'p': if (core->getDoc()) { sprintf(buf, "%d", core->getPageNum()); cmd->append(buf); } break; case 'x': case 'y': case 'X': case 'Y': if (!gotSel) { if (!core->getSelection(&selPage, &selURX, &selURY, &selLRX, &selLRY)) { selPage = 0; selURX = selURY = selLRX = selLRY = 0; } gotSel = gTrue; } sprintf(buf, "%g", (c1 == 'x') ? selURX : (c1 == 'y') ? selURY : (c1 == 'X') ? selLRX : selLRY); cmd->append(buf); break; case 'i': case 'j': case 'k': if (!gotMouse) { if (event->type == ButtonPress || event->type == ButtonRelease) { core->cvtWindowToUser(event->xbutton.x, event->xbutton.y, &mousePage, &mouseX, &mouseY); } else if (event->type == KeyPress) { core->cvtWindowToUser(event->xkey.x, event->xkey.y, &mousePage, &mouseX, &mouseY); } else { mousePage = 0; mouseX = mouseY = 0; } gotMouse = gTrue; } if (c1 == 'i') { sprintf(buf, "%d", mousePage); } else { sprintf(buf, "%g", (c1 == 'j') ? mouseX : mouseY); } cmd->append(buf); break; default: cmd->append(c1); break; } i += 2; } else { cmd->append(c0); ++i; } } #ifdef VMS cmd->insert(0, "spawn/nowait "); #elif defined(__EMX__) cmd->insert(0, "start /min /n "); #else cmd->append(" &"); #endif system(cmd->getCString()); delete cmd; } void XPDFViewer::cmdScrollDown(GString *args[], int nArgs, XEvent *event) { core->scrollDown(atoi(args[0]->getCString())); } void XPDFViewer::cmdScrollDownNextPage(GString *args[], int nArgs, XEvent *event) { core->scrollDownNextPage(atoi(args[0]->getCString())); } void XPDFViewer::cmdScrollLeft(GString *args[], int nArgs, XEvent *event) { core->scrollLeft(atoi(args[0]->getCString())); } void XPDFViewer::cmdScrollOutlineDown(GString *args[], int nArgs, XEvent *event) { #ifndef DISABLE_OUTLINE Widget sb; int val, inc, pageInc, m, slider; if (outlineScroll == None) { return; } if ((sb = XtNameToWidget(outlineScroll, "VertScrollBar"))) { XtVaGetValues(sb, XmNvalue, &val, XmNincrement, &inc, XmNpageIncrement, &pageInc, XmNmaximum, &m, XmNsliderSize, &slider, NULL); if ((val += inc * atoi(args[0]->getCString())) > m - slider) { val = m - slider; } XmScrollBarSetValues(sb, val, slider, inc, pageInc, True); } #endif } void XPDFViewer::cmdScrollOutlineUp(GString *args[], int nArgs, XEvent *event) { #ifndef DISABLE_OUTLINE Widget sb; int val, inc, pageInc, m, slider; if (outlineScroll == None) { return; } if ((sb = XtNameToWidget(outlineScroll, "VertScrollBar"))) { XtVaGetValues(sb, XmNvalue, &val, XmNincrement, &inc, XmNpageIncrement, &pageInc, XmNminimum, &m, XmNsliderSize, &slider, NULL); if ((val -= inc * atoi(args[0]->getCString())) < m) { val = m; } XmScrollBarSetValues(sb, val, slider, inc, pageInc, True); } #endif } void XPDFViewer::cmdScrollRight(GString *args[], int nArgs, XEvent *event) { core->scrollRight(atoi(args[0]->getCString())); } void XPDFViewer::cmdScrollToBottomEdge(GString *args[], int nArgs, XEvent *event) { core->scrollToBottomEdge(); } void XPDFViewer::cmdScrollToBottomRight(GString *args[], int nArgs, XEvent *event) { core->scrollToBottomRight(); } void XPDFViewer::cmdScrollToLeftEdge(GString *args[], int nArgs, XEvent *event) { core->scrollToLeftEdge(); } void XPDFViewer::cmdScrollToRightEdge(GString *args[], int nArgs, XEvent *event) { core->scrollToRightEdge(); } void XPDFViewer::cmdScrollToTopEdge(GString *args[], int nArgs, XEvent *event) { core->scrollToTopEdge(); } void XPDFViewer::cmdScrollToTopLeft(GString *args[], int nArgs, XEvent *event) { core->scrollToTopLeft(); } void XPDFViewer::cmdScrollUp(GString *args[], int nArgs, XEvent *event) { core->scrollUp(atoi(args[0]->getCString())); } void XPDFViewer::cmdScrollUpPrevPage(GString *args[], int nArgs, XEvent *event) { core->scrollUpPrevPage(atoi(args[0]->getCString())); } void XPDFViewer::cmdSetSelection(GString *args[], int nArgs, XEvent *event) { int pg, ulx, uly, lrx, lry; pg = atoi(args[0]->getCString()); core->cvtUserToDev(core->getPageNum(), atof(args[1]->getCString()), atof(args[2]->getCString()), &ulx, &uly); core->cvtUserToDev(core->getPageNum(), atof(args[3]->getCString()), atof(args[4]->getCString()), &lrx, &lry); core->setSelection(pg, ulx, uly, lrx, lry); } void XPDFViewer::cmdSinglePageMode(GString *args[], int nArgs, XEvent *event) { Widget btn; if (!core->getContinuousMode()) { return; } core->setContinuousMode(gFalse); btn = XtNameToWidget(popupMenu, "continuousMode"); XtVaSetValues(btn, XmNset, XmUNSET, NULL); } void XPDFViewer::cmdStartPan(GString *args[], int nArgs, XEvent *event) { core->startPan(mouseX(event), mouseY(event)); } void XPDFViewer::cmdStartSelection(GString *args[], int nArgs, XEvent *event) { core->startSelection(mouseX(event), mouseY(event)); } void XPDFViewer::cmdToggleContinuousMode(GString *args[], int nArgs, XEvent *event) { if (core->getContinuousMode()) { cmdSinglePageMode(NULL, 0, event); } else { cmdContinuousMode(NULL, 0, event); } } void XPDFViewer::cmdToggleFullScreenMode(GString *args[], int nArgs, XEvent *event) { if (core->getFullScreen()) { cmdWindowMode(NULL, 0, event); } else { cmdFullScreenMode(NULL, 0, event); } } void XPDFViewer::cmdToggleOutline(GString *args[], int nArgs, XEvent *event) { #ifndef DISABLE_OUTLINE Dimension w; if (outlineScroll == None) { return; } XtVaGetValues(outlineScroll, XmNwidth, &w, NULL); if (w > 1) { cmdCloseOutline(NULL, 0, event); } else { cmdOpenOutline(NULL, 0, event); } #endif } void XPDFViewer::cmdWindowMode(GString *args[], int nArgs, XEvent *event) { PDFDoc *doc; XPDFViewer *viewer; int pg; Widget btn; if (!core->getFullScreen()) { return; } pg = core->getPageNum(); XtPopdown(win); doc = core->takeDoc(gFalse); viewer = app->reopen(this, doc, pg, gFalse); btn = XtNameToWidget(viewer->popupMenu, "fullScreen"); XtVaSetValues(btn, XmNset, XmUNSET, NULL); } void XPDFViewer::cmdZoomFitPage(GString *args[], int nArgs, XEvent *event) { if (core->getZoom() != zoomPage) { setZoomIdx(zoomPageIdx); displayPage(core->getPageNum(), zoomPage, core->getRotate(), gTrue, gFalse); } } void XPDFViewer::cmdZoomFitWidth(GString *args[], int nArgs, XEvent *event) { if (core->getZoom() != zoomWidth) { setZoomIdx(zoomWidthIdx); displayPage(core->getPageNum(), zoomWidth, core->getRotate(), gTrue, gFalse); } } void XPDFViewer::cmdZoomIn(GString *args[], int nArgs, XEvent *event) { int z; z = getZoomIdx(); if (z <= minZoomIdx && z > maxZoomIdx) { --z; setZoomIdx(z); displayPage(core->getPageNum(), zoomMenuInfo[z].zoom, core->getRotate(), gTrue, gFalse); } } void XPDFViewer::cmdZoomOut(GString *args[], int nArgs, XEvent *event) { int z; z = getZoomIdx(); if (z < minZoomIdx && z >= maxZoomIdx) { ++z; setZoomIdx(z); displayPage(core->getPageNum(), zoomMenuInfo[z].zoom, core->getRotate(), gTrue, gFalse); } } void XPDFViewer::cmdZoomPercent(GString *args[], int nArgs, XEvent *event) { double z; z = atof(args[0]->getCString()); setZoomVal(z); displayPage(core->getPageNum(), z, core->getRotate(), gTrue, gFalse); } void XPDFViewer::cmdZoomToSelection(GString *args[], int nArgs, XEvent *event) { int pg; double ulx, uly, lrx, lry; if (core->getSelection(&pg, &ulx, &uly, &lrx, &lry)) { core->zoomToRect(pg, ulx, uly, lrx, lry); } } //------------------------------------------------------------------------ // GUI code: main window //------------------------------------------------------------------------ void XPDFViewer::initWindow(GBool fullScreen) { Colormap colormap; XColor xcol; Atom state, val; Arg args[20]; int n; char *title; display = XtDisplay(app->getAppShell()); screenNum = XScreenNumberOfScreen(XtScreen(app->getAppShell())); toolBar = None; #ifndef DISABLE_OUTLINE outlineScroll = None; #endif // private colormap if (app->getInstallCmap()) { XtVaGetValues(app->getAppShell(), XmNcolormap, &colormap, NULL); // ensure that BlackPixel and WhitePixel are reserved in the // new colormap xcol.red = xcol.green = xcol.blue = 0; XAllocColor(display, colormap, &xcol); xcol.red = xcol.green = xcol.blue = 65535; XAllocColor(display, colormap, &xcol); colormap = XCopyColormapAndFree(display, colormap); } // top-level window n = 0; title = app->getTitle() ? app->getTitle()->getCString() : (char *)xpdfAppName; XtSetArg(args[n], XmNtitle, title); ++n; XtSetArg(args[n], XmNiconName, title); ++n; XtSetArg(args[n], XmNminWidth, 100); ++n; XtSetArg(args[n], XmNminHeight, 100); ++n; XtSetArg(args[n], XmNbaseWidth, 0); ++n; XtSetArg(args[n], XmNbaseHeight, 0); ++n; XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); ++n; win = XtCreatePopupShell("win", topLevelShellWidgetClass, app->getAppShell(), args, n); if (app->getInstallCmap()) { XtVaSetValues(win, XmNcolormap, colormap, NULL); } XmAddWMProtocolCallback(win, XInternAtom(display, "WM_DELETE_WINDOW", False), &closeMsgCbk, this); // create the full-screen window if (fullScreen) { initCore(win, gTrue); // create the normal (non-full-screen) window } else { if (app->getGeometry()) { n = 0; XtSetArg(args[n], XmNgeometry, app->getGeometry()->getCString()); ++n; XtSetValues(win, args, n); } n = 0; form = XmCreateForm(win, "form", args, n); XtManageChild(form); #ifdef DISABLE_OUTLINE initToolbar(form); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetValues(toolBar, args, n); initCore(form, gFalse); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, toolBar); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetValues(core->getWidget(), args, n); #else initToolbar(form); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetValues(toolBar, args, n); initPanedWin(form); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, toolBar); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetValues(panedWin, args, n); initCore(panedWin, fullScreen); n = 0; XtSetArg(args[n], XmNpositionIndex, 1); ++n; XtSetArg(args[n], XmNallowResize, True); ++n; XtSetArg(args[n], XmNpaneMinimum, 1); ++n; XtSetArg(args[n], XmNpaneMaximum, 10000); ++n; XtSetValues(core->getWidget(), args, n); #endif } // set the zoom menu to match the initial zoom setting setZoomVal(core->getZoom()); // set traversal order XtVaSetValues(core->getDrawAreaWidget(), XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); if (toolBar != None) { XtVaSetValues(backBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(prevTenPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(prevPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(nextPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(nextTenPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(forwardBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(pageNumText, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(zoomWidget, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(findBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(printBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(aboutBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); XtVaSetValues(quitBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL); } initPopupMenu(); if (fullScreen) { // Set both the old-style Motif decorations hint and the new-style // _NET_WM_STATE property. This is redundant, but might be useful // for older window managers. We also set the geometry to +0+0 to // avoid interactive placement. (Note: we need to realize the // shell, so it has a Window on which to set the _NET_WM_STATE // property, but we don't want to map it until later, so we set // mappedWhenManaged to false.) n = 0; XtSetArg(args[n], XmNmappedWhenManaged, False); ++n; XtSetArg(args[n], XmNmwmDecorations, 0); ++n; XtSetArg(args[n], XmNgeometry, "+0+0"); ++n; XtSetValues(win, args, n); XtRealizeWidget(win); state = XInternAtom(display, "_NET_WM_STATE", False); val = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); XChangeProperty(display, XtWindow(win), state, XA_ATOM, 32, PropModeReplace, (Guchar *)&val, 1); } } void XPDFViewer::initToolbar(Widget parent) { Widget label, lastBtn; #ifndef USE_COMBO_BOX Widget btn; #endif Arg args[20]; int n; XmString s, emptyString; int i; // toolbar n = 0; toolBar = XmCreateForm(parent, "toolBar", args, n); XtManageChild(toolBar); // create an empty string -- this is used for buttons that will get // pixmaps later emptyString = XmStringCreateLocalized(""); // page movement buttons n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; backBtn = XmCreatePushButton(toolBar, "back", args, n); addToolTip(backBtn, "Back"); XtManageChild(backBtn); XtAddCallback(backBtn, XmNactivateCallback, &backCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, backBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; prevTenPageBtn = XmCreatePushButton(toolBar, "prevTenPage", args, n); addToolTip(prevTenPageBtn, "-10 pages"); XtManageChild(prevTenPageBtn); XtAddCallback(prevTenPageBtn, XmNactivateCallback, &prevTenPageCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, prevTenPageBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; prevPageBtn = XmCreatePushButton(toolBar, "prevPage", args, n); addToolTip(prevPageBtn, "Previous page"); XtManageChild(prevPageBtn); XtAddCallback(prevPageBtn, XmNactivateCallback, &prevPageCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, prevPageBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; nextPageBtn = XmCreatePushButton(toolBar, "nextPage", args, n); addToolTip(nextPageBtn, "Next page"); XtManageChild(nextPageBtn); XtAddCallback(nextPageBtn, XmNactivateCallback, &nextPageCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, nextPageBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; nextTenPageBtn = XmCreatePushButton(toolBar, "nextTenPage", args, n); addToolTip(nextTenPageBtn, "+10 pages"); XtManageChild(nextTenPageBtn); XtAddCallback(nextTenPageBtn, XmNactivateCallback, &nextTenPageCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, nextTenPageBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; forwardBtn = XmCreatePushButton(toolBar, "forward", args, n); addToolTip(forwardBtn, "Forward"); XtManageChild(forwardBtn); XtAddCallback(forwardBtn, XmNactivateCallback, &forwardCbk, (XtPointer)this); // page number display n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, forwardBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; s = XmStringCreateLocalized("Page "); XtSetArg(args[n], XmNlabelString, s); ++n; label = XmCreateLabel(toolBar, "pageLabel", args, n); XmStringFree(s); XtManageChild(label); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, label); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 3); ++n; XtSetArg(args[n], XmNmarginHeight, 3); ++n; XtSetArg(args[n], XmNcolumns, 5); ++n; pageNumText = XmCreateTextField(toolBar, "pageNum", args, n); XtManageChild(pageNumText); XtAddCallback(pageNumText, XmNactivateCallback, &pageNumCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, pageNumText); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; s = XmStringCreateLocalized(" of 00000"); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); ++n; XtSetArg(args[n], XmNrecomputeSize, False); ++n; pageCountLabel = XmCreateLabel(toolBar, "pageCountLabel", args, n); XmStringFree(s); XtManageChild(pageCountLabel); s = XmStringCreateLocalized(" of 0"); XtVaSetValues(pageCountLabel, XmNlabelString, s, NULL); XmStringFree(s); // zoom menu #if USE_COMBO_BOX XmString st[nZoomMenuItems]; n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, pageCountLabel); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 0); ++n; XtSetArg(args[n], XmNmarginHeight, 0); ++n; XtSetArg(args[n], XmNcomboBoxType, XmDROP_DOWN_COMBO_BOX); ++n; XtSetArg(args[n], XmNpositionMode, XmONE_BASED); ++n; XtSetArg(args[n], XmNcolumns, 7); ++n; for (i = 0; i < nZoomMenuItems; ++i) { st[i] = XmStringCreateLocalized((char *)zoomMenuInfo[i].label); } XtSetArg(args[n], XmNitems, st); ++n; XtSetArg(args[n], XmNitemCount, nZoomMenuItems); ++n; zoomComboBox = XmCreateComboBox(toolBar, "zoomComboBox", args, n); for (i = 0; i < nZoomMenuItems; ++i) { XmStringFree(st[i]); } addToolTip(zoomComboBox, "Zoom"); XtAddCallback(zoomComboBox, XmNselectionCallback, &zoomComboBoxCbk, (XtPointer)this); XtManageChild(zoomComboBox); zoomWidget = zoomComboBox; #else Widget menuPane; char buf[16]; n = 0; menuPane = XmCreatePulldownMenu(toolBar, "zoomMenuPane", args, n); for (i = 0; i < nZoomMenuItems; ++i) { n = 0; s = XmStringCreateLocalized((char *)zoomMenuInfo[i].label); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n; sprintf(buf, "zoom%d", i); btn = XmCreatePushButton(menuPane, buf, args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &zoomMenuCbk, (XtPointer)this); zoomMenuBtns[i] = btn; } n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, pageCountLabel); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 0); ++n; XtSetArg(args[n], XmNmarginHeight, 0); ++n; XtSetArg(args[n], XmNsubMenuId, menuPane); ++n; zoomMenu = XmCreateOptionMenu(toolBar, "zoomMenu", args, n); addToolTip(zoomMenu, "Zoom"); XtManageChild(zoomMenu); zoomWidget = zoomMenu; #endif // find/print/about buttons n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, zoomWidget); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; findBtn = XmCreatePushButton(toolBar, "find", args, n); addToolTip(findBtn, "Find"); XtManageChild(findBtn); XtAddCallback(findBtn, XmNactivateCallback, &findCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, findBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; printBtn = XmCreatePushButton(toolBar, "print", args, n); addToolTip(printBtn, "Print"); XtManageChild(printBtn); XtAddCallback(printBtn, XmNactivateCallback, &printCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, printBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; XtSetArg(args[n], XmNlabelString, emptyString); ++n; aboutBtn = XmCreatePushButton(toolBar, "about", args, n); addToolTip(aboutBtn, "About / help"); XtManageChild(aboutBtn); XtAddCallback(aboutBtn, XmNactivateCallback, &aboutCbk, (XtPointer)this); lastBtn = aboutBtn; // quit button n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNmarginWidth, 6); ++n; s = XmStringCreateLocalized("Quit"); XtSetArg(args[n], XmNlabelString, s); ++n; quitBtn = XmCreatePushButton(toolBar, "quit", args, n); XmStringFree(s); XtManageChild(quitBtn); XtAddCallback(quitBtn, XmNactivateCallback, &quitCbk, (XtPointer)this); // link label n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, lastBtn); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNrightWidget, quitBtn); ++n; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; s = XmStringCreateLocalized(""); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNrecomputeSize, True); ++n; XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); ++n; linkLabel = XmCreateLabel(toolBar, "linkLabel", args, n); XmStringFree(s); XtManageChild(linkLabel); XmStringFree(emptyString); } #ifndef DISABLE_OUTLINE void XPDFViewer::initPanedWin(Widget parent) { Widget clipWin; Arg args[20]; int n; // paned window n = 0; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; #if defined(__sgi) && (XmVERSION <= 1) panedWin = SgCreateHorzPanedWindow(parent, "panedWin", args, n); #else panedWin = XmCreatePanedWindow(parent, "panedWin", args, n); #endif XtManageChild(panedWin); // scrolled window for outline container n = 0; XtSetArg(args[n], XmNpositionIndex, 0); ++n; XtSetArg(args[n], XmNallowResize, True); ++n; XtSetArg(args[n], XmNpaneMinimum, 1); ++n; XtSetArg(args[n], XmNpaneMaximum, 10000); ++n; #if !(defined(__sgi) && (XmVERSION <= 1)) XtSetArg(args[n], XmNwidth, 1); ++n; #endif XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n; outlineScroll = XmCreateScrolledWindow(panedWin, "outlineScroll", args, n); XtManageChild(outlineScroll); XtVaGetValues(outlineScroll, XmNclipWindow, &clipWin, NULL); XtVaSetValues(clipWin, XmNbackground, app->getPaperPixel(), NULL); // outline tree n = 0; XtSetArg(args[n], XmNbackground, app->getPaperPixel()); ++n; outlineTree = XPDFCreateTree(outlineScroll, "outlineTree", args, n); XtManageChild(outlineTree); XtAddCallback(outlineTree, XPDFNselectionCallback, &outlineSelectCbk, (XtPointer)this); } #endif void XPDFViewer::initCore(Widget parent, GBool fullScreen) { core = new XPDFCore(win, parent, app->getPaperRGB(), app->getPaperPixel(), app->getMattePixel(fullScreen), fullScreen, app->getReverseVideo(), app->getInstallCmap(), app->getRGBCubeSize()); core->setUpdateCbk(&updateCbk, this); core->setActionCbk(&actionCbk, this); core->setKeyPressCbk(&keyPressCbk, this); core->setMouseCbk(&mouseCbk, this); } void XPDFViewer::initPopupMenu() { Widget btn; Arg args[20]; int n; XmString s, s2; n = 0; #if XmVersion < 1002 // older versions of Motif need this, newer ones choke on it, // sometimes not displaying the menu at all, maybe depending on the // state of the NumLock key (taken from DDD) XtSetArg(args[n], XmNmenuPost, ""); ++n; #endif popupMenu = XmCreatePopupMenu(core->getDrawAreaWidget(), "popupMenu", args, n); n = 0; s = XmStringCreateLocalized("Open..."); XtSetArg(args[n], XmNlabelString, s); ++n; s2 = XmStringCreateLocalized("O"); XtSetArg(args[n], XmNacceleratorText, s2); ++n; btn = XmCreatePushButton(popupMenu, "open", args, n); XmStringFree(s); XmStringFree(s2); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &openCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Open in new window..."); XtSetArg(args[n], XmNlabelString, s); ++n; btn = XmCreatePushButton(popupMenu, "openInNewWindow", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &openInNewWindowCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Reload"); XtSetArg(args[n], XmNlabelString, s); ++n; s2 = XmStringCreateLocalized("R"); XtSetArg(args[n], XmNacceleratorText, s2); ++n; btn = XmCreatePushButton(popupMenu, "reload", args, n); XmStringFree(s); XmStringFree(s2); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &reloadCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Save as..."); XtSetArg(args[n], XmNlabelString, s); ++n; btn = XmCreatePushButton(popupMenu, "saveAs", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &saveAsCbk, (XtPointer)this); n = 0; btn = XmCreateSeparator(popupMenu, "sep1", args, n); XtManageChild(btn); n = 0; s = XmStringCreateLocalized("Continuous view"); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n; XtSetArg(args[n], XmNvisibleWhenOff, True); ++n; XtSetArg(args[n], XmNset, core->getContinuousMode() ? XmSET : XmUNSET); ++n; btn = XmCreateToggleButton(popupMenu, "continuousMode", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNvalueChangedCallback, &continuousModeToggleCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Full screen"); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n; XtSetArg(args[n], XmNvisibleWhenOff, True); ++n; XtSetArg(args[n], XmNset, core->getFullScreen() ? XmSET : XmUNSET); ++n; btn = XmCreateToggleButton(popupMenu, "fullScreen", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNvalueChangedCallback, &fullScreenToggleCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Rotate counterclockwise"); XtSetArg(args[n], XmNlabelString, s); ++n; btn = XmCreatePushButton(popupMenu, "rotateCCW", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &rotateCCWCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Rotate clockwise"); XtSetArg(args[n], XmNlabelString, s); ++n; btn = XmCreatePushButton(popupMenu, "rotateCW", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &rotateCWCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Zoom to selection"); XtSetArg(args[n], XmNlabelString, s); ++n; btn = XmCreatePushButton(popupMenu, "zoomToSelection", args, n); XmStringFree(s); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &zoomToSelectionCbk, (XtPointer)this); n = 0; btn = XmCreateSeparator(popupMenu, "sep2", args, n); XtManageChild(btn); n = 0; s = XmStringCreateLocalized("Close"); XtSetArg(args[n], XmNlabelString, s); ++n; s2 = XmStringCreateLocalized("Ctrl+W"); XtSetArg(args[n], XmNacceleratorText, s2); ++n; btn = XmCreatePushButton(popupMenu, "close", args, n); XmStringFree(s); XmStringFree(s2); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &closeCbk, (XtPointer)this); n = 0; s = XmStringCreateLocalized("Quit"); XtSetArg(args[n], XmNlabelString, s); ++n; s2 = XmStringCreateLocalized("Q"); XtSetArg(args[n], XmNacceleratorText, s2); ++n; btn = XmCreatePushButton(popupMenu, "quit", args, n); XmStringFree(s); XmStringFree(s2); XtManageChild(btn); XtAddCallback(btn, XmNactivateCallback, &quitCbk, (XtPointer)this); // this is magic (taken from DDD) - weird things happen if this // call isn't made XtUngrabButton(core->getDrawAreaWidget(), AnyButton, AnyModifier); } void XPDFViewer::addToolTip(Widget widget, char *text) { #ifdef XmNtoolTipString XmString s; Cardinal n, i; WidgetList children; if (XtIsComposite(widget)) { XtVaGetValues(widget, XmNnumChildren, &n, XmNchildren, &children, NULL); for (i = 0; i < n; ++i) { addToolTip(children[i], text); } } else { s = XmStringCreateLocalized(text); XtVaSetValues(widget, XmNtoolTipString, s, NULL); XmStringFree(s); } #endif } void XPDFViewer::mapWindow() { #ifdef HAVE_X11_XPM_H Pixmap iconPixmap; #endif int depth; Pixel fg, bg, arm; // show the window XtPopup(win, XtGrabNone); core->takeFocus(); // create the icon #ifdef HAVE_X11_XPM_H if (XpmCreatePixmapFromData(display, XtWindow(win), xpdfIcon, &iconPixmap, NULL, NULL) == XpmSuccess) { XtVaSetValues(win, XmNiconPixmap, iconPixmap, NULL); } #endif // set button bitmaps (must be done after the window is mapped) if (toolBar != None) { XtVaGetValues(backBtn, XmNdepth, &depth, XmNforeground, &fg, XmNbackground, &bg, XmNarmColor, &arm, NULL); XtVaSetValues(backBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)backArrow_bits, backArrow_width, backArrow_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)backArrow_bits, backArrow_width, backArrow_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)backArrowDis_bits, backArrowDis_width, backArrowDis_height, fg, bg, depth), NULL); XtVaSetValues(prevTenPageBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)dblLeftArrow_bits, dblLeftArrow_width, dblLeftArrow_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)dblLeftArrow_bits, dblLeftArrow_width, dblLeftArrow_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)dblLeftArrowDis_bits, dblLeftArrowDis_width, dblLeftArrowDis_height, fg, bg, depth), NULL); XtVaSetValues(prevPageBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)leftArrow_bits, leftArrow_width, leftArrow_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)leftArrow_bits, leftArrow_width, leftArrow_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)leftArrowDis_bits, leftArrowDis_width, leftArrowDis_height, fg, bg, depth), NULL); XtVaSetValues(nextPageBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)rightArrow_bits, rightArrow_width, rightArrow_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)rightArrow_bits, rightArrow_width, rightArrow_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)rightArrowDis_bits, rightArrowDis_width, rightArrowDis_height, fg, bg, depth), NULL); XtVaSetValues(nextTenPageBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)dblRightArrow_bits, dblRightArrow_width, dblRightArrow_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)dblRightArrow_bits, dblRightArrow_width, dblRightArrow_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)dblRightArrowDis_bits, dblRightArrowDis_width, dblRightArrowDis_height, fg, bg, depth), NULL); XtVaSetValues(forwardBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)forwardArrow_bits, forwardArrow_width, forwardArrow_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)forwardArrow_bits, forwardArrow_width, forwardArrow_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)forwardArrowDis_bits, forwardArrowDis_width, forwardArrowDis_height, fg, bg, depth), NULL); XtVaSetValues(findBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)find_bits, find_width, find_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)find_bits, find_width, find_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)findDis_bits, findDis_width, findDis_height, fg, bg, depth), NULL); XtVaSetValues(printBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)print_bits, print_width, print_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)print_bits, print_width, print_height, fg, arm, depth), XmNlabelInsensitivePixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)printDis_bits, printDis_width, printDis_height, fg, bg, depth), NULL); XtVaSetValues(aboutBtn, XmNlabelType, XmPIXMAP, XmNlabelPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)about_bits, about_width, about_height, fg, bg, depth), XmNarmPixmap, XCreatePixmapFromBitmapData(display, XtWindow(toolBar), (char *)about_bits, about_width, about_height, fg, arm, depth), NULL); } } void XPDFViewer::closeWindow() { XtPopdown(win); XtDestroyWidget(win); } int XPDFViewer::getZoomIdx() { int i; for (i = 0; i < nZoomMenuItems; ++i) { if (core->getZoom() == zoomMenuInfo[i].zoom) { return i; } } return -1; } void XPDFViewer::setZoomIdx(int idx) { if (toolBar == None) { return; } #if USE_COMBO_BOX XtVaSetValues(zoomComboBox, XmNselectedPosition, idx + 1, NULL); #else XtVaSetValues(zoomMenu, XmNmenuHistory, zoomMenuBtns[idx], NULL); #endif } void XPDFViewer::setZoomVal(double z) { if (toolBar == None) { return; } #if USE_COMBO_BOX char buf[32]; XmString s; int i; for (i = 0; i < nZoomMenuItems; ++i) { if (z == zoomMenuInfo[i].zoom) { XtVaSetValues(zoomComboBox, XmNselectedPosition, i + 1, NULL); return; } } sprintf(buf, "%d%%", (int)z); s = XmStringCreateLocalized(buf); XtVaSetValues(zoomComboBox, XmNselectedItem, s, NULL); XmStringFree(s); #else int i; for (i = 0; i < nZoomMenuItems; ++i) { if (z == zoomMenuInfo[i].zoom) { XtVaSetValues(zoomMenu, XmNmenuHistory, zoomMenuBtns[i], NULL); return; } } for (i = maxZoomIdx; i < minZoomIdx; ++i) { if (z > zoomMenuInfo[i].zoom) { break; } } XtVaSetValues(zoomMenu, XmNmenuHistory, zoomMenuBtns[i], NULL); #endif } void XPDFViewer::prevPageCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->core->gotoPrevPage(1, gTrue, gFalse); viewer->core->takeFocus(); } void XPDFViewer::prevTenPageCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->core->gotoPrevPage(10, gTrue, gFalse); viewer->core->takeFocus(); } void XPDFViewer::nextPageCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->core->gotoNextPage(1, gTrue); viewer->core->takeFocus(); } void XPDFViewer::nextTenPageCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->core->gotoNextPage(10, gTrue); viewer->core->takeFocus(); } void XPDFViewer::backCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->core->goBackward(); viewer->core->takeFocus(); } void XPDFViewer::forwardCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->core->goForward(); viewer->core->takeFocus(); } #if USE_COMBO_BOX void XPDFViewer::zoomComboBoxCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmComboBoxCallbackStruct *data = (XmComboBoxCallbackStruct *)callData; double z; char *s; XmStringContext context; XmStringCharSet charSet; XmStringDirection dir; Boolean sep; z = viewer->core->getZoom(); if (data->item_position == 0) { XmStringInitContext(&context, data->item_or_text); if (XmStringGetNextSegment(context, &s, &charSet, &dir, &sep)) { z = atof(s); if (z <= 1) { z = defZoom; } XtFree(charSet); XtFree(s); } XmStringFreeContext(context); } else { z = zoomMenuInfo[data->item_position - 1].zoom; } // only redraw if this was triggered by an event; otherwise // the caller is responsible for doing the redraw if (z != viewer->core->getZoom() && data->event) { viewer->displayPage(viewer->core->getPageNum(), z, viewer->core->getRotate(), gTrue, gFalse); } viewer->core->takeFocus(); } #else // USE_COMBO_BOX void XPDFViewer::zoomMenuCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmPushButtonCallbackStruct *data = (XmPushButtonCallbackStruct *)callData; XtPointer userData; double z; XtVaGetValues(widget, XmNuserData, &userData, NULL); z = zoomMenuInfo[(long)userData].zoom; // only redraw if this was triggered by an event; otherwise // the caller is responsible for doing the redraw if (z != viewer->core->getZoom() && data->event) { viewer->displayPage(viewer->core->getPageNum(), z, viewer->core->getRotate(), gTrue, gFalse); } viewer->core->takeFocus(); } #endif // USE_COMBO_BOX void XPDFViewer::findCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; if (!viewer->core->getDoc()) { return; } viewer->mapFindDialog(); } void XPDFViewer::printCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; if (!viewer->core->getDoc()) { return; } XtManageChild(viewer->printDialog); } void XPDFViewer::aboutCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XtManageChild(viewer->aboutDialog); } void XPDFViewer::quitCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->app->quit(); } void XPDFViewer::openCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->mapOpenDialog(gFalse); } void XPDFViewer::openInNewWindowCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->mapOpenDialog(gTrue); } void XPDFViewer::reloadCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->reloadFile(); } void XPDFViewer::saveAsCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; if (!viewer->core->getDoc()) { return; } viewer->mapSaveAsDialog(); } void XPDFViewer::continuousModeToggleCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmToggleButtonCallbackStruct *data = (XmToggleButtonCallbackStruct *)callData; viewer->core->setContinuousMode(data->set == XmSET); } void XPDFViewer::fullScreenToggleCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmToggleButtonCallbackStruct *data = (XmToggleButtonCallbackStruct *)callData; if (data->set == XmSET) { viewer->cmdFullScreenMode(NULL, 0, NULL); } else { viewer->cmdWindowMode(NULL, 0, NULL); } } void XPDFViewer::rotateCCWCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->cmdRotateCCW(NULL, 0, NULL); } void XPDFViewer::rotateCWCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->cmdRotateCW(NULL, 0, NULL); } void XPDFViewer::zoomToSelectionCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; int pg; double ulx, uly, lrx, lry; if (viewer->core->getSelection(&pg, &ulx, &uly, &lrx, &lry)) { viewer->core->zoomToRect(pg, ulx, uly, lrx, lry); } } void XPDFViewer::closeCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->app->close(viewer, gFalse); } void XPDFViewer::closeMsgCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->app->close(viewer, gTrue); } void XPDFViewer::pageNumCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; char *s, *p; int pg; char buf[20]; if (!viewer->core->getDoc()) { goto err; } s = XmTextFieldGetString(viewer->pageNumText); for (p = s; *p; ++p) { if (!isdigit(*p)) { goto err; } } pg = atoi(s); if (pg < 1 || pg > viewer->core->getDoc()->getNumPages()) { goto err; } viewer->displayPage(pg, viewer->core->getZoom(), viewer->core->getRotate(), gFalse, gTrue); viewer->core->takeFocus(); return; err: XBell(viewer->display, 0); sprintf(buf, "%d", viewer->core->getPageNum()); XmTextFieldSetString(viewer->pageNumText, buf); } void XPDFViewer::updateCbk(void *data, GString *fileName, int pageNum, int numPages, const char *linkString) { XPDFViewer *viewer = (XPDFViewer *)data; GString *title; char buf[20]; XmString s; if (fileName) { if (!(title = viewer->app->getTitle())) { title = (new GString(xpdfAppName))->append(": ")->append(fileName); } XtVaSetValues(viewer->win, XmNtitle, title->getCString(), XmNiconName, title->getCString(), NULL); if (!viewer->app->getTitle()) { delete title; } #ifndef DISABLE_OUTLINE viewer->setupOutline(); #endif viewer->setupPrintDialog(); } if (viewer->toolBar != None) { if (pageNum >= 0) { s = XmStringCreateLocalized(""); XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL); XmStringFree(s); sprintf(buf, "%d", pageNum); XmTextFieldSetString(viewer->pageNumText, buf); XtVaSetValues(viewer->prevTenPageBtn, XmNsensitive, pageNum > 1, NULL); XtVaSetValues(viewer->prevPageBtn, XmNsensitive, pageNum > 1, NULL); XtVaSetValues(viewer->nextTenPageBtn, XmNsensitive, pageNum < viewer->core->getDoc()->getNumPages(), NULL); XtVaSetValues(viewer->nextPageBtn, XmNsensitive, pageNum < viewer->core->getDoc()->getNumPages(), NULL); XtVaSetValues(viewer->backBtn, XmNsensitive, viewer->core->canGoBack(), NULL); XtVaSetValues(viewer->forwardBtn, XmNsensitive, viewer->core->canGoForward(), NULL); } if (numPages >= 0) { sprintf(buf, " of %d", numPages); s = XmStringCreateLocalized(buf); XtVaSetValues(viewer->pageCountLabel, XmNlabelString, s, NULL); XmStringFree(s); } if (linkString) { s = XmStringCreateLocalized((char *)linkString); XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL); XmStringFree(s); } } } //------------------------------------------------------------------------ // GUI code: outline //------------------------------------------------------------------------ #ifndef DISABLE_OUTLINE void XPDFViewer::setupOutline() { GList *items; UnicodeMap *uMap; GString *enc; int i; if (outlineScroll == None) { return; } // unmanage and destroy the old labels if (outlineLabels) { XtUnmanageChildren(outlineLabels, outlineLabelsLength); for (i = 0; i < outlineLabelsLength; ++i) { XtDestroyWidget(outlineLabels[i]); } gfree(outlineLabels); outlineLabels = NULL; outlineLabelsLength = outlineLabelsSize = 0; } if (core->getDoc()) { // create the new labels items = core->getDoc()->getOutline()->getItems(); if (items && items->getLength() > 0) { enc = new GString("Latin1"); uMap = globalParams->getUnicodeMap(enc); delete enc; setupOutlineItems(items, NULL, uMap); uMap->decRefCnt(); } // manage the new labels XtManageChildren(outlineLabels, outlineLabelsLength); } } void XPDFViewer::setupOutlineItems(GList *items, Widget parent, UnicodeMap *uMap) { OutlineItem *item; GList *kids; Widget label; Arg args[20]; GString *title; char buf[8]; XmString s; int i, j, n; for (i = 0; i < items->getLength(); ++i) { item = (OutlineItem *)items->get(i); title = new GString(); for (j = 0; j < item->getTitleLength(); ++j) { n = uMap->mapUnicode(item->getTitle()[j], buf, sizeof(buf)); title->append(buf, n); } n = 0; XtSetArg(args[n], XPDFNentryPosition, i); ++n; if (parent) { XtSetArg(args[n], XPDFNentryParent, parent); ++n; } XtSetArg(args[n], XPDFNentryExpanded, item->isOpen()); ++n; s = XmStringCreateLocalized(title->getCString()); delete title; XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNuserData, item); ++n; XtSetArg(args[n], XmNmarginWidth, 0); ++n; XtSetArg(args[n], XmNmarginHeight, 2); ++n; XtSetArg(args[n], XmNshadowThickness, 0); ++n; XtSetArg(args[n], XmNforeground, app->getReverseVideo() ? WhitePixel(display, screenNum) : BlackPixel(display, screenNum)); ++n; XtSetArg(args[n], XmNbackground, app->getPaperPixel()); ++n; label = XmCreateLabelGadget(outlineTree, "label", args, n); XmStringFree(s); if (outlineLabelsLength == outlineLabelsSize) { outlineLabelsSize += 64; outlineLabels = (Widget *)greallocn(outlineLabels, outlineLabelsSize, sizeof(Widget *)); } outlineLabels[outlineLabelsLength++] = label; item->open(); if ((kids = item->getKids())) { setupOutlineItems(kids, label, uMap); } } } void XPDFViewer::outlineSelectCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XPDFTreeSelectCallbackStruct *data = (XPDFTreeSelectCallbackStruct *)callData; OutlineItem *item; XtVaGetValues(data->selectedItem, XmNuserData, &item, NULL); if (item) { if (item->getAction()) { viewer->core->doAction(item->getAction()); } } viewer->core->takeFocus(); } #endif // !DISABLE_OUTLINE //------------------------------------------------------------------------ // GUI code: "about" dialog //------------------------------------------------------------------------ void XPDFViewer::initAboutDialog() { Widget scrolledWin, col, label, sep, closeBtn; Arg args[20]; int n, i; XmString s; char buf[20]; //----- dialog n = 0; s = XmStringCreateLocalized(xpdfAppName ": About"); XtSetArg(args[n], XmNdialogTitle, s); ++n; XtSetArg(args[n], XmNwidth, 450); ++n; XtSetArg(args[n], XmNheight, 300); ++n; aboutDialog = XmCreateFormDialog(win, "aboutDialog", args, n); XmStringFree(s); //----- "close" button n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; closeBtn = XmCreatePushButton(aboutDialog, "Close", args, n); XtManageChild(closeBtn); n = 0; XtSetArg(args[n], XmNdefaultButton, closeBtn); ++n; XtSetArg(args[n], XmNcancelButton, closeBtn); ++n; XtSetValues(aboutDialog, args, n); //----- scrolled window and RowColumn n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, closeBtn); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n; scrolledWin = XmCreateScrolledWindow(aboutDialog, "scrolledWin", args, n); XtManageChild(scrolledWin); n = 0; XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n; XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n; col = XmCreateRowColumn(scrolledWin, "col", args, n); XtManageChild(col); //----- fonts aboutBigFont = createFontList("-*-times-bold-i-normal--20-*-*-*-*-*-iso8859-1"); aboutVersionFont = createFontList("-*-times-medium-r-normal--16-*-*-*-*-*-iso8859-1"); aboutFixedFont = createFontList("-*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1"); //----- heading n = 0; s = XmStringCreateLocalized("Xpdf"); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNfontList, aboutBigFont); ++n; label = XmCreateLabel(col, "h0", args, n); XmStringFree(s); XtManageChild(label); n = 0; s = XmStringCreateLocalized("Version " xpdfVersion); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n; label = XmCreateLabel(col, "h1", args, n); XmStringFree(s); XtManageChild(label); n = 0; s = XmStringCreateLocalized(xpdfCopyright); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n; label = XmCreateLabel(col, "h2", args, n); XmStringFree(s); XtManageChild(label); n = 0; s = XmStringCreateLocalized(" "); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n; label = XmCreateLabel(col, "h3", args, n); XmStringFree(s); XtManageChild(label); n = 0; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; sep = XmCreateSeparator(col, "sep", args, n); XtManageChild(sep); n = 0; s = XmStringCreateLocalized(" "); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n; label = XmCreateLabel(col, "h4", args, n); XmStringFree(s); XtManageChild(label); n = 0; //----- text for (i = 0; aboutWinText[i]; ++i) { n = 0; s = XmStringCreateLocalized((char *)aboutWinText[i]); XtSetArg(args[n], XmNlabelString, s); ++n; XtSetArg(args[n], XmNfontList, aboutFixedFont); ++n; sprintf(buf, "t%d", i); label = XmCreateLabel(col, buf, args, n); XtManageChild(label); XmStringFree(s); } } //------------------------------------------------------------------------ // GUI code: "open" dialog //------------------------------------------------------------------------ void XPDFViewer::initOpenDialog() { Arg args[20]; int n; XmString s1, s2, s3; GString *dir; n = 0; s1 = XmStringCreateLocalized("Open"); XtSetArg(args[n], XmNokLabelString, s1); ++n; s2 = XmStringCreateLocalized("*.[Pp][Dd][Ff]"); XtSetArg(args[n], XmNpattern, s2); ++n; s3 = XmStringCreateLocalized(xpdfAppName ": Open"); XtSetArg(args[n], XmNdialogTitle, s3); ++n; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; XtSetArg(args[n], XmNautoUnmanage, True); ++n; openDialog = XmCreateFileSelectionDialog(win, "openDialog", args, n); XmStringFree(s1); XmStringFree(s2); XmStringFree(s3); XtUnmanageChild(XmFileSelectionBoxGetChild(openDialog, XmDIALOG_HELP_BUTTON)); XtAddCallback(openDialog, XmNokCallback, &openOkCbk, (XtPointer)this); if (core->getDoc() && core->getDoc()->getFileName()) { dir = makePathAbsolute(grabPath( core->getDoc()->getFileName()->getCString())); s1 = XmStringCreateLocalized(dir->getCString()); XtVaSetValues(openDialog, XmNdirectory, s1, NULL); XmStringFree(s1); delete dir; } } void XPDFViewer::mapOpenDialog(GBool openInNewWindowA) { if (!openDialog) { initOpenDialog(); } openInNewWindow = openInNewWindowA; XmFileSelectionDoSearch(openDialog, NULL); XtManageChild(openDialog); } void XPDFViewer::openOkCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmFileSelectionBoxCallbackStruct *data = (XmFileSelectionBoxCallbackStruct *)callData; char *fileName; XmStringContext context; XmStringCharSet charSet; XmStringDirection dir; Boolean sep; GString *fileNameStr; XmStringInitContext(&context, data->value); if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) { fileNameStr = new GString(fileName); if (viewer->openInNewWindow) { viewer->app->open(fileNameStr); } else { if (viewer->loadFile(fileNameStr)) { viewer->displayPage(1, viewer->core->getZoom(), viewer->core->getRotate(), gTrue, gTrue); } } delete fileNameStr; XtFree(charSet); XtFree(fileName); } XmStringFreeContext(context); } //------------------------------------------------------------------------ // GUI code: "find" dialog //------------------------------------------------------------------------ void XPDFViewer::initFindDialog() { Widget form1, label, okBtn, closeBtn; Arg args[20]; int n; XmString s; //----- dialog n = 0; s = XmStringCreateLocalized(xpdfAppName ": Find"); XtSetArg(args[n], XmNdialogTitle, s); ++n; XtSetArg(args[n], XmNnavigationType, XmNONE); ++n; XtSetArg(args[n], XmNautoUnmanage, False); ++n; findDialog = XmCreateFormDialog(win, "findDialog", args, n); XmStringFree(s); //----- "find" and "close" buttons n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n; okBtn = XmCreatePushButton(findDialog, "Find", args, n); XtManageChild(okBtn); XtAddCallback(okBtn, XmNactivateCallback, &findFindCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n; closeBtn = XmCreatePushButton(findDialog, "Close", args, n); XtManageChild(closeBtn); XtAddCallback(closeBtn, XmNactivateCallback, &findCloseCbk, (XtPointer)this); //----- checkboxes n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, okBtn); ++n; XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n; #if XmVERSION <= 1 XtSetArg(args[n], XmNindicatorOn, True); ++n; #else XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_FILL); ++n; #endif XtSetArg(args[n], XmNset, XmUNSET); ++n; s = XmStringCreateLocalized("Search backward"); XtSetArg(args[n], XmNlabelString, s); ++n; findBackwardToggle = XmCreateToggleButton(findDialog, "backward", args, n); XmStringFree(s); XtManageChild(findBackwardToggle); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, findBackwardToggle); ++n; XtSetArg(args[n], XmNleftOffset, 16); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, okBtn); ++n; XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n; #if XmVERSION <= 1 XtSetArg(args[n], XmNindicatorOn, True); ++n; #else XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_FILL); ++n; #endif XtSetArg(args[n], XmNset, XmUNSET); ++n; s = XmStringCreateLocalized("Match case"); XtSetArg(args[n], XmNlabelString, s); ++n; findCaseSensitiveToggle = XmCreateToggleButton(findDialog, "matchCase", args, n); XmStringFree(s); XtManageChild(findCaseSensitiveToggle); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, findCaseSensitiveToggle); ++n; XtSetArg(args[n], XmNleftOffset, 16); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, okBtn); ++n; XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n; #if XmVERSION <= 1 XtSetArg(args[n], XmNindicatorOn, True); ++n; #else XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_FILL); ++n; #endif XtSetArg(args[n], XmNset, XmUNSET); ++n; s = XmStringCreateLocalized("Whole words only"); XtSetArg(args[n], XmNlabelString, s); ++n; findWholeWordToggle = XmCreateToggleButton(findDialog, "wholeWord", args, n); XmStringFree(s); XtManageChild(findWholeWordToggle); //----- search string entry n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNtopOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNbottomWidget, findBackwardToggle); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 2); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 2); ++n; form1 = XmCreateForm(findDialog, "form", args, n); XtManageChild(form1); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; s = XmStringCreateLocalized("Find text: "); XtSetArg(args[n], XmNlabelString, s); ++n; label = XmCreateLabel(form1, "label", args, n); XmStringFree(s); XtManageChild(label); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNleftWidget, label); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; findText = XmCreateTextField(form1, "text", args, n); XtManageChild(findText); #ifdef LESSTIF_VERSION XtAddCallback(findText, XmNactivateCallback, &findFindCbk, (XtPointer)this); #endif //----- dialog parameters n = 0; XtSetArg(args[n], XmNdefaultButton, okBtn); ++n; XtSetArg(args[n], XmNcancelButton, closeBtn); ++n; #if XmVersion > 1001 XtSetArg(args[n], XmNinitialFocus, findText); ++n; #endif XtSetValues(findDialog, args, n); } void XPDFViewer::findFindCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; viewer->doFind(gFalse); } void XPDFViewer::mapFindDialog() { XmTextFieldSetSelection(findText, 0, XmTextFieldGetLastPosition(findText), XtLastTimestampProcessed(display)); XmTextFieldSetInsertionPosition(findText, 0); XtManageChild(findDialog); } void XPDFViewer::doFind(GBool next) { if (XtWindow(findDialog)) { XDefineCursor(display, XtWindow(findDialog), core->getBusyCursor()); } core->find(XmTextFieldGetString(findText), XmToggleButtonGetState(findCaseSensitiveToggle), next, XmToggleButtonGetState(findBackwardToggle), XmToggleButtonGetState(findWholeWordToggle), gFalse); if (XtWindow(findDialog)) { XUndefineCursor(display, XtWindow(findDialog)); } } void XPDFViewer::findCloseCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XtUnmanageChild(viewer->findDialog); } //------------------------------------------------------------------------ // GUI code: "save as" dialog //------------------------------------------------------------------------ void XPDFViewer::initSaveAsDialog() { Arg args[20]; int n; XmString s1, s2, s3; GString *dir; n = 0; s1 = XmStringCreateLocalized("Save"); XtSetArg(args[n], XmNokLabelString, s1); ++n; s2 = XmStringCreateLocalized("*.[Pp][Dd][Ff]"); XtSetArg(args[n], XmNpattern, s2); ++n; s3 = XmStringCreateLocalized(xpdfAppName ": Save as"); XtSetArg(args[n], XmNdialogTitle, s3); ++n; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; XtSetArg(args[n], XmNautoUnmanage, True); ++n; saveAsDialog = XmCreateFileSelectionDialog(win, "saveAsDialog", args, n); XmStringFree(s1); XmStringFree(s2); XmStringFree(s3); XtUnmanageChild(XmFileSelectionBoxGetChild(saveAsDialog, XmDIALOG_HELP_BUTTON)); XtAddCallback(saveAsDialog, XmNokCallback, &saveAsOkCbk, (XtPointer)this); if (core->getDoc() && core->getDoc()->getFileName()) { dir = makePathAbsolute(grabPath( core->getDoc()->getFileName()->getCString())); s1 = XmStringCreateLocalized(dir->getCString()); XtVaSetValues(saveAsDialog, XmNdirectory, s1, NULL); XmStringFree(s1); delete dir; } } void XPDFViewer::mapSaveAsDialog() { if (!saveAsDialog) { initSaveAsDialog(); } XmFileSelectionDoSearch(saveAsDialog, NULL); XtManageChild(saveAsDialog); } void XPDFViewer::saveAsOkCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmFileSelectionBoxCallbackStruct *data = (XmFileSelectionBoxCallbackStruct *)callData; char *fileName; GString *fileNameStr; XmStringContext context; XmStringCharSet charSet; XmStringDirection dir; Boolean sep; XmStringInitContext(&context, data->value); if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) { fileNameStr = new GString(fileName); viewer->core->getDoc()->saveAs(fileNameStr); delete fileNameStr; XtFree(charSet); XtFree(fileName); } XmStringFreeContext(context); } //------------------------------------------------------------------------ // GUI code: "print" dialog //------------------------------------------------------------------------ void XPDFViewer::initPrintDialog() { Widget sep1, sep2, row, label1, label2, okBtn, cancelBtn; Arg args[20]; int n; XmString s; GString *psFileName; //----- dialog n = 0; s = XmStringCreateLocalized(xpdfAppName ": Print"); XtSetArg(args[n], XmNdialogTitle, s); ++n; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; printDialog = XmCreateFormDialog(win, "printDialog", args, n); XmStringFree(s); //----- "print with command" n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNtopOffset, 4); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); ++n; XtSetArg(args[n], XmNset, XmSET); ++n; s = XmStringCreateLocalized("Print with command:"); XtSetArg(args[n], XmNlabelString, s); ++n; printWithCmdBtn = XmCreateToggleButton(printDialog, "printWithCmd", args, n); XmStringFree(s); XtManageChild(printWithCmdBtn); XtAddCallback(printWithCmdBtn, XmNvalueChangedCallback, &printWithCmdBtnCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, printWithCmdBtn); ++n; XtSetArg(args[n], XmNtopOffset, 2); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 16); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNcolumns, 40); ++n; printCmdText = XmCreateTextField(printDialog, "printCmd", args, n); XtManageChild(printCmdText); //----- "print to file" n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, printCmdText); ++n; XtSetArg(args[n], XmNtopOffset, 4); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); ++n; XtSetArg(args[n], XmNset, XmUNSET); ++n; s = XmStringCreateLocalized("Print to file:"); XtSetArg(args[n], XmNlabelString, s); ++n; printToFileBtn = XmCreateToggleButton(printDialog, "printToFile", args, n); XmStringFree(s); XtManageChild(printToFileBtn); XtAddCallback(printToFileBtn, XmNvalueChangedCallback, &printToFileBtnCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, printToFileBtn); ++n; XtSetArg(args[n], XmNtopOffset, 2); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 16); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNcolumns, 40); ++n; XtSetArg(args[n], XmNsensitive, False); ++n; printFileText = XmCreateTextField(printDialog, "printFile", args, n); XtManageChild(printFileText); //----- separator n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, printFileText); ++n; XtSetArg(args[n], XmNtopOffset, 8); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 8); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 8); ++n; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; sep1 = XmCreateSeparator(printDialog, "sep1", args, n); XtManageChild(sep1); //----- page range n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, sep1); ++n; XtSetArg(args[n], XmNtopOffset, 8); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n; row = XmCreateRowColumn(printDialog, "row", args, n); XtManageChild(row); n = 0; s = XmStringCreateLocalized("Pages:"); XtSetArg(args[n], XmNlabelString, s); ++n; label1 = XmCreateLabel(row, "label1", args, n); XmStringFree(s); XtManageChild(label1); n = 0; XtSetArg(args[n], XmNcolumns, 5); ++n; printFirstPage = XmCreateTextField(row, "printFirstPage", args, n); XtManageChild(printFirstPage); n = 0; s = XmStringCreateLocalized("to"); XtSetArg(args[n], XmNlabelString, s); ++n; label2 = XmCreateLabel(row, "label2", args, n); XmStringFree(s); XtManageChild(label2); n = 0; XtSetArg(args[n], XmNcolumns, 5); ++n; printLastPage = XmCreateTextField(row, "printLastPage", args, n); XtManageChild(printLastPage); //----- separator n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, row); ++n; XtSetArg(args[n], XmNtopOffset, 8); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 8); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 8); ++n; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; sep2 = XmCreateSeparator(printDialog, "sep2", args, n); XtManageChild(sep2); //----- "print" and "cancel" buttons n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, sep2); ++n; XtSetArg(args[n], XmNtopOffset, 8); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; okBtn = XmCreatePushButton(printDialog, "Print", args, n); XtManageChild(okBtn); XtAddCallback(okBtn, XmNactivateCallback, &printPrintCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, sep2); ++n; XtSetArg(args[n], XmNtopOffset, 8); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; cancelBtn = XmCreatePushButton(printDialog, "Cancel", args, n); XtManageChild(cancelBtn); n = 0; XtSetArg(args[n], XmNdefaultButton, okBtn); ++n; XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n; XtSetValues(printDialog, args, n); //----- initial values if ((psFileName = globalParams->getPSFile())) { if (psFileName->getChar(0) == '|') { XmTextFieldSetString(printCmdText, psFileName->getCString() + 1); } else { XmTextFieldSetString(printFileText, psFileName->getCString()); } delete psFileName; } } void XPDFViewer::setupPrintDialog() { PDFDoc *doc; char buf[20]; GString *pdfFileName, *psFileName, *psFileName2; char *p; doc = core->getDoc(); psFileName = globalParams->getPSFile(); if (!psFileName || psFileName->getChar(0) == '|') { 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; } } if (psFileName && psFileName->getChar(0) == '|') { XmToggleButtonSetState(printWithCmdBtn, True, False); XmToggleButtonSetState(printToFileBtn, False, False); XtVaSetValues(printCmdText, XmNsensitive, True, NULL); XtVaSetValues(printFileText, XmNsensitive, False, NULL); } else { XmToggleButtonSetState(printWithCmdBtn, False, False); XmToggleButtonSetState(printToFileBtn, True, False); XtVaSetValues(printCmdText, XmNsensitive, False, NULL); XtVaSetValues(printFileText, XmNsensitive, True, NULL); } if (psFileName) { delete psFileName; } sprintf(buf, "%d", doc->getNumPages()); XmTextFieldSetString(printFirstPage, "1"); XmTextFieldSetString(printLastPage, buf); } void XPDFViewer::printWithCmdBtnCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmToggleButtonCallbackStruct *data = (XmToggleButtonCallbackStruct *)callData; if (data->set != XmSET) { XmToggleButtonSetState(viewer->printWithCmdBtn, True, False); } XmToggleButtonSetState(viewer->printToFileBtn, False, False); XtVaSetValues(viewer->printCmdText, XmNsensitive, True, NULL); XtVaSetValues(viewer->printFileText, XmNsensitive, False, NULL); } void XPDFViewer::printToFileBtnCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; XmToggleButtonCallbackStruct *data = (XmToggleButtonCallbackStruct *)callData; if (data->set != XmSET) { XmToggleButtonSetState(viewer->printToFileBtn, True, False); } XmToggleButtonSetState(viewer->printWithCmdBtn, False, False); XtVaSetValues(viewer->printFileText, XmNsensitive, True, NULL); XtVaSetValues(viewer->printCmdText, XmNsensitive, False, NULL); } void XPDFViewer::printPrintCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFViewer *viewer = (XPDFViewer *)ptr; unsigned char withCmd; GString *psFileName; int firstPage, lastPage; PDFDoc *doc; PSOutputDev *psOut; doc = viewer->core->getDoc(); if (!doc->okToPrint()) { error(errNotAllowed, -1, "Printing this document is not allowed."); return; } viewer->core->setBusyCursor(gTrue); XtVaGetValues(viewer->printWithCmdBtn, XmNset, &withCmd, NULL); if (withCmd) { psFileName = new GString(XmTextFieldGetString(viewer->printCmdText)); psFileName->insert(0, '|'); } else { psFileName = new GString(XmTextFieldGetString(viewer->printFileText)); } firstPage = atoi(XmTextFieldGetString(viewer->printFirstPage)); lastPage = atoi(XmTextFieldGetString(viewer->printLastPage)); if (firstPage < 1) { firstPage = 1; } else if (firstPage > doc->getNumPages()) { firstPage = doc->getNumPages(); } if (lastPage < firstPage) { lastPage = firstPage; } else if (lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } psOut = new PSOutputDev(psFileName->getCString(), doc, firstPage, lastPage, psModePS); if (psOut->isOk()) { doc->displayPages(psOut, firstPage, lastPage, 72, 72, 0, gTrue, globalParams->getPSCrop(), gTrue); } delete psOut; delete psFileName; viewer->core->setBusyCursor(gFalse); } //------------------------------------------------------------------------ // Motif support //------------------------------------------------------------------------ XmFontList XPDFViewer::createFontList(char *xlfd) { XmFontList fontList; #if XmVersion <= 1001 XFontStruct *font; String params; Cardinal nParams; font = XLoadQueryFont(display, xlfd); if (font) { fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET); } else { params = (String)xlfd; nParams = 1; XtAppWarningMsg(app->getAppContext(), "noSuchFont", "CvtStringToXmFontList", "XtToolkitError", "No such font: %s", ¶ms, &nParams); fontList = NULL; } #else XmFontListEntry entry; entry = XmFontListEntryLoad(display, xlfd, XmFONT_IS_FONT, XmFONTLIST_DEFAULT_TAG); fontList = XmFontListAppendEntry(NULL, entry); XmFontListEntryFree(&entry); #endif return fontList; } xpdf-3.04/xpdf/Function.cc0000644000076400007640000010461212341430012014742 0ustar dereknderekn//======================================================================== // // Function.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "GList.h" #include "Object.h" #include "Dict.h" #include "Stream.h" #include "Error.h" #include "Function.h" //------------------------------------------------------------------------ // Max depth of nested functions. This is used to catch infinite // loops in the function object structure. #define recursionLimit 8 //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function() { } Function::~Function() { } Function *Function::parse(Object *funcObj, int recursion) { Function *func; Dict *dict; int funcType; Object obj1; if (recursion > recursionLimit) { error(errSyntaxError, -1, "Loop detected in function objects"); return NULL; } if (funcObj->isStream()) { dict = funcObj->streamGetDict(); } else if (funcObj->isDict()) { dict = funcObj->getDict(); } else if (funcObj->isName("Identity")) { return new IdentityFunction(); } else { error(errSyntaxError, -1, "Expected function dictionary or stream"); return NULL; } if (!dict->lookup("FunctionType", &obj1)->isInt()) { error(errSyntaxError, -1, "Function type is missing or wrong type"); obj1.free(); return NULL; } funcType = obj1.getInt(); obj1.free(); if (funcType == 0) { func = new SampledFunction(funcObj, dict); } else if (funcType == 2) { func = new ExponentialFunction(funcObj, dict); } else if (funcType == 3) { func = new StitchingFunction(funcObj, dict, recursion); } else if (funcType == 4) { func = new PostScriptFunction(funcObj, dict); } else { error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); return NULL; } if (!func->isOk()) { delete func; return NULL; } return func; } GBool Function::init(Dict *dict) { Object obj1, obj2; int i; //----- Domain if (!dict->lookup("Domain", &obj1)->isArray()) { error(errSyntaxError, -1, "Function is missing domain"); goto err2; } m = obj1.arrayGetLength() / 2; if (m > funcMaxInputs) { error(errSyntaxError, -1, "Functions with more than {0:d} inputs are unsupported", funcMaxInputs); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][1] = obj2.getNum(); obj2.free(); } obj1.free(); //----- Range hasRange = gFalse; n = 0; if (dict->lookup("Range", &obj1)->isArray()) { hasRange = gTrue; n = obj1.arrayGetLength() / 2; if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][1] = obj2.getNum(); obj2.free(); } } obj1.free(); return gTrue; err1: obj2.free(); err2: obj1.free(); return gFalse; } //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ IdentityFunction::IdentityFunction() { int i; // fill these in with arbitrary values just in case they get used // somewhere m = funcMaxInputs; n = funcMaxOutputs; for (i = 0; i < funcMaxInputs; ++i) { domain[i][0] = 0; domain[i][1] = 1; } hasRange = gFalse; } IdentityFunction::~IdentityFunction() { } void IdentityFunction::transform(double *in, double *out) { int i; for (i = 0; i < funcMaxOutputs; ++i) { out[i] = in[i]; } } //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { Stream *str; int sampleBits; double sampleMul; Object obj1, obj2; Guint buf, bitMask; int bits; Guint s; double in[funcMaxInputs]; int i, j, t, bit, idx; idxOffset = NULL; samples = NULL; sBuf = NULL; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 0 function is missing range"); goto err1; } if (m > sampledFuncMaxInputs) { error(errSyntaxError, -1, "Sampled functions with more than {0:d} inputs are unsupported", sampledFuncMaxInputs); goto err1; } //----- buffer sBuf = (double *)gmallocn(1 << m, sizeof(double)); //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 0 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- Size if (!dict->lookup("Size", &obj1)->isArray() || obj1.arrayGetLength() != m) { error(errSyntaxError, -1, "Function has missing or invalid size array"); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isInt()) { error(errSyntaxError, -1, "Illegal value in function size array"); goto err3; } sampleSize[i] = obj2.getInt(); if (sampleSize[i] <= 0) { error(errSyntaxError, -1, "Illegal non-positive value in function size array"); goto err3; } obj2.free(); } obj1.free(); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); for (i = 0; i < (1<= 1; --j, t <<= 1) { if (sampleSize[j] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idx = (idx + bit) * sampleSize[j-1]; } if (sampleSize[0] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idxOffset[i] = (idx + bit) * n; } //----- BitsPerSample if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); goto err2; } sampleBits = obj1.getInt(); sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); obj1.free(); //----- Encode if (dict->lookup("Encode", &obj1)->isArray() && obj1.arrayGetLength() == 2*m) { for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < m; ++i) { encode[i][0] = 0; encode[i][1] = sampleSize[i] - 1; } } obj1.free(); for (i = 0; i < m; ++i) { inputMul[i] = (encode[i][1] - encode[i][0]) / (domain[i][1] - domain[i][0]); } //----- Decode if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() == 2*n) { for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < n; ++i) { decode[i][0] = range[i][0]; decode[i][1] = range[i][1]; } } obj1.free(); //----- samples nSamples = n; for (i = 0; i < m; ++i) nSamples *= sampleSize[i]; samples = (double *)gmallocn(nSamples, sizeof(double)); buf = 0; bits = 0; bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; str->reset(); for (i = 0; i < nSamples; ++i) { if (sampleBits == 8) { s = str->getChar(); } else if (sampleBits == 16) { s = str->getChar(); s = (s << 8) + str->getChar(); } else if (sampleBits == 32) { s = str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); } else { while (bits < sampleBits) { buf = (buf << 8) | (str->getChar() & 0xff); bits += 8; } s = (buf >> (bits - sampleBits)) & bitMask; bits -= sampleBits; } samples[i] = (double)s * sampleMul; } str->close(); // set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } SampledFunction::~SampledFunction() { if (idxOffset) { gfree(idxOffset); } if (samples) { gfree(samples); } if (sBuf) { gfree(sBuf); } } SampledFunction::SampledFunction(SampledFunction *func) { memcpy(this, func, sizeof(SampledFunction)); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); samples = (double *)gmallocn(nSamples, sizeof(double)); memcpy(samples, func->samples, nSamples * sizeof(double)); sBuf = (double *)gmallocn(1 << m, sizeof(double)); } void SampledFunction::transform(double *in, double *out) { double x; int e[funcMaxInputs]; double efrac0[funcMaxInputs]; double efrac1[funcMaxInputs]; int i, j, k, idx0, t; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } // map input values into sample array for (i = 0; i < m; ++i) { x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) x = 0; } else if (x > sampleSize[i] - 1) { x = sampleSize[i] - 1; } e[i] = (int)x; if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { // this happens if in[i] = domain[i][1] e[i] = sampleSize[i] - 2; } efrac1[i] = x - e[i]; efrac0[i] = 1 - efrac1[i]; } // compute index for the first sample to be used idx0 = 0; for (k = m - 1; k >= 1; --k) { idx0 = (idx0 + e[k]) * sampleSize[k-1]; } idx0 = (idx0 + e[0]) * n; // for each output, do m-linear interpolation for (i = 0; i < n; ++i) { // pull 2^m values out of the sample array for (j = 0; j < (1<>= 1) { for (k = 0; k < t; k += 2) { sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; } } // map output value to range out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { Object obj1, obj2; int i; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Exponential function with more than one input"); goto err1; } //----- C0 if (dict->lookup("C0", &obj1)->isArray()) { if (hasRange && obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = obj1.arrayGetLength(); for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C0 array"); goto err3; } c0[i] = obj2.getNum(); obj2.free(); } } else { if (hasRange && n != 1) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = 1; c0[0] = 0; } obj1.free(); //----- C1 if (dict->lookup("C1", &obj1)->isArray()) { if (obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C1 array"); goto err3; } c1[i] = obj2.getNum(); obj2.free(); } } else { if (n != 1) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } c1[0] = 1; } obj1.free(); //----- N (exponent) if (!dict->lookup("N", &obj1)->isNum()) { error(errSyntaxError, -1, "Function has missing or invalid N"); goto err2; } e = obj1.getNum(); obj1.free(); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } ExponentialFunction::~ExponentialFunction() { } ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { memcpy(this, func, sizeof(ExponentialFunction)); } void ExponentialFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < n; ++i) { out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); if (hasRange) { if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } } return; } //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, int recursion) { Object obj1, obj2; int i; ok = gFalse; funcs = NULL; bounds = NULL; encode = NULL; scale = NULL; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Stitching function with more than one input"); goto err1; } //----- Functions if (!dict->lookup("Functions", &obj1)->isArray()) { error(errSyntaxError, -1, "Missing 'Functions' entry in stitching function"); goto err1; } k = obj1.arrayGetLength(); funcs = (Function **)gmallocn(k, sizeof(Function *)); bounds = (double *)gmallocn(k + 1, sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); for (i = 0; i < k; ++i) { funcs[i] = NULL; } for (i = 0; i < k; ++i) { if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), recursion + 1))) { goto err2; } if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { error(errSyntaxError, -1, "Incompatible subfunctions in stitching function"); goto err2; } obj2.free(); } obj1.free(); //----- Bounds if (!dict->lookup("Bounds", &obj1)->isArray() || obj1.arrayGetLength() != k - 1) { error(errSyntaxError, -1, "Missing or invalid 'Bounds' entry in stitching function"); goto err1; } bounds[0] = domain[0][0]; for (i = 1; i < k; ++i) { if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Bounds' array in stitching function"); goto err2; } bounds[i] = obj2.getNum(); obj2.free(); } bounds[k] = domain[0][1]; obj1.free(); //----- Encode if (!dict->lookup("Encode", &obj1)->isArray() || obj1.arrayGetLength() != 2 * k) { error(errSyntaxError, -1, "Missing or invalid 'Encode' entry in stitching function"); goto err1; } for (i = 0; i < 2 * k; ++i) { if (!obj1.arrayGet(i, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Encode' array in stitching function"); goto err2; } encode[i] = obj2.getNum(); obj2.free(); } obj1.free(); //----- pre-compute the scale factors for (i = 0; i < k; ++i) { if (bounds[i] == bounds[i+1]) { // avoid a divide-by-zero -- in this situation, function i will // never be used anyway scale[i] = 0; } else { scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); } } ok = gTrue; return; err2: obj2.free(); err1: obj1.free(); } StitchingFunction::StitchingFunction(StitchingFunction *func) { int i; memcpy(this, func, sizeof(StitchingFunction)); funcs = (Function **)gmallocn(k, sizeof(Function *)); for (i = 0; i < k; ++i) { funcs[i] = func->funcs[i]->copy(); } bounds = (double *)gmallocn(k + 1, sizeof(double)); memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); memcpy(encode, func->encode, 2 * k * sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); memcpy(scale, func->scale, k * sizeof(double)); ok = gTrue; } StitchingFunction::~StitchingFunction() { int i; if (funcs) { for (i = 0; i < k; ++i) { if (funcs[i]) { delete funcs[i]; } } } gfree(funcs); gfree(bounds); gfree(encode); gfree(scale); } void StitchingFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < k - 1; ++i) { if (x < bounds[i+1]) { break; } } x = encode[2*i] + (x - bounds[i]) * scale[i]; funcs[i]->transform(&x, out); } //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ // This is not an enum, because we can't foreward-declare the enum // type in Function.h #define psOpAbs 0 #define psOpAdd 1 #define psOpAnd 2 #define psOpAtan 3 #define psOpBitshift 4 #define psOpCeiling 5 #define psOpCopy 6 #define psOpCos 7 #define psOpCvi 8 #define psOpCvr 9 #define psOpDiv 10 #define psOpDup 11 #define psOpEq 12 #define psOpExch 13 #define psOpExp 14 #define psOpFalse 15 #define psOpFloor 16 #define psOpGe 17 #define psOpGt 18 #define psOpIdiv 19 #define psOpIndex 20 #define psOpLe 21 #define psOpLn 22 #define psOpLog 23 #define psOpLt 24 #define psOpMod 25 #define psOpMul 26 #define psOpNe 27 #define psOpNeg 28 #define psOpNot 29 #define psOpOr 30 #define psOpPop 31 #define psOpRoll 32 #define psOpRound 33 #define psOpSin 34 #define psOpSqrt 35 #define psOpSub 36 #define psOpTrue 37 #define psOpTruncate 38 #define psOpXor 39 #define psOpPush 40 #define psOpJ 41 #define psOpJz 42 #define nPSOps 43 // Note: 'if' and 'ifelse' are parsed separately. // The rest are listed here in alphabetical order. // The index in this table is equivalent to the psOpXXX defines. static const char *psOpNames[] = { "abs", "add", "and", "atan", "bitshift", "ceiling", "copy", "cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp", "false", "floor", "ge", "gt", "idiv", "index", "le", "ln", "log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop", "roll", "round", "sin", "sqrt", "sub", "true", "truncate", "xor" }; struct PSCode { int op; union { double d; int i; } val; }; #define psStackSize 100 PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { Stream *str; GList *tokens; GString *tok; double in[funcMaxInputs]; int tokPtr, codePtr, i; codeString = NULL; code = NULL; codeSize = 0; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 4 function is missing range"); goto err1; } //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 4 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- tokenize the function codeString = new GString(); tokens = new GList(); str->reset(); while ((tok = getToken(str))) { tokens->append(tok); } str->close(); //----- parse the function if (tokens->getLength() < 1 || ((GString *)tokens->get(0))->cmp("{")) { error(errSyntaxError, -1, "Expected '{' at start of PostScript function"); goto err2; } tokPtr = 1; codePtr = 0; if (!parseCode(tokens, &tokPtr, &codePtr)) { goto err2; } codeLen = codePtr; //----- set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; err2: deleteGList(tokens, GString); err1: return; } PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { memcpy(this, func, sizeof(PostScriptFunction)); codeString = func->codeString->copy(); code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); memcpy(code, func->code, codeSize * sizeof(PSCode)); } PostScriptFunction::~PostScriptFunction() { gfree(code); if (codeString) { delete codeString; } } void PostScriptFunction::transform(double *in, double *out) { double stack[psStackSize]; double x; int sp, i; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } for (i = 0; i < m; ++i) { stack[psStackSize - 1 - i] = in[i]; } sp = exec(stack, psStackSize - m); // if (sp < psStackSize - n) { // error(errSyntaxWarning, -1, // "Extra values on stack at end of PostScript function"); // } if (sp > psStackSize - n) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); sp = psStackSize - n; } for (i = 0; i < n; ++i) { x = stack[sp + n - 1 - i]; if (x < range[i][0]) { out[i] = range[i][0]; } else if (x > range[i][1]) { out[i] = range[i][1]; } else { out[i] = x; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { GString *tok; char *p; int a, b, mid, cmp; int codePtr0, codePtr1; while (1) { if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); p = tok->getCString(); if (isdigit(*p) || *p == '.' || *p == '-') { addCodeD(codePtr, psOpPush, atof(tok->getCString())); } else if (!tok->cmp("{")) { codePtr0 = *codePtr; addCodeI(codePtr, psOpJz, 0); if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("if")) { code[codePtr0].val.i = *codePtr; } else if (!tok->cmp("{")) { codePtr1 = *codePtr; addCodeI(codePtr, psOpJ, 0); code[codePtr0].val.i = *codePtr; if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("ifelse")) { code[codePtr1].val.i = *codePtr; } else { error(errSyntaxError, -1, "Expected 'ifelse' in PostScript function stream"); return gFalse; } } else { error(errSyntaxError, -1, "Expected 'if' in PostScript function stream"); return gFalse; } } else if (!tok->cmp("}")) { break; } else if (!tok->cmp("if")) { error(errSyntaxError, -1, "Unexpected 'if' in PostScript function stream"); return gFalse; } else if (!tok->cmp("ifelse")) { error(errSyntaxError, -1, "Unexpected 'ifelse' in PostScript function stream"); return gFalse; } else { a = -1; b = nPSOps; cmp = 0; // make gcc happy // invariant: psOpNames[a] < tok < psOpNames[b] while (b - a > 1) { mid = (a + b) / 2; cmp = tok->cmp(psOpNames[mid]); if (cmp > 0) { a = mid; } else if (cmp < 0) { b = mid; } else { a = b = mid; } } if (cmp != 0) { error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", tok); return gFalse; } addCode(codePtr, a); } } return gTrue; } void PostScriptFunction::addCode(int *codePtr, int op) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; ++(*codePtr); } void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.i = x; ++(*codePtr); } void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.d = x; ++(*codePtr); } GString *PostScriptFunction::getToken(Stream *str) { GString *s; int c; GBool comment; s = new GString(); comment = gFalse; while (1) { if ((c = str->getChar()) == EOF) { delete s; return NULL; } codeString->append(c); if (comment) { if (c == '\x0a' || c == '\x0d') { comment = gFalse; } } else if (c == '%') { comment = gTrue; } else if (!isspace(c)) { break; } } if (c == '{' || c == '}') { s->append((char)c); } else if (isdigit(c) || c == '.' || c == '-') { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { break; } str->getChar(); codeString->append(c); } } else { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !isalnum(c)) { break; } str->getChar(); codeString->append(c); } } return s; } int PostScriptFunction::exec(double *stack, int sp0) { PSCode *c; double tmp[psStackSize]; double t; int sp, ip, nn, k, i; sp = sp0; ip = 0; while (ip < codeLen) { c = &code[ip++]; switch(c->op) { case psOpAbs: if (sp >= psStackSize) { goto underflow; } stack[sp] = fabs(stack[sp]); break; case psOpAdd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] + stack[sp]; ++sp; break; case psOpAnd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; ++sp; break; case psOpAtan: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); ++sp; break; case psOpBitshift: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp + 1]; nn = (int)stack[sp]; if (nn > 0) { stack[sp + 1] = k << nn; } else if (nn < 0) { stack[sp + 1] = k >> -nn; } else { stack[sp + 1] = k; } ++sp; break; case psOpCeiling: if (sp >= psStackSize) { goto underflow; } stack[sp] = ceil(stack[sp]); break; case psOpCopy: if (sp >= psStackSize) { goto underflow; } nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (sp - nn < 0) { goto overflow; } for (i = 0; i < nn; ++i) { stack[sp - nn + i] = stack[sp + i]; } sp -= nn; break; case psOpCos: if (sp >= psStackSize) { goto underflow; } stack[sp] = cos(stack[sp]); break; case psOpCvi: if (sp >= psStackSize) { goto underflow; } stack[sp] = (int)stack[sp]; break; case psOpCvr: if (sp >= psStackSize) { goto underflow; } break; case psOpDiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] / stack[sp]; ++sp; break; case psOpDup: if (sp >= psStackSize) { goto underflow; } if (sp < 1) { goto overflow; } stack[sp - 1] = stack[sp]; --sp; break; case psOpEq: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; ++sp; break; case psOpExch: if (sp + 1 >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = stack[sp + 1]; stack[sp + 1] = t; break; case psOpExp: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = pow(stack[sp + 1], stack[sp]); ++sp; break; case psOpFalse: if (sp < 1) { goto overflow; } stack[sp - 1] = 0; --sp; break; case psOpFloor: if (sp >= psStackSize) { goto underflow; } stack[sp] = floor(stack[sp]); break; case psOpGe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; ++sp; break; case psOpGt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; ++sp; break; case psOpIdiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; ++sp; break; case psOpIndex: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp]; if (k < 0) { goto invalidArg; } if (sp + 1 + k >= psStackSize) { goto underflow; } stack[sp] = stack[sp + 1 + k]; break; case psOpLe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; ++sp; break; case psOpLn: if (sp >= psStackSize) { goto underflow; } stack[sp] = log(stack[sp]); break; case psOpLog: if (sp >= psStackSize) { goto underflow; } stack[sp] = log10(stack[sp]); break; case psOpLt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; ++sp; break; case psOpMod: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; ++sp; break; case psOpMul: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] * stack[sp]; ++sp; break; case psOpNe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; ++sp; break; case psOpNeg: if (sp >= psStackSize) { goto underflow; } stack[sp] = -stack[sp]; break; case psOpNot: if (sp >= psStackSize) { goto underflow; } stack[sp] = stack[sp] == 0 ? 1 : 0; break; case psOpOr: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; ++sp; break; case psOpPop: if (sp >= psStackSize) { goto underflow; } ++sp; break; case psOpRoll: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp++]; nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (k >= 0) { k %= nn; } else { k = -k % nn; if (k) { k = nn - k; } } for (i = 0; i < nn; ++i) { tmp[i] = stack[sp + i]; } for (i = 0; i < nn; ++i) { stack[sp + i] = tmp[(i + k) % nn]; } break; case psOpRound: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); break; case psOpSin: if (sp >= psStackSize) { goto underflow; } stack[sp] = sin(stack[sp]); break; case psOpSqrt: if (sp >= psStackSize) { goto underflow; } stack[sp] = sqrt(stack[sp]); break; case psOpSub: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] - stack[sp]; ++sp; break; case psOpTrue: if (sp < 1) { goto overflow; } stack[sp - 1] = 1; --sp; break; case psOpTruncate: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t) : ceil(t); break; case psOpXor: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; ++sp; break; case psOpPush: if (sp < 1) { goto overflow; } stack[--sp] = c->val.d; break; case psOpJ: ip = c->val.i; break; case psOpJz: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp++]; if (k == 0) { ip = c->val.i; } break; } } return sp; underflow: error(errSyntaxError, -1, "Stack underflow in PostScript function"); return sp; overflow: error(errSyntaxError, -1, "Stack overflow in PostScript function"); return sp; invalidArg: error(errSyntaxError, -1, "Invalid arg in PostScript function"); return sp; } xpdf-3.04/xpdf/HTMLGen.h0000644000076400007640000000261112341430012014211 0ustar dereknderekn//======================================================================== // // 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 xpdf-3.04/xpdf/Dict.cc0000644000076400007640000000563512341430012014045 0ustar dereknderekn//======================================================================== // // Dict.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "Object.h" #include "XRef.h" #include "Dict.h" //------------------------------------------------------------------------ struct DictEntry { char *key; Object val; DictEntry *next; }; //------------------------------------------------------------------------ // Dict //------------------------------------------------------------------------ Dict::Dict(XRef *xrefA) { xref = xrefA; size = 8; length = 0; entries = (DictEntry *)gmallocn(size, sizeof(DictEntry)); hashTab = (DictEntry **)gmallocn(2 * size - 1, sizeof(DictEntry *)); memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *)); ref = 1; } Dict::~Dict() { int i; for (i = 0; i < length; ++i) { gfree(entries[i].key); entries[i].val.free(); } gfree(entries); gfree(hashTab); } void Dict::add(char *key, Object *val) { DictEntry *e; int h; if ((e = find(key))) { e->val.free(); e->val = *val; gfree(key); } else { if (length == size) { expand(); } h = hash(key); entries[length].key = key; entries[length].val = *val; entries[length].next = hashTab[h]; hashTab[h] = &entries[length]; ++length; } } void Dict::expand() { int h, i; size *= 2; entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry)); hashTab = (DictEntry **)greallocn(hashTab, 2 * size - 1, sizeof(DictEntry *)); memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *)); for (i = 0; i < length; ++i) { h = hash(entries[i].key); entries[i].next = hashTab[h]; hashTab[h] = &entries[i]; } } inline DictEntry *Dict::find(const char *key) { DictEntry *e; int h; h = hash(key); for (e = hashTab[h]; e; e = e->next) { if (!strcmp(key, e->key)) { return e; } } return NULL; } int Dict::hash(const char *key) { const char *p; unsigned int h; h = 0; for (p = key; *p; ++p) { h = 17 * h + (int)(*p & 0xff); } return (int)(h % (2 * size - 1)); } GBool Dict::is(const char *type) { DictEntry *e; return (e = find("Type")) && e->val.isName(type); } Object *Dict::lookup(const char *key, Object *obj, int recursion) { DictEntry *e; return (e = find(key)) ? e->val.fetch(xref, obj, recursion) : obj->initNull(); } Object *Dict::lookupNF(const char *key, Object *obj) { DictEntry *e; return (e = find(key)) ? e->val.copy(obj) : obj->initNull(); } char *Dict::getKey(int i) { return entries[i].key; } Object *Dict::getVal(int i, Object *obj) { return entries[i].val.fetch(xref, obj); } Object *Dict::getValNF(int i, Object *obj) { return entries[i].val.copy(obj); } xpdf-3.04/xpdf/BuiltinFont.cc0000644000076400007640000000251112341430012015405 0ustar dereknderekn//======================================================================== // // BuiltinFont.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "FontEncodingTables.h" #include "BuiltinFont.h" //------------------------------------------------------------------------ BuiltinFontWidths::BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA) { int i, h; size = sizeA; tab = (BuiltinFontWidth **)gmallocn(size, sizeof(BuiltinFontWidth *)); for (i = 0; i < size; ++i) { tab[i] = NULL; } for (i = 0; i < sizeA; ++i) { h = hash(widths[i].name); widths[i].next = tab[h]; tab[h] = &widths[i]; } } BuiltinFontWidths::~BuiltinFontWidths() { gfree(tab); } GBool BuiltinFontWidths::getWidth(const char *name, Gushort *width) { int h; BuiltinFontWidth *p; h = hash(name); for (p = tab[h]; p; p = p->next) { if (!strcmp(p->name, name)) { *width = p->width; return gTrue; } } return gFalse; } int BuiltinFontWidths::hash(const char *name) { const char *p; unsigned int h; h = 0; for (p = name; *p; ++p) { h = 17 * h + (int)(*p & 0xff); } return (int)(h % size); } xpdf-3.04/xpdf/Link.cc0000644000076400007640000005215412341430012014055 0ustar dereknderekn//======================================================================== // // Link.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "GString.h" #include "Error.h" #include "Object.h" #include "Array.h" #include "Dict.h" #include "Link.h" //------------------------------------------------------------------------ // LinkAction //------------------------------------------------------------------------ LinkAction *LinkAction::parseDest(Object *obj) { LinkAction *action; action = new LinkGoTo(obj); if (!action->isOk()) { delete action; return NULL; } return action; } LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) { LinkAction *action; Object obj2, obj3, obj4, obj5; if (!obj->isDict()) { error(errSyntaxWarning, -1, "Bad annotation action"); return NULL; } obj->dictLookup("S", &obj2); // GoTo action if (obj2.isName("GoTo")) { obj->dictLookup("D", &obj3); action = new LinkGoTo(&obj3); obj3.free(); // GoToR action } else if (obj2.isName("GoToR")) { obj->dictLookup("F", &obj3); obj->dictLookup("D", &obj4); action = new LinkGoToR(&obj3, &obj4); obj3.free(); obj4.free(); // Launch action } else if (obj2.isName("Launch")) { action = new LinkLaunch(obj); // URI action } else if (obj2.isName("URI")) { obj->dictLookup("URI", &obj3); action = new LinkURI(&obj3, baseURI); obj3.free(); // Named action } else if (obj2.isName("Named")) { obj->dictLookup("N", &obj3); action = new LinkNamed(&obj3); obj3.free(); // Movie action } else if (obj2.isName("Movie")) { obj->dictLookupNF("Annot", &obj3); obj->dictLookup("T", &obj4); action = new LinkMovie(&obj3, &obj4); obj3.free(); obj4.free(); // 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()); // action is missing or wrong type } else { error(errSyntaxWarning, -1, "Bad annotation action"); action = NULL; } obj2.free(); if (action && !action->isOk()) { delete action; return NULL; } return action; } GString *LinkAction::getFileSpecName(Object *fileSpecObj) { GString *name; Object obj1; name = NULL; // string if (fileSpecObj->isString()) { name = fileSpecObj->getString()->copy(); // dictionary } else if (fileSpecObj->isDict()) { #ifdef _WIN32 if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) { #else if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) { #endif obj1.free(); fileSpecObj->dictLookup("F", &obj1); } if (obj1.isString()) { name = obj1.getString()->copy(); } else { error(errSyntaxWarning, -1, "Illegal file spec in link"); } obj1.free(); // error } else { error(errSyntaxWarning, -1, "Illegal file spec in link"); } // system-dependent path manipulation if (name) { #ifdef _WIN32 int i, j; // "//...." --> "\...." // "/x/...." --> "x:\...." // "/server/share/...." --> "\\server\share\...." // convert escaped slashes to slashes and unescaped slashes to backslashes i = 0; if (name->getChar(0) == '/') { if (name->getLength() >= 2 && name->getChar(1) == '/') { name->del(0); i = 0; } else if (name->getLength() >= 2 && ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') || (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) && (name->getLength() == 2 || name->getChar(2) == '/')) { name->setChar(0, name->getChar(1)); name->setChar(1, ':'); i = 2; } else { for (j = 2; j < name->getLength(); ++j) { if (name->getChar(j-1) != '\\' && name->getChar(j) == '/') { break; } } if (j < name->getLength()) { name->setChar(0, '\\'); name->insert(0, '\\'); i = 2; } } } for (; i < name->getLength(); ++i) { if (name->getChar(i) == '/') { name->setChar(i, '\\'); } else if (name->getChar(i) == '\\' && i+1 < name->getLength() && name->getChar(i+1) == '/') { name->del(i); } } #else // no manipulation needed for Unix #endif } return name; } //------------------------------------------------------------------------ // LinkDest //------------------------------------------------------------------------ LinkDest::LinkDest(Array *a) { Object obj1, obj2; // initialize fields left = bottom = right = top = zoom = 0; ok = gFalse; // get page if (a->getLength() < 2) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); return; } a->getNF(0, &obj1); if (obj1.isInt()) { pageNum = obj1.getInt() + 1; pageIsRef = gFalse; } else if (obj1.isRef()) { pageRef.num = obj1.getRefNum(); pageRef.gen = obj1.getRefGen(); pageIsRef = gTrue; } else { error(errSyntaxWarning, -1, "Bad annotation destination"); goto err2; } obj1.free(); // get destination type a->get(1, &obj1); // XYZ link if (obj1.isName("XYZ")) { kind = destXYZ; if (a->getLength() < 3) { changeLeft = gFalse; } else { a->get(2, &obj2); if (obj2.isNull()) { changeLeft = gFalse; } else if (obj2.isNum()) { changeLeft = gTrue; left = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); goto err1; } obj2.free(); } if (a->getLength() < 4) { changeTop = gFalse; } else { a->get(3, &obj2); if (obj2.isNull()) { changeTop = gFalse; } else if (obj2.isNum()) { changeTop = gTrue; top = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); goto err1; } obj2.free(); } if (a->getLength() < 5) { changeZoom = gFalse; } else { a->get(4, &obj2); if (obj2.isNull()) { changeZoom = gFalse; } else if (obj2.isNum()) { changeZoom = gTrue; zoom = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); goto err1; } obj2.free(); } // Fit link } else if (obj1.isName("Fit")) { if (a->getLength() < 2) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFit; // FitH link } else if (obj1.isName("FitH")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFitH; if (a->get(2, &obj2)->isNum()) { top = obj2.getNum(); changeTop = gTrue; } else if (obj2.isNull()) { changeTop = gFalse; } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2.free(); // FitV link } else if (obj1.isName("FitV")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFitV; if (a->get(2, &obj2)->isNum()) { left = obj2.getNum(); changeLeft = gTrue; } else if (obj2.isNull()) { changeLeft = gFalse; } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2.free(); // FitR link } else if (obj1.isName("FitR")) { if (a->getLength() < 6) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFitR; if (a->get(2, &obj2)->isNum()) { left = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2.free(); if (!a->get(3, &obj2)->isNum()) { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } bottom = obj2.getNum(); obj2.free(); if (!a->get(4, &obj2)->isNum()) { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } right = obj2.getNum(); obj2.free(); if (!a->get(5, &obj2)->isNum()) { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } top = obj2.getNum(); obj2.free(); // FitB link } else if (obj1.isName("FitB")) { if (a->getLength() < 2) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFitB; // FitBH link } else if (obj1.isName("FitBH")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFitBH; if (a->get(2, &obj2)->isNum()) { top = obj2.getNum(); changeTop = gTrue; } else if (obj2.isNull()) { changeTop = gFalse; } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2.free(); // FitBV link } else if (obj1.isName("FitBV")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); goto err2; } kind = destFitBV; if (a->get(2, &obj2)->isNum()) { left = obj2.getNum(); changeLeft = gTrue; } else if (obj2.isNull()) { changeLeft = gFalse; } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2.free(); // unknown link kind } else { error(errSyntaxWarning, -1, "Unknown annotation destination type"); goto err2; } obj1.free(); ok = gTrue; return; err1: obj2.free(); err2: obj1.free(); } LinkDest::LinkDest(LinkDest *dest) { kind = dest->kind; pageIsRef = dest->pageIsRef; if (pageIsRef) pageRef = dest->pageRef; else pageNum = dest->pageNum; left = dest->left; bottom = dest->bottom; right = dest->right; top = dest->top; zoom = dest->zoom; changeLeft = dest->changeLeft; changeTop = dest->changeTop; changeZoom = dest->changeZoom; ok = gTrue; } //------------------------------------------------------------------------ // LinkGoTo //------------------------------------------------------------------------ LinkGoTo::LinkGoTo(Object *destObj) { dest = NULL; namedDest = NULL; // named destination if (destObj->isName()) { namedDest = new GString(destObj->getName()); } else if (destObj->isString()) { namedDest = destObj->getString()->copy(); // destination dictionary } else if (destObj->isArray()) { dest = new LinkDest(destObj->getArray()); if (!dest->isOk()) { delete dest; dest = NULL; } // error } else { error(errSyntaxWarning, -1, "Illegal annotation destination"); } } LinkGoTo::~LinkGoTo() { if (dest) delete dest; if (namedDest) delete namedDest; } //------------------------------------------------------------------------ // LinkGoToR //------------------------------------------------------------------------ LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) { dest = NULL; namedDest = NULL; // get file name fileName = getFileSpecName(fileSpecObj); // named destination if (destObj->isName()) { namedDest = new GString(destObj->getName()); } else if (destObj->isString()) { namedDest = destObj->getString()->copy(); // destination dictionary } else if (destObj->isArray()) { dest = new LinkDest(destObj->getArray()); if (!dest->isOk()) { delete dest; dest = NULL; } // error } else { error(errSyntaxWarning, -1, "Illegal annotation destination"); } } LinkGoToR::~LinkGoToR() { if (fileName) delete fileName; if (dest) delete dest; if (namedDest) delete namedDest; } //------------------------------------------------------------------------ // LinkLaunch //------------------------------------------------------------------------ LinkLaunch::LinkLaunch(Object *actionObj) { Object obj1, obj2; fileName = NULL; params = NULL; if (actionObj->isDict()) { if (!actionObj->dictLookup("F", &obj1)->isNull()) { fileName = getFileSpecName(&obj1); } else { obj1.free(); #ifdef _WIN32 if (actionObj->dictLookup("Win", &obj1)->isDict()) { obj1.dictLookup("F", &obj2); fileName = getFileSpecName(&obj2); obj2.free(); if (obj1.dictLookup("P", &obj2)->isString()) { params = obj2.getString()->copy(); } obj2.free(); } else { error(errSyntaxWarning, -1, "Bad launch-type link action"); } #else //~ This hasn't been defined by Adobe yet, so assume it looks //~ just like the Win dictionary until they say otherwise. if (actionObj->dictLookup("Unix", &obj1)->isDict()) { obj1.dictLookup("F", &obj2); fileName = getFileSpecName(&obj2); obj2.free(); if (obj1.dictLookup("P", &obj2)->isString()) { params = obj2.getString()->copy(); } obj2.free(); } else { error(errSyntaxWarning, -1, "Bad launch-type link action"); } #endif } obj1.free(); } } LinkLaunch::~LinkLaunch() { if (fileName) delete fileName; if (params) delete params; } //------------------------------------------------------------------------ // LinkURI //------------------------------------------------------------------------ LinkURI::LinkURI(Object *uriObj, GString *baseURI) { GString *uri2; int n; char c; uri = NULL; if (uriObj->isString()) { uri2 = uriObj->getString(); n = (int)strcspn(uri2->getCString(), "/:"); if (n < uri2->getLength() && uri2->getChar(n) == ':') { // "http:..." etc. uri = uri2->copy(); } else if (!uri2->cmpN("www.", 4)) { // "www.[...]" without the leading "http://" uri = new GString("http://"); uri->append(uri2); } else { // relative URI if (baseURI) { uri = baseURI->copy(); c = uri->getChar(uri->getLength() - 1); if (c != '/' && c != '?') { uri->append('/'); } if (uri2->getChar(0) == '/') { uri->append(uri2->getCString() + 1, uri2->getLength() - 1); } else { uri->append(uri2); } } else { uri = uri2->copy(); } } } else { error(errSyntaxWarning, -1, "Illegal URI-type link"); } } LinkURI::~LinkURI() { if (uri) delete uri; } //------------------------------------------------------------------------ // LinkNamed //------------------------------------------------------------------------ LinkNamed::LinkNamed(Object *nameObj) { name = NULL; if (nameObj->isName()) { name = new GString(nameObj->getName()); } } LinkNamed::~LinkNamed() { if (name) { delete name; } } //------------------------------------------------------------------------ // LinkMovie //------------------------------------------------------------------------ LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) { annotRef.num = -1; title = NULL; if (annotObj->isRef()) { annotRef = annotObj->getRef(); } else if (titleObj->isString()) { title = titleObj->getString()->copy(); } else { error(errSyntaxError, -1, "Movie action is missing both the Annot and T keys"); } } LinkMovie::~LinkMovie() { if (title) { delete title; } } //------------------------------------------------------------------------ // 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 //------------------------------------------------------------------------ LinkUnknown::LinkUnknown(char *actionA) { action = new GString(actionA); } LinkUnknown::~LinkUnknown() { delete action; } //------------------------------------------------------------------------ // Link //------------------------------------------------------------------------ Link::Link(Dict *dict, GString *baseURI) { Object obj1, obj2; double t; action = NULL; ok = gFalse; // get rectangle if (!dict->lookup("Rect", &obj1)->isArray()) { error(errSyntaxError, -1, "Annotation rectangle is wrong type"); goto err2; } if (!obj1.arrayGet(0, &obj2)->isNum()) { error(errSyntaxError, -1, "Bad annotation rectangle"); goto err1; } x1 = obj2.getNum(); obj2.free(); if (!obj1.arrayGet(1, &obj2)->isNum()) { error(errSyntaxError, -1, "Bad annotation rectangle"); goto err1; } y1 = obj2.getNum(); obj2.free(); if (!obj1.arrayGet(2, &obj2)->isNum()) { error(errSyntaxError, -1, "Bad annotation rectangle"); goto err1; } x2 = obj2.getNum(); obj2.free(); if (!obj1.arrayGet(3, &obj2)->isNum()) { error(errSyntaxError, -1, "Bad annotation rectangle"); goto err1; } y2 = obj2.getNum(); obj2.free(); obj1.free(); if (x1 > x2) { t = x1; x1 = x2; x2 = t; } if (y1 > y2) { t = y1; y1 = y2; y2 = t; } // look for destination if (!dict->lookup("Dest", &obj1)->isNull()) { action = LinkAction::parseDest(&obj1); // look for action } else { obj1.free(); if (dict->lookup("A", &obj1)->isDict()) { action = LinkAction::parseAction(&obj1, baseURI); } } obj1.free(); // check for bad action if (action) { ok = gTrue; } return; err1: obj2.free(); err2: obj1.free(); } Link::~Link() { if (action) { delete action; } } //------------------------------------------------------------------------ // Links //------------------------------------------------------------------------ Links::Links(Object *annots, GString *baseURI) { Link *link; Object obj1, obj2, obj3; int size; int i; links = NULL; size = 0; numLinks = 0; if (annots->isArray()) { for (i = 0; i < annots->arrayGetLength(); ++i) { if (annots->arrayGet(i, &obj1)->isDict()) { 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) { size += 16; links = (Link **)greallocn(links, size, sizeof(Link *)); } links[numLinks++] = link; } else { delete link; } } obj3.free(); obj2.free(); } obj1.free(); } } } Links::~Links() { int i; for (i = 0; i < numLinks; ++i) delete links[i]; gfree(links); } LinkAction *Links::find(double x, double y) { int i; for (i = numLinks - 1; i >= 0; --i) { if (links[i]->inRect(x, y)) { return links[i]->getAction(); } } return NULL; } GBool Links::onLink(double x, double y) { int i; for (i = 0; i < numLinks; ++i) { if (links[i]->inRect(x, y)) return gTrue; } return gFalse; } xpdf-3.04/xpdf/HTMLGen.cc0000644000076400007640000004314212341430012014353 0ustar dereknderekn//======================================================================== // // 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"); } xpdf-3.04/xpdf/xpdfIcon.xpm0000644000076400007640000000527712341430012015155 0ustar dereknderekn/* XPM */ static char *xpdfIcon[] = { /* width height num_colors chars_per_pixel */ " 48 48 7 1", /* colors */ ". c #000000", "# c #a00000", "a c #a0a0a0", "b c #c0c0c0", "c c #e00000", "d c #e0e0e0", "e c #ffffff", /* pixels */ "................................................", "................................................", "................................................", "................................................", "#ccccccc#................................#ccc#..", ".#ccccccc#..............................#ccc#...", "..#ccccccc#............................#ccc#....", "...#ccccccc#................bbba.....abbbc#.....", "....#ccccccc#...............beea....debbed......", ".....#ccccccc#...............ee....bebccee......", "......#ccccccc#..............ee....eecc#bb......", ".......#ccccccc#............aee...#eec#.........", "........#ccccccc#...........bed..#beb#..........", ".........#ccccccc#..........beb.#cbeb...........", "..........#ccccccc#.........beb#ccbeb...........", "...........#ccccccc#........bebcccbeb...........", "............#ccccccc#.......eebcc#dea...........", ".............#ccccccc#......eecc#.ee............", ".........ae...#ccccccc#....#eec#..ee............", "........aeeaeeeebcccccabeeeaee#.beeeeee.........", ".......aeeeea..debcccaed##ceee....ee............", "......addee.....eeccaedccccbed...beb............", "......a.bee.....ee#cbeaccccbeb...beb............", "........beb.....ee.#eecccccbeb...beb............", "........beb.....ee..eecccccbeb...beb............", "........beb.....ee..eeccccceeb...dea............", "........deb....aeb.#eeccccceec#..ee.............", "........eea....dea#cee##ccceecc#.ee.............", "........eee...dea#ccbeda#cdeeccc#ee.............", "........eeaeeeba#ccc#beeeeaeeecceeee............", "........ee.....#ccc#......#ccccccc#.............", ".......bee....#ccc#........#ccccccc#............", ".......beb...#ccc#..........#ccccccc#...........", ".......beb..#ccc#............#ccccccc#..........", ".......beb.#ccc#..............#ccccccc#.........", ".......deb#ccc#................#ccccccc#........", ".......eeaccc#..................#ccccccc#.......", ".......eeccc#....................#ccccccc#......", ".......eecc#......................#ccccccc#.....", "......beeb#........................#ccccccc#....", ".....#bbbb..........................#ccccccc#...", "....#ccc#............................#ccccccc#..", "...#ccc#..............................#ccccccc#.", "..#ccc#................................#ccccccc.", "................................................", "................................................", "................................................", "................................................" }; xpdf-3.04/xpdf/FontEncodingTables.cc0000644000076400007640000005145412341430012016672 0ustar dereknderekn//======================================================================== // // FontEncodingTables.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #include #include "FontEncodingTables.h" const char *macRomanEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", NULL, "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis", "space", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron" }; const char *macExpertEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "exclamsmall", "Hungarumlautsmall", "centoldstyle", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", NULL, "threequartersemdash", NULL, "questionsmall", NULL, NULL, NULL, NULL, "Ethsmall", NULL, NULL, "onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", NULL, NULL, NULL, NULL, NULL, NULL, "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", NULL, "parenrightinferior", "Circumflexsmall", "hypheninferior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", NULL, NULL, "asuperior", "centsuperior", NULL, NULL, NULL, NULL, "Aacutesmall", "Agravesmall", "Acircumflexsmall", "Adieresissmall", "Atildesmall", "Aringsmall", "Ccedillasmall", "Eacutesmall", "Egravesmall", "Ecircumflexsmall", "Edieresissmall", "Iacutesmall", "Igravesmall", "Icircumflexsmall", "Idieresissmall", "Ntildesmall", "Oacutesmall", "Ogravesmall", "Ocircumflexsmall", "Odieresissmall", "Otildesmall", "Uacutesmall", "Ugravesmall", "Ucircumflexsmall", "Udieresissmall", NULL, "eightsuperior", "fourinferior", "threeinferior", "sixinferior", "eightinferior", "seveninferior", "Scaronsmall", NULL, "centinferior", "twoinferior", NULL, "Dieresissmall", NULL, "Caronsmall", "osuperior", "fiveinferior", NULL, "commainferior", "periodinferior", "Yacutesmall", NULL, "dollarinferior", NULL, NULL, "Thornsmall", NULL, "nineinferior", "zeroinferior", "Zcaronsmall", "AEsmall", "Oslashsmall", "questiondownsmall", "oneinferior", "Lslashsmall", NULL, NULL, NULL, NULL, NULL, NULL, "Cedillasmall", NULL, NULL, NULL, NULL, NULL, "OEsmall", "figuredash", "hyphensuperior", NULL, NULL, NULL, NULL, "exclamdownsmall", NULL, "Ydieresissmall", NULL, "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "ninesuperior", "zerosuperior", NULL, "esuperior", "rsuperior", "tsuperior", NULL, NULL, "isuperior", "ssuperior", "dsuperior", NULL, NULL, NULL, NULL, NULL, "lsuperior", "Ogoneksmall", "Brevesmall", "Macronsmall", "bsuperior", "nsuperior", "msuperior", "commasuperior", "periodsuperior", "Dotaccentsmall", "Ringsmall", NULL, NULL, NULL, NULL }; const char *winAnsiEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "bullet", "Euro", "bullet", "quotesinglbase", "florin", "quotedblbase", "ellipsis", "dagger", "daggerdbl", "circumflex", "perthousand", "Scaron", "guilsinglleft", "OE", "bullet", "Zcaron", "bullet", "bullet", "quoteleft", "quoteright", "quotedblleft", "quotedblright", "bullet", "endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright", "oe", "bullet", "zcaron", "Ydieresis", "space", "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", "copyright", "ordfeminine", "guillemotleft", "logicalnot", "hyphen", "registered", "macron", "degree", "plusminus", "twosuperior", "threesuperior", "acute", "mu", "paragraph", "periodcentered", "cedilla", "onesuperior", "ordmasculine", "guillemotright", "onequarter", "onehalf", "threequarters", "questiondown", "Agrave", "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", "Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve", "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", "germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis", "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", "edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth", "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", "yacute", "thorn", "ydieresis" }; const char *standardEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", NULL, "endash", "dagger", "daggerdbl", "periodcentered", NULL, "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", NULL, "questiondown", NULL, "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", NULL, "ring", "cedilla", NULL, "hungarumlaut", "ogonek", "caron", "emdash", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "AE", NULL, "ordfeminine", NULL, NULL, NULL, NULL, "Lslash", "Oslash", "OE", "ordmasculine", NULL, NULL, NULL, NULL, NULL, "ae", NULL, NULL, NULL, "dotlessi", NULL, NULL, "lslash", "oslash", "oe", "germandbls", NULL, NULL, NULL, NULL }; const char *expertEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "exclamsmall", "Hungarumlautsmall", NULL, "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", NULL, "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", NULL, NULL, NULL, "isuperior", NULL, NULL, "lsuperior", "msuperior", "nsuperior", "osuperior", NULL, NULL, "rsuperior", "ssuperior", "tsuperior", NULL, "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", NULL, "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "exclamdownsmall", "centoldstyle", "Lslashsmall", NULL, NULL, "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", NULL, "Dotaccentsmall", NULL, NULL, "Macronsmall", NULL, NULL, "figuredash", "hypheninferior", NULL, NULL, "Ogoneksmall", "Ringsmall", "Cedillasmall", NULL, NULL, NULL, "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", NULL, NULL, "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall" }; const char *symbolEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "exclam", "universal", "numbersign", "existential", "percent", "ampersand", "suchthat", "parenleft", "parenright", "asteriskmath", "plus", "comma", "minus", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "congruent", "Alpha", "Beta", "Chi", "Delta", "Epsilon", "Phi", "Gamma", "Eta", "Iota", "theta1", "Kappa", "Lambda", "Mu", "Nu", "Omicron", "Pi", "Theta", "Rho", "Sigma", "Tau", "Upsilon", "sigma1", "Omega", "Xi", "Psi", "Zeta", "bracketleft", "therefore", "bracketright", "perpendicular", "underscore", "radicalex", "alpha", "beta", "chi", "delta", "epsilon", "phi", "gamma", "eta", "iota", "phi1", "kappa", "lambda", "mu", "nu", "omicron", "pi", "theta", "rho", "sigma", "tau", "upsilon", "omega1", "omega", "xi", "psi", "zeta", "braceleft", "bar", "braceright", "similar", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Upsilon1", "minute", "lessequal", "fraction", "infinity", "florin", "club", "diamond", "heart", "spade", "arrowboth", "arrowleft", "arrowup", "arrowright", "arrowdown", "degree", "plusminus", "second", "greaterequal", "multiply", "proportional", "partialdiff", "bullet", "divide", "notequal", "equivalence", "approxequal", "ellipsis", "arrowvertex", "arrowhorizex", "carriagereturn", "aleph", "Ifraktur", "Rfraktur", "weierstrass", "circlemultiply", "circleplus", "emptyset", "intersection", "union", "propersuperset", "reflexsuperset", "notsubset", "propersubset", "reflexsubset", "element", "notelement", "angle", "gradient", "registerserif", "copyrightserif", "trademarkserif", "product", "radical", "dotmath", "logicalnot", "logicaland", "logicalor", "arrowdblboth", "arrowdblleft", "arrowdblup", "arrowdblright", "arrowdbldown", "lozenge", "angleleft", "registersans", "copyrightsans", "trademarksans", "summation", "parenlefttp", "parenleftex", "parenleftbt", "bracketlefttp", "bracketleftex", "bracketleftbt", "bracelefttp", "braceleftmid", "braceleftbt", "braceex", NULL, "angleright", "integral", "integraltp", "integralex", "integralbt", "parenrighttp", "parenrightex", "parenrightbt", "bracketrighttp", "bracketrightex", "bracketrightbt", "bracerighttp", "bracerightmid", "bracerightbt", NULL }; const char *zapfDingbatsEncoding[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "space", "a1", "a2", "a202", "a3", "a4", "a5", "a119", "a118", "a117", "a11", "a12", "a13", "a14", "a15", "a16", "a105", "a17", "a18", "a19", "a20", "a21", "a22", "a23", "a24", "a25", "a26", "a27", "a28", "a6", "a7", "a8", "a9", "a10", "a29", "a30", "a31", "a32", "a33", "a34", "a35", "a36", "a37", "a38", "a39", "a40", "a41", "a42", "a43", "a44", "a45", "a46", "a47", "a48", "a49", "a50", "a51", "a52", "a53", "a54", "a55", "a56", "a57", "a58", "a59", "a60", "a61", "a62", "a63", "a64", "a65", "a66", "a67", "a68", "a69", "a70", "a71", "a72", "a73", "a74", "a203", "a75", "a204", "a76", "a77", "a78", "a79", "a81", "a82", "a83", "a84", "a97", "a98", "a99", "a100", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "a101", "a102", "a103", "a104", "a106", "a107", "a108", "a112", "a111", "a110", "a109", "a120", "a121", "a122", "a123", "a124", "a125", "a126", "a127", "a128", "a129", "a130", "a131", "a132", "a133", "a134", "a135", "a136", "a137", "a138", "a139", "a140", "a141", "a142", "a143", "a144", "a145", "a146", "a147", "a148", "a149", "a150", "a151", "a152", "a153", "a154", "a155", "a156", "a157", "a158", "a159", "a160", "a161", "a163", "a164", "a196", "a165", "a192", "a166", "a167", "a168", "a169", "a170", "a171", "a172", "a173", "a162", "a174", "a175", "a176", "a177", "a178", "a179", "a193", "a180", "a199", "a181", "a200", "a182", NULL, "a201", "a183", "a184", "a197", "a185", "a194", "a198", "a186", "a195", "a187", "a188", "a189", "a190", "a191", NULL }; xpdf-3.04/xpdf/OptionalContent.cc0000644000076400007640000002610612341430012016276 0ustar dereknderekn//======================================================================== // // OptionalContent.cc // // Copyright 2008-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "GString.h" #include "GList.h" #include "Error.h" #include "Object.h" #include "PDFDoc.h" #include "TextString.h" #include "OptionalContent.h" //------------------------------------------------------------------------ #define ocPolicyAllOn 1 #define ocPolicyAnyOn 2 #define ocPolicyAnyOff 3 #define ocPolicyAllOff 4 //------------------------------------------------------------------------ // Max depth of nested visibility expressions. This is used to catch // infinite loops in the visibility expression object structure. #define visibilityExprRecursionLimit 50 // Max depth of nested display nodes. This is used to catch infinite // loops in the "Order" object structure. #define displayNodeRecursionLimit 50 //------------------------------------------------------------------------ OptionalContent::OptionalContent(PDFDoc *doc) { Object *ocProps; Object ocgList, defView, obj1, obj2; Ref ref1; OptionalContentGroup *ocg; int i; xref = doc->getXRef(); ocgs = new GList(); display = NULL; if ((ocProps = doc->getCatalog()->getOCProperties())->isDict()) { if (ocProps->dictLookup("OCGs", &ocgList)->isArray()) { //----- read the OCG list for (i = 0; i < ocgList.arrayGetLength(); ++i) { if (ocgList.arrayGetNF(i, &obj1)->isRef()) { ref1 = obj1.getRef(); obj1.fetch(xref, &obj2); if ((ocg = OptionalContentGroup::parse(&ref1, &obj2))) { ocgs->append(ocg); } obj2.free(); } obj1.free(); } //----- read the default viewing OCCD if (ocProps->dictLookup("D", &defView)->isDict()) { //----- initial state if (defView.dictLookup("OFF", &obj1)->isArray()) { for (i = 0; i < obj1.arrayGetLength(); ++i) { if (obj1.arrayGetNF(i, &obj2)->isRef()) { ref1 = obj2.getRef(); if ((ocg = findOCG(&ref1))) { ocg->setState(gFalse); } else { error(errSyntaxError, -1, "Invalid OCG reference in OFF array in default viewing OCCD"); } } obj2.free(); } } obj1.free(); //----- display order if (defView.dictLookup("Order", &obj1)->isArray()) { display = OCDisplayNode::parse(&obj1, this, xref); } obj1.free(); } else { error(errSyntaxError, -1, "Missing or invalid default viewing OCCD"); } defView.free(); } ocgList.free(); } if (!display) { display = new OCDisplayNode(); } } OptionalContent::~OptionalContent() { deleteGList(ocgs, OptionalContentGroup); delete display; } int OptionalContent::getNumOCGs() { return ocgs->getLength(); } OptionalContentGroup *OptionalContent::getOCG(int idx) { return (OptionalContentGroup *)ocgs->get(idx); } OptionalContentGroup *OptionalContent::findOCG(Ref *ref) { OptionalContentGroup *ocg; int i; for (i = 0; i < ocgs->getLength(); ++i) { ocg = (OptionalContentGroup *)ocgs->get(i); if (ocg->matches(ref)) { return ocg; } } return NULL; } GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) { OptionalContentGroup *ocg; int policy; Ref ref; Object obj2, obj3, obj4, obj5; int i; if (obj->isNull()) { return gFalse; } if (obj->isRef()) { ref = obj->getRef(); if ((ocg = findOCG(&ref))) { *visible = ocg->getState(); return gTrue; } } obj->fetch(xref, &obj2); 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); } 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; } 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; } } obj5.free(); } obj4.free(); } obj3.free(); } obj2.free(); return gTrue; } GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) { OptionalContentGroup *ocg; Object expr2, op, obj; Ref ref; GBool ret; int i; if (recursion > visibilityExprRecursionLimit) { error(errSyntaxError, -1, "Loop detected in optional content visibility expression"); return gTrue; } if (expr->isRef()) { ref = expr->getRef(); if ((ocg = findOCG(&ref))) { return ocg->getState(); } } expr->fetch(xref, &expr2); if (!expr2.isArray() || expr2.arrayGetLength() < 1) { error(errSyntaxError, -1, "Invalid optional content visibility expression"); expr2.free(); return gTrue; } expr2.arrayGet(0, &op); if (op.isName("Not")) { if (expr2.arrayGetLength() == 2) { expr2.arrayGetNF(1, &obj); ret = !evalOCVisibilityExpr(&obj, recursion + 1); obj.free(); } else { error(errSyntaxError, -1, "Invalid optional content visibility expression"); ret = gTrue; } } else if (op.isName("And")) { ret = gTrue; for (i = 1; i < expr2.arrayGetLength() && ret; ++i) { expr2.arrayGetNF(i, &obj); ret = evalOCVisibilityExpr(&obj, recursion + 1); obj.free(); } } else if (op.isName("Or")) { ret = gFalse; for (i = 1; i < expr2.arrayGetLength() && !ret; ++i) { expr2.arrayGetNF(i, &obj); ret = evalOCVisibilityExpr(&obj, recursion + 1); obj.free(); } } else { error(errSyntaxError, -1, "Invalid optional content visibility expression"); ret = gTrue; } op.free(); expr2.free(); return ret; } //------------------------------------------------------------------------ OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) { TextString *nameA; Object obj1, obj2, obj3; OCUsageState viewStateA, printStateA; if (!obj->isDict()) { return NULL; } if (!obj->dictLookup("Name", &obj1)->isString()) { error(errSyntaxError, -1, "Missing or invalid Name in OCG"); obj1.free(); return NULL; } nameA = new TextString(obj1.getString()); obj1.free(); viewStateA = printStateA = ocUsageUnset; if (obj->dictLookup("Usage", &obj1)->isDict()) { if (obj1.dictLookup("View", &obj2)->isDict()) { if (obj2.dictLookup("ViewState", &obj3)->isName()) { if (obj3.isName("ON")) { viewStateA = ocUsageOn; } else { viewStateA = ocUsageOff; } } obj3.free(); } obj2.free(); if (obj1.dictLookup("Print", &obj2)->isDict()) { if (obj2.dictLookup("PrintState", &obj3)->isName()) { if (obj3.isName("ON")) { printStateA = ocUsageOn; } else { printStateA = ocUsageOff; } } obj3.free(); } obj2.free(); } obj1.free(); return new OptionalContentGroup(refA, nameA, viewStateA, printStateA); } OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA, OCUsageState viewStateA, OCUsageState printStateA) { ref = *refA; name = nameA; viewState = viewStateA; printState = printStateA; state = gTrue; } OptionalContentGroup::~OptionalContentGroup() { 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, XRef *xref, int recursion) { Object obj2, obj3; Ref ref; OptionalContentGroup *ocgA; OCDisplayNode *node, *child; int i; if (recursion > displayNodeRecursionLimit) { error(errSyntaxError, -1, "Loop detected in optional content order"); return NULL; } if (obj->isRef()) { ref = obj->getRef(); if ((ocgA = oc->findOCG(&ref))) { return new OCDisplayNode(ocgA); } } obj->fetch(xref, &obj2); if (!obj2.isArray()) { obj2.free(); return NULL; } i = 0; if (obj2.arrayGetLength() >= 1) { if (obj2.arrayGet(0, &obj3)->isString()) { node = new OCDisplayNode(obj3.getString()); i = 1; } else { node = new OCDisplayNode(); } obj3.free(); } else { node = new OCDisplayNode(); } for (; i < obj2.arrayGetLength(); ++i) { obj2.arrayGetNF(i, &obj3); if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) { if (!child->ocg && !child->name && node->getNumChildren() > 0) { if (child->getNumChildren() > 0) { node->getChild(node->getNumChildren() - 1)-> addChildren(child->takeChildren()); } delete child; } else { node->addChild(child); } } obj3.free(); } obj2.free(); return node; } OCDisplayNode::OCDisplayNode() { name = new TextString(); ocg = NULL; children = NULL; } OCDisplayNode::OCDisplayNode(GString *nameA) { name = new TextString(nameA); ocg = NULL; children = NULL; } OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) { name = new TextString(ocgA->name); ocg = ocgA; children = NULL; } void OCDisplayNode::addChild(OCDisplayNode *child) { if (!children) { children = new GList(); } children->append(child); } void OCDisplayNode::addChildren(GList *childrenA) { if (!children) { children = new GList(); } children->append(childrenA); delete childrenA; } GList *OCDisplayNode::takeChildren() { GList *childrenA; childrenA = children; children = NULL; return childrenA; } OCDisplayNode::~OCDisplayNode() { 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; } return children->getLength(); } OCDisplayNode *OCDisplayNode::getChild(int idx) { return (OCDisplayNode *)children->get(idx); } xpdf-3.04/xpdf/SecurityHandler.h0000644000076400007640000001202112341430012016114 0ustar dereknderekn//======================================================================== // // SecurityHandler.h // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #ifndef SECURITYHANDLER_H #define SECURITYHANDLER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" class GString; class PDFDoc; struct XpdfSecurityHandler; //------------------------------------------------------------------------ // SecurityHandler //------------------------------------------------------------------------ class SecurityHandler { public: static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA); SecurityHandler(PDFDoc *docA); virtual ~SecurityHandler(); // Returns true if the file is actually unencrypted. virtual GBool isUnencrypted() { return gFalse; } // Check the document's encryption. If the document is encrypted, // this will first try and (in // "batch" mode), and if those fail, it will attempt to request a // password from the user. This is the high-level function that // calls the lower level functions for the specific security handler // (requesting a password three times, etc.). Returns true if the // document can be opened (if it's unencrypted, or if a correct // password is obtained); false otherwise (encrypted and no correct // password). GBool checkEncryption(GString *ownerPassword, GString *userPassword); // Create authorization data for the specified owner and user // passwords. If the security handler doesn't support "batch" mode, // this function should return NULL. virtual void *makeAuthData(GString *ownerPassword, GString *userPassword) = 0; // Construct authorization data, typically by prompting the user for // a password. Returns an authorization data object, or NULL to // cancel. virtual void *getAuthData() = 0; // Free the authorization data returned by makeAuthData or // getAuthData. virtual void freeAuthData(void *authData) = 0; // Attempt to authorize the document, using the supplied // authorization data (which may be NULL). Returns true if // successful (i.e., if at least the right to open the document was // granted). virtual GBool authorize(void *authData) = 0; // Return the various authorization parameters. These are only // valid after authorize has returned true. virtual int getPermissionFlags() = 0; virtual GBool getOwnerPasswordOk() = 0; virtual Guchar *getFileKey() = 0; virtual int getFileKeyLength() = 0; virtual int getEncVersion() = 0; virtual CryptAlgorithm getEncAlgorithm() = 0; protected: PDFDoc *doc; }; //------------------------------------------------------------------------ // StandardSecurityHandler //------------------------------------------------------------------------ class StandardSecurityHandler: public SecurityHandler { public: StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA); virtual ~StandardSecurityHandler(); virtual GBool isUnencrypted(); virtual void *makeAuthData(GString *ownerPassword, GString *userPassword); virtual void *getAuthData(); virtual void freeAuthData(void *authData); virtual GBool authorize(void *authData); virtual int getPermissionFlags() { return permFlags; } virtual GBool getOwnerPasswordOk() { return ownerPasswordOk; } virtual Guchar *getFileKey() { return fileKey; } virtual int getFileKeyLength() { return fileKeyLength; } virtual int getEncVersion() { return encVersion; } virtual CryptAlgorithm getEncAlgorithm() { return encAlgorithm; } private: int permFlags; GBool ownerPasswordOk; Guchar fileKey[32]; int fileKeyLength; int encVersion; int encRevision; CryptAlgorithm encAlgorithm; GBool encryptMetadata; GString *ownerKey, *userKey; GString *ownerEnc, *userEnc; GString *fileID; GBool ok; }; #ifdef ENABLE_PLUGINS //------------------------------------------------------------------------ // ExternalSecurityHandler //------------------------------------------------------------------------ class ExternalSecurityHandler: public SecurityHandler { public: ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA, XpdfSecurityHandler *xshA); virtual ~ExternalSecurityHandler(); virtual void *makeAuthData(GString *ownerPassword, GString *userPassword); virtual void *getAuthData(); virtual void freeAuthData(void *authData); virtual GBool authorize(void *authData); virtual int getPermissionFlags() { return permFlags; } virtual GBool getOwnerPasswordOk() { return gFalse; } virtual Guchar *getFileKey() { return fileKey; } virtual int getFileKeyLength() { return fileKeyLength; } virtual int getEncVersion() { return encVersion; } virtual CryptAlgorithm getEncAlgorithm() { return encAlgorithm; } private: Object encryptDict; XpdfSecurityHandler *xsh; void *docData; int permFlags; Guchar fileKey[16]; int fileKeyLength; int encVersion; CryptAlgorithm encAlgorithm; GBool ok; }; #endif // ENABLE_PLUGINS #endif xpdf-3.04/xpdf/Lexer.h0000644000076400007640000000353212341430012014075 0ustar dereknderekn//======================================================================== // // Lexer.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef LEXER_H #define LEXER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Object.h" #include "Stream.h" class XRef; #define tokBufSize 128 // size of token buffer //------------------------------------------------------------------------ // Lexer //------------------------------------------------------------------------ class Lexer { public: // Construct a lexer for a single stream. Deletes the stream when // lexer is deleted. Lexer(XRef *xref, Stream *str); // Construct a lexer for a stream or array of streams (assumes obj // is either a stream or array of streams). Lexer(XRef *xref, Object *obj); // Destructor. ~Lexer(); // Get the next object from the input stream. Object *getObj(Object *obj); // Skip to the beginning of the next line in the input stream. void skipToNextLine(); // Skip over one character. void skipChar() { getChar(); } // Get stream. Stream *getStream() { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); } // Get current position in file. GFileOffset getPos() { return curStr.isNone() ? -1 : curStr.streamGetPos(); } // Set position in file. void setPos(GFileOffset pos, int dir = 0) { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); } // Returns true if is a whitespace character. static GBool isSpace(int c); private: int getChar(); int lookChar(); Array *streams; // array of input streams int strPtr; // index of current stream Object curStr; // current stream GBool freeArray; // should lexer free the streams array? char tokBuf[tokBufSize]; // temporary token buffer }; #endif xpdf-3.04/xpdf/Decrypt.cc0000644000076400007640000012757512341430012014604 0ustar dereknderekn//======================================================================== // // Decrypt.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmem.h" #include "Decrypt.h" static void aes256KeyExpansion(DecryptAES256State *s, Guchar *objKey, int objKeyLen); static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last); static void sha256(Guchar *msg, int msgLen, Guchar *hash); static void sha384(Guchar *msg, int msgLen, Guchar *hash); static void sha512(Guchar *msg, int msgLen, Guchar *hash); static Guchar passwordPad[32] = { 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a }; //------------------------------------------------------------------------ // Decrypt //------------------------------------------------------------------------ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, GString *ownerKey, GString *userKey, GString *ownerEnc, GString *userEnc, int permissions, GString *fileID, GString *ownerPassword, GString *userPassword, Guchar *fileKey, GBool encryptMetadata, GBool *ownerPasswordOk) { DecryptAES256State state; Guchar test[127 + 56], test2[32]; GString *userPassword2; const char *userPW; Guchar fState[256]; Guchar tmpKey[16]; Guchar fx, fy; int len, i, j; *ownerPasswordOk = gFalse; if (encRevision == 5 || encRevision == 6) { // check the owner password if (ownerPassword) { //~ this is supposed to convert the password to UTF-8 using "SASLprep" len = ownerPassword->getLength(); if (len > 127) { len = 127; } memcpy(test, ownerPassword->getCString(), len); memcpy(test + len, ownerKey->getCString() + 32, 8); memcpy(test + len + 8, userKey->getCString(), 48); sha256(test, len + 56, test); if (encRevision == 6) { r6Hash(test, 32, ownerPassword->getCString(), len, userKey->getCString()); } if (!memcmp(test, ownerKey->getCString(), 32)) { // compute the file key from the owner password memcpy(test, ownerPassword->getCString(), len); memcpy(test + len, ownerKey->getCString() + 40, 8); memcpy(test + len + 8, userKey->getCString(), 48); sha256(test, len + 56, test); if (encRevision == 6) { r6Hash(test, 32, ownerPassword->getCString(), len, userKey->getCString()); } aes256KeyExpansion(&state, test, 32); for (i = 0; i < 16; ++i) { state.cbc[i] = 0; } aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString(), gFalse); memcpy(fileKey, state.buf, 16); aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString() + 16, gFalse); memcpy(fileKey + 16, state.buf, 16); *ownerPasswordOk = gTrue; return gTrue; } } // check the user password if (userPassword) { //~ this is supposed to convert the password to UTF-8 using "SASLprep" userPW = userPassword->getCString(); len = userPassword->getLength(); if (len > 127) { len = 127; } } else { userPW = ""; len = 0; } memcpy(test, userPW, len); memcpy(test + len, userKey->getCString() + 32, 8); sha256(test, len + 8, test); if (encRevision == 6) { r6Hash(test, 32, userPW, len, NULL); } if (!memcmp(test, userKey->getCString(), 32)) { // compute the file key from the user password memcpy(test, userPW, len); memcpy(test + len, userKey->getCString() + 40, 8); sha256(test, len + 8, test); if (encRevision == 6) { r6Hash(test, 32, userPW, len, NULL); } aes256KeyExpansion(&state, test, 32); for (i = 0; i < 16; ++i) { state.cbc[i] = 0; } aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse); memcpy(fileKey, state.buf, 16); aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16, gFalse); memcpy(fileKey + 16, state.buf, 16); return gTrue; } return gFalse; } else { // try using the supplied owner password to generate the user password if (ownerPassword) { len = ownerPassword->getLength(); if (len < 32) { memcpy(test, ownerPassword->getCString(), len); memcpy(test + len, passwordPad, 32 - len); } else { memcpy(test, ownerPassword->getCString(), 32); } md5(test, 32, test); if (encRevision == 3) { for (i = 0; i < 50; ++i) { md5(test, keyLength, test); } } if (encRevision == 2) { rc4InitKey(test, keyLength, fState); fx = fy = 0; for (i = 0; i < 32; ++i) { test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i)); } } else { memcpy(test2, ownerKey->getCString(), 32); for (i = 19; i >= 0; --i) { for (j = 0; j < keyLength; ++j) { tmpKey[j] = test[j] ^ i; } rc4InitKey(tmpKey, keyLength, fState); fx = fy = 0; for (j = 0; j < 32; ++j) { test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]); } } } userPassword2 = new GString((char *)test2, 32); if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey, permissions, fileID, userPassword2, fileKey, encryptMetadata)) { *ownerPasswordOk = gTrue; delete userPassword2; return gTrue; } delete userPassword2; } // try using the supplied user password return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey, permissions, fileID, userPassword, fileKey, encryptMetadata); } } void Decrypt::r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen, char *userKey) { Guchar key1[64*(127+64+48)]; DecryptAESState state128; int n, i, j, k; i = 0; while (1) { memcpy(key1, pwd, pwdLen); memcpy(key1 + pwdLen, key, keyLen); n = pwdLen + keyLen; if (userKey) { memcpy(key1 + pwdLen + keyLen, userKey, 48); n += 48; } for (j = 1; j < 64; ++j) { memcpy(key1 + j * n, key1, n); } n *= 64; aesKeyExpansion(&state128, key, 16, gFalse); for (j = 0; j < 16; ++j) { state128.cbc[j] = key[16+j]; } for (j = 0; j < n; j += 16) { aesEncryptBlock(&state128, key1 + j); memcpy(key1 + j, state128.buf, 16); } k = 0; for (j = 0; j < 16; ++j) { k += key1[j] % 3; } k %= 3; switch (k) { case 0: sha256(key1, n, key); keyLen = 32; break; case 1: sha384(key1, n, key); keyLen = 48; break; case 2: sha512(key1, n, key); keyLen = 64; break; } // from the spec, it appears that i should be incremented after // the test, but that doesn't match what Adobe does ++i; if (i >= 64 && key1[n - 1] <= i - 32) { break; } } } GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength, GString *ownerKey, GString *userKey, int permissions, GString *fileID, GString *userPassword, Guchar *fileKey, GBool encryptMetadata) { Guchar *buf; Guchar test[32]; Guchar fState[256]; Guchar tmpKey[16]; Guchar fx, fy; int len, i, j; GBool ok; // generate file key buf = (Guchar *)gmalloc(72 + fileID->getLength()); if (userPassword) { len = userPassword->getLength(); if (len < 32) { memcpy(buf, userPassword->getCString(), len); memcpy(buf + len, passwordPad, 32 - len); } else { memcpy(buf, userPassword->getCString(), 32); } } else { memcpy(buf, passwordPad, 32); } memcpy(buf + 32, ownerKey->getCString(), 32); buf[64] = permissions & 0xff; buf[65] = (permissions >> 8) & 0xff; buf[66] = (permissions >> 16) & 0xff; buf[67] = (permissions >> 24) & 0xff; memcpy(buf + 68, fileID->getCString(), fileID->getLength()); len = 68 + fileID->getLength(); if (!encryptMetadata) { buf[len++] = 0xff; buf[len++] = 0xff; buf[len++] = 0xff; buf[len++] = 0xff; } md5(buf, len, fileKey); if (encRevision == 3) { for (i = 0; i < 50; ++i) { md5(fileKey, keyLength, fileKey); } } // test user password if (encRevision == 2) { rc4InitKey(fileKey, keyLength, fState); fx = fy = 0; for (i = 0; i < 32; ++i) { test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i)); } ok = memcmp(test, passwordPad, 32) == 0; } else if (encRevision == 3) { memcpy(test, userKey->getCString(), 32); for (i = 19; i >= 0; --i) { for (j = 0; j < keyLength; ++j) { tmpKey[j] = fileKey[j] ^ i; } rc4InitKey(tmpKey, keyLength, fState); fx = fy = 0; for (j = 0; j < 32; ++j) { test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]); } } memcpy(buf, passwordPad, 32); memcpy(buf + 32, fileID->getCString(), fileID->getLength()); md5(buf, 32 + fileID->getLength(), buf); ok = memcmp(test, buf, 16) == 0; } else { ok = gFalse; } gfree(buf); return ok; } //------------------------------------------------------------------------ // DecryptStream //------------------------------------------------------------------------ DecryptStream::DecryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA, int keyLength, int objNum, int objGen): FilterStream(strA) { int i; algo = algoA; // construct object key for (i = 0; i < keyLength; ++i) { objKey[i] = fileKey[i]; } switch (algo) { case cryptRC4: objKey[keyLength] = objNum & 0xff; objKey[keyLength + 1] = (objNum >> 8) & 0xff; objKey[keyLength + 2] = (objNum >> 16) & 0xff; objKey[keyLength + 3] = objGen & 0xff; objKey[keyLength + 4] = (objGen >> 8) & 0xff; md5(objKey, keyLength + 5, objKey); if ((objKeyLength = keyLength + 5) > 16) { objKeyLength = 16; } break; case cryptAES: objKey[keyLength] = objNum & 0xff; objKey[keyLength + 1] = (objNum >> 8) & 0xff; objKey[keyLength + 2] = (objNum >> 16) & 0xff; objKey[keyLength + 3] = objGen & 0xff; objKey[keyLength + 4] = (objGen >> 8) & 0xff; objKey[keyLength + 5] = 0x73; // 's' objKey[keyLength + 6] = 0x41; // 'A' objKey[keyLength + 7] = 0x6c; // 'l' objKey[keyLength + 8] = 0x54; // 'T' md5(objKey, keyLength + 9, objKey); if ((objKeyLength = keyLength + 5) > 16) { objKeyLength = 16; } break; case cryptAES256: objKeyLength = keyLength; break; } } DecryptStream::~DecryptStream() { delete str; } void DecryptStream::reset() { str->reset(); switch (algo) { case cryptRC4: state.rc4.x = state.rc4.y = 0; rc4InitKey(objKey, objKeyLength, state.rc4.state); state.rc4.buf = EOF; break; case cryptAES: aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue); str->getBlock((char *)state.aes.cbc, 16); state.aes.bufIdx = 16; break; case cryptAES256: aes256KeyExpansion(&state.aes256, objKey, objKeyLength); str->getBlock((char *)state.aes256.cbc, 16); state.aes256.bufIdx = 16; break; } } int DecryptStream::getChar() { Guchar in[16]; int c; c = EOF; // make gcc happy switch (algo) { case cryptRC4: if (state.rc4.buf == EOF) { c = str->getChar(); if (c != EOF) { state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c); } } c = state.rc4.buf; state.rc4.buf = EOF; break; case cryptAES: if (state.aes.bufIdx == 16) { if (str->getBlock((char *)in, 16) != 16) { return EOF; } aesDecryptBlock(&state.aes, in, str->lookChar() == EOF); } if (state.aes.bufIdx == 16) { c = EOF; } else { c = state.aes.buf[state.aes.bufIdx++]; } break; case cryptAES256: if (state.aes256.bufIdx == 16) { if (str->getBlock((char *)in, 16) != 16) { return EOF; } aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF); } if (state.aes256.bufIdx == 16) { c = EOF; } else { c = state.aes256.buf[state.aes256.bufIdx++]; } break; } return c; } int DecryptStream::lookChar() { Guchar in[16]; int c; c = EOF; // make gcc happy switch (algo) { case cryptRC4: if (state.rc4.buf == EOF) { c = str->getChar(); if (c != EOF) { state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c); } } c = state.rc4.buf; break; case cryptAES: if (state.aes.bufIdx == 16) { if (str->getBlock((char *)in, 16) != 16) { return EOF; } aesDecryptBlock(&state.aes, in, str->lookChar() == EOF); } if (state.aes.bufIdx == 16) { c = EOF; } else { c = state.aes.buf[state.aes.bufIdx]; } break; case cryptAES256: if (state.aes256.bufIdx == 16) { if (str->getBlock((char *)in, 16) != 16) { return EOF; } aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF); } if (state.aes256.bufIdx == 16) { c = EOF; } else { c = state.aes256.buf[state.aes256.bufIdx]; } break; } return c; } GBool DecryptStream::isBinary(GBool last) { return str->isBinary(last); } //------------------------------------------------------------------------ // RC4-compatible decryption //------------------------------------------------------------------------ void rc4InitKey(Guchar *key, int keyLen, Guchar *state) { Guchar index1, index2; Guchar t; int i; for (i = 0; i < 256; ++i) state[i] = i; index1 = index2 = 0; for (i = 0; i < 256; ++i) { index2 = (key[index1] + state[i] + index2) % 256; t = state[i]; state[i] = state[index2]; state[index2] = t; index1 = (index1 + 1) % keyLen; } } Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) { Guchar x1, y1, tx, ty; x1 = *x = (*x + 1) % 256; y1 = *y = (state[*x] + *y) % 256; tx = state[x1]; ty = state[y1]; state[x1] = ty; state[y1] = tx; return c ^ state[(tx + ty) % 256]; } //------------------------------------------------------------------------ // AES decryption //------------------------------------------------------------------------ static Guchar sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; static Guchar invSbox[256] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; static Guint rcon[11] = { 0x00000000, // unused 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 }; static inline Guint subWord(Guint x) { return (sbox[x >> 24] << 24) | (sbox[(x >> 16) & 0xff] << 16) | (sbox[(x >> 8) & 0xff] << 8) | sbox[x & 0xff]; } static inline Guint rotWord(Guint x) { return ((x << 8) & 0xffffffff) | (x >> 24); } static inline void subBytes(Guchar *state) { int i; for (i = 0; i < 16; ++i) { state[i] = sbox[state[i]]; } } static inline void invSubBytes(Guchar *state) { int i; for (i = 0; i < 16; ++i) { state[i] = invSbox[state[i]]; } } static inline void shiftRows(Guchar *state) { Guchar t; t = state[4]; state[4] = state[5]; state[5] = state[6]; state[6] = state[7]; state[7] = t; t = state[8]; state[8] = state[10]; state[10] = t; t = state[9]; state[9] = state[11]; state[11] = t; t = state[15]; state[15] = state[14]; state[14] = state[13]; state[13] = state[12]; state[12] = t; } static inline void invShiftRows(Guchar *state) { Guchar t; t = state[7]; state[7] = state[6]; state[6] = state[5]; state[5] = state[4]; state[4] = t; t = state[8]; state[8] = state[10]; state[10] = t; t = state[9]; state[9] = state[11]; state[11] = t; t = state[12]; state[12] = state[13]; state[13] = state[14]; state[14] = state[15]; state[15] = t; } // {02} \cdot s static inline Guchar mul02(Guchar s) { Guchar s2; s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); return s2; } // {03} \cdot s static inline Guchar mul03(Guchar s) { Guchar s2; s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); return s ^ s2; } // {09} \cdot s static inline Guchar mul09(Guchar s) { Guchar s2, s4, s8; s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); return s ^ s8; } // {0b} \cdot s static inline Guchar mul0b(Guchar s) { Guchar s2, s4, s8; s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); return s ^ s2 ^ s8; } // {0d} \cdot s static inline Guchar mul0d(Guchar s) { Guchar s2, s4, s8; s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); return s ^ s4 ^ s8; } // {0e} \cdot s static inline Guchar mul0e(Guchar s) { Guchar s2, s4, s8; s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); return s2 ^ s4 ^ s8; } static inline void mixColumns(Guchar *state) { int c; Guchar s0, s1, s2, s3; for (c = 0; c < 4; ++c) { s0 = state[c]; s1 = state[4+c]; s2 = state[8+c]; s3 = state[12+c]; state[c] = mul02(s0) ^ mul03(s1) ^ s2 ^ s3; state[4+c] = s0 ^ mul02(s1) ^ mul03(s2) ^ s3; state[8+c] = s0 ^ s1 ^ mul02(s2) ^ mul03(s3); state[12+c] = mul03(s0) ^ s1 ^ s2 ^ mul02(s3); } } static inline void invMixColumns(Guchar *state) { int c; Guchar s0, s1, s2, s3; for (c = 0; c < 4; ++c) { s0 = state[c]; s1 = state[4+c]; s2 = state[8+c]; s3 = state[12+c]; state[c] = mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3); state[4+c] = mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3); state[8+c] = mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3); state[12+c] = mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3); } } static inline void invMixColumnsW(Guint *w) { int c; Guchar s0, s1, s2, s3; for (c = 0; c < 4; ++c) { s0 = w[c] >> 24; s1 = w[c] >> 16; s2 = w[c] >> 8; s3 = w[c]; w[c] = ((mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3)) << 24) | ((mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3)) << 16) | ((mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3)) << 8) | (mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3)); } } static inline void addRoundKey(Guchar *state, Guint *w) { int c; for (c = 0; c < 4; ++c) { state[c] ^= w[c] >> 24; state[4+c] ^= w[c] >> 16; state[8+c] ^= w[c] >> 8; state[12+c] ^= w[c]; } } void aesKeyExpansion(DecryptAESState *s, Guchar *objKey, int objKeyLen, GBool decrypt) { Guint temp; int i, round; //~ this assumes objKeyLen == 16 for (i = 0; i < 4; ++i) { s->w[i] = (objKey[4*i] << 24) + (objKey[4*i+1] << 16) + (objKey[4*i+2] << 8) + objKey[4*i+3]; } for (i = 4; i < 44; ++i) { temp = s->w[i-1]; if (!(i & 3)) { temp = subWord(rotWord(temp)) ^ rcon[i/4]; } s->w[i] = s->w[i-4] ^ temp; } if (decrypt) { for (round = 1; round <= 9; ++round) { invMixColumnsW(&s->w[round * 4]); } } } void aesEncryptBlock(DecryptAESState *s, Guchar *in) { int c, round; // initial state + CBC for (c = 0; c < 4; ++c) { s->state[c] = in[4*c] ^ s->cbc[4*c]; s->state[4+c] = in[4*c+1] ^ s->cbc[4*c+1]; s->state[8+c] = in[4*c+2] ^ s->cbc[4*c+2]; s->state[12+c] = in[4*c+3] ^ s->cbc[4*c+3]; } // round 0 addRoundKey(s->state, &s->w[0]); // rounds 1 .. 9 for (round = 1; round <= 9; ++round) { subBytes(s->state); shiftRows(s->state); mixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 10 subBytes(s->state); shiftRows(s->state); addRoundKey(s->state, &s->w[10 * 4]); // output + save for next CBC for (c = 0; c < 4; ++c) { s->buf[4*c] = s->cbc[4*c] = s->state[c]; s->buf[4*c+1] = s->cbc[4*c+1] = s->state[4+c]; s->buf[4*c+2] = s->cbc[4*c+2] = s->state[8+c]; s->buf[4*c+3] = s->cbc[4*c+3] = s->state[12+c]; } } void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) { int c, round, n, i; // initial state for (c = 0; c < 4; ++c) { s->state[c] = in[4*c]; s->state[4+c] = in[4*c+1]; s->state[8+c] = in[4*c+2]; s->state[12+c] = in[4*c+3]; } // round 0 addRoundKey(s->state, &s->w[10 * 4]); // rounds 1-9 for (round = 9; round >= 1; --round) { invSubBytes(s->state); invShiftRows(s->state); invMixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 10 invSubBytes(s->state); invShiftRows(s->state); addRoundKey(s->state, &s->w[0]); // CBC for (c = 0; c < 4; ++c) { s->buf[4*c] = s->state[c] ^ s->cbc[4*c]; s->buf[4*c+1] = s->state[4+c] ^ s->cbc[4*c+1]; s->buf[4*c+2] = s->state[8+c] ^ s->cbc[4*c+2]; s->buf[4*c+3] = s->state[12+c] ^ s->cbc[4*c+3]; } // save the input block for the next CBC for (i = 0; i < 16; ++i) { s->cbc[i] = in[i]; } // remove padding s->bufIdx = 0; if (last) { n = s->buf[15]; if (n < 1 || n > 16) { // this should never happen n = 16; } for (i = 15; i >= n; --i) { s->buf[i] = s->buf[i-n]; } s->bufIdx = n; } } //------------------------------------------------------------------------ // AES-256 decryption //------------------------------------------------------------------------ static void aes256KeyExpansion(DecryptAES256State *s, Guchar *objKey, int objKeyLen) { Guint temp; int i, round; //~ this assumes objKeyLen == 32 for (i = 0; i < 8; ++i) { s->w[i] = (objKey[4*i] << 24) + (objKey[4*i+1] << 16) + (objKey[4*i+2] << 8) + objKey[4*i+3]; } for (i = 8; i < 60; ++i) { temp = s->w[i-1]; if ((i & 7) == 0) { temp = subWord(rotWord(temp)) ^ rcon[i/8]; } else if ((i & 7) == 4) { temp = subWord(temp); } s->w[i] = s->w[i-8] ^ temp; } for (round = 1; round <= 13; ++round) { invMixColumnsW(&s->w[round * 4]); } } static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last) { int c, round, n, i; // initial state for (c = 0; c < 4; ++c) { s->state[c] = in[4*c]; s->state[4+c] = in[4*c+1]; s->state[8+c] = in[4*c+2]; s->state[12+c] = in[4*c+3]; } // round 0 addRoundKey(s->state, &s->w[14 * 4]); // rounds 13-1 for (round = 13; round >= 1; --round) { invSubBytes(s->state); invShiftRows(s->state); invMixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 14 invSubBytes(s->state); invShiftRows(s->state); addRoundKey(s->state, &s->w[0]); // CBC for (c = 0; c < 4; ++c) { s->buf[4*c] = s->state[c] ^ s->cbc[4*c]; s->buf[4*c+1] = s->state[4+c] ^ s->cbc[4*c+1]; s->buf[4*c+2] = s->state[8+c] ^ s->cbc[4*c+2]; s->buf[4*c+3] = s->state[12+c] ^ s->cbc[4*c+3]; } // save the input block for the next CBC for (i = 0; i < 16; ++i) { s->cbc[i] = in[i]; } // remove padding s->bufIdx = 0; if (last) { n = s->buf[15]; if (n < 1 || n > 16) { // this should never happen n = 16; } for (i = 15; i >= n; --i) { s->buf[i] = s->buf[i-n]; } s->bufIdx = n; } } //------------------------------------------------------------------------ // MD5 message digest //------------------------------------------------------------------------ // this works around a bug in older Sun compilers static inline Gulong rotateLeft(Gulong x, int r) { x &= 0xffffffff; return ((x << r) | (x >> (32 - r))) & 0xffffffff; } static inline Gulong md5Round1(Gulong a, Gulong b, Gulong c, Gulong d, Gulong Xk, Gulong s, Gulong Ti) { return b + rotateLeft((a + ((b & c) | (~b & d)) + Xk + Ti), s); } static inline Gulong md5Round2(Gulong a, Gulong b, Gulong c, Gulong d, Gulong Xk, Gulong s, Gulong Ti) { return b + rotateLeft((a + ((b & d) | (c & ~d)) + Xk + Ti), s); } static inline Gulong md5Round3(Gulong a, Gulong b, Gulong c, Gulong d, Gulong Xk, Gulong s, Gulong Ti) { return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s); } static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d, Gulong Xk, Gulong s, Gulong Ti) { return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s); } void md5Start(MD5State *state) { state->a = 0x67452301; state->b = 0xefcdab89; state->c = 0x98badcfe; state->d = 0x10325476; state->bufLen = 0; state->msgLen = 0; } static void md5ProcessBlock(MD5State *state) { Gulong x[16]; Gulong a, b, c, d; int i; for (i = 0; i < 16; ++i) { x[i] = state->buf[4*i] | (state->buf[4*i+1] << 8) | (state->buf[4*i+2] << 16) | (state->buf[4*i+3] << 24); } a = state->a; b = state->b; c = state->c; d = state->d; // round 1 a = md5Round1(a, b, c, d, x[0], 7, 0xd76aa478); d = md5Round1(d, a, b, c, x[1], 12, 0xe8c7b756); c = md5Round1(c, d, a, b, x[2], 17, 0x242070db); b = md5Round1(b, c, d, a, x[3], 22, 0xc1bdceee); a = md5Round1(a, b, c, d, x[4], 7, 0xf57c0faf); d = md5Round1(d, a, b, c, x[5], 12, 0x4787c62a); c = md5Round1(c, d, a, b, x[6], 17, 0xa8304613); b = md5Round1(b, c, d, a, x[7], 22, 0xfd469501); a = md5Round1(a, b, c, d, x[8], 7, 0x698098d8); d = md5Round1(d, a, b, c, x[9], 12, 0x8b44f7af); c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1); b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be); a = md5Round1(a, b, c, d, x[12], 7, 0x6b901122); d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193); c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e); b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821); // round 2 a = md5Round2(a, b, c, d, x[1], 5, 0xf61e2562); d = md5Round2(d, a, b, c, x[6], 9, 0xc040b340); c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51); b = md5Round2(b, c, d, a, x[0], 20, 0xe9b6c7aa); a = md5Round2(a, b, c, d, x[5], 5, 0xd62f105d); d = md5Round2(d, a, b, c, x[10], 9, 0x02441453); c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681); b = md5Round2(b, c, d, a, x[4], 20, 0xe7d3fbc8); a = md5Round2(a, b, c, d, x[9], 5, 0x21e1cde6); d = md5Round2(d, a, b, c, x[14], 9, 0xc33707d6); c = md5Round2(c, d, a, b, x[3], 14, 0xf4d50d87); b = md5Round2(b, c, d, a, x[8], 20, 0x455a14ed); a = md5Round2(a, b, c, d, x[13], 5, 0xa9e3e905); d = md5Round2(d, a, b, c, x[2], 9, 0xfcefa3f8); c = md5Round2(c, d, a, b, x[7], 14, 0x676f02d9); b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a); // round 3 a = md5Round3(a, b, c, d, x[5], 4, 0xfffa3942); d = md5Round3(d, a, b, c, x[8], 11, 0x8771f681); c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122); b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c); a = md5Round3(a, b, c, d, x[1], 4, 0xa4beea44); d = md5Round3(d, a, b, c, x[4], 11, 0x4bdecfa9); c = md5Round3(c, d, a, b, x[7], 16, 0xf6bb4b60); b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70); a = md5Round3(a, b, c, d, x[13], 4, 0x289b7ec6); d = md5Round3(d, a, b, c, x[0], 11, 0xeaa127fa); c = md5Round3(c, d, a, b, x[3], 16, 0xd4ef3085); b = md5Round3(b, c, d, a, x[6], 23, 0x04881d05); a = md5Round3(a, b, c, d, x[9], 4, 0xd9d4d039); d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5); c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8); b = md5Round3(b, c, d, a, x[2], 23, 0xc4ac5665); // round 4 a = md5Round4(a, b, c, d, x[0], 6, 0xf4292244); d = md5Round4(d, a, b, c, x[7], 10, 0x432aff97); c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7); b = md5Round4(b, c, d, a, x[5], 21, 0xfc93a039); a = md5Round4(a, b, c, d, x[12], 6, 0x655b59c3); d = md5Round4(d, a, b, c, x[3], 10, 0x8f0ccc92); c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d); b = md5Round4(b, c, d, a, x[1], 21, 0x85845dd1); a = md5Round4(a, b, c, d, x[8], 6, 0x6fa87e4f); d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0); c = md5Round4(c, d, a, b, x[6], 15, 0xa3014314); b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1); a = md5Round4(a, b, c, d, x[4], 6, 0xf7537e82); d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235); c = md5Round4(c, d, a, b, x[2], 15, 0x2ad7d2bb); b = md5Round4(b, c, d, a, x[9], 21, 0xeb86d391); // increment a, b, c, d state->a += a; state->b += b; state->c += c; state->d += d; state->bufLen = 0; } void md5Append(MD5State *state, Guchar *data, int dataLen) { Guchar *p; int remain, k; p = data; remain = dataLen; while (state->bufLen + remain >= 64) { k = 64 - state->bufLen; memcpy(state->buf + state->bufLen, p, k); state->bufLen = 64; md5ProcessBlock(state); p += k; remain -= k; } if (remain > 0) { memcpy(state->buf + state->bufLen, p, remain); state->bufLen += remain; } state->msgLen += dataLen; } void md5Finish(MD5State *state) { // padding and length state->buf[state->bufLen++] = 0x80; if (state->bufLen > 56) { while (state->bufLen < 64) { state->buf[state->bufLen++] = 0x00; } md5ProcessBlock(state); } while (state->bufLen < 56) { state->buf[state->bufLen++] = 0x00; } state->buf[56] = (Guchar)(state->msgLen << 3); state->buf[57] = (Guchar)(state->msgLen >> 5); state->buf[58] = (Guchar)(state->msgLen >> 13); state->buf[59] = (Guchar)(state->msgLen >> 21); state->buf[60] = (Guchar)(state->msgLen >> 29); state->buf[61] = (Guchar)0; state->buf[62] = (Guchar)0; state->buf[63] = (Guchar)0; state->bufLen = 64; md5ProcessBlock(state); // break digest into bytes state->digest[0] = (Guchar)state->a; state->digest[1] = (Guchar)(state->a >> 8); state->digest[2] = (Guchar)(state->a >> 16); state->digest[3] = (Guchar)(state->a >> 24); state->digest[4] = (Guchar)state->b; state->digest[5] = (Guchar)(state->b >> 8); state->digest[6] = (Guchar)(state->b >> 16); state->digest[7] = (Guchar)(state->b >> 24); state->digest[8] = (Guchar)state->c; state->digest[9] = (Guchar)(state->c >> 8); state->digest[10] = (Guchar)(state->c >> 16); state->digest[11] = (Guchar)(state->c >> 24); state->digest[12] = (Guchar)state->d; state->digest[13] = (Guchar)(state->d >> 8); state->digest[14] = (Guchar)(state->d >> 16); state->digest[15] = (Guchar)(state->d >> 24); } void md5(Guchar *msg, int msgLen, Guchar *digest) { MD5State state; int i; if (msgLen < 0) { return; } md5Start(&state); md5Append(&state, msg, msgLen); md5Finish(&state); for (i = 0; i < 16; ++i) { digest[i] = state.digest[i]; } } //------------------------------------------------------------------------ // SHA-256 hash //------------------------------------------------------------------------ static Guint sha256K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; static inline Guint rotr(Guint x, Guint n) { return (x >> n) | (x << (32 - n)); } static inline Guint sha256Ch(Guint x, Guint y, Guint z) { return (x & y) ^ (~x & z); } static inline Guint sha256Maj(Guint x, Guint y, Guint z) { return (x & y) ^ (x & z) ^ (y & z); } static inline Guint sha256Sigma0(Guint x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); } static inline Guint sha256Sigma1(Guint x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); } static inline Guint sha256sigma0(Guint x) { return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3); } static inline Guint sha256sigma1(Guint x) { return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10); } static void sha256HashBlock(Guchar *blk, Guint *H) { Guint W[64]; Guint a, b, c, d, e, f, g, h; Guint T1, T2; Guint t; // 1. prepare the message schedule for (t = 0; t < 16; ++t) { W[t] = (blk[t*4] << 24) | (blk[t*4 + 1] << 16) | (blk[t*4 + 2] << 8) | blk[t*4 + 3]; } for (t = 16; t < 64; ++t) { W[t] = sha256sigma1(W[t-2]) + W[t-7] + sha256sigma0(W[t-15]) + W[t-16]; } // 2. initialize the eight working variables a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; // 3. for (t = 0; t < 64; ++t) { T1 = h + sha256Sigma1(e) + sha256Ch(e,f,g) + sha256K[t] + W[t]; T2 = sha256Sigma0(a) + sha256Maj(a,b,c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } // 4. compute the intermediate hash value H[0] += a; H[1] += b; H[2] += c; H[3] += d; H[4] += e; H[5] += f; H[6] += g; H[7] += h; } static void sha256(Guchar *msg, int msgLen, Guchar *hash) { Guchar blk[64]; Guint H[8]; int blkLen, i; H[0] = 0x6a09e667; H[1] = 0xbb67ae85; H[2] = 0x3c6ef372; H[3] = 0xa54ff53a; H[4] = 0x510e527f; H[5] = 0x9b05688c; H[6] = 0x1f83d9ab; H[7] = 0x5be0cd19; blkLen = 0; for (i = 0; i + 64 <= msgLen; i += 64) { sha256HashBlock(msg + i, H); } blkLen = msgLen - i; if (blkLen > 0) { memcpy(blk, msg + i, blkLen); } // pad the message blk[blkLen++] = 0x80; if (blkLen > 56) { while (blkLen < 64) { blk[blkLen++] = 0; } sha256HashBlock(blk, H); blkLen = 0; } while (blkLen < 56) { blk[blkLen++] = 0; } blk[56] = 0; blk[57] = 0; blk[58] = 0; blk[59] = 0; blk[60] = (Guchar)(msgLen >> 21); blk[61] = (Guchar)(msgLen >> 13); blk[62] = (Guchar)(msgLen >> 5); blk[63] = (Guchar)(msgLen << 3); sha256HashBlock(blk, H); // copy the output into the buffer (convert words to bytes) for (i = 0; i < 8; ++i) { hash[i*4] = (Guchar)(H[i] >> 24); hash[i*4 + 1] = (Guchar)(H[i] >> 16); hash[i*4 + 2] = (Guchar)(H[i] >> 8); hash[i*4 + 3] = (Guchar)H[i]; } } //------------------------------------------------------------------------ // SHA-384 and SHA-512 hashes //------------------------------------------------------------------------ typedef unsigned long long SHA512Uint64; static SHA512Uint64 sha512K[80] = { 0x428a2f98d728ae22LL, 0x7137449123ef65cdLL, 0xb5c0fbcfec4d3b2fLL, 0xe9b5dba58189dbbcLL, 0x3956c25bf348b538LL, 0x59f111f1b605d019LL, 0x923f82a4af194f9bLL, 0xab1c5ed5da6d8118LL, 0xd807aa98a3030242LL, 0x12835b0145706fbeLL, 0x243185be4ee4b28cLL, 0x550c7dc3d5ffb4e2LL, 0x72be5d74f27b896fLL, 0x80deb1fe3b1696b1LL, 0x9bdc06a725c71235LL, 0xc19bf174cf692694LL, 0xe49b69c19ef14ad2LL, 0xefbe4786384f25e3LL, 0x0fc19dc68b8cd5b5LL, 0x240ca1cc77ac9c65LL, 0x2de92c6f592b0275LL, 0x4a7484aa6ea6e483LL, 0x5cb0a9dcbd41fbd4LL, 0x76f988da831153b5LL, 0x983e5152ee66dfabLL, 0xa831c66d2db43210LL, 0xb00327c898fb213fLL, 0xbf597fc7beef0ee4LL, 0xc6e00bf33da88fc2LL, 0xd5a79147930aa725LL, 0x06ca6351e003826fLL, 0x142929670a0e6e70LL, 0x27b70a8546d22ffcLL, 0x2e1b21385c26c926LL, 0x4d2c6dfc5ac42aedLL, 0x53380d139d95b3dfLL, 0x650a73548baf63deLL, 0x766a0abb3c77b2a8LL, 0x81c2c92e47edaee6LL, 0x92722c851482353bLL, 0xa2bfe8a14cf10364LL, 0xa81a664bbc423001LL, 0xc24b8b70d0f89791LL, 0xc76c51a30654be30LL, 0xd192e819d6ef5218LL, 0xd69906245565a910LL, 0xf40e35855771202aLL, 0x106aa07032bbd1b8LL, 0x19a4c116b8d2d0c8LL, 0x1e376c085141ab53LL, 0x2748774cdf8eeb99LL, 0x34b0bcb5e19b48a8LL, 0x391c0cb3c5c95a63LL, 0x4ed8aa4ae3418acbLL, 0x5b9cca4f7763e373LL, 0x682e6ff3d6b2b8a3LL, 0x748f82ee5defb2fcLL, 0x78a5636f43172f60LL, 0x84c87814a1f0ab72LL, 0x8cc702081a6439ecLL, 0x90befffa23631e28LL, 0xa4506cebde82bde9LL, 0xbef9a3f7b2c67915LL, 0xc67178f2e372532bLL, 0xca273eceea26619cLL, 0xd186b8c721c0c207LL, 0xeada7dd6cde0eb1eLL, 0xf57d4f7fee6ed178LL, 0x06f067aa72176fbaLL, 0x0a637dc5a2c898a6LL, 0x113f9804bef90daeLL, 0x1b710b35131c471bLL, 0x28db77f523047d84LL, 0x32caab7b40c72493LL, 0x3c9ebe0a15c9bebcLL, 0x431d67c49c100d4cLL, 0x4cc5d4becb3e42b6LL, 0x597f299cfc657e2aLL, 0x5fcb6fab3ad6faecLL, 0x6c44198c4a475817LL }; static inline SHA512Uint64 rotr64(SHA512Uint64 x, Guint n) { return (x >> n) | (x << (64 - n)); } static inline SHA512Uint64 sha512Ch(SHA512Uint64 x, SHA512Uint64 y, SHA512Uint64 z) { return (x & y) ^ (~x & z); } static inline SHA512Uint64 sha512Maj(SHA512Uint64 x, SHA512Uint64 y, SHA512Uint64 z) { return (x & y) ^ (x & z) ^ (y & z); } static inline SHA512Uint64 sha512Sigma0(SHA512Uint64 x) { return rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39); } static inline SHA512Uint64 sha512Sigma1(SHA512Uint64 x) { return rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41); } static inline SHA512Uint64 sha512sigma0(SHA512Uint64 x) { return rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7); } static inline SHA512Uint64 sha512sigma1(SHA512Uint64 x) { return rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6); } static void sha512HashBlock(Guchar *blk, SHA512Uint64 *H) { SHA512Uint64 W[80]; SHA512Uint64 a, b, c, d, e, f, g, h; SHA512Uint64 T1, T2; Guint t; // 1. prepare the message schedule for (t = 0; t < 16; ++t) { W[t] = ((SHA512Uint64)blk[t*8] << 56) | ((SHA512Uint64)blk[t*8 + 1] << 48) | ((SHA512Uint64)blk[t*8 + 2] << 40) | ((SHA512Uint64)blk[t*8 + 3] << 32) | ((SHA512Uint64)blk[t*8 + 4] << 24) | ((SHA512Uint64)blk[t*8 + 5] << 16) | ((SHA512Uint64)blk[t*8 + 6] << 8) | (SHA512Uint64)blk[t*8 + 7]; } for (t = 16; t < 80; ++t) { W[t] = sha512sigma1(W[t-2]) + W[t-7] + sha512sigma0(W[t-15]) + W[t-16]; } // 2. initialize the eight working variables a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; // 3. for (t = 0; t < 80; ++t) { T1 = h + sha512Sigma1(e) + sha512Ch(e,f,g) + sha512K[t] + W[t]; T2 = sha512Sigma0(a) + sha512Maj(a,b,c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } // 4. compute the intermediate hash value H[0] += a; H[1] += b; H[2] += c; H[3] += d; H[4] += e; H[5] += f; H[6] += g; H[7] += h; } static void sha512(Guchar *msg, int msgLen, Guchar *hash) { Guchar blk[128]; SHA512Uint64 H[8]; int blkLen, i; H[0] = 0x6a09e667f3bcc908LL; H[1] = 0xbb67ae8584caa73bLL; H[2] = 0x3c6ef372fe94f82bLL; H[3] = 0xa54ff53a5f1d36f1LL; H[4] = 0x510e527fade682d1LL; H[5] = 0x9b05688c2b3e6c1fLL; H[6] = 0x1f83d9abfb41bd6bLL; H[7] = 0x5be0cd19137e2179LL; blkLen = 0; for (i = 0; i + 128 <= msgLen; i += 128) { sha512HashBlock(msg + i, H); } blkLen = msgLen - i; if (blkLen > 0) { memcpy(blk, msg + i, blkLen); } // pad the message blk[blkLen++] = 0x80; if (blkLen > 112) { while (blkLen < 128) { blk[blkLen++] = 0; } sha512HashBlock(blk, H); blkLen = 0; } while (blkLen < 112) { blk[blkLen++] = 0; } blk[112] = 0; blk[113] = 0; blk[114] = 0; blk[115] = 0; blk[116] = 0; blk[117] = 0; blk[118] = 0; blk[119] = 0; blk[120] = 0; blk[121] = 0; blk[122] = 0; blk[123] = 0; blk[124] = (Guchar)(msgLen >> 21); blk[125] = (Guchar)(msgLen >> 13); blk[126] = (Guchar)(msgLen >> 5); blk[127] = (Guchar)(msgLen << 3); sha512HashBlock(blk, H); // copy the output into the buffer (convert words to bytes) for (i = 0; i < 8; ++i) { hash[i*8] = (Guchar)(H[i] >> 56); hash[i*8 + 1] = (Guchar)(H[i] >> 48); hash[i*8 + 2] = (Guchar)(H[i] >> 40); hash[i*8 + 3] = (Guchar)(H[i] >> 32); hash[i*8 + 4] = (Guchar)(H[i] >> 24); hash[i*8 + 5] = (Guchar)(H[i] >> 16); hash[i*8 + 6] = (Guchar)(H[i] >> 8); hash[i*8 + 7] = (Guchar)H[i]; } } static void sha384(Guchar *msg, int msgLen, Guchar *hash) { Guchar blk[128]; SHA512Uint64 H[8]; int blkLen, i; H[0] = 0xcbbb9d5dc1059ed8LL; H[1] = 0x629a292a367cd507LL; H[2] = 0x9159015a3070dd17LL; H[3] = 0x152fecd8f70e5939LL; H[4] = 0x67332667ffc00b31LL; H[5] = 0x8eb44a8768581511LL; H[6] = 0xdb0c2e0d64f98fa7LL; H[7] = 0x47b5481dbefa4fa4LL; blkLen = 0; for (i = 0; i + 128 <= msgLen; i += 128) { sha512HashBlock(msg + i, H); } blkLen = msgLen - i; if (blkLen > 0) { memcpy(blk, msg + i, blkLen); } // pad the message blk[blkLen++] = 0x80; if (blkLen > 112) { while (blkLen < 128) { blk[blkLen++] = 0; } sha512HashBlock(blk, H); blkLen = 0; } while (blkLen < 112) { blk[blkLen++] = 0; } blk[112] = 0; blk[113] = 0; blk[114] = 0; blk[115] = 0; blk[116] = 0; blk[117] = 0; blk[118] = 0; blk[119] = 0; blk[120] = 0; blk[121] = 0; blk[122] = 0; blk[123] = 0; blk[124] = (Guchar)(msgLen >> 21); blk[125] = (Guchar)(msgLen >> 13); blk[126] = (Guchar)(msgLen >> 5); blk[127] = (Guchar)(msgLen << 3); sha512HashBlock(blk, H); // copy the output into the buffer (convert words to bytes) for (i = 0; i < 6; ++i) { hash[i*8] = (Guchar)(H[i] >> 56); hash[i*8 + 1] = (Guchar)(H[i] >> 48); hash[i*8 + 2] = (Guchar)(H[i] >> 40); hash[i*8 + 3] = (Guchar)(H[i] >> 32); hash[i*8 + 4] = (Guchar)(H[i] >> 24); hash[i*8 + 5] = (Guchar)(H[i] >> 16); hash[i*8 + 6] = (Guchar)(H[i] >> 8); hash[i*8 + 7] = (Guchar)H[i]; } } xpdf-3.04/xpdf/SplashOutputDev.h0000644000076400007640000002317112341430012016131 0ustar dereknderekn//======================================================================== // // SplashOutputDev.h // // Copyright 2003 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHOUTPUTDEV_H #define SPLASHOUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "SplashTypes.h" #include "config.h" #include "OutputDev.h" #include "GfxState.h" class Gfx8BitFont; class SplashBitmap; class Splash; class SplashPath; class SplashPattern; class SplashFontEngine; class SplashFont; class T3FontCache; struct T3FontCacheTag; struct T3GlyphStack; struct SplashTransparencyGroup; //------------------------------------------------------------------------ // number of Type 3 fonts to cache #define splashOutT3FontCacheSize 8 //------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ class SplashOutputDev: public OutputDev { public: // Constructor. SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool bitmapTopDownA = gTrue, GBool allowAntialiasA = gTrue); // Destructor. virtual ~SplashOutputDev(); //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return bitmapTopDown ^ bitmapUpsideDown; } // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() { return gTrue; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual GBool useTilingPatternFill() { return gTrue; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gTrue; } //----- initialization and control // Start a page. virtual void startPage(int pageNum, GfxState *state); // End a page. virtual void endPage(); //----- save/restore graphics state virtual void saveState(GfxState *state); virtual void restoreState(GfxState *state); //----- update graphics state virtual void updateAll(GfxState *state); virtual void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32); virtual void updateLineDash(GfxState *state); virtual void updateFlatness(GfxState *state); virtual void updateLineJoin(GfxState *state); virtual void updateLineCap(GfxState *state); virtual void updateMiterLimit(GfxState *state); virtual void updateLineWidth(GfxState *state); virtual void updateStrokeAdjust(GfxState *state); virtual void updateFillColor(GfxState *state); virtual void updateStrokeColor(GfxState *state); virtual void updateBlendMode(GfxState *state); virtual void updateFillOpacity(GfxState *state); virtual void updateStrokeOpacity(GfxState *state); virtual void updateTransfer(GfxState *state); //----- update text state virtual void updateFont(GfxState *state); //----- path painting virtual void stroke(GfxState *state); virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); 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, double xStep, double yStep); //----- path clipping virtual void clip(GfxState *state); virtual void eoClip(GfxState *state); virtual void clipToStrokePath(GfxState *state); //----- text drawing virtual void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode *u, int uLen); virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen); virtual void endType3Char(GfxState *state); virtual void endTextObject(GfxState *state); //----- image drawing virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, 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); //----- Type 3 font operators virtual void type3D0(GfxState *state, double wx, double wy); virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury); //----- transparency groups and soft masks virtual void beginTransparencyGroup(GfxState *state, double *bbox, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, GBool forSoftMask); virtual void endTransparencyGroup(GfxState *state); virtual void paintTransparencyGroup(GfxState *state, double *bbox); virtual void setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor); virtual void clearSoftMask(GfxState *state); //----- special access // Called to indicate that a new PDF document has been loaded. void startDoc(XRef *xrefA); void setPaperColor(SplashColorPtr paperColorA); GBool isReverseVideo() { return reverseVideo; } void setReverseVideo(GBool reverseVideoA) { reverseVideo = reverseVideoA; } // Get the bitmap and its size. SplashBitmap *getBitmap() { return bitmap; } int getBitmapWidth(); int getBitmapHeight(); // Returns the last rasterized bitmap, transferring ownership to the // caller. SplashBitmap *takeBitmap(); // Set this flag to true to generate an upside-down bitmap (useful // 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; } // Get the modified region. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax); // Clear the modified region. void clearModRegion(); // Set the Splash fill color. void setFillColor(int r, int g, int b); // Get a font object for a Base-14 font, using the Latin-1 encoding. SplashFont *getFont(GString *name, SplashCoord *textMatA); SplashFont *getCurrentFont() { return font; } // If is true, don't draw horizontal text. // If is true, don't draw rotated (non-horizontal) text. void setSkipText(GBool skipHorizTextA, GBool skipRotatedTextA) { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; } int getNestCount() { return nestCount; } #if 1 //~tmp: turn off anti-aliasing temporarily virtual void setInShading(GBool sh); #endif private: void setupScreenParams(double hDPI, double vDPI); SplashPattern *getColor(GfxGray gray); SplashPattern *getColor(GfxRGB *rgb); #if SPLASH_CMYK SplashPattern *getColor(GfxCMYK *cmyk); #endif void setOverprintMask(GfxColorSpace *colorSpace, GBool overprintFlag, int overprintMode, GfxColor *singleColor); SplashPath *convertPath(GfxState *state, GfxPath *path, GBool dropEmptySubpaths); void doUpdateFont(GfxState *state); void drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag *tag, Guchar *data); static GBool imageMaskSrc(void *data, SplashColorPtr line); static GBool imageSrc(void *data, SplashColorPtr colorLine, Guchar *alphaLine); static GBool alphaImageSrc(void *data, SplashColorPtr line, Guchar *alphaLine); static GBool maskedImageSrc(void *data, SplashColorPtr line, Guchar *alphaLine); 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 SplashColor paperColor; // paper color SplashScreenParams screenParams; GBool skipHorizText; GBool skipRotatedText; XRef *xref; // xref table for current document SplashBitmap *bitmap; Splash *splash; SplashFontEngine *fontEngine; T3FontCache * // Type 3 font cache t3FontCache[splashOutT3FontCacheSize]; int nT3Fonts; // number of valid entries in t3FontCache T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack SplashFont *font; // current font GBool needFontUpdate; // set when the font needs to be updated SplashPath *textClipPath; // clipping path built with text object SplashTransparencyGroup * // transparency group stack transpGroupStack; int nestCount; }; #endif xpdf-3.04/xpdf/PreScanOutputDev.h0000644000076400007640000001203012341430012016222 0ustar dereknderekn//======================================================================== // // PreScanOutputDev.h // // Copyright 2005 Glyph & Cog, LLC // //======================================================================== #ifndef PRESCANOUTPUTDEV_H #define PRESCANOUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "GfxState.h" #include "OutputDev.h" //------------------------------------------------------------------------ // PreScanOutputDev //------------------------------------------------------------------------ class PreScanOutputDev: public OutputDev { public: // Constructor. PreScanOutputDev(); // Destructor. virtual ~PreScanOutputDev(); //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return gTrue; } // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() { return gTrue; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual GBool useTilingPatternFill() { return gTrue; } // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. virtual GBool useShadedFills() { return gTrue; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gTrue; } //----- initialization and control // Start a page. virtual void startPage(int pageNum, GfxState *state); // End a page. virtual void endPage(); //----- path painting virtual void stroke(GfxState *state); virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); 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, double xStep, double yStep); virtual GBool functionShadedFill(GfxState *state, GfxFunctionShading *shading); virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); //----- path clipping virtual void clip(GfxState *state); virtual void eoClip(GfxState *state); //----- text drawing virtual void beginStringOp(GfxState *state); virtual void endStringOp(GfxState *state); virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen); virtual void endType3Char(GfxState *state); //----- image drawing virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, 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); //----- transparency groups and soft masks virtual void beginTransparencyGroup(GfxState *state, double *bbox, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, GBool forSoftMask); //----- special access // Returns true if the operations performed since the last call to // clearStats() are all monochrome (black or white). GBool isMonochrome() { return mono; } // Returns true if the operations performed since the last call to // clearStats() are all gray. GBool isGray() { return gray; } // Returns true if the operations performed since the last call to // clearStats() included any transparency. GBool usesTransparency() { return transparency; } // Returns true if the operations performed since the last call to // clearStats() included any image mask fills with a pattern color // space. GBool usesPatternImageMask() { return patternImgMask; } // Returns true if the operations performed since the last call to // clearStats() are all rasterizable by GDI calls in GDIOutputDev. GBool isAllGDI() { return gdi; } // Clear the stats used by the above functions. void clearStats(); private: void check(GfxColorSpace *colorSpace, GfxColor *color, double opacity, GfxBlendMode blendMode); GBool mono; GBool gray; GBool transparency; GBool patternImgMask; GBool gdi; }; #endif xpdf-3.04/xpdf/CharCodeToUnicode.cc0000644000076400007640000003577412341430012016453 0ustar dereknderekn//======================================================================== // // CharCodeToUnicode.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "gfile.h" #include "GString.h" #include "Error.h" #include "GlobalParams.h" #include "PSTokenizer.h" #include "CharCodeToUnicode.h" //------------------------------------------------------------------------ #define maxUnicodeString 8 struct CharCodeToUnicodeString { CharCode c; Unicode u[maxUnicodeString]; int len; }; //------------------------------------------------------------------------ static int getCharFromString(void *data) { char *p; int c; p = *(char **)data; if (*p) { c = *p++; *(char **)data = p; } else { c = EOF; } return c; } static int getCharFromFile(void *data) { return fgetc((FILE *)data); } //------------------------------------------------------------------------ static int hexCharVals[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5x -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // Fx }; // Parse a -byte hex string into *. Returns false on // error. static GBool parseHex(char *s, int len, Guint *val) { int i, x; *val = 0; for (i = 0; i < len; ++i) { x = hexCharVals[s[i] & 0xff]; if (x < 0) { return gFalse; } *val = (*val << 4) + x; } return gTrue; } //------------------------------------------------------------------------ CharCodeToUnicode *CharCodeToUnicode::makeIdentityMapping() { return new CharCodeToUnicode(); } CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName, GString *collection) { FILE *f; Unicode *mapA; CharCode size, mapLenA; char buf[64]; Unicode u; CharCodeToUnicode *ctu; if (!(f = openFile(fileName->getCString(), "r"))) { error(errSyntaxError, -1, "Couldn't open cidToUnicode file '{0:t}'", fileName); return NULL; } size = 32768; mapA = (Unicode *)gmallocn(size, sizeof(Unicode)); mapLenA = 0; while (getLine(buf, sizeof(buf), f)) { if (mapLenA == size) { size *= 2; mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode)); } if (sscanf(buf, "%x", &u) == 1) { mapA[mapLenA] = u; } else { error(errSyntaxWarning, -1, "Bad line ({0:d}) in cidToUnicode file '{1:t}'", (int)(mapLenA + 1), fileName); mapA[mapLenA] = 0; } ++mapLenA; } fclose(f); ctu = new CharCodeToUnicode(collection->copy(), mapA, mapLenA, gTrue, NULL, 0, 0); gfree(mapA); return ctu; } CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode( GString *fileName) { FILE *f; Unicode *mapA; CharCodeToUnicodeString *sMapA; CharCode size, oldSize, len, sMapSizeA, sMapLenA; char buf[256]; char *tok; Unicode u0; Unicode uBuf[maxUnicodeString]; CharCodeToUnicode *ctu; int line, n, i; if (!(f = openFile(fileName->getCString(), "r"))) { error(errSyntaxError, -1, "Couldn't open unicodeToUnicode file '{0:t}'", fileName); return NULL; } size = 4096; mapA = (Unicode *)gmallocn(size, sizeof(Unicode)); memset(mapA, 0, size * sizeof(Unicode)); len = 0; sMapA = NULL; sMapSizeA = sMapLenA = 0; line = 0; while (getLine(buf, sizeof(buf), f)) { ++line; if (!(tok = strtok(buf, " \t\r\n")) || !parseHex(tok, (int)strlen(tok), &u0)) { error(errSyntaxWarning, -1, "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'", line, fileName); continue; } n = 0; while (n < maxUnicodeString) { if (!(tok = strtok(NULL, " \t\r\n"))) { break; } if (!parseHex(tok, (int)strlen(tok), &uBuf[n])) { error(errSyntaxWarning, -1, "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'", line, fileName); break; } ++n; } if (n < 1) { error(errSyntaxWarning, -1, "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'", line, fileName); continue; } if (u0 >= size) { oldSize = size; while (u0 >= size) { size *= 2; } mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode)); memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode)); } if (n == 1) { mapA[u0] = uBuf[0]; } else { mapA[u0] = 0; if (sMapLenA == sMapSizeA) { sMapSizeA += 16; sMapA = (CharCodeToUnicodeString *) greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString)); } sMapA[sMapLenA].c = u0; for (i = 0; i < n; ++i) { sMapA[sMapLenA].u[i] = uBuf[i]; } sMapA[sMapLenA].len = n; ++sMapLenA; } if (u0 >= len) { len = u0 + 1; } } fclose(f); ctu = new CharCodeToUnicode(fileName->copy(), mapA, len, gTrue, sMapA, sMapLenA, sMapSizeA); gfree(mapA); return ctu; } CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) { return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0); } CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) { CharCodeToUnicode *ctu; char *p; ctu = new CharCodeToUnicode(NULL); p = buf->getCString(); ctu->parseCMap1(&getCharFromString, &p, nBits); return ctu; } void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) { char *p; p = buf->getCString(); parseCMap1(&getCharFromString, &p, nBits); } void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, int nBits) { PSTokenizer *pst; char tok1[256], tok2[256], tok3[256]; int n1, n2, n3; CharCode i; CharCode maxCode, code1, code2; GString *name; FILE *f; maxCode = (nBits == 8) ? 0xff : (nBits == 16) ? 0xffff : 0xffffffff; pst = new PSTokenizer(getCharFunc, data); pst->getToken(tok1, sizeof(tok1), &n1); while (pst->getToken(tok2, sizeof(tok2), &n2)) { if (!strcmp(tok2, "usecmap")) { if (tok1[0] == '/') { name = new GString(tok1 + 1); if ((f = globalParams->findToUnicodeFile(name))) { parseCMap1(&getCharFromFile, f, nBits); fclose(f); } else { error(errSyntaxError, -1, "Couldn't find ToUnicode CMap file for '{1:t}'", name); } delete name; } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "beginbfchar")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endbfchar")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endbfchar")) { error(errSyntaxWarning, -1, "Illegal entry in bfchar block in ToUnicode CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && tok2[0] == '<' && tok2[n2 - 1] == '>')) { error(errSyntaxWarning, -1, "Illegal entry in bfchar block in ToUnicode CMap"); continue; } tok1[n1 - 1] = tok2[n2 - 1] = '\0'; if (!parseHex(tok1 + 1, n1 - 2, &code1)) { error(errSyntaxWarning, -1, "Illegal entry in bfchar block in ToUnicode CMap"); continue; } if (code1 > maxCode) { error(errSyntaxWarning, -1, "Invalid entry in bfchar block in ToUnicode CMap"); } addMapping(code1, tok2 + 1, n2 - 2, 0); } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "beginbfrange")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endbfrange")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endbfrange") || !pst->getToken(tok3, sizeof(tok3), &n3) || !strcmp(tok3, "endbfrange")) { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && tok2[0] == '<' && tok2[n2 - 1] == '>')) { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); continue; } tok1[n1 - 1] = tok2[n2 - 1] = '\0'; if (!parseHex(tok1 + 1, n1 - 2, &code1) || !parseHex(tok2 + 1, n2 - 2, &code2)) { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); continue; } if (code1 > maxCode || code2 > maxCode) { error(errSyntaxWarning, -1, "Invalid entry in bfrange block in ToUnicode CMap"); if (code2 > maxCode) { code2 = maxCode; } } if (!strcmp(tok3, "[")) { i = 0; while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "]")) { break; } if (tok1[0] == '<' && tok1[n1 - 1] == '>') { if (code1 + i <= code2) { tok1[n1 - 1] = '\0'; addMapping(code1 + i, tok1 + 1, n1 - 2, 0); } } else { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); } ++i; } } else if (tok3[0] == '<' && tok3[n3 - 1] == '>') { tok3[n3 - 1] = '\0'; for (i = 0; code1 <= code2; ++code1, ++i) { addMapping(code1, tok3 + 1, n3 - 2, i); } } else { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); } } pst->getToken(tok1, sizeof(tok1), &n1); } else { strcpy(tok1, tok2); } } delete pst; } void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset) { CharCode oldLen, i; Unicode u; int j; if (code > 0xffffff) { // This is an arbitrary limit to avoid integer overflow issues. // (I've seen CMaps with mappings for .) return; } if (code >= mapLen) { oldLen = mapLen; mapLen = mapLen ? 2 * mapLen : 256; if (code >= mapLen) { mapLen = (code + 256) & ~255; } map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode)); for (i = oldLen; i < mapLen; ++i) { map[i] = 0; } } if (n <= 4) { if (!parseHex(uStr, n, &u)) { error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); return; } map[code] = u + offset; } else { if (sMapLen >= sMapSize) { sMapSize = sMapSize + 16; sMap = (CharCodeToUnicodeString *) greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString)); } map[code] = 0; sMap[sMapLen].c = code; if ((sMap[sMapLen].len = n / 4) > maxUnicodeString) { sMap[sMapLen].len = maxUnicodeString; } for (j = 0; j < sMap[sMapLen].len; ++j) { if (!parseHex(uStr + j*4, 4, &sMap[sMapLen].u[j])) { error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); return; } } sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset; ++sMapLen; } } CharCodeToUnicode::CharCodeToUnicode() { tag = NULL; map = NULL; mapLen = 0; sMap = NULL; sMapLen = sMapSize = 0; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } CharCodeToUnicode::CharCodeToUnicode(GString *tagA) { CharCode i; tag = tagA; mapLen = 256; map = (Unicode *)gmallocn(mapLen, sizeof(Unicode)); for (i = 0; i < mapLen; ++i) { map[i] = 0; } sMap = NULL; sMapLen = sMapSize = 0; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA, CharCode mapLenA, GBool copyMap, CharCodeToUnicodeString *sMapA, int sMapLenA, int sMapSizeA) { tag = tagA; mapLen = mapLenA; if (copyMap) { map = (Unicode *)gmallocn(mapLen, sizeof(Unicode)); memcpy(map, mapA, mapLen * sizeof(Unicode)); } else { map = mapA; } sMap = sMapA; sMapLen = sMapLenA; sMapSize = sMapSizeA; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } CharCodeToUnicode::~CharCodeToUnicode() { if (tag) { delete tag; } gfree(map); gfree(sMap); #if MULTITHREADED gDestroyMutex(&mutex); #endif } void CharCodeToUnicode::incRefCnt() { #if MULTITHREADED gLockMutex(&mutex); #endif ++refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif } void CharCodeToUnicode::decRefCnt() { GBool done; #if MULTITHREADED gLockMutex(&mutex); #endif done = --refCnt == 0; #if MULTITHREADED gUnlockMutex(&mutex); #endif if (done) { delete this; } } GBool CharCodeToUnicode::match(GString *tagA) { return tag && !tag->cmp(tagA); } void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) { int i, j; if (!map) { return; } if (len == 1) { map[c] = u[0]; } else { for (i = 0; i < sMapLen; ++i) { if (sMap[i].c == c) { break; } } if (i == sMapLen) { if (sMapLen == sMapSize) { sMapSize += 8; sMap = (CharCodeToUnicodeString *) greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString)); } ++sMapLen; } map[c] = 0; sMap[i].c = c; sMap[i].len = len; for (j = 0; j < len && j < maxUnicodeString; ++j) { sMap[i].u[j] = u[j]; } } } int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) { int i, j; if (!map) { u[0] = (Unicode)c; return 1; } if (c >= mapLen) { return 0; } if (map[c]) { u[0] = map[c]; return 1; } for (i = 0; i < sMapLen; ++i) { if (sMap[i].c == c) { for (j = 0; j < sMap[i].len && j < size; ++j) { u[j] = sMap[i].u[j]; } return j; } } return 0; } //------------------------------------------------------------------------ CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) { int i; size = sizeA; cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *)); for (i = 0; i < size; ++i) { cache[i] = NULL; } } CharCodeToUnicodeCache::~CharCodeToUnicodeCache() { int i; for (i = 0; i < size; ++i) { if (cache[i]) { cache[i]->decRefCnt(); } } gfree(cache); } CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) { CharCodeToUnicode *ctu; int i, j; if (cache[0] && cache[0]->match(tag)) { cache[0]->incRefCnt(); return cache[0]; } for (i = 1; i < size; ++i) { if (cache[i] && cache[i]->match(tag)) { ctu = cache[i]; for (j = i; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = ctu; ctu->incRefCnt(); return ctu; } } return NULL; } void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) { int i; if (cache[size - 1]) { cache[size - 1]->decRefCnt(); } for (i = size - 1; i >= 1; --i) { cache[i] = cache[i - 1]; } cache[0] = ctu; ctu->incRefCnt(); } xpdf-3.04/xpdf/Stream-CCITT.h0000644000076400007640000004207712341430012015124 0ustar dereknderekn//======================================================================== // // Stream-CCITT.h // // Tables for CCITT Fax decoding. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef STREAM_CCITT_H #define STREAM_CCITT_H struct CCITTCode { short bits; short n; }; #define ccittEOL -2 //------------------------------------------------------------------------ // 2D codes //------------------------------------------------------------------------ #define twoDimPass 0 #define twoDimHoriz 1 #define twoDimVert0 2 #define twoDimVertR1 3 #define twoDimVertL1 4 #define twoDimVertR2 5 #define twoDimVertL2 6 #define twoDimVertR3 7 #define twoDimVertL3 8 // 1-7 bit codes static CCITTCode twoDimTab1[128] = { {-1, -1}, {-1, -1}, // 000000x {7, twoDimVertL3}, // 0000010 {7, twoDimVertR3}, // 0000011 {6, twoDimVertL2}, {6, twoDimVertL2}, // 000010x {6, twoDimVertR2}, {6, twoDimVertR2}, // 000011x {4, twoDimPass}, {4, twoDimPass}, // 0001xxx {4, twoDimPass}, {4, twoDimPass}, {4, twoDimPass}, {4, twoDimPass}, {4, twoDimPass}, {4, twoDimPass}, {3, twoDimHoriz}, {3, twoDimHoriz}, // 001xxxx {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimHoriz}, {3, twoDimVertL1}, {3, twoDimVertL1}, // 010xxxx {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertL1}, {3, twoDimVertR1}, {3, twoDimVertR1}, // 011xxxx {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {3, twoDimVertR1}, {1, twoDimVert0}, {1, twoDimVert0}, // 1xxxxxx {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0}, {1, twoDimVert0} }; //------------------------------------------------------------------------ // white run lengths //------------------------------------------------------------------------ // 11-12 bit codes (upper 7 bits are 0) static CCITTCode whiteTab1[32] = { {-1, -1}, // 00000 {12, ccittEOL}, // 00001 {-1, -1}, {-1, -1}, // 0001x {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 001xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 010xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 011xx {11, 1792}, {11, 1792}, // 1000x {12, 1984}, // 10010 {12, 2048}, // 10011 {12, 2112}, // 10100 {12, 2176}, // 10101 {12, 2240}, // 10110 {12, 2304}, // 10111 {11, 1856}, {11, 1856}, // 1100x {11, 1920}, {11, 1920}, // 1101x {12, 2368}, // 11100 {12, 2432}, // 11101 {12, 2496}, // 11110 {12, 2560} // 11111 }; // 1-9 bit codes static CCITTCode whiteTab2[512] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 0000000xx {8, 29}, {8, 29}, // 00000010x {8, 30}, {8, 30}, // 00000011x {8, 45}, {8, 45}, // 00000100x {8, 46}, {8, 46}, // 00000101x {7, 22}, {7, 22}, {7, 22}, {7, 22}, // 0000011xx {7, 23}, {7, 23}, {7, 23}, {7, 23}, // 0000100xx {8, 47}, {8, 47}, // 00001010x {8, 48}, {8, 48}, // 00001011x {6, 13}, {6, 13}, {6, 13}, {6, 13}, // 000011xxx {6, 13}, {6, 13}, {6, 13}, {6, 13}, {7, 20}, {7, 20}, {7, 20}, {7, 20}, // 0001000xx {8, 33}, {8, 33}, // 00010010x {8, 34}, {8, 34}, // 00010011x {8, 35}, {8, 35}, // 00010100x {8, 36}, {8, 36}, // 00010101x {8, 37}, {8, 37}, // 00010110x {8, 38}, {8, 38}, // 00010111x {7, 19}, {7, 19}, {7, 19}, {7, 19}, // 0001100xx {8, 31}, {8, 31}, // 00011010x {8, 32}, {8, 32}, // 00011011x {6, 1}, {6, 1}, {6, 1}, {6, 1}, // 000111xxx {6, 1}, {6, 1}, {6, 1}, {6, 1}, {6, 12}, {6, 12}, {6, 12}, {6, 12}, // 001000xxx {6, 12}, {6, 12}, {6, 12}, {6, 12}, {8, 53}, {8, 53}, // 00100100x {8, 54}, {8, 54}, // 00100101x {7, 26}, {7, 26}, {7, 26}, {7, 26}, // 0010011xx {8, 39}, {8, 39}, // 00101000x {8, 40}, {8, 40}, // 00101001x {8, 41}, {8, 41}, // 00101010x {8, 42}, {8, 42}, // 00101011x {8, 43}, {8, 43}, // 00101100x {8, 44}, {8, 44}, // 00101101x {7, 21}, {7, 21}, {7, 21}, {7, 21}, // 0010111xx {7, 28}, {7, 28}, {7, 28}, {7, 28}, // 0011000xx {8, 61}, {8, 61}, // 00110010x {8, 62}, {8, 62}, // 00110011x {8, 63}, {8, 63}, // 00110100x {8, 0}, {8, 0}, // 00110101x {8, 320}, {8, 320}, // 00110110x {8, 384}, {8, 384}, // 00110111x {5, 10}, {5, 10}, {5, 10}, {5, 10}, // 00111xxxx {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 10}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, // 01000xxxx {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {5, 11}, {7, 27}, {7, 27}, {7, 27}, {7, 27}, // 0100100xx {8, 59}, {8, 59}, // 01001010x {8, 60}, {8, 60}, // 01001011x {9, 1472}, // 010011000 {9, 1536}, // 010011001 {9, 1600}, // 010011010 {9, 1728}, // 010011011 {7, 18}, {7, 18}, {7, 18}, {7, 18}, // 0100111xx {7, 24}, {7, 24}, {7, 24}, {7, 24}, // 0101000xx {8, 49}, {8, 49}, // 01010010x {8, 50}, {8, 50}, // 01010011x {8, 51}, {8, 51}, // 01010100x {8, 52}, {8, 52}, // 01010101x {7, 25}, {7, 25}, {7, 25}, {7, 25}, // 0101011xx {8, 55}, {8, 55}, // 01011000x {8, 56}, {8, 56}, // 01011001x {8, 57}, {8, 57}, // 01011010x {8, 58}, {8, 58}, // 01011011x {6, 192}, {6, 192}, {6, 192}, {6, 192}, // 010111xxx {6, 192}, {6, 192}, {6, 192}, {6, 192}, {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664}, // 011000xxx {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664}, {8, 448}, {8, 448}, // 01100100x {8, 512}, {8, 512}, // 01100101x {9, 704}, // 011001100 {9, 768}, // 011001101 {8, 640}, {8, 640}, // 01100111x {8, 576}, {8, 576}, // 01101000x {9, 832}, // 011010010 {9, 896}, // 011010011 {9, 960}, // 011010100 {9, 1024}, // 011010101 {9, 1088}, // 011010110 {9, 1152}, // 011010111 {9, 1216}, // 011011000 {9, 1280}, // 011011001 {9, 1344}, // 011011010 {9, 1408}, // 011011011 {7, 256}, {7, 256}, {7, 256}, {7, 256}, // 0110111xx {4, 2}, {4, 2}, {4, 2}, {4, 2}, // 0111xxxxx {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 2}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, // 1000xxxxx {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {4, 3}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, // 10010xxxx {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 128}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, // 10011xxxx {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 8}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, // 10100xxxx {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {5, 9}, {6, 16}, {6, 16}, {6, 16}, {6, 16}, // 101010xxx {6, 16}, {6, 16}, {6, 16}, {6, 16}, {6, 17}, {6, 17}, {6, 17}, {6, 17}, // 101011xxx {6, 17}, {6, 17}, {6, 17}, {6, 17}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, // 1011xxxxx {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, // 1100xxxxx {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {4, 5}, {6, 14}, {6, 14}, {6, 14}, {6, 14}, // 110100xxx {6, 14}, {6, 14}, {6, 14}, {6, 14}, {6, 15}, {6, 15}, {6, 15}, {6, 15}, // 110101xxx {6, 15}, {6, 15}, {6, 15}, {6, 15}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, // 11011xxxx {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {5, 64}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, // 1110xxxxx {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, // 1111xxxxx {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7}, {4, 7} }; //------------------------------------------------------------------------ // black run lengths //------------------------------------------------------------------------ // 10-13 bit codes (upper 6 bits are 0) static CCITTCode blackTab1[128] = { {-1, -1}, {-1, -1}, // 000000000000x {12, ccittEOL}, {12, ccittEOL}, // 000000000001x {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000001xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000010xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000011xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000100xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000101xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000110xx {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000111xx {11, 1792}, {11, 1792}, {11, 1792}, {11, 1792}, // 00000001000xx {12, 1984}, {12, 1984}, // 000000010010x {12, 2048}, {12, 2048}, // 000000010011x {12, 2112}, {12, 2112}, // 000000010100x {12, 2176}, {12, 2176}, // 000000010101x {12, 2240}, {12, 2240}, // 000000010110x {12, 2304}, {12, 2304}, // 000000010111x {11, 1856}, {11, 1856}, {11, 1856}, {11, 1856}, // 00000001100xx {11, 1920}, {11, 1920}, {11, 1920}, {11, 1920}, // 00000001101xx {12, 2368}, {12, 2368}, // 000000011100x {12, 2432}, {12, 2432}, // 000000011101x {12, 2496}, {12, 2496}, // 000000011110x {12, 2560}, {12, 2560}, // 000000011111x {10, 18}, {10, 18}, {10, 18}, {10, 18}, // 0000001000xxx {10, 18}, {10, 18}, {10, 18}, {10, 18}, {12, 52}, {12, 52}, // 000000100100x {13, 640}, // 0000001001010 {13, 704}, // 0000001001011 {13, 768}, // 0000001001100 {13, 832}, // 0000001001101 {12, 55}, {12, 55}, // 000000100111x {12, 56}, {12, 56}, // 000000101000x {13, 1280}, // 0000001010010 {13, 1344}, // 0000001010011 {13, 1408}, // 0000001010100 {13, 1472}, // 0000001010101 {12, 59}, {12, 59}, // 000000101011x {12, 60}, {12, 60}, // 000000101100x {13, 1536}, // 0000001011010 {13, 1600}, // 0000001011011 {11, 24}, {11, 24}, {11, 24}, {11, 24}, // 00000010111xx {11, 25}, {11, 25}, {11, 25}, {11, 25}, // 00000011000xx {13, 1664}, // 0000001100100 {13, 1728}, // 0000001100101 {12, 320}, {12, 320}, // 000000110011x {12, 384}, {12, 384}, // 000000110100x {12, 448}, {12, 448}, // 000000110101x {13, 512}, // 0000001101100 {13, 576}, // 0000001101101 {12, 53}, {12, 53}, // 000000110111x {12, 54}, {12, 54}, // 000000111000x {13, 896}, // 0000001110010 {13, 960}, // 0000001110011 {13, 1024}, // 0000001110100 {13, 1088}, // 0000001110101 {13, 1152}, // 0000001110110 {13, 1216}, // 0000001110111 {10, 64}, {10, 64}, {10, 64}, {10, 64}, // 0000001111xxx {10, 64}, {10, 64}, {10, 64}, {10, 64} }; // 7-12 bit codes (upper 4 bits are 0) static CCITTCode blackTab2[192] = { {8, 13}, {8, 13}, {8, 13}, {8, 13}, // 00000100xxxx {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {8, 13}, {11, 23}, {11, 23}, // 00000101000x {12, 50}, // 000001010010 {12, 51}, // 000001010011 {12, 44}, // 000001010100 {12, 45}, // 000001010101 {12, 46}, // 000001010110 {12, 47}, // 000001010111 {12, 57}, // 000001011000 {12, 58}, // 000001011001 {12, 61}, // 000001011010 {12, 256}, // 000001011011 {10, 16}, {10, 16}, {10, 16}, {10, 16}, // 0000010111xx {10, 17}, {10, 17}, {10, 17}, {10, 17}, // 0000011000xx {12, 48}, // 000001100100 {12, 49}, // 000001100101 {12, 62}, // 000001100110 {12, 63}, // 000001100111 {12, 30}, // 000001101000 {12, 31}, // 000001101001 {12, 32}, // 000001101010 {12, 33}, // 000001101011 {12, 40}, // 000001101100 {12, 41}, // 000001101101 {11, 22}, {11, 22}, // 00000110111x {8, 14}, {8, 14}, {8, 14}, {8, 14}, // 00000111xxxx {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {8, 14}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, // 0000100xxxxx {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 10}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, // 0000101xxxxx {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {7, 11}, {9, 15}, {9, 15}, {9, 15}, {9, 15}, // 000011000xxx {9, 15}, {9, 15}, {9, 15}, {9, 15}, {12, 128}, // 000011001000 {12, 192}, // 000011001001 {12, 26}, // 000011001010 {12, 27}, // 000011001011 {12, 28}, // 000011001100 {12, 29}, // 000011001101 {11, 19}, {11, 19}, // 00001100111x {11, 20}, {11, 20}, // 00001101000x {12, 34}, // 000011010010 {12, 35}, // 000011010011 {12, 36}, // 000011010100 {12, 37}, // 000011010101 {12, 38}, // 000011010110 {12, 39}, // 000011010111 {11, 21}, {11, 21}, // 00001101100x {12, 42}, // 000011011010 {12, 43}, // 000011011011 {10, 0}, {10, 0}, {10, 0}, {10, 0}, // 0000110111xx {7, 12}, {7, 12}, {7, 12}, {7, 12}, // 0000111xxxxx {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12}, {7, 12} }; // 2-6 bit codes static CCITTCode blackTab3[64] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 0000xx {6, 9}, // 000100 {6, 8}, // 000101 {5, 7}, {5, 7}, // 00011x {4, 6}, {4, 6}, {4, 6}, {4, 6}, // 0010xx {4, 5}, {4, 5}, {4, 5}, {4, 5}, // 0011xx {3, 1}, {3, 1}, {3, 1}, {3, 1}, // 010xxx {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 4}, {3, 4}, {3, 4}, {3, 4}, // 011xxx {3, 4}, {3, 4}, {3, 4}, {3, 4}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, // 10xxxx {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, // 11xxxx {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2} }; #endif xpdf-3.04/xpdf/Object.cc0000644000076400007640000001013012341430012014352 0ustar dereknderekn//======================================================================== // // Object.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "Object.h" #include "Array.h" #include "Dict.h" #include "Error.h" #include "Stream.h" #include "XRef.h" //------------------------------------------------------------------------ // Object //------------------------------------------------------------------------ const char *objTypeNames[numObjTypes] = { "boolean", "integer", "real", "string", "name", "null", "array", "dictionary", "stream", "ref", "cmd", "error", "eof", "none" }; #ifdef DEBUG_MEM int Object::numAlloc[numObjTypes] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #endif Object *Object::initArray(XRef *xref) { initObj(objArray); array = new Array(xref); return this; } Object *Object::initDict(XRef *xref) { initObj(objDict); dict = new Dict(xref); return this; } Object *Object::initDict(Dict *dictA) { initObj(objDict); dict = dictA; dict->incRef(); return this; } Object *Object::initStream(Stream *streamA) { initObj(objStream); stream = streamA; return this; } Object *Object::copy(Object *obj) { *obj = *this; switch (type) { case objString: obj->string = string->copy(); break; case objName: obj->name = copyString(name); break; case objArray: array->incRef(); break; case objDict: dict->incRef(); break; case objStream: stream->incRef(); break; case objCmd: obj->cmd = copyString(cmd); break; default: break; } #ifdef DEBUG_MEM ++numAlloc[type]; #endif return obj; } Object *Object::fetch(XRef *xref, Object *obj, int recursion) { return (type == objRef && xref) ? xref->fetch(ref.num, ref.gen, obj, recursion) : copy(obj); } void Object::free() { switch (type) { case objString: delete string; break; case objName: gfree(name); break; case objArray: if (!array->decRef()) { delete array; } break; case objDict: if (!dict->decRef()) { delete dict; } break; case objStream: if (!stream->decRef()) { delete stream; } break; case objCmd: gfree(cmd); break; default: break; } #ifdef DEBUG_MEM --numAlloc[type]; #endif type = objNone; } const char *Object::getTypeName() { return objTypeNames[type]; } void Object::print(FILE *f) { Object obj; int i; switch (type) { case objBool: fprintf(f, "%s", booln ? "true" : "false"); break; case objInt: fprintf(f, "%d", intg); break; case objReal: fprintf(f, "%g", real); break; case objString: fprintf(f, "("); fwrite(string->getCString(), 1, string->getLength(), f); fprintf(f, ")"); break; case objName: fprintf(f, "/%s", name); break; case objNull: fprintf(f, "null"); break; case objArray: fprintf(f, "["); for (i = 0; i < arrayGetLength(); ++i) { if (i > 0) fprintf(f, " "); arrayGetNF(i, &obj); obj.print(f); obj.free(); } fprintf(f, "]"); break; case objDict: fprintf(f, "<<"); for (i = 0; i < dictGetLength(); ++i) { fprintf(f, " /%s ", dictGetKey(i)); dictGetValNF(i, &obj); obj.print(f); obj.free(); } fprintf(f, " >>"); break; case objStream: fprintf(f, ""); break; case objRef: fprintf(f, "%d %d R", ref.num, ref.gen); break; case objCmd: fprintf(f, "%s", cmd); break; case objError: fprintf(f, ""); break; case objEOF: fprintf(f, ""); break; case objNone: fprintf(f, ""); break; } } void Object::memCheck(FILE *f) { #ifdef DEBUG_MEM int i; int t; t = 0; for (i = 0; i < numObjTypes; ++i) t += numAlloc[i]; if (t > 0) { fprintf(f, "Allocated objects:\n"); for (i = 0; i < numObjTypes; ++i) { if (numAlloc[i] > 0) fprintf(f, " %-20s: %6d\n", objTypeNames[i], numAlloc[i]); } } #endif } xpdf-3.04/xpdf/PreScanOutputDev.cc0000644000076400007640000002063012341430012016365 0ustar dereknderekn//======================================================================== // // PreScanOutputDev.cc // // Copyright 2005 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "GlobalParams.h" #include "Page.h" #include "Gfx.h" #include "GfxFont.h" #include "Link.h" #include "PreScanOutputDev.h" //------------------------------------------------------------------------ // PreScanOutputDev //------------------------------------------------------------------------ PreScanOutputDev::PreScanOutputDev() { clearStats(); } PreScanOutputDev::~PreScanOutputDev() { } void PreScanOutputDev::startPage(int pageNum, GfxState *state) { } void PreScanOutputDev::endPage() { } void PreScanOutputDev::stroke(GfxState *state) { double *dash; int dashLen; double dashStart; check(state->getStrokeColorSpace(), state->getStrokeColor(), state->getStrokeOpacity(), state->getBlendMode()); state->getLineDash(&dash, &dashLen, &dashStart); if (dashLen != 0) { gdi = gFalse; } } void PreScanOutputDev::fill(GfxState *state) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } void PreScanOutputDev::eoFill(GfxState *state) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, 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(strRef, resDict, mat, bbox); } else { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } } GBool PreScanOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = gFalse; } mono = gFalse; if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = gTrue; } return gTrue; } GBool PreScanOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = gFalse; } mono = gFalse; if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = gTrue; } return gTrue; } GBool PreScanOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = gFalse; } mono = gFalse; if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = gTrue; } return gTrue; } void PreScanOutputDev::clip(GfxState *state) { //~ check for a rectangle "near" the edge of the page; //~ else set gdi to false } void PreScanOutputDev::eoClip(GfxState *state) { //~ see clip() } void PreScanOutputDev::beginStringOp(GfxState *state) { int render; GfxFont *font; double m11, m12, m21, m22; GBool simpleTTF; render = state->getRender(); if (!(render & 1)) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } if ((render & 3) == 1 || (render & 3) == 2) { check(state->getStrokeColorSpace(), state->getStrokeColor(), state->getStrokeOpacity(), state->getBlendMode()); } font = state->getFont(); state->getFontTransMat(&m11, &m12, &m21, &m22); //~ this should check for external fonts that are non-TrueType simpleTTF = fabs(m11 + m22) < 0.01 && m11 > 0 && fabs(m12) < 0.01 && fabs(m21) < 0.01 && fabs(state->getHorizScaling() - 1) < 0.001 && (font->getType() == fontTrueType || font->getType() == fontTrueTypeOT); if (simpleTTF) { //~ need to create a FoFiTrueType object, and check for a Unicode cmap } if (state->getRender() != 0 || !simpleTTF) { gdi = gFalse; } } void PreScanOutputDev::endStringOp(GfxState *state) { } GBool PreScanOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) { // return false so all Type 3 chars get rendered (no caching) return gFalse; } void PreScanOutputDev::endType3Char(GfxState *state) { } void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); if (state->getFillColorSpace()->getMode() == csPattern) { patternImgMask = gTrue; } gdi = gFalse; if (inlineImg) { str->reset(); str->discardChars(height * ((width + 7) / 8)); str->close(); } } void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate) { GfxColorSpace *colorSpace; colorSpace = colorMap->getColorSpace(); if (colorSpace->getMode() == csIndexed) { colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); } if (colorSpace->getMode() == csDeviceGray || colorSpace->getMode() == csCalGray) { if (colorMap->getBits() > 1) { mono = gFalse; } } else { gray = gFalse; mono = gFalse; } if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = gTrue; } gdi = gFalse; if (inlineImg) { str->reset(); str->discardChars(height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8)); str->close(); } } void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { GfxColorSpace *colorSpace; colorSpace = colorMap->getColorSpace(); if (colorSpace->getMode() == csIndexed) { colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); } if (colorSpace->getMode() == csDeviceGray || colorSpace->getMode() == csCalGray) { if (colorMap->getBits() > 1) { mono = gFalse; } } else { gray = gFalse; mono = gFalse; } if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = gTrue; } gdi = gFalse; } void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, GBool interpolate) { GfxColorSpace *colorSpace; colorSpace = colorMap->getColorSpace(); if (colorSpace->getMode() == csIndexed) { colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); } if (colorSpace->getMode() != csDeviceGray && colorSpace->getMode() != csCalGray) { gray = gFalse; } mono = gFalse; transparency = gTrue; gdi = gFalse; } void PreScanOutputDev::beginTransparencyGroup( GfxState *state, double *bbox, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, GBool forSoftMask) { transparency = gTrue; gdi = gFalse; } void PreScanOutputDev::check(GfxColorSpace *colorSpace, GfxColor *color, double opacity, GfxBlendMode blendMode) { GfxRGB rgb; if (colorSpace->getMode() == csPattern) { mono = gFalse; gray = gFalse; gdi = gFalse; } else { colorSpace->getRGB(color, &rgb); if (rgb.r != rgb.g || rgb.g != rgb.b || rgb.b != rgb.r) { mono = gFalse; gray = gFalse; } else if (!((rgb.r == 0 && rgb.g == 0 && rgb.b == 0) || (rgb.r == gfxColorComp1 && rgb.g == gfxColorComp1 && rgb.b == gfxColorComp1))) { mono = gFalse; } } if (opacity != 1 || blendMode != gfxBlendNormal) { transparency = gTrue; } } void PreScanOutputDev::clearStats() { mono = gTrue; gray = gTrue; transparency = gFalse; patternImgMask = gFalse; gdi = gTrue; } xpdf-3.04/xpdf/OptionalContent.h0000644000076400007640000000632612341430012016142 0ustar dereknderekn//======================================================================== // // OptionalContent.h // // Copyright 2008-2013 Glyph & Cog, LLC // //======================================================================== #ifndef OPTIONALCONTENT_H #define OPTIONALCONTENT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" #include "CharTypes.h" class GString; class GList; class PDFDoc; class TextString; class XRef; class OptionalContentGroup; class OCDisplayNode; //------------------------------------------------------------------------ class OptionalContent { public: OptionalContent(PDFDoc *doc); ~OptionalContent(); // Walk the list of optional content groups. int getNumOCGs(); OptionalContentGroup *getOCG(int idx); // Find an OCG by indirect reference. OptionalContentGroup *findOCG(Ref *ref); // Get the root node of the optional content group display tree // (which does not necessarily include all of the OCGs). OCDisplayNode *getDisplayRoot() { return display; } // Evaluate an optional content object -- either an OCG or an OCMD. // If is a valid OCG or OCMD, sets * and returns // true; otherwise returns false. GBool evalOCObject(Object *obj, GBool *visible); private: GBool evalOCVisibilityExpr(Object *expr, int recursion); XRef *xref; GList *ocgs; // all OCGs [OptionalContentGroup] OCDisplayNode *display; // root node of display tree }; //------------------------------------------------------------------------ // Values from the optional content usage dictionary. enum OCUsageState { ocUsageOn, ocUsageOff, ocUsageUnset }; //------------------------------------------------------------------------ class OptionalContentGroup { public: static OptionalContentGroup *parse(Ref *refA, Object *obj); ~OptionalContentGroup(); GBool matches(Ref *refA); Unicode *getName(); int getNameLength(); OCUsageState getViewState() { return viewState; } OCUsageState getPrintState() { return printState; } GBool getState() { return state; } void setState(GBool stateA) { state = stateA; } private: OptionalContentGroup(Ref *refA, TextString *nameA, OCUsageState viewStateA, OCUsageState printStateA); Ref ref; TextString *name; OCUsageState viewState, // suggested state when viewing printState; // suggested state when printing GBool state; // current state (on/off) friend class OCDisplayNode; }; //------------------------------------------------------------------------ class OCDisplayNode { public: static OCDisplayNode *parse(Object *obj, OptionalContent *oc, XRef *xref, int recursion = 0); OCDisplayNode(); ~OCDisplayNode(); Unicode *getName(); int getNameLength(); OptionalContentGroup *getOCG() { return ocg; } int getNumChildren(); OCDisplayNode *getChild(int idx); private: OCDisplayNode(GString *nameA); OCDisplayNode(OptionalContentGroup *ocgA); void addChild(OCDisplayNode *child); void addChildren(GList *childrenA); GList *takeChildren(); TextString *name; // display name OptionalContentGroup *ocg; // NULL for display labels GList *children; // NULL if there are no children // [OCDisplayNode] }; #endif xpdf-3.04/xpdf/XpdfPluginAPI.h0000644000076400007640000002332212341430012015427 0ustar dereknderekn/* * XpdfPluginAPI.h * * Copyright 2004 Glyph & Cog, LLC */ #ifndef XPDFPLUGINAPI_H #define XPDFPLUGINAPI_H #ifdef _WIN32 #include #else #define Object XtObject #include #undef Object #endif #ifdef __cplusplus extern "C" { #endif /*------------------------------------------------------------------------ * Macros *------------------------------------------------------------------------*/ /* * The current API version. */ #define xpdfPluginAPIVersion 1 #ifdef _WIN32 # ifdef __cplusplus # define PLUGINFUNC(retType) extern "C" __declspec(dllexport) retType # else # define PLUGINFUNC(retType) extern __declspec(dllexport) retType # endif #else # ifdef __cplusplus # define PLUGINFUNC(retType) extern "C" retType # else # define PLUGINFUNC(retType) extern retType # endif #endif /*------------------------------------------------------------------------ * Plugin setup/cleanup *------------------------------------------------------------------------*/ /* * All plugins are required to implement two functions: * * -- Initialize the plugin. Returns non-zero if successful. * PLUGINFUNC(XpdfBool) xpdfInitPlugin(void); * * -- Free the plugin. * PLUGINFUNC(void) xpdfFreePlugin(void); */ /*------------------------------------------------------------------------ * Types *------------------------------------------------------------------------*/ /* * Standard C boolean -- zero = false, non-zero = true. */ typedef int XpdfBool; #define xpdfTrue 1 #define xpdfFalse 0 /* * PDF document handle. */ typedef struct _XpdfDoc *XpdfDoc; /* * PDF object handle. */ typedef struct _XpdfObject *XpdfObject; /* * Document access permissions. Any of these can be bitwise 'or'ed * together. If xpdfPermissionOpen is not included, the document * cannot be opened at all, and the other bits are ignored. */ typedef unsigned int XpdfPermission; #define xpdfPermissionOpen (1 << 0) #define xpdfPermissionPrint (1 << 2) #define xpdfPermissionChange (1 << 3) #define xpdfPermissionCopy (1 << 4) #define xpdfPermissionNotes (1 << 5) /*------------------------------------------------------------------------ * Security handler *------------------------------------------------------------------------*/ /* * XpdfSecurityHandler - a security handler plugin should create one * of these and pass it to xpdfRegisterSecurityHandler. */ #ifdef __cplusplus struct XpdfSecurityHandler { #else typedef struct { #endif /* * Version of the security handler spec (this document) -- use * xpdfPluginAPIVersion. */ int version; /* * Security handler name. */ char *name; /* * Any global data the security handler needs. XpdfViewer will pass * this pointer to all handler functions as the * argument. */ void *handlerData; /* * Allocate and initialize data for a new document. XpdfViewer will * pass the returned pointer to all other handler functions as the * argument. Returns non-zero if successful. */ XpdfBool (*newDoc)(void *handlerData, XpdfDoc doc, XpdfObject encryptDict, void **docData); /* * Free the data allocated by newDoc. */ void (*freeDoc)(void *handlerData, void *docData); /* * Construct authorization data based on the supplied owner and user * passwords (either or both of which may be NULL). This function * is called in "batch" mode, i.e., if the password was supplied on * the command line or via an Xpdf library API. It should not * generate any user interaction (e.g., a password dialog). It is * not required to support this function: the makeAuthData function * pointer can be set to NULL. Returns non-zero if successful. */ XpdfBool (*makeAuthData)(void *handlerData, void *docData, char *ownerPassword, char *userPassword, void **authData); /* * Request any needed information (e.g., a password) from the user, * and construct an authorization data object. Returns non-zero if * successful. */ XpdfBool (*getAuthData)(void *handlerData, void *docData, void **authData); /* * Free the data allocated by getAuthData. */ void (*freeAuthData)(void *handlerData, void *docData, void *authData); /* * Request permission to access the document. This returns all * permissions granted by authData. */ XpdfPermission (*authorize)(void *handlerData, void *docData, void *authData); /* * Get the decryption key and algorithm version associated with the * document. Returns non-zero if successful. */ XpdfBool (*getKey)(void *handlerData, void *docData, char **key, int *keyLen, int *cryptVersion); /* * Free the data allocated by getKey. */ void (*freeKey)(void *handlerData, void *docData, char *key, int keyLen); #ifdef __cplusplus }; #else } XpdfSecurityHandler; #endif /*------------------------------------------------------------------------*/ typedef struct { int version; /*------------------------------------------------------------------------ * Document access functions *------------------------------------------------------------------------*/ /* * Get a document's info dictionary. (The returned object must be * freed with xpdfFreeObj.) */ XpdfObject (*_xpdfGetInfoDict)(XpdfDoc doc); /* * Get a document's catalog ("root") dictionary. (The returned object * must be freed with xpdfFreeObj.) */ XpdfObject (*_xpdfGetCatalog)(XpdfDoc doc); #ifdef _WIN32 /* * Get the handle for the viewer window associated with the specified * document. [Win32 only] */ HWND (*_xpdfWin32GetWindow)(XpdfDoc doc); #else /* * Get the Motif widget for the viewer window associated with the * specified document. [X only] */ Widget (*_xpdfXGetWindow)(XpdfDoc doc); #endif /*------------------------------------------------------------------------ * Object access functions *------------------------------------------------------------------------*/ /* * Check an object's type. */ XpdfBool (*_xpdfObjIsBool)(XpdfObject obj); XpdfBool (*_xpdfObjIsInt)(XpdfObject obj); XpdfBool (*_xpdfObjIsReal)(XpdfObject obj); XpdfBool (*_xpdfObjIsString)(XpdfObject obj); XpdfBool (*_xpdfObjIsName)(XpdfObject obj); XpdfBool (*_xpdfObjIsNull)(XpdfObject obj); XpdfBool (*_xpdfObjIsArray)(XpdfObject obj); XpdfBool (*_xpdfObjIsDict)(XpdfObject obj); XpdfBool (*_xpdfObjIsStream)(XpdfObject obj); XpdfBool (*_xpdfObjIsRef)(XpdfObject obj); /* * Value access. * (Objects returned by xpdfArrayGet and xpdfDictGet must be freed * with xpdfFreeObj.) */ XpdfBool (*_xpdfBoolValue)(XpdfObject obj); int (*_xpdfIntValue)(XpdfObject obj); double (*_xpdfRealValue)(XpdfObject obj); int (*_xpdfStringLength)(XpdfObject obj); char *(*_xpdfStringValue)(XpdfObject obj); char *(*_xpdfNameValue)(XpdfObject obj); int (*_xpdfArrayLength)(XpdfObject obj); XpdfObject (*_xpdfArrayGet)(XpdfObject obj, int idx); XpdfObject (*_xpdfDictGet)(XpdfObject obj, char *key); /* * Object destruction. NB: *all* objects must be freed after use. */ void (*_xpdfFreeObj)(XpdfObject obj); /*------------------------------------------------------------------------ * Memory allocation functions *------------------------------------------------------------------------*/ void *(*_xpdfMalloc)(int size); void *(*_xpdfRealloc)(void *p, int size); void (*_xpdfFree)(void *p); /*------------------------------------------------------------------------ * Security handler functions *------------------------------------------------------------------------*/ /* * Register a new security handler. */ void (*_xpdfRegisterSecurityHandler)(XpdfSecurityHandler *handler); /*------------------------------------------------------------------------*/ } XpdfPluginVecTable; #ifdef _WIN32 extern __declspec(dllexport) XpdfPluginVecTable xpdfPluginVecTable; #define xpdfPluginSetup \ extern __declspec(dllexport) \ XpdfPluginVecTable xpdfPluginVecTable = {xpdfPluginAPIVersion}; #else extern XpdfPluginVecTable xpdfPluginVecTable; #define xpdfPluginSetup \ XpdfPluginVecTable xpdfPluginVecTable = {xpdfPluginAPIVersion}; #endif #define xpdfGetInfoDict (*xpdfPluginVecTable._xpdfGetInfoDict) #define xpdfGetCatalog (*xpdfPluginVecTable._xpdfGetCatalog) #ifdef _WIN32 #define xpdfWin32GetWindow (*xpdfPluginVecTable._xpdfWin32GetWindow) #else #define xpdfXGetWindow (*xpdfPluginVecTable._xpdfXGetWindow) #endif #define xpdfObjIsBool (*xpdfPluginVecTable._xpdfObjIsBool) #define xpdfObjIsInt (*xpdfPluginVecTable._xpdfObjIsInt) #define xpdfObjIsReal (*xpdfPluginVecTable._xpdfObjIsReal) #define xpdfObjIsString (*xpdfPluginVecTable._xpdfObjIsString) #define xpdfObjIsName (*xpdfPluginVecTable._xpdfObjIsName) #define xpdfObjIsNull (*xpdfPluginVecTable._xpdfObjIsNull) #define xpdfObjIsArray (*xpdfPluginVecTable._xpdfObjIsArray) #define xpdfObjIsDict (*xpdfPluginVecTable._xpdfObjIsDict) #define xpdfObjIsStream (*xpdfPluginVecTable._xpdfObjIsStream) #define xpdfObjIsRef (*xpdfPluginVecTable._xpdfObjIsRef) #define xpdfBoolValue (*xpdfPluginVecTable._xpdfBoolValue) #define xpdfIntValue (*xpdfPluginVecTable._xpdfIntValue) #define xpdfRealValue (*xpdfPluginVecTable._xpdfRealValue) #define xpdfStringLength (*xpdfPluginVecTable._xpdfStringLength) #define xpdfStringValue (*xpdfPluginVecTable._xpdfStringValue) #define xpdfNameValue (*xpdfPluginVecTable._xpdfNameValue) #define xpdfArrayLength (*xpdfPluginVecTable._xpdfArrayLength) #define xpdfArrayGet (*xpdfPluginVecTable._xpdfArrayGet) #define xpdfDictGet (*xpdfPluginVecTable._xpdfDictGet) #define xpdfFreeObj (*xpdfPluginVecTable._xpdfFreeObj) #define xpdfMalloc (*xpdfPluginVecTable._xpdfMalloc) #define xpdfRealloc (*xpdfPluginVecTable._xpdfRealloc) #define xpdfFree (*xpdfPluginVecTable._xpdfFree) #define xpdfRegisterSecurityHandler (*xpdfPluginVecTable._xpdfRegisterSecurityHandler) #ifdef __cplusplus } #endif #endif xpdf-3.04/xpdf/pdftotext.cc0000644000076400007640000001643212341430012015200 0ustar dereknderekn//======================================================================== // // pdftotext.cc // // Copyright 1997-2013 Glyph & Cog, LLC // //======================================================================== #include #include #include #include #include #ifdef DEBUG_FP_LINUX # include # include #endif #include "parseargs.h" #include "GString.h" #include "gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "TextOutputDev.h" #include "CharTypes.h" #include "UnicodeMap.h" #include "TextString.h" #include "Error.h" #include "config.h" static int firstPage = 1; static int lastPage = 0; static GBool physLayout = gFalse; static GBool tableLayout = gFalse; static GBool linePrinter = gFalse; static GBool rawOrder = 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; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static GBool quiet = gFalse; static char cfgFileName[256] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-f", argInt, &firstPage, 0, "first page to convert"}, {"-l", argInt, &lastPage, 0, "last page to convert"}, {"-layout", argFlag, &physLayout, 0, "maintain original physical layout"}, {"-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"}, {"-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), "output end-of-line convention (unix, dos, or mac)"}, {"-nopgbrk", argFlag, &noPageBreaks, 0, "don't insert page breaks between pages"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-q", argFlag, &quiet, 0, "don't print any messages or errors"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; int main(int argc, char *argv[]) { PDFDoc *doc; GString *fileName; GString *textFileName; GString *ownerPW, *userPW; TextOutputControl textOutControl; TextOutputDev *textOut; 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 ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { fprintf(stderr, "pdftotext version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftotext", " []", argDesc); } goto err0; } fileName = new GString(argv[1]); // read config file globalParams = new GlobalParams(cfgFileName); if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } if (textEOL[0]) { if (!globalParams->setTextEOL(textEOL)) { fprintf(stderr, "Bad '-eol' value on command line\n"); } } if (noPageBreaks) { globalParams->setTextPageBreaks(gFalse); } if (quiet) { globalParams->setErrQuiet(quiet); } // get mapping to output encoding if (!(uMap = globalParams->getTextEncoding())) { error(errConfig, -1, "Couldn't get text encoding"); delete fileName; goto err1; } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = new GString(ownerPassword); } else { ownerPW = NULL; } if (userPassword[0] != '\001') { userPW = new GString(userPassword); } else { userPW = NULL; } doc = new PDFDoc(fileName, ownerPW, userPW); if (userPW) { delete userPW; } if (ownerPW) { delete ownerPW; } if (!doc->isOk()) { exitCode = 1; goto err2; } // check for copy permission if (!doc->okToCopy()) { error(errNotAllowed, -1, "Copying of text from this document is not allowed."); exitCode = 3; goto err2; } // construct text file name if (argc == 3) { textFileName = new GString(argv[2]); } else { p = fileName->getCString() + fileName->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { textFileName = new GString(fileName->getCString(), fileName->getLength() - 4); } else { textFileName = fileName->copy(); } textFileName->append(".txt"); } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } // write text file 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); } else { delete textOut; exitCode = 2; goto err3; } delete textOut; exitCode = 0; // clean up err3: delete textFileName; err2: delete doc; uMap->decRefCnt(); err1: delete globalParams; err0: // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); return exitCode; } xpdf-3.04/xpdf/forwardArrowDis.xbm0000644000076400007640000000046112341430012016472 0ustar dereknderekn#define forwardArrowDis_width 16 #define forwardArrowDis_height 15 static unsigned char forwardArrowDis_bits[] = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x15, 0x22, 0x2a, 0x11, 0x55, 0x22, 0xaa, 0x11, 0x55, 0x22, 0x2a, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x02, 0x00, 0x01}; xpdf-3.04/xpdf/Decrypt.h0000644000076400007640000000655712341430012014442 0ustar dereknderekn//======================================================================== // // Decrypt.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef DECRYPT_H #define DECRYPT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "GString.h" #include "Object.h" #include "Stream.h" //------------------------------------------------------------------------ // Decrypt //------------------------------------------------------------------------ class Decrypt { public: // Generate a file key. The buffer must have space for at // least 16 bytes. Checks and then // and returns true if either is correct. Sets if // the owner password was correct. Either or both of the passwords // may be NULL, which is treated as an empty string. static GBool makeFileKey(int encVersion, int encRevision, int keyLength, GString *ownerKey, GString *userKey, GString *ownerEnc, GString *userEnc, int permissions, GString *fileID, GString *ownerPassword, GString *userPassword, Guchar *fileKey, GBool encryptMetadata, GBool *ownerPasswordOk); private: static void r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen, char *userKey); static GBool makeFileKey2(int encVersion, int encRevision, int keyLength, GString *ownerKey, GString *userKey, int permissions, GString *fileID, GString *userPassword, Guchar *fileKey, GBool encryptMetadata); }; //------------------------------------------------------------------------ // DecryptStream //------------------------------------------------------------------------ struct DecryptRC4State { Guchar state[256]; Guchar x, y; int buf; }; struct DecryptAESState { Guint w[44]; Guchar state[16]; Guchar cbc[16]; Guchar buf[16]; int bufIdx; }; struct DecryptAES256State { Guint w[60]; Guchar state[16]; Guchar cbc[16]; Guchar buf[16]; int bufIdx; }; class DecryptStream: public FilterStream { public: DecryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA, int keyLength, int objNum, int objGen); virtual ~DecryptStream(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GBool isBinary(GBool last); virtual Stream *getUndecodedStream() { return this; } private: CryptAlgorithm algo; int objKeyLength; Guchar objKey[32]; union { DecryptRC4State rc4; DecryptAESState aes; DecryptAES256State aes256; } state; }; //------------------------------------------------------------------------ struct MD5State { Gulong a, b, c, d; Guchar buf[64]; int bufLen; int msgLen; Guchar digest[16]; }; extern void rc4InitKey(Guchar *key, int keyLen, Guchar *state); extern Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c); void md5Start(MD5State *state); void md5Append(MD5State *state, Guchar *data, int dataLen); void md5Finish(MD5State *state); extern void md5(Guchar *msg, int msgLen, Guchar *digest); extern void aesKeyExpansion(DecryptAESState *s, Guchar *objKey, int objKeyLen, GBool decrypt); extern void aesEncryptBlock(DecryptAESState *s, Guchar *in); extern void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last); #endif xpdf-3.04/xpdf/pdfimages.cc0000644000076400007640000000745412341430012015122 0ustar dereknderekn//======================================================================== // // pdfimages.cc // // Copyright 1998-2003 Glyph & Cog, LLC // //======================================================================== #include #include #include #include #include #include "parseargs.h" #include "GString.h" #include "gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "ImageOutputDev.h" #include "Error.h" #include "config.h" static int firstPage = 1; static int lastPage = 0; static GBool dumpJPEG = gFalse; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static GBool quiet = gFalse; static char cfgFileName[256] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-f", argInt, &firstPage, 0, "first page to convert"}, {"-l", argInt, &lastPage, 0, "last page to convert"}, {"-j", argFlag, &dumpJPEG, 0, "write JPEG images as JPEG files"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-q", argFlag, &quiet, 0, "don't print any messages or errors"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; int main(int argc, char *argv[]) { PDFDoc *doc; GString *fileName; char *imgRoot; GString *ownerPW, *userPW; ImageOutputDev *imgOut; GBool ok; int exitCode; exitCode = 99; // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 3 || printVersion || printHelp) { fprintf(stderr, "pdfimages version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfimages", " ", argDesc); } goto err0; } fileName = new GString(argv[1]); imgRoot = argv[2]; // read config file globalParams = new GlobalParams(cfgFileName); if (quiet) { globalParams->setErrQuiet(quiet); } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = new GString(ownerPassword); } else { ownerPW = NULL; } if (userPassword[0] != '\001') { userPW = new GString(userPassword); } else { userPW = NULL; } doc = new PDFDoc(fileName, ownerPW, userPW); if (userPW) { delete userPW; } if (ownerPW) { delete ownerPW; } if (!doc->isOk()) { exitCode = 1; goto err1; } // check for copy permission if (!doc->okToCopy()) { error(errNotAllowed, -1, "Copying of images from this document is not allowed."); exitCode = 3; goto err1; } // get page range if (firstPage < 1) firstPage = 1; if (lastPage < 1 || lastPage > doc->getNumPages()) lastPage = doc->getNumPages(); // write image files imgOut = new ImageOutputDev(imgRoot, dumpJPEG); if (imgOut->isOk()) { doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0, gFalse, gTrue, gFalse); } delete imgOut; exitCode = 0; // clean up err1: delete doc; delete globalParams; err0: // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); return exitCode; } xpdf-3.04/xpdf/XpdfPluginAPI.cc0000644000076400007640000001316112341430012015565 0ustar dereknderekn//======================================================================== // // XpdfPluginAPI.cc // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #include "aconf.h" #ifdef ENABLE_PLUGINS #include "gmem.h" #include "GlobalParams.h" #include "Object.h" #include "PDFDoc.h" #ifdef _WIN32 #include "WinPDFCore.h" #else #include "XPDFCore.h" #endif #include "XpdfPluginAPI.h" //------------------------------------------------------------------------ //~ This should use a pool of Objects; change xpdfFreeObj to match. static Object *allocObj() { return (Object *)gmalloc(sizeof(Object)); } //------------------------------------------------------------------------ // Document access functions //------------------------------------------------------------------------ XpdfObject _xpdfGetInfoDict(XpdfDoc doc) { Object *obj; obj = allocObj(); return (XpdfObject)((PDFDoc *)doc)->getDocInfo(obj); } XpdfObject _xpdfGetCatalog(XpdfDoc doc) { Object *obj; obj = allocObj(); return (XpdfObject)((PDFDoc *)doc)->getXRef()->getCatalog(obj); } #ifdef _WIN32 HWND _xpdfWin32GetWindow(XpdfDoc doc) { WinPDFCore *core; if (!(core = (WinPDFCore *)((PDFDoc *)doc)->getCore())) { return NULL; } return core->getDrawFrame(); } #else Widget _xpdfXGetWindow(XpdfDoc doc) { XPDFCore *core; if (!(core = (XPDFCore *)((PDFDoc *)doc)->getCore())) { return NULL; } return core->getWidget(); } #endif //------------------------------------------------------------------------ // Object access functions. //------------------------------------------------------------------------ XpdfBool _xpdfObjIsBool(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isBool(); } XpdfBool _xpdfObjIsInt(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isInt(); } XpdfBool _xpdfObjIsReal(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isReal(); } XpdfBool _xpdfObjIsNumber(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isNum(); } XpdfBool _xpdfObjIsString(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isString(); } XpdfBool _xpdfObjIsName(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isName(); } XpdfBool _xpdfObjIsNull(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isNull(); } XpdfBool _xpdfObjIsArray(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isArray(); } XpdfBool _xpdfObjIsDict(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isDict(); } XpdfBool _xpdfObjIsStream(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isStream(); } XpdfBool _xpdfObjIsRef(XpdfObject obj) { return (XpdfBool)((Object *)obj)->isRef(); } XpdfBool _xpdfBoolValue(XpdfObject obj) { return (XpdfBool)((Object *)obj)->getBool(); } int _xpdfIntValue(XpdfObject obj) { if (!((Object *)obj)->isInt()) { return 0; } return ((Object *)obj)->getInt(); } double _xpdfRealValue(XpdfObject obj) { if (!((Object *)obj)->isReal()) { return 0; } return ((Object *)obj)->getReal(); } double _xpdfNumberValue(XpdfObject obj) { if (!((Object *)obj)->isNum()) { return 0; } return ((Object *)obj)->getNum(); } int _xpdfStringLength(XpdfObject obj) { if (!((Object *)obj)->isString()) { return 0; } return ((Object *)obj)->getString()->getLength(); } char *_xpdfStringValue(XpdfObject obj) { if (!((Object *)obj)->isString()) { return 0; } return ((Object *)obj)->getString()->getCString(); } char *_xpdfNameValue(XpdfObject obj) { if (!((Object *)obj)->isName()) { return NULL; } return ((Object *)obj)->getName(); } int _xpdfArrayLength(XpdfObject obj) { if (!((Object *)obj)->isArray()) { return 0; } return ((Object *)obj)->arrayGetLength(); } XpdfObject _xpdfArrayGet(XpdfObject obj, int idx) { Object *elem; elem = allocObj(); if (!((Object *)obj)->isArray()) { return (XpdfObject)elem->initNull(); } return (XpdfObject)((Object *)obj)->arrayGet(idx, elem); } XpdfObject _xpdfDictGet(XpdfObject obj, char *key) { Object *elem; elem = allocObj(); if (!((Object *)obj)->isDict()) { return (XpdfObject)elem->initNull(); } return (XpdfObject)((Object *)obj)->dictLookup(key, elem); } void _xpdfFreeObj(XpdfObject obj) { ((Object *)obj)->free(); gfree(obj); } //------------------------------------------------------------------------ // Memory allocation functions //------------------------------------------------------------------------ void *_xpdfMalloc(int size) { return gmalloc(size); } void *_xpdfRealloc(void *p, int size) { return grealloc(p, size); } void _xpdfFree(void *p) { gfree(p); } //------------------------------------------------------------------------ // Security handlers //------------------------------------------------------------------------ void _xpdfRegisterSecurityHandler(XpdfSecurityHandler *handler) { if (handler->version <= xpdfPluginAPIVersion) { globalParams->addSecurityHandler(handler); } } //------------------------------------------------------------------------ XpdfPluginVecTable xpdfPluginVecTable = { xpdfPluginAPIVersion, &_xpdfGetInfoDict, &_xpdfGetCatalog, #ifdef _WIN32 &_xpdfWin32GetWindow, #else &_xpdfXGetWindow, #endif &_xpdfObjIsBool, &_xpdfObjIsInt, &_xpdfObjIsReal, &_xpdfObjIsString, &_xpdfObjIsName, &_xpdfObjIsNull, &_xpdfObjIsArray, &_xpdfObjIsDict, &_xpdfObjIsStream, &_xpdfObjIsRef, &_xpdfBoolValue, &_xpdfIntValue, &_xpdfRealValue, &_xpdfStringLength, &_xpdfStringValue, &_xpdfNameValue, &_xpdfArrayLength, &_xpdfArrayGet, &_xpdfDictGet, &_xpdfFreeObj, &_xpdfMalloc, &_xpdfRealloc, &_xpdfFree, &_xpdfRegisterSecurityHandler, }; #endif // ENABLE_PLUGINS xpdf-3.04/xpdf/config.h0000644000076400007640000000600612341430012014262 0ustar dereknderekn//======================================================================== // // config.h // // Copyright 1996-2014 Glyph & Cog, LLC // //======================================================================== #ifndef CONFIG_H #define CONFIG_H //------------------------------------------------------------------------ // version //------------------------------------------------------------------------ // xpdf version #define xpdfVersion "3.04" #define xpdfVersionNum 3.04 #define xpdfMajorVersion 3 #define xpdfMinorVersion 4 #define xpdfUpdateVersion 0 #define xpdfMajorVersionStr "3" #define xpdfMinorVersionStr "4" #define xpdfUpdateVersionStr "0" // supported PDF version #define supportedPDFVersionStr "1.7" #define supportedPDFVersionNum 1.7 // copyright notice #define xpdfCopyright "Copyright 1996-2014 Glyph & Cog, LLC" // Windows resource file stuff #define winxpdfVersion "WinXpdf 3.04" #define xpdfCopyrightAmp "Copyright 1996-2014 Glyph && Cog, LLC" //------------------------------------------------------------------------ // paper size //------------------------------------------------------------------------ // default paper size (in points) for PostScript output #ifdef A4_PAPER #define defPaperWidth 595 // ISO A4 (210x297 mm) #define defPaperHeight 842 #else #define defPaperWidth 612 // American letter (8.5x11") #define defPaperHeight 792 #endif //------------------------------------------------------------------------ // config file (xpdfrc) path //------------------------------------------------------------------------ // user config file name, relative to the user's home directory #if defined(VMS) || defined(_WIN32) #define xpdfUserConfigFile "xpdfrc" #else #define xpdfUserConfigFile ".xpdfrc" #endif // system config file name (set via the configure script) #ifdef SYSTEM_XPDFRC #define xpdfSysConfigFile SYSTEM_XPDFRC #else // under Windows, we get the directory with the executable and then // append this file name #define xpdfSysConfigFile "xpdfrc" #endif //------------------------------------------------------------------------ // X-related constants //------------------------------------------------------------------------ // default maximum size of color cube to allocate #define defaultRGBCube 5 //------------------------------------------------------------------------ // popen //------------------------------------------------------------------------ #if defined(_MSC_VER) || defined(__BORLANDC__) #define popen _popen #define pclose _pclose #endif #if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(_WIN32) || defined(__DJGPP__) || defined(MACOS) #define POPEN_READ_MODE "rb" #else #define POPEN_READ_MODE "r" #endif //------------------------------------------------------------------------ // Win32 stuff //------------------------------------------------------------------------ #ifdef CDECL #undef CDECL #endif #if defined(_MSC_VER) || defined(__BORLANDC__) #define CDECL __cdecl #else #define CDECL #endif #endif xpdf-3.04/xpdf/GfxState.h0000644000076400007640000011605412341430012014547 0ustar dereknderekn//======================================================================== // // GfxState.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef GFXSTATE_H #define GFXSTATE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" #include "Function.h" class Array; class GfxFont; class PDFRectangle; class GfxShading; //------------------------------------------------------------------------ // GfxBlendMode //------------------------------------------------------------------------ enum GfxBlendMode { gfxBlendNormal, gfxBlendMultiply, gfxBlendScreen, gfxBlendOverlay, gfxBlendDarken, gfxBlendLighten, gfxBlendColorDodge, gfxBlendColorBurn, gfxBlendHardLight, gfxBlendSoftLight, gfxBlendDifference, gfxBlendExclusion, gfxBlendHue, gfxBlendSaturation, gfxBlendColor, gfxBlendLuminosity }; //------------------------------------------------------------------------ // GfxColorComp //------------------------------------------------------------------------ // 16.16 fixed point color component typedef int GfxColorComp; #define gfxColorComp1 0x10000 static inline GfxColorComp dblToCol(double x) { return (GfxColorComp)(x * gfxColorComp1); } static inline double colToDbl(GfxColorComp x) { return (double)x / (double)gfxColorComp1; } static inline GfxColorComp byteToCol(Guchar x) { // (x / 255) << 16 = (0.0000000100000001... * x) << 16 // = ((x << 8) + (x) + (x >> 8) + ...) << 16 // = (x << 8) + (x) + (x >> 7) // [for rounding] return (GfxColorComp)((x << 8) + x + (x >> 7)); } static inline Guchar colToByte(GfxColorComp x) { // 255 * x + 0.5 = 256 * x - x + 0x8000 return (Guchar)(((x << 8) - x + 0x8000) >> 16); } //------------------------------------------------------------------------ // GfxColor //------------------------------------------------------------------------ #define gfxColorMaxComps funcMaxOutputs struct GfxColor { GfxColorComp c[gfxColorMaxComps]; }; //------------------------------------------------------------------------ // GfxGray //------------------------------------------------------------------------ typedef GfxColorComp GfxGray; //------------------------------------------------------------------------ // GfxRGB //------------------------------------------------------------------------ struct GfxRGB { GfxColorComp r, g, b; }; //------------------------------------------------------------------------ // GfxCMYK //------------------------------------------------------------------------ struct GfxCMYK { GfxColorComp c, m, y, k; }; //------------------------------------------------------------------------ // GfxColorSpace //------------------------------------------------------------------------ // NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames // array defined in GfxState.cc must match this enum. enum GfxColorSpaceMode { csDeviceGray, csCalGray, csDeviceRGB, csCalRGB, csDeviceCMYK, csLab, csICCBased, csIndexed, csSeparation, csDeviceN, csPattern }; class GfxColorSpace { public: GfxColorSpace(); virtual ~GfxColorSpace(); virtual GfxColorSpace *copy() = 0; virtual GfxColorSpaceMode getMode() = 0; // Construct a color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Object *csObj, 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; virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0; virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 0; // Return the number of color components. virtual int getNComps() = 0; // Get this color space's default color. virtual void getDefaultColor(GfxColor *color) = 0; // Return the default ranges for each component, assuming an image // with a max pixel value of . virtual void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel); // Returns true if painting operations in this color space never // mark the page (e.g., the "None" colorant). virtual GBool isNonMarking() { return gFalse; } // Return the color space's overprint mask. Guint getOverprintMask() { return overprintMask; } // Return the number of color space modes static int getNumColorSpaceModes(); // Return the name of the th color space mode. static const char *getColorSpaceModeName(int idx); protected: Guint overprintMask; }; //------------------------------------------------------------------------ // GfxDeviceGrayColorSpace //------------------------------------------------------------------------ class GfxDeviceGrayColorSpace: public GfxColorSpace { public: GfxDeviceGrayColorSpace(); virtual ~GfxDeviceGrayColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csDeviceGray; } virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 1; } virtual void getDefaultColor(GfxColor *color); private: }; //------------------------------------------------------------------------ // GfxCalGrayColorSpace //------------------------------------------------------------------------ class GfxCalGrayColorSpace: public GfxColorSpace { public: GfxCalGrayColorSpace(); virtual ~GfxCalGrayColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csCalGray; } // Construct a CalGray color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 1; } virtual void getDefaultColor(GfxColor *color); // CalGray-specific access. double getWhiteX() { return whiteX; } double getWhiteY() { return whiteY; } double getWhiteZ() { return whiteZ; } double getBlackX() { return blackX; } double getBlackY() { return blackY; } double getBlackZ() { return blackZ; } double getGamma() { return gamma; } private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double gamma; // gamma value }; //------------------------------------------------------------------------ // GfxDeviceRGBColorSpace //------------------------------------------------------------------------ class GfxDeviceRGBColorSpace: public GfxColorSpace { public: GfxDeviceRGBColorSpace(); virtual ~GfxDeviceRGBColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csDeviceRGB; } virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 3; } virtual void getDefaultColor(GfxColor *color); private: }; //------------------------------------------------------------------------ // GfxCalRGBColorSpace //------------------------------------------------------------------------ class GfxCalRGBColorSpace: public GfxColorSpace { public: GfxCalRGBColorSpace(); virtual ~GfxCalRGBColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csCalRGB; } // Construct a CalRGB color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 3; } virtual void getDefaultColor(GfxColor *color); // CalRGB-specific access. double getWhiteX() { return whiteX; } double getWhiteY() { return whiteY; } double getWhiteZ() { return whiteZ; } double getBlackX() { return blackX; } double getBlackY() { return blackY; } double getBlackZ() { return blackZ; } double getGammaR() { return gammaR; } double getGammaG() { return gammaG; } double getGammaB() { return gammaB; } double *getMatrix() { return mat; } private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double gammaR, gammaG, gammaB; // gamma values double mat[9]; // ABC -> XYZ transform matrix }; //------------------------------------------------------------------------ // GfxDeviceCMYKColorSpace //------------------------------------------------------------------------ class GfxDeviceCMYKColorSpace: public GfxColorSpace { public: GfxDeviceCMYKColorSpace(); virtual ~GfxDeviceCMYKColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csDeviceCMYK; } virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 4; } virtual void getDefaultColor(GfxColor *color); private: }; //------------------------------------------------------------------------ // GfxLabColorSpace //------------------------------------------------------------------------ class GfxLabColorSpace: public GfxColorSpace { public: GfxLabColorSpace(); virtual ~GfxLabColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csLab; } // Construct a Lab color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 3; } virtual void getDefaultColor(GfxColor *color); virtual void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel); // Lab-specific access. double getWhiteX() { return whiteX; } double getWhiteY() { return whiteY; } double getWhiteZ() { return whiteZ; } double getBlackX() { return blackX; } double getBlackY() { return blackY; } double getBlackZ() { return blackZ; } double getAMin() { return aMin; } double getAMax() { return aMax; } double getBMin() { return bMin; } double getBMax() { return bMax; } private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double aMin, aMax, bMin, bMax; // range for the a and b components double kr, kg, kb; // gamut mapping mulitpliers }; //------------------------------------------------------------------------ // GfxICCBasedColorSpace //------------------------------------------------------------------------ class GfxICCBasedColorSpace: public GfxColorSpace { public: GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, Ref *iccProfileStreamA); virtual ~GfxICCBasedColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csICCBased; } // Construct an ICCBased color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return nComps; } virtual void getDefaultColor(GfxColor *color); virtual void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel); // ICCBased-specific access. GfxColorSpace *getAlt() { return alt; } private: int nComps; // number of color components (1, 3, or 4) GfxColorSpace *alt; // alternate color space double rangeMin[4]; // min values for each component double rangeMax[4]; // max values for each component Ref iccProfileStream; // the ICC profile }; //------------------------------------------------------------------------ // GfxIndexedColorSpace //------------------------------------------------------------------------ class GfxIndexedColorSpace: public GfxColorSpace { public: GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA); virtual ~GfxIndexedColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csIndexed; } // Construct an Indexed color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 1; } virtual void getDefaultColor(GfxColor *color); virtual void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel); // Indexed-specific access. GfxColorSpace *getBase() { return base; } int getIndexHigh() { return indexHigh; } Guchar *getLookup() { return lookup; } GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor); private: GfxColorSpace *base; // base color space int indexHigh; // max pixel value Guchar *lookup; // lookup table }; //------------------------------------------------------------------------ // GfxSeparationColorSpace //------------------------------------------------------------------------ class GfxSeparationColorSpace: public GfxColorSpace { public: GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA, Function *funcA); virtual ~GfxSeparationColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csSeparation; } // Construct a Separation color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 1; } virtual void getDefaultColor(GfxColor *color); virtual GBool isNonMarking() { return nonMarking; } // Separation-specific access. GString *getName() { return name; } GfxColorSpace *getAlt() { return alt; } Function *getFunc() { return func; } private: GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA, Function *funcA, GBool nonMarkingA, Guint overprintMaskA); GString *name; // colorant name GfxColorSpace *alt; // alternate color space Function *func; // tint transform (into alternate color space) GBool nonMarking; }; //------------------------------------------------------------------------ // GfxDeviceNColorSpace //------------------------------------------------------------------------ class GfxDeviceNColorSpace: public GfxColorSpace { public: GfxDeviceNColorSpace(int nCompsA, GString **namesA, GfxColorSpace *alt, Function *func); virtual ~GfxDeviceNColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csDeviceN; } // Construct a DeviceN color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return nComps; } virtual void getDefaultColor(GfxColor *color); virtual GBool isNonMarking() { return nonMarking; } // DeviceN-specific access. GString *getColorantName(int i) { return names[i]; } GfxColorSpace *getAlt() { return alt; } Function *getTintTransformFunc() { return func; } private: GfxDeviceNColorSpace(int nCompsA, GString **namesA, GfxColorSpace *alt, Function *func, GBool nonMarkingA, Guint overprintMaskA); int nComps; // number of components GString // colorant names *names[gfxColorMaxComps]; GfxColorSpace *alt; // alternate color space Function *func; // tint transform (into alternate color space) GBool nonMarking; }; //------------------------------------------------------------------------ // GfxPatternColorSpace //------------------------------------------------------------------------ class GfxPatternColorSpace: public GfxColorSpace { public: GfxPatternColorSpace(GfxColorSpace *underA); virtual ~GfxPatternColorSpace(); virtual GfxColorSpace *copy(); virtual GfxColorSpaceMode getMode() { return csPattern; } // Construct a Pattern color space. Returns NULL if unsuccessful. static GfxColorSpace *parse(Array *arr, int recursion); virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); virtual int getNComps() { return 0; } virtual void getDefaultColor(GfxColor *color); // Pattern-specific access. GfxColorSpace *getUnder() { return under; } private: GfxColorSpace *under; // underlying color space (for uncolored // patterns) }; //------------------------------------------------------------------------ // GfxPattern //------------------------------------------------------------------------ class GfxPattern { public: GfxPattern(int typeA); virtual ~GfxPattern(); static GfxPattern *parse(Object *objRef, Object *obj ); virtual GfxPattern *copy() = 0; int getType() { return type; } private: int type; }; //------------------------------------------------------------------------ // GfxTilingPattern //------------------------------------------------------------------------ class GfxTilingPattern: public GfxPattern { public: static GfxTilingPattern *parse(Object *patObjRef, Object *patObj); virtual ~GfxTilingPattern(); virtual GfxPattern *copy(); int getPaintType() { return paintType; } int getTilingType() { return tilingType; } double *getBBox() { return bbox; } double getXStep() { return xStep; } double getYStep() { return yStep; } Dict *getResDict() { return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; } double *getMatrix() { return matrix; } Object *getContentStreamRef() { return &contentStreamRef; } private: GfxTilingPattern(int paintTypeA, int tilingTypeA, double *bboxA, double xStepA, double yStepA, Object *resDictA, double *matrixA, Object *contentStreamA); int paintType; int tilingType; double bbox[4]; double xStep, yStep; Object resDict; double matrix[6]; Object contentStreamRef; }; //------------------------------------------------------------------------ // GfxShadingPattern //------------------------------------------------------------------------ class GfxShadingPattern: public GfxPattern { public: static GfxShadingPattern *parse(Object *patObj ); virtual ~GfxShadingPattern(); virtual GfxPattern *copy(); GfxShading *getShading() { return shading; } double *getMatrix() { return matrix; } private: GfxShadingPattern(GfxShading *shadingA, double *matrixA); GfxShading *shading; double matrix[6]; }; //------------------------------------------------------------------------ // GfxShading //------------------------------------------------------------------------ class GfxShading { public: GfxShading(int typeA); GfxShading(GfxShading *shading); virtual ~GfxShading(); static GfxShading *parse(Object *obj ); virtual GfxShading *copy() = 0; int getType() { return type; } GfxColorSpace *getColorSpace() { return colorSpace; } GfxColor *getBackground() { return &background; } GBool getHasBackground() { return hasBackground; } void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; } GBool getHasBBox() { return hasBBox; } protected: GBool init(Dict *dict ); int type; GfxColorSpace *colorSpace; GfxColor background; GBool hasBackground; double xMin, yMin, xMax, yMax; GBool hasBBox; }; //------------------------------------------------------------------------ // GfxFunctionShading //------------------------------------------------------------------------ class GfxFunctionShading: public GfxShading { public: GfxFunctionShading(double x0A, double y0A, double x1A, double y1A, double *matrixA, Function **funcsA, int nFuncsA); GfxFunctionShading(GfxFunctionShading *shading); virtual ~GfxFunctionShading(); static GfxFunctionShading *parse(Dict *dict ); virtual GfxShading *copy(); void getDomain(double *x0A, double *y0A, double *x1A, double *y1A) { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; } double *getMatrix() { return matrix; } int getNFuncs() { return nFuncs; } Function *getFunc(int i) { return funcs[i]; } void getColor(double x, double y, GfxColor *color); private: double x0, y0, x1, y1; double matrix[6]; Function *funcs[gfxColorMaxComps]; int nFuncs; }; //------------------------------------------------------------------------ // GfxAxialShading //------------------------------------------------------------------------ class GfxAxialShading: public GfxShading { public: GfxAxialShading(double x0A, double y0A, double x1A, double y1A, double t0A, double t1A, Function **funcsA, int nFuncsA, GBool extend0A, GBool extend1A); GfxAxialShading(GfxAxialShading *shading); virtual ~GfxAxialShading(); static GfxAxialShading *parse(Dict *dict ); virtual GfxShading *copy(); void getCoords(double *x0A, double *y0A, double *x1A, double *y1A) { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; } double getDomain0() { return t0; } double getDomain1() { return t1; } GBool getExtend0() { return extend0; } GBool getExtend1() { return extend1; } int getNFuncs() { return nFuncs; } Function *getFunc(int i) { return funcs[i]; } void getColor(double t, GfxColor *color); private: double x0, y0, x1, y1; double t0, t1; Function *funcs[gfxColorMaxComps]; int nFuncs; GBool extend0, extend1; }; //------------------------------------------------------------------------ // GfxRadialShading //------------------------------------------------------------------------ class GfxRadialShading: public GfxShading { public: GfxRadialShading(double x0A, double y0A, double r0A, double x1A, double y1A, double r1A, double t0A, double t1A, Function **funcsA, int nFuncsA, GBool extend0A, GBool extend1A); GfxRadialShading(GfxRadialShading *shading); virtual ~GfxRadialShading(); static GfxRadialShading *parse(Dict *dict ); virtual GfxShading *copy(); void getCoords(double *x0A, double *y0A, double *r0A, double *x1A, double *y1A, double *r1A) { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; } double getDomain0() { return t0; } double getDomain1() { return t1; } GBool getExtend0() { return extend0; } GBool getExtend1() { return extend1; } int getNFuncs() { return nFuncs; } Function *getFunc(int i) { return funcs[i]; } void getColor(double t, GfxColor *color); private: double x0, y0, r0, x1, y1, r1; double t0, t1; Function *funcs[gfxColorMaxComps]; int nFuncs; GBool extend0, extend1; }; //------------------------------------------------------------------------ // GfxGouraudTriangleShading //------------------------------------------------------------------------ struct GfxGouraudVertex { double x, y; double color[gfxColorMaxComps]; }; class GfxGouraudTriangleShading: public GfxShading { public: GfxGouraudTriangleShading(int typeA, GfxGouraudVertex *verticesA, int nVerticesA, int (*trianglesA)[3], int nTrianglesA, int nCompsA, Function **funcsA, int nFuncsA); GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading); virtual ~GfxGouraudTriangleShading(); 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, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2); void getColor(double *in, GfxColor *out); private: GfxGouraudVertex *vertices; int nVertices; int (*triangles)[3]; int nTriangles; Function *funcs[gfxColorMaxComps]; int nComps; // number of color components (1 if nFuncs > 0) int nFuncs; }; //------------------------------------------------------------------------ // GfxPatchMeshShading //------------------------------------------------------------------------ struct GfxPatch { double x[4][4]; double y[4][4]; double color[2][2][gfxColorMaxComps]; }; class GfxPatchMeshShading: public GfxShading { public: GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA, int nCompsA, Function **funcsA, int nFuncsA); GfxPatchMeshShading(GfxPatchMeshShading *shading); virtual ~GfxPatchMeshShading(); 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; }; //------------------------------------------------------------------------ // GfxImageColorMap //------------------------------------------------------------------------ class GfxImageColorMap { public: // Constructor. GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA); // Destructor. ~GfxImageColorMap(); // Return a copy of this color map. GfxImageColorMap *copy() { return new GfxImageColorMap(this); } // Is color map valid? GBool isOk() { return ok; } // Get the color space. GfxColorSpace *getColorSpace() { return colorSpace; } // Get stream decoding info. int getNumPixelComps() { return nComps; } int getBits() { return bits; } // Get decode table. double getDecodeLow(int i) { return decodeLow[i]; } double getDecodeHigh(int i) { return decodeLow[i] + decodeRange[i]; } // Convert an image pixel to a color. void getGray(Guchar *x, GfxGray *gray); void getRGB(Guchar *x, GfxRGB *rgb); void getCMYK(Guchar *x, GfxCMYK *cmyk); void getColor(Guchar *x, GfxColor *color); // Convert a line of pixels to 8-bit colors. void getGrayByteLine(Guchar *in, Guchar *out, int n); void getRGBByteLine(Guchar *in, Guchar *out, int n); void getCMYKByteLine(Guchar *in, Guchar *out, int n); private: GfxImageColorMap(GfxImageColorMap *colorMap); GfxColorSpace *colorSpace; // the image color space int bits; // bits per component int nComps; // number of components in a pixel GfxColorSpace *colorSpace2; // secondary color space int nComps2; // number of components in colorSpace2 GfxColorComp * // lookup table lookup[gfxColorMaxComps]; GfxColorComp * // optimized case lookup table lookup2[gfxColorMaxComps]; double // minimum values for each component decodeLow[gfxColorMaxComps]; double // max - min value for each component decodeRange[gfxColorMaxComps]; GBool ok; }; //------------------------------------------------------------------------ // GfxSubpath and GfxPath //------------------------------------------------------------------------ class GfxSubpath { public: // Constructor. GfxSubpath(double x1, double y1); // Destructor. ~GfxSubpath(); // Copy. GfxSubpath *copy() { return new GfxSubpath(this); } // Get points. int getNumPoints() { return n; } double getX(int i) { return x[i]; } double getY(int i) { return y[i]; } GBool getCurve(int i) { return curve[i]; } // Get last point. double getLastX() { return x[n-1]; } double getLastY() { return y[n-1]; } // Add a line segment. void lineTo(double x1, double y1); // Add a Bezier curve. void curveTo(double x1, double y1, double x2, double y2, double x3, double y3); // Close the subpath. void close(); GBool isClosed() { return closed; } // Add (, ) to each point in the subpath. void offset(double dx, double dy); private: double *x, *y; // points GBool *curve; // curve[i] => point i is a control point // for a Bezier curve int n; // number of points int size; // size of x/y arrays GBool closed; // set if path is closed GfxSubpath(GfxSubpath *subpath); }; class GfxPath { public: // Constructor. GfxPath(); // Destructor. ~GfxPath(); // Copy. GfxPath *copy() { return new GfxPath(justMoved, firstX, firstY, subpaths, n, size); } // Is there a current point? GBool isCurPt() { return n > 0 || justMoved; } // Is the path non-empty, i.e., is there at least one segment? GBool isPath() { return n > 0; } // Get subpaths. int getNumSubpaths() { return n; } GfxSubpath *getSubpath(int i) { return subpaths[i]; } // Get last point on last subpath. double getLastX() { return subpaths[n-1]->getLastX(); } double getLastY() { return subpaths[n-1]->getLastY(); } // Move the current point. void moveTo(double x, double y); // Add a segment to the last subpath. void lineTo(double x, double y); // Add a Bezier curve to the last subpath void curveTo(double x1, double y1, double x2, double y2, double x3, double y3); // Close the last subpath. void close(); // Append to . void append(GfxPath *path); // Add (, ) to each point in the path. void offset(double dx, double dy); private: GBool justMoved; // set if a new subpath was just started double firstX, firstY; // first point in new subpath GfxSubpath **subpaths; // subpaths int n; // number of subpaths int size; // size of subpaths array GfxPath(GBool justMoved1, double firstX1, double firstY1, GfxSubpath **subpaths1, int n1, int size1); }; //------------------------------------------------------------------------ // GfxState //------------------------------------------------------------------------ class GfxState { public: // Construct a default GfxState, for a device with resolution // x , page box , page rotation , and // coordinate system specified by . GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox, int rotateA, GBool upsideDown ); // Destructor. ~GfxState(); // Copy. GfxState *copy(GBool copyPath = gFalse) { return new GfxState(this, copyPath); } // Accessors. double getHDPI() { return hDPI; } double getVDPI() { return vDPI; } double *getCTM() { return ctm; } double getX1() { return px1; } double getY1() { return py1; } double getX2() { return px2; } double getY2() { return py2; } double getPageWidth() { return pageWidth; } double getPageHeight() { return pageHeight; } int getRotate() { return rotate; } GfxColor *getFillColor() { return &fillColor; } GfxColor *getStrokeColor() { return &strokeColor; } void getFillGray(GfxGray *gray) { fillColorSpace->getGray(&fillColor, gray); } void getStrokeGray(GfxGray *gray) { strokeColorSpace->getGray(&strokeColor, gray); } void getFillRGB(GfxRGB *rgb) { fillColorSpace->getRGB(&fillColor, rgb); } void getStrokeRGB(GfxRGB *rgb) { strokeColorSpace->getRGB(&strokeColor, rgb); } void getFillCMYK(GfxCMYK *cmyk) { fillColorSpace->getCMYK(&fillColor, cmyk); } void getStrokeCMYK(GfxCMYK *cmyk) { strokeColorSpace->getCMYK(&strokeColor, cmyk); } GfxColorSpace *getFillColorSpace() { return fillColorSpace; } GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; } GfxPattern *getFillPattern() { return fillPattern; } GfxPattern *getStrokePattern() { return strokePattern; } GfxBlendMode getBlendMode() { return blendMode; } double getFillOpacity() { return fillOpacity; } double getStrokeOpacity() { return strokeOpacity; } GBool getFillOverprint() { return fillOverprint; } GBool getStrokeOverprint() { return strokeOverprint; } int getOverprintMode() { return overprintMode; } Function **getTransfer() { return transfer; } double getLineWidth() { return lineWidth; } void getLineDash(double **dash, int *length, double *start) { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; } int getFlatness() { return flatness; } int getLineJoin() { return lineJoin; } int getLineCap() { return lineCap; } double getMiterLimit() { return miterLimit; } GBool getStrokeAdjust() { return strokeAdjust; } GfxFont *getFont() { return font; } double getFontSize() { return fontSize; } double *getTextMat() { return textMat; } double getCharSpace() { return charSpace; } double getWordSpace() { return wordSpace; } double getHorizScaling() { return horizScaling; } double getLeading() { return leading; } double getRise() { return rise; } int getRender() { return render; } GfxPath *getPath() { return path; } void setPath(GfxPath *pathA); double getCurX() { return curX; } double getCurY() { return curY; } void getClipBBox(double *xMin, double *yMin, double *xMax, double *yMax) { *xMin = clipXMin; *yMin = clipYMin; *xMax = clipXMax; *yMax = clipYMax; } void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax); double getLineX() { return lineX; } double getLineY() { return lineY; } // Is there a current point/path? GBool isCurPt() { return path->isCurPt(); } GBool isPath() { return path->isPath(); } // Transforms. void transform(double x1, double y1, double *x2, double *y2) { *x2 = ctm[0] * x1 + ctm[2] * y1 + ctm[4]; *y2 = ctm[1] * x1 + ctm[3] * y1 + ctm[5]; } void transformDelta(double x1, double y1, double *x2, double *y2) { *x2 = ctm[0] * x1 + ctm[2] * y1; *y2 = ctm[1] * x1 + ctm[3] * y1; } void textTransform(double x1, double y1, double *x2, double *y2) { *x2 = textMat[0] * x1 + textMat[2] * y1 + textMat[4]; *y2 = textMat[1] * x1 + textMat[3] * y1 + textMat[5]; } void textTransformDelta(double x1, double y1, double *x2, double *y2) { *x2 = textMat[0] * x1 + textMat[2] * y1; *y2 = textMat[1] * x1 + textMat[3] * y1; } double transformWidth(double w); double getTransformedLineWidth() { return transformWidth(lineWidth); } double getTransformedFontSize(); void getFontTransMat(double *m11, double *m12, double *m21, double *m22); // Change state parameters. void setCTM(double a, double b, double c, double d, double e, double f); void concatCTM(double a, double b, double c, double d, double e, double f); void shiftCTM(double tx, double ty); void setFillColorSpace(GfxColorSpace *colorSpace); void setStrokeColorSpace(GfxColorSpace *colorSpace); void setFillColor(GfxColor *color) { fillColor = *color; } void setStrokeColor(GfxColor *color) { strokeColor = *color; } void setFillPattern(GfxPattern *pattern); void setStrokePattern(GfxPattern *pattern); void setBlendMode(GfxBlendMode mode) { blendMode = mode; } void setFillOpacity(double opac) { fillOpacity = opac; } void setStrokeOpacity(double opac) { strokeOpacity = opac; } void setFillOverprint(GBool op) { fillOverprint = op; } void setStrokeOverprint(GBool op) { strokeOverprint = op; } void setOverprintMode(int opm) { overprintMode = opm; } void setTransfer(Function **funcs); void setLineWidth(double width) { lineWidth = width; } void setLineDash(double *dash, int length, double start); void setFlatness(int flatness1) { flatness = flatness1; } void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; } void setLineCap(int lineCap1) { lineCap = lineCap1; } void setMiterLimit(double limit) { miterLimit = limit; } void setStrokeAdjust(GBool sa) { strokeAdjust = sa; } void setFont(GfxFont *fontA, double fontSizeA) { font = fontA; fontSize = fontSizeA; } void setTextMat(double a, double b, double c, double d, double e, double f) { textMat[0] = a; textMat[1] = b; textMat[2] = c; textMat[3] = d; textMat[4] = e; textMat[5] = f; } void setCharSpace(double space) { charSpace = space; } void setWordSpace(double space) { wordSpace = space; } void setHorizScaling(double scale) { horizScaling = 0.01 * scale; } void setLeading(double leadingA) { leading = leadingA; } void setRise(double riseA) { rise = riseA; } void setRender(int renderA) { render = renderA; } // Add to path. void moveTo(double x, double y) { path->moveTo(curX = x, curY = y); } void lineTo(double x, double y) { path->lineTo(curX = x, curY = y); } void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); } void closePath() { path->close(); curX = path->getLastX(); curY = path->getLastY(); } void clearPath(); // Update clip region. void clip(); void clipToStrokePath(); void clipToRect(double xMin, double yMin, double xMax, double yMax); // Text position. void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; } void textMoveTo(double tx, double ty) { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); } void textShift(double tx, double ty); void shift(double dx, double dy); // Push/pop GfxState on/off stack. GfxState *save(); GfxState *restore(); GBool hasSaves() { return saved != NULL; } // Misc GBool parseBlendMode(Object *obj, GfxBlendMode *mode); private: double hDPI, vDPI; // resolution double ctm[6]; // coord transform matrix double px1, py1, px2, py2; // page corners (user coords) double pageWidth, pageHeight; // page size (pixels) int rotate; // page rotation angle GfxColorSpace *fillColorSpace; // fill color space GfxColorSpace *strokeColorSpace; // stroke color space GfxColor fillColor; // fill color GfxColor strokeColor; // stroke color GfxPattern *fillPattern; // fill pattern GfxPattern *strokePattern; // stroke pattern GfxBlendMode blendMode; // transparency blend mode double fillOpacity; // fill opacity double strokeOpacity; // stroke opacity GBool fillOverprint; // fill overprint GBool strokeOverprint; // stroke overprint int overprintMode; // overprint mode ("OPM") Function *transfer[4]; // transfer function (entries may be: all // NULL = identity; last three NULL = // single function; all four non-NULL = // R,G,B,gray functions) double lineWidth; // line width double *lineDash; // line dash int lineDashLength; double lineDashStart; int flatness; // curve flatness int lineJoin; // line join style int lineCap; // line cap style double miterLimit; // line miter limit GBool strokeAdjust; // stroke adjustment GfxFont *font; // font double fontSize; // font size double textMat[6]; // text matrix double charSpace; // character spacing double wordSpace; // word spacing double horizScaling; // horizontal scaling double leading; // text leading double rise; // text rise int render; // text rendering mode GfxPath *path; // array of path elements double curX, curY; // current point (user coords) double lineX, lineY; // start of current text line (text coords) double clipXMin, clipYMin, // bounding box for clip region clipXMax, clipYMax; GfxState *saved; // next GfxState on stack GfxState(GfxState *state, GBool copyPath); }; #endif xpdf-3.04/xpdf/GfxFont.cc0000644000076400007640000015646312341430012014543 0ustar dereknderekn//======================================================================== // // GfxFont.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include #if HAVE_STD_SORT #include #endif #include "gmem.h" #include "Error.h" #include "Object.h" #include "Dict.h" #include "GlobalParams.h" #include "CMap.h" #include "CharCodeToUnicode.h" #include "FontEncodingTables.h" #include "BuiltinFontTables.h" #include "FoFiIdentifier.h" #include "FoFiType1.h" #include "FoFiType1C.h" #include "FoFiTrueType.h" #include "GfxFont.h" //------------------------------------------------------------------------ struct Base14FontMapEntry { const char *altName; const char *base14Name; }; static Base14FontMapEntry base14FontMap[] = { { "Arial", "Helvetica" }, { "Arial,Bold", "Helvetica-Bold" }, { "Arial,BoldItalic", "Helvetica-BoldOblique" }, { "Arial,Italic", "Helvetica-Oblique" }, { "Arial-Bold", "Helvetica-Bold" }, { "Arial-BoldItalic", "Helvetica-BoldOblique" }, { "Arial-BoldItalicMT", "Helvetica-BoldOblique" }, { "Arial-BoldMT", "Helvetica-Bold" }, { "Arial-Italic", "Helvetica-Oblique" }, { "Arial-ItalicMT", "Helvetica-Oblique" }, { "ArialMT", "Helvetica" }, { "Courier", "Courier" }, { "Courier,Bold", "Courier-Bold" }, { "Courier,BoldItalic", "Courier-BoldOblique" }, { "Courier,Italic", "Courier-Oblique" }, { "Courier-Bold", "Courier-Bold" }, { "Courier-BoldOblique", "Courier-BoldOblique" }, { "Courier-Oblique", "Courier-Oblique" }, { "CourierNew", "Courier" }, { "CourierNew,Bold", "Courier-Bold" }, { "CourierNew,BoldItalic", "Courier-BoldOblique" }, { "CourierNew,Italic", "Courier-Oblique" }, { "CourierNew-Bold", "Courier-Bold" }, { "CourierNew-BoldItalic", "Courier-BoldOblique" }, { "CourierNew-Italic", "Courier-Oblique" }, { "CourierNewPS-BoldItalicMT", "Courier-BoldOblique" }, { "CourierNewPS-BoldMT", "Courier-Bold" }, { "CourierNewPS-ItalicMT", "Courier-Oblique" }, { "CourierNewPSMT", "Courier" }, { "Helvetica", "Helvetica" }, { "Helvetica,Bold", "Helvetica-Bold" }, { "Helvetica,BoldItalic", "Helvetica-BoldOblique" }, { "Helvetica,Italic", "Helvetica-Oblique" }, { "Helvetica-Bold", "Helvetica-Bold" }, { "Helvetica-BoldItalic", "Helvetica-BoldOblique" }, { "Helvetica-BoldOblique", "Helvetica-BoldOblique" }, { "Helvetica-Italic", "Helvetica-Oblique" }, { "Helvetica-Oblique", "Helvetica-Oblique" }, { "Symbol", "Symbol" }, { "Symbol,Bold", "Symbol" }, { "Symbol,BoldItalic", "Symbol" }, { "Symbol,Italic", "Symbol" }, { "Times-Bold", "Times-Bold" }, { "Times-BoldItalic", "Times-BoldItalic" }, { "Times-Italic", "Times-Italic" }, { "Times-Roman", "Times-Roman" }, { "TimesNewRoman", "Times-Roman" }, { "TimesNewRoman,Bold", "Times-Bold" }, { "TimesNewRoman,BoldItalic", "Times-BoldItalic" }, { "TimesNewRoman,Italic", "Times-Italic" }, { "TimesNewRoman-Bold", "Times-Bold" }, { "TimesNewRoman-BoldItalic", "Times-BoldItalic" }, { "TimesNewRoman-Italic", "Times-Italic" }, { "TimesNewRomanPS", "Times-Roman" }, { "TimesNewRomanPS-Bold", "Times-Bold" }, { "TimesNewRomanPS-BoldItalic", "Times-BoldItalic" }, { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" }, { "TimesNewRomanPS-BoldMT", "Times-Bold" }, { "TimesNewRomanPS-Italic", "Times-Italic" }, { "TimesNewRomanPS-ItalicMT", "Times-Italic" }, { "TimesNewRomanPSMT", "Times-Roman" }, { "TimesNewRomanPSMT,Bold", "Times-Bold" }, { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" }, { "TimesNewRomanPSMT,Italic", "Times-Italic" }, { "ZapfDingbats", "ZapfDingbats" } }; //------------------------------------------------------------------------ // index: {fixed:0, sans-serif:4, serif:8} + bold*2 + italic // NB: must be in same order as psSubstFonts in PSOutputDev.cc static const char *base14SubstFonts[14] = { "Courier", "Courier-Oblique", "Courier-Bold", "Courier-BoldOblique", "Helvetica", "Helvetica-Oblique", "Helvetica-Bold", "Helvetica-BoldOblique", "Times-Roman", "Times-Italic", "Times-Bold", "Times-BoldItalic", // the last two are never used for substitution "Symbol", "ZapfDingbats" }; //------------------------------------------------------------------------ static int readFromStream(void *data) { return ((Stream *)data)->getChar(); } //------------------------------------------------------------------------ // GfxFontLoc //------------------------------------------------------------------------ GfxFontLoc::GfxFontLoc() { path = NULL; fontNum = 0; oblique = 0; encoding = NULL; substIdx = -1; } GfxFontLoc::~GfxFontLoc() { if (path) { delete path; } if (encoding) { delete encoding; } } //------------------------------------------------------------------------ // GfxFont //------------------------------------------------------------------------ GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { GString *nameA; Ref embFontIDA; GfxFontType typeA; GfxFont *font; Object obj1; // get base font name nameA = NULL; fontDict->lookup("BaseFont", &obj1); if (obj1.isName()) { nameA = new GString(obj1.getName()); } else if (obj1.isString()) { nameA = obj1.getString()->copy(); } obj1.free(); // get embedded font ID and font type typeA = getFontType(xref, fontDict, &embFontIDA); // create the font object font = NULL; if (typeA < fontCIDType0) { font = new Gfx8BitFont(xref, tagA, idA, nameA, typeA, embFontIDA, fontDict); } else { font = new GfxCIDFont(xref, tagA, idA, nameA, typeA, embFontIDA, fontDict); } return font; } GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA) { ok = gFalse; tag = new GString(tagA); id = idA; name = nameA; type = typeA; embFontID = embFontIDA; embFontName = NULL; } GfxFont::~GfxFont() { delete tag; if (name) { delete name; } if (embFontName) { delete embFontName; } } // This function extracts three pieces of information: // 1. the "expected" font type, i.e., the font type implied by // Font.Subtype, DescendantFont.Subtype, and // FontDescriptor.FontFile3.Subtype // 2. the embedded font object ID // 3. the actual font type - determined by examining the embedded font // if there is one, otherwise equal to the expected font type // If the expected and actual font types don't match, a warning // message is printed. The expected font type is not used for // anything else. GfxFontType GfxFont::getFontType(XRef *xref, Dict *fontDict, Ref *embID) { GfxFontType t, expectedType; FoFiIdentifierType fft; Dict *fontDict2; Object subtype, fontDesc, obj1, obj2, obj3, obj4; GBool isType0, err; t = fontUnknownType; embID->num = embID->gen = -1; err = gFalse; fontDict->lookup("Subtype", &subtype); expectedType = fontUnknownType; isType0 = gFalse; if (subtype.isName("Type1") || subtype.isName("MMType1")) { expectedType = fontType1; } else if (subtype.isName("Type1C")) { expectedType = fontType1C; } else if (subtype.isName("Type3")) { expectedType = fontType3; } else if (subtype.isName("TrueType")) { expectedType = fontTrueType; } else if (subtype.isName("Type0")) { isType0 = gTrue; } else { error(errSyntaxWarning, -1, "Unknown font type: '{0:s}'", subtype.isName() ? subtype.getName() : "???"); } subtype.free(); fontDict2 = fontDict; if (fontDict->lookup("DescendantFonts", &obj1)->isArray()) { if (obj1.arrayGetLength() == 0) { error(errSyntaxWarning, -1, "Empty DescendantFonts array in font"); obj2.initNull(); } else if (obj1.arrayGet(0, &obj2)->isDict()) { if (!isType0) { error(errSyntaxWarning, -1, "Non-CID font with DescendantFonts array"); } fontDict2 = obj2.getDict(); fontDict2->lookup("Subtype", &subtype); if (subtype.isName("CIDFontType0")) { if (isType0) { expectedType = fontCIDType0; } } else if (subtype.isName("CIDFontType2")) { if (isType0) { expectedType = fontCIDType2; } } subtype.free(); } } else { obj2.initNull(); } if (fontDict2->lookup("FontDescriptor", &fontDesc)->isDict()) { if (fontDesc.dictLookupNF("FontFile", &obj3)->isRef()) { *embID = obj3.getRef(); if (expectedType != fontType1) { err = gTrue; } } obj3.free(); if (embID->num == -1 && fontDesc.dictLookupNF("FontFile2", &obj3)->isRef()) { *embID = obj3.getRef(); if (isType0) { expectedType = fontCIDType2; } else if (expectedType != fontTrueType) { err = gTrue; } } obj3.free(); if (embID->num == -1 && fontDesc.dictLookupNF("FontFile3", &obj3)->isRef()) { *embID = obj3.getRef(); if (obj3.fetch(xref, &obj4)->isStream()) { obj4.streamGetDict()->lookup("Subtype", &subtype); if (subtype.isName("Type1")) { if (expectedType != fontType1) { err = gTrue; expectedType = isType0 ? fontCIDType0 : fontType1; } } else if (subtype.isName("Type1C")) { if (expectedType == fontType1) { expectedType = fontType1C; } else if (expectedType != fontType1C) { err = gTrue; expectedType = isType0 ? fontCIDType0C : fontType1C; } } else if (subtype.isName("TrueType")) { if (expectedType != fontTrueType) { err = gTrue; expectedType = isType0 ? fontCIDType2 : fontTrueType; } } else if (subtype.isName("CIDFontType0C")) { if (expectedType == fontCIDType0) { expectedType = fontCIDType0C; } else { err = gTrue; expectedType = isType0 ? fontCIDType0C : fontType1C; } } else if (subtype.isName("OpenType")) { if (expectedType == fontTrueType) { expectedType = fontTrueTypeOT; } else if (expectedType == fontType1) { expectedType = fontType1COT; } else if (expectedType == fontCIDType0) { expectedType = fontCIDType0COT; } else if (expectedType == fontCIDType2) { expectedType = fontCIDType2OT; } else { err = gTrue; } } else { error(errSyntaxError, -1, "Unknown font type '{0:s}'", subtype.isName() ? subtype.getName() : "???"); } subtype.free(); } obj4.free(); } obj3.free(); } fontDesc.free(); t = fontUnknownType; if (embID->num >= 0) { obj3.initRef(embID->num, embID->gen); obj3.fetch(xref, &obj4); if (obj4.isStream()) { obj4.streamReset(); fft = FoFiIdentifier::identifyStream(&readFromStream, obj4.getStream()); obj4.streamClose(); switch (fft) { case fofiIdType1PFA: case fofiIdType1PFB: t = fontType1; break; case fofiIdCFF8Bit: t = isType0 ? fontCIDType0C : fontType1C; break; case fofiIdCFFCID: t = fontCIDType0C; break; case fofiIdTrueType: case fofiIdTrueTypeCollection: t = isType0 ? fontCIDType2 : fontTrueType; break; case fofiIdOpenTypeCFF8Bit: t = isType0 ? fontCIDType0COT : fontType1COT; break; case fofiIdOpenTypeCFFCID: t = fontCIDType0COT; break; default: error(errSyntaxError, -1, "Embedded font file may be invalid"); break; } } obj4.free(); obj3.free(); } if (t == fontUnknownType) { t = expectedType; } if (t != expectedType) { err = gTrue; } if (err) { error(errSyntaxWarning, -1, "Mismatch between font type and embedded font file"); } obj2.free(); obj1.free(); return t; } void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { Object obj1, obj2, obj3, obj4; double t; int i; // assume Times-Roman by default (for substitution purposes) flags = fontSerif; missingWidth = 0; if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) { // get flags if (obj1.dictLookup("Flags", &obj2)->isInt()) { flags = obj2.getInt(); } obj2.free(); // get name obj1.dictLookup("FontName", &obj2); if (obj2.isName()) { embFontName = new GString(obj2.getName()); } obj2.free(); // look for MissingWidth obj1.dictLookup("MissingWidth", &obj2); if (obj2.isNum()) { missingWidth = obj2.getNum(); } obj2.free(); // get Ascent and Descent obj1.dictLookup("Ascent", &obj2); if (obj2.isNum()) { t = 0.001 * obj2.getNum(); // some broken font descriptors specify a negative ascent if (t < 0) { t = -t; } // some broken font descriptors set ascent and descent to 0; // others set it to ridiculous values (e.g., 32768) if (t != 0 && t < 1.9) { ascent = t; } } obj2.free(); obj1.dictLookup("Descent", &obj2); if (obj2.isNum()) { t = 0.001 * obj2.getNum(); // some broken font descriptors specify a positive descent if (t > 0) { t = -t; } // some broken font descriptors set ascent and descent to 0 if (t != 0 && t > -1.9) { descent = t; } } obj2.free(); // font FontBBox if (obj1.dictLookup("FontBBox", &obj2)->isArray()) { for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) { if (obj2.arrayGet(i, &obj3)->isNum()) { fontBBox[i] = 0.001 * obj3.getNum(); } obj3.free(); } } obj2.free(); } obj1.free(); } CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits, CharCodeToUnicode *ctu) { GString *buf; Object obj1; char buf2[4096]; int n; if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) { obj1.free(); return NULL; } buf = new GString(); obj1.streamReset(); while ((n = obj1.streamGetBlock(buf2, sizeof(buf2))) > 0) { buf->append(buf2, n); } obj1.streamClose(); obj1.free(); if (ctu) { ctu->mergeCMap(buf, nBits); } else { ctu = CharCodeToUnicode::parseCMap(buf, nBits); } delete buf; return ctu; } GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) { GfxFontLoc *fontLoc; SysFontType sysFontType; GString *path, *base14Name, *substName; PSFontParam16 *psFont16; Object refObj, embFontObj; int substIdx, fontNum; double oblique; GBool embed; if (type == fontType3) { return NULL; } //----- embedded font if (embFontID.num >= 0) { embed = gTrue; refObj.initRef(embFontID.num, embFontID.gen); refObj.fetch(xref, &embFontObj); if (!embFontObj.isStream()) { error(errSyntaxError, -1, "Embedded font object is wrong type"); embed = gFalse; } embFontObj.free(); refObj.free(); if (embed) { if (ps) { switch (type) { case fontType1: case fontType1C: case fontType1COT: embed = globalParams->getPSEmbedType1(); break; case fontTrueType: case fontTrueTypeOT: embed = globalParams->getPSEmbedTrueType(); break; case fontCIDType0C: case fontCIDType0COT: embed = globalParams->getPSEmbedCIDPostScript(); break; case fontCIDType2: case fontCIDType2OT: embed = globalParams->getPSEmbedCIDTrueType(); break; default: break; } } if (embed) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocEmbedded; fontLoc->fontType = type; fontLoc->embFontID = embFontID; return fontLoc; } } } //----- PS passthrough if (ps && name && !isCIDFont() && globalParams->getPSFontPassthrough()) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocResident; fontLoc->fontType = fontType1; fontLoc->path = name->copy(); return fontLoc; } //----- external font file (fontFile, fontDir) if (name && (path = globalParams->findFontFile(name))) { if ((fontLoc = getExternalFont(path, 0, 0, isCIDFont()))) { return fontLoc; } } //----- PS resident Base-14 font if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocResident; fontLoc->fontType = fontType1; fontLoc->path = new GString(((Gfx8BitFont *)this)->base14->base14Name); return fontLoc; } //----- external font file for Base-14 font if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { base14Name = new GString(((Gfx8BitFont *)this)->base14->base14Name); path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique); delete base14Name; if (path && (fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) { return fontLoc; } } //----- system font if (name && (path = globalParams->findSystemFontFile(name, &sysFontType, &fontNum))) { if (isCIDFont()) { if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontCIDType2; fontLoc->path = path; fontLoc->fontNum = fontNum; return fontLoc; } } else { if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontTrueType; fontLoc->path = path; fontLoc->fontNum = fontNum; return fontLoc; } else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontType1; fontLoc->path = path; return fontLoc; } } delete path; } if (!isCIDFont()) { //----- 8-bit PS resident font if (ps) { if (name && (path = globalParams->getPSResidentFont(name))) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocResident; fontLoc->fontType = fontType1; fontLoc->path = path; return fontLoc; } } //----- 8-bit font substitution if (flags & fontFixedWidth) { substIdx = 0; } else if (flags & fontSerif) { substIdx = 8; } else { substIdx = 4; } if (isBold()) { substIdx += 2; } if (isItalic()) { substIdx += 1; } substName = new GString(base14SubstFonts[substIdx]); if (ps) { error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'", base14SubstFonts[substIdx], name); fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocResident; fontLoc->fontType = fontType1; fontLoc->path = substName; fontLoc->substIdx = substIdx; return fontLoc; } else { path = globalParams->findBase14FontFile(substName, &fontNum, &oblique); delete substName; if (path) { if ((fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) { error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'", base14SubstFonts[substIdx], name); fontLoc->substIdx = substIdx; return fontLoc; } } } // failed to find a substitute font return NULL; } //----- 16-bit PS resident font if (ps && name && ((psFont16 = globalParams->getPSResidentFont16( name, ((GfxCIDFont *)this)->getWMode())))) { fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocResident; fontLoc->fontType = fontCIDType0; // this is not used fontLoc->path = psFont16->psFontName->copy(); fontLoc->encoding = psFont16->encoding->copy(); fontLoc->wMode = psFont16->wMode; return fontLoc; } if (ps && ((psFont16 = globalParams->getPSResidentFontCC( ((GfxCIDFont *)this)->getCollection(), ((GfxCIDFont *)this)->getWMode())))) { error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'", psFont16->psFontName, name); fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocResident; fontLoc->fontType = fontCIDType0; // this is not used fontLoc->path = psFont16->psFontName->copy(); fontLoc->encoding = psFont16->encoding->copy(); fontLoc->wMode = psFont16->wMode; return fontLoc; } //----- CID font substitution if ((path = globalParams->findCCFontFile( ((GfxCIDFont *)this)->getCollection()))) { if ((fontLoc = getExternalFont(path, 0, 0, gTrue))) { error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'", fontLoc->path, name); return fontLoc; } } // failed to find a substitute font return NULL; } GfxFontLoc *GfxFont::locateBase14Font(GString *base14Name) { GString *path; int fontNum; double oblique; path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique); if (!path) { return NULL; } return getExternalFont(path, fontNum, oblique, gFalse); } GfxFontLoc *GfxFont::getExternalFont(GString *path, int fontNum, double oblique, GBool cid) { FoFiIdentifierType fft; GfxFontType fontType; GfxFontLoc *fontLoc; fft = FoFiIdentifier::identifyFile(path->getCString()); switch (fft) { case fofiIdType1PFA: case fofiIdType1PFB: fontType = fontType1; break; case fofiIdCFF8Bit: fontType = fontType1C; break; case fofiIdCFFCID: fontType = fontCIDType0C; break; case fofiIdTrueType: case fofiIdTrueTypeCollection: fontType = cid ? fontCIDType2 : fontTrueType; break; case fofiIdOpenTypeCFF8Bit: fontType = fontType1COT; break; case fofiIdOpenTypeCFFCID: fontType = fontCIDType0COT; break; case fofiIdDfont: fontType = cid ? fontCIDType2 : fontTrueType; break; case fofiIdUnknown: case fofiIdError: default: fontType = fontUnknownType; break; } if (fontType == fontUnknownType || (cid ? (fontType < fontCIDType0) : (fontType >= fontCIDType0))) { delete path; return NULL; } fontLoc = new GfxFontLoc(); fontLoc->locType = gfxFontLocExternal; fontLoc->fontType = fontType; fontLoc->path = path; fontLoc->fontNum = fontNum; fontLoc->oblique = oblique; return fontLoc; } char *GfxFont::readEmbFontFile(XRef *xref, int *len) { char *buf; Object obj1, obj2; Stream *str; int size, n; obj1.initRef(embFontID.num, embFontID.gen); obj1.fetch(xref, &obj2); if (!obj2.isStream()) { error(errSyntaxError, -1, "Embedded font file is not a stream"); obj2.free(); obj1.free(); embFontID.num = -1; return NULL; } str = obj2.getStream(); size = 0; buf = NULL; str->reset(); do { if (size > INT_MAX - 4096) { error(errSyntaxError, -1, "Embedded font file is too large"); break; } buf = (char *)grealloc(buf, size + 4096); n = str->getBlock(buf + size, 4096); size += n; } while (n == 4096); *len = size; str->close(); obj2.free(); obj1.free(); return buf; } //------------------------------------------------------------------------ // Gfx8BitFont //------------------------------------------------------------------------ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict): GfxFont(tagA, idA, nameA, typeA, embFontIDA) { GString *name2; BuiltinFont *builtinFont; const char **baseEnc; GBool baseEncFromFontFile; char *buf; int len; FoFiType1 *ffT1; FoFiType1C *ffT1C; int code, code2; char *charName; GBool missing, hex; Unicode toUnicode[256]; CharCodeToUnicode *utu, *ctu2; Unicode uBuf[8]; double mul; int firstChar, lastChar; Gushort w; Object obj1, obj2, obj3; int n, i, a, b, m; ctu = NULL; // do font name substitution for various aliases of the Base 14 font // names base14 = NULL; if (name) { name2 = name->copy(); i = 0; while (i < name2->getLength()) { if (name2->getChar(i) == ' ') { name2->del(i); } else { ++i; } } a = 0; b = sizeof(base14FontMap) / sizeof(Base14FontMapEntry); // invariant: base14FontMap[a].altName <= name2 < base14FontMap[b].altName while (b - a > 1) { m = (a + b) / 2; if (name2->cmp(base14FontMap[m].altName) >= 0) { a = m; } else { b = m; } } if (!name2->cmp(base14FontMap[a].altName)) { base14 = &base14FontMap[a]; } delete name2; } // is it a built-in font? builtinFont = NULL; if (base14) { for (i = 0; i < nBuiltinFonts; ++i) { if (!strcmp(base14->base14Name, builtinFonts[i].name)) { builtinFont = &builtinFonts[i]; break; } } } // default ascent/descent values if (builtinFont) { ascent = 0.001 * builtinFont->ascent; descent = 0.001 * builtinFont->descent; fontBBox[0] = 0.001 * builtinFont->bbox[0]; fontBBox[1] = 0.001 * builtinFont->bbox[1]; fontBBox[2] = 0.001 * builtinFont->bbox[2]; fontBBox[3] = 0.001 * builtinFont->bbox[3]; } else { ascent = 0.75; descent = -0.25; fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; } // get info from font descriptor readFontDescriptor(xref, fontDict); // for non-embedded fonts, don't trust the ascent/descent/bbox // values from the font descriptor if (builtinFont && embFontID.num < 0) { ascent = 0.001 * builtinFont->ascent; descent = 0.001 * builtinFont->descent; fontBBox[0] = 0.001 * builtinFont->bbox[0]; fontBBox[1] = 0.001 * builtinFont->bbox[1]; fontBBox[2] = 0.001 * builtinFont->bbox[2]; fontBBox[3] = 0.001 * builtinFont->bbox[3]; } // get font matrix fontMat[0] = fontMat[3] = 1; fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; if (fontDict->lookup("FontMatrix", &obj1)->isArray()) { for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { fontMat[i] = obj2.getNum(); } obj2.free(); } } obj1.free(); // get Type 3 bounding box, font definition, and resources if (type == fontType3) { if (fontDict->lookup("FontBBox", &obj1)->isArray()) { for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) { if (obj1.arrayGet(i, &obj2)->isNum()) { fontBBox[i] = obj2.getNum(); } obj2.free(); } } obj1.free(); if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) { error(errSyntaxError, -1, "Missing or invalid CharProcs dictionary in Type 3 font"); charProcs.free(); } if (!fontDict->lookup("Resources", &resources)->isDict()) { resources.free(); } } //----- build the font encoding ----- // Encodings start with a base encoding, which can come from // (in order of priority): // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding // - MacRoman / MacExpert / WinAnsi / Standard // 2. embedded or external font file // 3. default: // - builtin --> builtin encoding // - TrueType --> WinAnsiEncoding // - others --> StandardEncoding // and then add a list of differences (if any) from // FontDict.Encoding.Differences. // check FontDict for base encoding hasEncoding = gFalse; usesMacRomanEnc = gFalse; baseEnc = NULL; baseEncFromFontFile = gFalse; fontDict->lookup("Encoding", &obj1); if (obj1.isDict()) { obj1.dictLookup("BaseEncoding", &obj2); if (obj2.isName("MacRomanEncoding")) { hasEncoding = gTrue; usesMacRomanEnc = gTrue; baseEnc = macRomanEncoding; } else if (obj2.isName("MacExpertEncoding")) { hasEncoding = gTrue; baseEnc = macExpertEncoding; } else if (obj2.isName("WinAnsiEncoding")) { hasEncoding = gTrue; baseEnc = winAnsiEncoding; } obj2.free(); } else if (obj1.isName("MacRomanEncoding")) { hasEncoding = gTrue; usesMacRomanEnc = gTrue; baseEnc = macRomanEncoding; } else if (obj1.isName("MacExpertEncoding")) { hasEncoding = gTrue; baseEnc = macExpertEncoding; } else if (obj1.isName("WinAnsiEncoding")) { hasEncoding = gTrue; baseEnc = winAnsiEncoding; } // check embedded font file for base encoding // (only for Type 1 fonts - trying to get an encoding out of a // TrueType font is a losing proposition) ffT1 = NULL; ffT1C = NULL; buf = NULL; if (type == fontType1 && embFontID.num >= 0) { if ((buf = readEmbFontFile(xref, &len))) { if ((ffT1 = FoFiType1::make(buf, len))) { if (ffT1->getName()) { if (embFontName) { delete embFontName; } embFontName = new GString(ffT1->getName()); } if (!baseEnc) { baseEnc = (const char **)ffT1->getEncoding(); baseEncFromFontFile = gTrue; } } gfree(buf); } } else if (type == fontType1C && embFontID.num >= 0) { if ((buf = readEmbFontFile(xref, &len))) { if ((ffT1C = FoFiType1C::make(buf, len))) { if (ffT1C->getName()) { if (embFontName) { delete embFontName; } embFontName = new GString(ffT1C->getName()); } if (!baseEnc) { baseEnc = (const char **)ffT1C->getEncoding(); baseEncFromFontFile = gTrue; } } gfree(buf); } } // get default base encoding if (!baseEnc) { if (builtinFont && embFontID.num < 0) { baseEnc = builtinFont->defaultBaseEnc; hasEncoding = gTrue; } else if (type == fontTrueType) { baseEnc = winAnsiEncoding; } else { baseEnc = standardEncoding; } } // copy the base encoding for (i = 0; i < 256; ++i) { enc[i] = (char *)baseEnc[i]; if ((encFree[i] = baseEncFromFontFile) && enc[i]) { enc[i] = copyString(baseEnc[i]); } } // some Type 1C font files have empty encodings, which can break the // T1C->T1 conversion (since the 'seac' operator depends on having // the accents in the encoding), so we fill in any gaps from // StandardEncoding if (type == fontType1C && embFontID.num >= 0 && baseEncFromFontFile) { for (i = 0; i < 256; ++i) { if (!enc[i] && standardEncoding[i]) { enc[i] = (char *)standardEncoding[i]; encFree[i] = gFalse; } } } // merge differences into encoding if (obj1.isDict()) { obj1.dictLookup("Differences", &obj2); if (obj2.isArray()) { hasEncoding = gTrue; code = 0; for (i = 0; i < obj2.arrayGetLength(); ++i) { obj2.arrayGet(i, &obj3); if (obj3.isInt()) { code = obj3.getInt(); } else if (obj3.isName()) { if (code >= 0 && code < 256) { if (encFree[code]) { gfree(enc[code]); } enc[code] = copyString(obj3.getName()); encFree[code] = gTrue; } ++code; } else { error(errSyntaxError, -1, "Wrong type in font encoding resource differences ({0:s})", obj3.getTypeName()); } obj3.free(); } } obj2.free(); } obj1.free(); if (ffT1) { delete ffT1; } if (ffT1C) { delete ffT1C; } //----- build the mapping to Unicode ----- // pass 1: use the name-to-Unicode mapping table missing = hex = gFalse; for (code = 0; code < 256; ++code) { if ((charName = enc[code])) { if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) && strcmp(charName, ".notdef")) { // if it wasn't in the name-to-Unicode table, check for a // name that looks like 'Axx' or 'xx', where 'A' is any letter // and 'xx' is two hex digits if ((strlen(charName) == 3 && isalpha(charName[0]) && isxdigit(charName[1]) && isxdigit(charName[2]) && ((charName[1] >= 'a' && charName[1] <= 'f') || (charName[1] >= 'A' && charName[1] <= 'F') || (charName[2] >= 'a' && charName[2] <= 'f') || (charName[2] >= 'A' && charName[2] <= 'F'))) || (strlen(charName) == 2 && isxdigit(charName[0]) && isxdigit(charName[1]) && ((charName[0] >= 'a' && charName[0] <= 'f') || (charName[0] >= 'A' && charName[0] <= 'F') || (charName[1] >= 'a' && charName[1] <= 'f') || (charName[1] >= 'A' && charName[1] <= 'F')))) { hex = gTrue; } missing = gTrue; } } else { toUnicode[code] = 0; } } // pass 2: try to fill in the missing chars, looking for names of // any of the following forms: // - 'xx' // - 'Axx' // - 'nn' // - 'Ann' // - 'ABnn' // - 'unixxxx' (possibly followed by garbage - some Arabic files // use 'uni0628.medi', etc.) // where 'A' and 'B' are any letters, 'xx' is two hex digits, 'xxxx' // is four hex digits, and 'nn' is 2-4 decimal digits if (missing && globalParams->getMapNumericCharNames()) { for (code = 0; code < 256; ++code) { if ((charName = enc[code]) && !toUnicode[code] && strcmp(charName, ".notdef")) { n = (int)strlen(charName); code2 = -1; if (hex && n == 3 && isalpha(charName[0]) && isxdigit(charName[1]) && isxdigit(charName[2])) { sscanf(charName+1, "%x", &code2); } else if (hex && n == 2 && isxdigit(charName[0]) && isxdigit(charName[1])) { sscanf(charName, "%x", &code2); } else if (!hex && n >= 2 && n <= 4 && isdigit(charName[0]) && isdigit(charName[1])) { code2 = atoi(charName); } else if (n >= 3 && n <= 5 && isdigit(charName[1]) && isdigit(charName[2])) { code2 = atoi(charName+1); } else if (n >= 4 && n <= 6 && isdigit(charName[2]) && isdigit(charName[3])) { code2 = atoi(charName+2); } else if (n >= 7 && charName[0] == 'u' && charName[1] == 'n' && charName[2] == 'i' && isxdigit(charName[3]) && isxdigit(charName[4]) && isxdigit(charName[5]) && isxdigit(charName[6])) { sscanf(charName + 3, "%x", &code2); } if (code2 >= 0 && code2 <= 0xffff) { toUnicode[code] = (Unicode)code2; } } } // if the 'mapUnknownCharNames' flag is set, do a simple pass-through // mapping for unknown character names } else if (missing && globalParams->getMapUnknownCharNames()) { for (code = 0; code < 256; ++code) { if (!toUnicode[code]) { toUnicode[code] = code; } } } // construct the char code -> Unicode mapping object ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode); // merge in a ToUnicode CMap, if there is one -- this overwrites // existing entries in ctu, i.e., the ToUnicode CMap takes // precedence, but the other encoding info is allowed to fill in any // holes readToUnicodeCMap(fontDict, 8, ctu); // look for a Unicode-to-Unicode mapping if (name && (utu = globalParams->getUnicodeToUnicode(name))) { for (i = 0; i < 256; ++i) { toUnicode[i] = 0; } ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode); for (i = 0; i < 256; ++i) { n = ctu->mapToUnicode((CharCode)i, uBuf, 8); if (n >= 1) { n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8); if (n >= 1) { ctu2->setMapping((CharCode)i, uBuf, n); } } } utu->decRefCnt(); delete ctu; ctu = ctu2; } //----- get the character widths ----- // initialize all widths for (code = 0; code < 256; ++code) { widths[code] = missingWidth * 0.001; } // use widths from font dict, if present fontDict->lookup("FirstChar", &obj1); firstChar = obj1.isInt() ? obj1.getInt() : 0; obj1.free(); if (firstChar < 0 || firstChar > 255) { firstChar = 0; } fontDict->lookup("LastChar", &obj1); lastChar = obj1.isInt() ? obj1.getInt() : 255; obj1.free(); if (lastChar < 0 || lastChar > 255) { lastChar = 255; } mul = (type == fontType3) ? fontMat[0] : 0.001; fontDict->lookup("Widths", &obj1); if (obj1.isArray()) { flags |= fontFixedWidth; if (obj1.arrayGetLength() < lastChar - firstChar + 1) { lastChar = firstChar + obj1.arrayGetLength() - 1; } for (code = firstChar; code <= lastChar; ++code) { obj1.arrayGet(code - firstChar, &obj2); if (obj2.isNum()) { widths[code] = obj2.getNum() * mul; if (fabs(widths[code] - widths[firstChar]) > 0.00001) { flags &= ~fontFixedWidth; } } obj2.free(); } // use widths from built-in font } else if (builtinFont) { // this is a kludge for broken PDF files that encode char 32 // as .notdef if (builtinFont->widths->getWidth("space", &w)) { widths[32] = 0.001 * w; } for (code = 0; code < 256; ++code) { if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) { widths[code] = 0.001 * w; } } // couldn't find widths -- use defaults } else { // this is technically an error -- the Widths entry is required // for all but the Base-14 fonts -- but certain PDF generators // apparently don't include widths for Arial and TimesNewRoman if (isFixedWidth()) { i = 0; } else if (isSerif()) { i = 8; } else { i = 4; } if (isBold()) { i += 2; } if (isItalic()) { i += 1; } builtinFont = builtinFontSubst[i]; // this is a kludge for broken PDF files that encode char 32 // as .notdef if (builtinFont->widths->getWidth("space", &w)) { widths[32] = 0.001 * w; } for (code = 0; code < 256; ++code) { if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) { widths[code] = 0.001 * w; } } } obj1.free(); ok = gTrue; } Gfx8BitFont::~Gfx8BitFont() { int i; for (i = 0; i < 256; ++i) { if (encFree[i] && enc[i]) { gfree(enc[i]); } } ctu->decRefCnt(); if (charProcs.isDict()) { charProcs.free(); } if (resources.isDict()) { resources.free(); } } int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code, Unicode *u, int uSize, int *uLen, double *dx, double *dy, double *ox, double *oy) { CharCode c; *code = c = (CharCode)(*s & 0xff); *uLen = ctu->mapToUnicode(c, u, uSize); *dx = widths[c]; *dy = *ox = *oy = 0; return 1; } CharCodeToUnicode *Gfx8BitFont::getToUnicode() { ctu->incRefCnt(); return ctu; } int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) { int *map; int cmapPlatform, cmapEncoding; int unicodeCmap, macRomanCmap, msSymbolCmap, cmap; GBool useMacRoman, useUnicode; char *charName; Unicode u; int code, i, n; map = (int *)gmallocn(256, sizeof(int)); for (i = 0; i < 256; ++i) { map[i] = 0; } // To match up with the Adobe-defined behaviour, we choose a cmap // like this: // 1. If the PDF font has an encoding: // 1a. If the PDF font specified MacRomanEncoding and the // TrueType font has a Macintosh Roman cmap, use it, and // reverse map the char names through MacRomanEncoding to // get char codes. // 1b. If the PDF font is not symbolic or the PDF font is not // embedded, and the TrueType font has a Microsoft Unicode // cmap or a non-Microsoft Unicode cmap, use it, and use the // Unicode indexes, not the char codes. // 1c. If the PDF font is symbolic and the TrueType font has a // Microsoft Symbol cmap, use it, and use char codes // directly (possibly with an offset of 0xf000). // 1d. If the TrueType font has a Macintosh Roman cmap, use it, // as in case 1a. // 2. If the PDF font does not have an encoding or the PDF font is // symbolic: // 2a. If the TrueType font has a Macintosh Roman cmap, use it, // and use char codes directly (possibly with an offset of // 0xf000). // 2b. If the TrueType font has a Microsoft Symbol cmap, use it, // and use char codes directly (possible with an offset of // 0xf000). // 3. If none of these rules apply, use the first cmap and hope for // the best (this shouldn't happen). unicodeCmap = macRomanCmap = msSymbolCmap = -1; for (i = 0; i < ff->getNumCmaps(); ++i) { cmapPlatform = ff->getCmapPlatform(i); cmapEncoding = ff->getCmapEncoding(i); if ((cmapPlatform == 3 && cmapEncoding == 1) || cmapPlatform == 0) { unicodeCmap = i; } else if (cmapPlatform == 1 && cmapEncoding == 0) { macRomanCmap = i; } else if (cmapPlatform == 3 && cmapEncoding == 0) { msSymbolCmap = i; } } cmap = 0; useMacRoman = gFalse; useUnicode = gFalse; if (hasEncoding) { if (usesMacRomanEnc && macRomanCmap >= 0) { cmap = macRomanCmap; useMacRoman = gTrue; } else if ((!(flags & fontSymbolic) || embFontID.num < 0) && unicodeCmap >= 0) { cmap = unicodeCmap; useUnicode = gTrue; } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) { cmap = msSymbolCmap; } else if ((flags & fontSymbolic) && macRomanCmap >= 0) { cmap = macRomanCmap; } else if (macRomanCmap >= 0) { cmap = macRomanCmap; useMacRoman = gTrue; } } else { if (msSymbolCmap >= 0) { cmap = msSymbolCmap; } else if (macRomanCmap >= 0) { cmap = macRomanCmap; } } // reverse map the char names through MacRomanEncoding, then map the // char codes through the cmap if (useMacRoman) { for (i = 0; i < 256; ++i) { if ((charName = enc[i])) { if ((code = globalParams->getMacRomanCharCode(charName))) { map[i] = ff->mapCodeToGID(cmap, code); } } else { map[i] = -1; } } // map Unicode through the cmap } else if (useUnicode) { for (i = 0; i < 256; ++i) { if (((charName = enc[i]) && (u = globalParams->mapNameToUnicode(charName))) || (n = ctu->mapToUnicode((CharCode)i, &u, 1))) { map[i] = ff->mapCodeToGID(cmap, u); } else { map[i] = -1; } } // map the char codes through the cmap, possibly with an offset of // 0xf000 } else { for (i = 0; i < 256; ++i) { if (!(map[i] = ff->mapCodeToGID(cmap, i))) { map[i] = ff->mapCodeToGID(cmap, 0xf000 + i); } } } // try the TrueType 'post' table to handle any unmapped characters for (i = 0; i < 256; ++i) { if (map[i] <= 0 && (charName = enc[i])) { map[i] = ff->mapNameToGID(charName); } } return map; } Dict *Gfx8BitFont::getCharProcs() { return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL; } Object *Gfx8BitFont::getCharProc(int code, Object *proc) { if (enc[code] && charProcs.isDict()) { charProcs.dictLookup(enc[code], proc); } else { proc->initNull(); } return proc; } Object *Gfx8BitFont::getCharProcNF(int code, Object *proc) { if (enc[code] && charProcs.isDict()) { charProcs.dictLookupNF(enc[code], proc); } else { proc->initNull(); } return proc; } Dict *Gfx8BitFont::getResources() { return resources.isDict() ? resources.getDict() : (Dict *)NULL; } //------------------------------------------------------------------------ // GfxCIDFont //------------------------------------------------------------------------ #if HAVE_STD_SORT struct cmpWidthExcepFunctor { bool operator()(const GfxFontCIDWidthExcep &w1, const GfxFontCIDWidthExcep &w2) { return w1.first < w2.first; } }; struct cmpWidthExcepVFunctor { bool operator()(const GfxFontCIDWidthExcepV &w1, const GfxFontCIDWidthExcepV &w2) { return w1.first < w2.first; } }; #else // HAVE_STD_SORT static int CDECL cmpWidthExcep(const void *w1, const void *w2) { return ((GfxFontCIDWidthExcep *)w1)->first - ((GfxFontCIDWidthExcep *)w2)->first; } static int CDECL cmpWidthExcepV(const void *w1, const void *w2) { return ((GfxFontCIDWidthExcepV *)w1)->first - ((GfxFontCIDWidthExcepV *)w2)->first; } #endif GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict): GfxFont(tagA, idA, nameA, typeA, embFontIDA) { Dict *desFontDict; Object desFontDictObj; Object obj1, obj2, obj3, obj4, obj5, obj6; CharCodeToUnicode *utu; CharCode c; Unicode uBuf[8]; int c1, c2; int excepsSize, i, j, k, n; ascent = 0.95; descent = -0.35; fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; collection = NULL; cMap = NULL; ctu = NULL; ctuUsesCharCode = gTrue; widths.defWidth = 1.0; widths.defHeight = -1.0; widths.defVY = 0.880; widths.exceps = NULL; widths.nExceps = 0; widths.excepsV = NULL; widths.nExcepsV = 0; cidToGID = NULL; cidToGIDLen = 0; // get the descendant font if (!fontDict->lookup("DescendantFonts", &obj1)->isArray() || obj1.arrayGetLength() == 0) { error(errSyntaxError, -1, "Missing or empty DescendantFonts entry in Type 0 font"); obj1.free(); goto err1; } if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) { error(errSyntaxError, -1, "Bad descendant font in Type 0 font"); goto err2; } obj1.free(); desFontDict = desFontDictObj.getDict(); // get info from font descriptor readFontDescriptor(xref, desFontDict); //----- encoding info ----- // char collection if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) { error(errSyntaxError, -1, "Missing CIDSystemInfo dictionary in Type 0 descendant font"); goto err2; } obj1.dictLookup("Registry", &obj2); obj1.dictLookup("Ordering", &obj3); if (!obj2.isString() || !obj3.isString()) { error(errSyntaxError, -1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); goto err3; } collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); obj3.free(); obj2.free(); obj1.free(); // look for a ToUnicode CMap if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) { ctuUsesCharCode = gFalse; // use an identity mapping for the "Adobe-Identity" and // "Adobe-UCS" collections if (!collection->cmp("Adobe-Identity") || !collection->cmp("Adobe-UCS")) { ctu = CharCodeToUnicode::makeIdentityMapping(); // look for a user-supplied .cidToUnicode file } else if (!(ctu = globalParams->getCIDToUnicode(collection))) { error(errSyntaxError, -1, "Unknown character collection '{0:t}'", collection); } } // look for a Unicode-to-Unicode mapping if (name && (utu = globalParams->getUnicodeToUnicode(name))) { if (ctu) { for (c = 0; c < ctu->getLength(); ++c) { n = ctu->mapToUnicode(c, uBuf, 8); if (n >= 1) { n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8); if (n >= 1) { ctu->setMapping(c, uBuf, n); } } } utu->decRefCnt(); } else { ctu = utu; } } // encoding (i.e., CMap) if (fontDict->lookup("Encoding", &obj1)->isNull()) { error(errSyntaxError, -1, "Missing Encoding entry in Type 0 font"); goto err2; } if (!(cMap = CMap::parse(NULL, collection, &obj1))) { goto err2; } obj1.free(); // CIDToGIDMap // (the PDF spec only allows these for TrueType fonts, but Acrobat // apparently also allows them for OpenType CFF fonts) if (type == fontCIDType2 || type == fontCIDType0COT) { desFontDict->lookup("CIDToGIDMap", &obj1); if (obj1.isStream()) { cidToGIDLen = 0; i = 64; cidToGID = (int *)gmallocn(i, sizeof(int)); obj1.streamReset(); while ((c1 = obj1.streamGetChar()) != EOF && (c2 = obj1.streamGetChar()) != EOF) { if (cidToGIDLen == i) { i *= 2; cidToGID = (int *)greallocn(cidToGID, i, sizeof(int)); } cidToGID[cidToGIDLen++] = (c1 << 8) + c2; } obj1.streamClose(); } else if (!obj1.isName("Identity") && !obj1.isNull()) { error(errSyntaxError, -1, "Invalid CIDToGIDMap entry in CID font"); } obj1.free(); } //----- character metrics ----- // default char width if (desFontDict->lookup("DW", &obj1)->isInt()) { widths.defWidth = obj1.getInt() * 0.001; } obj1.free(); // char width exceptions if (desFontDict->lookup("W", &obj1)->isArray()) { excepsSize = 0; i = 0; while (i + 1 < obj1.arrayGetLength()) { obj1.arrayGet(i, &obj2); obj1.arrayGet(i + 1, &obj3); if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) { if (obj1.arrayGet(i + 2, &obj4)->isNum()) { if (widths.nExceps == excepsSize) { excepsSize += 16; widths.exceps = (GfxFontCIDWidthExcep *) greallocn(widths.exceps, excepsSize, sizeof(GfxFontCIDWidthExcep)); } widths.exceps[widths.nExceps].first = obj2.getInt(); widths.exceps[widths.nExceps].last = obj3.getInt(); widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; ++widths.nExceps; } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); } obj4.free(); i += 3; } else if (obj2.isInt() && obj3.isArray()) { if (widths.nExceps + obj3.arrayGetLength() > excepsSize) { excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15; widths.exceps = (GfxFontCIDWidthExcep *) greallocn(widths.exceps, excepsSize, sizeof(GfxFontCIDWidthExcep)); } j = obj2.getInt(); for (k = 0; k < obj3.arrayGetLength(); ++k) { if (obj3.arrayGet(k, &obj4)->isNum()) { widths.exceps[widths.nExceps].first = j; widths.exceps[widths.nExceps].last = j; widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; ++j; ++widths.nExceps; } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); } obj4.free(); } i += 2; } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); ++i; } obj3.free(); obj2.free(); } #if HAVE_STD_SORT std::sort(widths.exceps, widths.exceps + widths.nExceps, cmpWidthExcepFunctor()); #else qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep), &cmpWidthExcep); #endif } obj1.free(); // default metrics for vertical font if (desFontDict->lookup("DW2", &obj1)->isArray() && obj1.arrayGetLength() == 2) { if (obj1.arrayGet(0, &obj2)->isNum()) { widths.defVY = obj2.getNum() * 0.001; } obj2.free(); if (obj1.arrayGet(1, &obj2)->isNum()) { widths.defHeight = obj2.getNum() * 0.001; } obj2.free(); } obj1.free(); // char metric exceptions for vertical font if (desFontDict->lookup("W2", &obj1)->isArray()) { excepsSize = 0; i = 0; while (i + 1 < obj1.arrayGetLength()) { obj1.arrayGet(i, &obj2); obj1.arrayGet(i+ 1, &obj3); if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) { if (obj1.arrayGet(i + 2, &obj4)->isNum() && obj1.arrayGet(i + 3, &obj5)->isNum() && obj1.arrayGet(i + 4, &obj6)->isNum()) { if (widths.nExcepsV == excepsSize) { excepsSize += 16; widths.excepsV = (GfxFontCIDWidthExcepV *) greallocn(widths.excepsV, excepsSize, sizeof(GfxFontCIDWidthExcepV)); } widths.excepsV[widths.nExcepsV].first = obj2.getInt(); widths.excepsV[widths.nExcepsV].last = obj3.getInt(); widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001; widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001; widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001; ++widths.nExcepsV; } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); } obj6.free(); obj5.free(); obj4.free(); i += 5; } else if (obj2.isInt() && obj3.isArray()) { if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) { excepsSize = (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15; widths.excepsV = (GfxFontCIDWidthExcepV *) greallocn(widths.excepsV, excepsSize, sizeof(GfxFontCIDWidthExcepV)); } j = obj2.getInt(); for (k = 0; k < obj3.arrayGetLength(); k += 3) { if (obj3.arrayGet(k, &obj4)->isNum() && obj3.arrayGet(k+1, &obj5)->isNum() && obj3.arrayGet(k+2, &obj6)->isNum()) { widths.excepsV[widths.nExcepsV].first = j; widths.excepsV[widths.nExcepsV].last = j; widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001; widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001; widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001; ++j; ++widths.nExcepsV; } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); } obj6.free(); obj5.free(); obj4.free(); } i += 2; } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); ++i; } obj3.free(); obj2.free(); } #if HAVE_STD_SORT std::sort(widths.excepsV, widths.excepsV + widths.nExcepsV, cmpWidthExcepVFunctor()); #else qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV), &cmpWidthExcepV); #endif } obj1.free(); desFontDictObj.free(); ok = gTrue; return; err3: obj3.free(); obj2.free(); err2: obj1.free(); desFontDictObj.free(); err1: error(errSyntaxError, -1, "Failed to parse font object for '{0:t}'", name); } GfxCIDFont::~GfxCIDFont() { if (collection) { delete collection; } if (cMap) { cMap->decRefCnt(); } if (ctu) { ctu->decRefCnt(); } gfree(widths.exceps); gfree(widths.excepsV); if (cidToGID) { gfree(cidToGID); } } int GfxCIDFont::getNextChar(char *s, int len, CharCode *code, Unicode *u, int uSize, int *uLen, double *dx, double *dy, double *ox, double *oy) { CID cid; CharCode c; double w, h, vx, vy; int n, a, b, m; if (!cMap) { *code = 0; *uLen = 0; *dx = *dy = 0; return 1; } *code = (CharCode)(cid = cMap->getCID(s, len, &c, &n)); if (ctu) { *uLen = ctu->mapToUnicode(ctuUsesCharCode ? c : cid, u, uSize); } else { *uLen = 0; } if (!*uLen && uSize >= 1 && globalParams->getMapUnknownCharNames()) { u[0] = *code; *uLen = 1; } // horizontal if (cMap->getWMode() == 0) { w = widths.defWidth; h = vx = vy = 0; if (widths.nExceps > 0 && cid >= widths.exceps[0].first) { a = 0; b = widths.nExceps; // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first while (b - a > 1) { m = (a + b) / 2; if (widths.exceps[m].first <= cid) { a = m; } else { b = m; } } if (cid <= widths.exceps[a].last) { w = widths.exceps[a].width; } } // vertical } else { w = 0; h = widths.defHeight; vx = widths.defWidth / 2; vy = widths.defVY; if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) { a = 0; b = widths.nExcepsV; // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first while (b - a > 1) { m = (a + b) / 2; if (widths.excepsV[m].last <= cid) { a = m; } else { b = m; } } if (cid <= widths.excepsV[a].last) { h = widths.excepsV[a].height; vx = widths.excepsV[a].vx; vy = widths.excepsV[a].vy; } } } *dx = w; *dy = h; *ox = vx; *oy = vy; return n; } int GfxCIDFont::getWMode() { return cMap ? cMap->getWMode() : 0; } CharCodeToUnicode *GfxCIDFont::getToUnicode() { if (ctu) { ctu->incRefCnt(); } return ctu; } GString *GfxCIDFont::getCollection() { return cMap ? cMap->getCollection() : (GString *)NULL; } //------------------------------------------------------------------------ // GfxFontDict //------------------------------------------------------------------------ GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) { int i; Object obj1, obj2; Ref r; numFonts = fontDict->getLength(); fonts = (GfxFont **)gmallocn(numFonts, sizeof(GfxFont *)); for (i = 0; i < numFonts; ++i) { fontDict->getValNF(i, &obj1); obj1.fetch(xref, &obj2); if (obj2.isDict()) { if (obj1.isRef()) { r = obj1.getRef(); } else { // no indirect reference for this font, so invent a unique one // (legal generation numbers are five digits, so any 6-digit // number would be safe) r.num = i; if (fontDictRef) { r.gen = 100000 + fontDictRef->num; } else { r.gen = 999999; } } fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i), r, obj2.getDict()); if (fonts[i] && !fonts[i]->isOk()) { delete fonts[i]; fonts[i] = NULL; } } else { error(errSyntaxError, -1, "font resource is not a dictionary"); fonts[i] = NULL; } obj1.free(); obj2.free(); } } GfxFontDict::~GfxFontDict() { int i; for (i = 0; i < numFonts; ++i) { if (fonts[i]) { delete fonts[i]; } } gfree(fonts); } GfxFont *GfxFontDict::lookup(char *tag) { int i; for (i = 0; i < numFonts; ++i) { if (fonts[i] && fonts[i]->matches(tag)) { return fonts[i]; } } return NULL; } GfxFont *GfxFontDict::lookupByRef(Ref ref) { int i; for (i = 0; i < numFonts; ++i) { if (fonts[i] && fonts[i]->getID()->num == ref.num && fonts[i]->getID()->gen == ref.gen) { return fonts[i]; } } return NULL; } xpdf-3.04/xpdf/TextString.cc0000644000076400007640000000643612341430012015275 0ustar dereknderekn//======================================================================== // // 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; } xpdf-3.04/xpdf/OutputDev.cc0000644000076400007640000000773612341430012015125 0ustar dereknderekn//======================================================================== // // OutputDev.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "Object.h" #include "Stream.h" #include "GfxState.h" #include "OutputDev.h" //------------------------------------------------------------------------ // OutputDev //------------------------------------------------------------------------ void OutputDev::setDefaultCTM(double *ctm) { int i; double det; for (i = 0; i < 6; ++i) { defCTM[i] = ctm[i]; } det = 1 / (defCTM[0] * defCTM[3] - defCTM[1] * defCTM[2]); defICTM[0] = defCTM[3] * det; defICTM[1] = -defCTM[1] * det; defICTM[2] = -defCTM[2] * det; defICTM[3] = defCTM[0] * det; defICTM[4] = (defCTM[2] * defCTM[5] - defCTM[3] * defCTM[4]) * det; defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det; } void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) { *ux = defICTM[0] * dx + defICTM[2] * dy + defICTM[4]; *uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5]; } void OutputDev::cvtUserToDev(double ux, double uy, 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); } void OutputDev::updateAll(GfxState *state) { updateLineDash(state); updateFlatness(state); updateLineJoin(state); updateLineCap(state); updateMiterLimit(state); updateLineWidth(state); updateStrokeAdjust(state); updateFillColorSpace(state); updateFillColor(state); updateStrokeColorSpace(state); updateStrokeColor(state); updateBlendMode(state); updateFillOpacity(state); updateStrokeOpacity(state); updateFillOverprint(state); updateStrokeOverprint(state); updateOverprintMode(state); updateTransfer(state); updateFont(state); } GBool OutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) { return gFalse; } void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { if (inlineImg) { str->reset(); str->discardChars(height * ((width + 7) / 8)); str->close(); } } void OutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, 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, GBool interpolate) { if (inlineImg) { str->reset(); str->discardChars(height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8)); str->close(); } } void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { drawImage(state, ref, str, width, height, colorMap, NULL, gFalse, interpolate); } void OutputDev::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); } #if OPI_SUPPORT void OutputDev::opiBegin(GfxState *state, Dict *opiDict) { } void OutputDev::opiEnd(GfxState *state, Dict *opiDict) { } #endif xpdf-3.04/xpdf/xpdf.cc0000644000076400007640000002374012341430012014120 0ustar dereknderekn//======================================================================== // // xpdf.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #include "gtypes.h" #include "GString.h" #include "parseargs.h" #include "gfile.h" #include "gmem.h" #include "GlobalParams.h" #include "Object.h" #include "XPDFApp.h" #include "config.h" //------------------------------------------------------------------------ // command line options //------------------------------------------------------------------------ static GBool contView = gFalse; static char enableFreeTypeStr[16] = ""; static char antialiasStr[16] = ""; static char vectorAntialiasStr[16] = ""; static char psFileArg[256]; static char paperSize[15] = ""; static int paperWidth = 0; static int paperHeight = 0; static GBool level1 = gFalse; static char textEncName[128] = ""; static char textEOL[16] = ""; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static GBool fullScreen = gFalse; static char remoteName[100] = "xpdf_"; static char remoteCmd[512] = ""; static GBool doRemoteReload = gFalse; static GBool doRemoteRaise = gFalse; static GBool doRemoteQuit = gFalse; static GBool printCommands = gFalse; static GBool quiet = gFalse; static char cfgFileName[256] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-g", argStringDummy, NULL, 0, "initial window geometry"}, {"-geometry", argStringDummy, NULL, 0, "initial window geometry"}, {"-title", argStringDummy, NULL, 0, "window title"}, {"-cmap", argFlagDummy, NULL, 0, "install a private colormap"}, {"-rgb", argIntDummy, NULL, 0, "biggest RGB cube to allocate (default is 5)"}, {"-rv", argFlagDummy, NULL, 0, "reverse video"}, {"-papercolor", argStringDummy, NULL, 0, "color of paper background"}, {"-z", argStringDummy, NULL, 0, "initial zoom level (percent, 'page', 'width')"}, {"-cont", argFlag, &contView, 0, "start in continuous view mode" }, #if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H {"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), "enable FreeType font rasterizer: yes, no"}, #endif {"-aa", argString, antialiasStr, sizeof(antialiasStr), "enable font anti-aliasing: yes, no"}, {"-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr), "enable vector anti-aliasing: yes, no"}, {"-ps", argString, psFileArg, sizeof(psFileArg), "default PostScript file name or command"}, {"-paper", argString, paperSize, sizeof(paperSize), "paper size (letter, legal, A4, A3, match)"}, {"-paperw", argInt, &paperWidth, 0, "paper width, in points"}, {"-paperh", argInt, &paperHeight, 0, "paper height, in points"}, {"-level1", argFlag, &level1, 0, "generate Level 1 PostScript"}, {"-enc", argString, textEncName, sizeof(textEncName), "output text encoding name"}, {"-eol", argString, textEOL, sizeof(textEOL), "output end-of-line convention (unix, dos, or mac)"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-fullscreen", argFlag, &fullScreen, 0, "run in full-screen (presentation) mode"}, {"-remote", argString, remoteName + 5, sizeof(remoteName) - 5, "start/contact xpdf remote server with specified name"}, {"-exec", argString, remoteCmd, sizeof(remoteCmd), "execute command on xpdf remote server (with -remote only)"}, {"-reload", argFlag, &doRemoteReload, 0, "reload xpdf remote server window (with -remote only)"}, {"-raise", argFlag, &doRemoteRaise, 0, "raise xpdf remote server window (with -remote only)"}, {"-quit", argFlag, &doRemoteQuit, 0, "kill xpdf remote server (with -remote only)"}, {"-cmd", argFlag, &printCommands, 0, "print commands as they're executed"}, {"-q", argFlag, &quiet, 0, "don't print any messages or errors"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; //------------------------------------------------------------------------ int main(int argc, char *argv[]) { XPDFApp *app; GString *fileName; int pg; GString *destName; GString *userPasswordStr, *ownerPasswordStr; GBool ok; int exitCode; exitCode = 0; userPasswordStr = ownerPasswordStr = NULL; // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || printVersion || printHelp) { fprintf(stderr, "xpdf version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("xpdf", "[ [ | +]]", argDesc); } exitCode = 99; goto done0; } // read config file globalParams = new GlobalParams(cfgFileName); globalParams->setupBaseFonts(NULL); if (contView) { globalParams->setContinuousView(contView); } if (psFileArg[0]) { globalParams->setPSFile(psFileArg); } if (paperSize[0]) { if (!globalParams->setPSPaperSize(paperSize)) { fprintf(stderr, "Invalid paper size\n"); } } else { if (paperWidth) { globalParams->setPSPaperWidth(paperWidth); } if (paperHeight) { globalParams->setPSPaperHeight(paperHeight); } } if (level1) { globalParams->setPSLevel(psLevel1); } if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } if (textEOL[0]) { if (!globalParams->setTextEOL(textEOL)) { fprintf(stderr, "Bad '-eol' value on command line\n"); } } if (enableFreeTypeStr[0]) { if (!globalParams->setEnableFreeType(enableFreeTypeStr)) { fprintf(stderr, "Bad '-freetype' value on command line\n"); } } if (antialiasStr[0]) { if (!globalParams->setAntialias(antialiasStr)) { fprintf(stderr, "Bad '-aa' value on command line\n"); } } if (vectorAntialiasStr[0]) { if (!globalParams->setVectorAntialias(vectorAntialiasStr)) { fprintf(stderr, "Bad '-aaVector' value on command line\n"); } } if (printCommands) { globalParams->setPrintCommands(printCommands); } if (quiet) { globalParams->setErrQuiet(quiet); } // create the XPDFApp object app = new XPDFApp(&argc, argv); // the initialZoom parameter can be set in either the config file or // as an X resource (or command line arg) if (app->getInitialZoom()) { globalParams->setInitialZoom(app->getInitialZoom()->getCString()); } // check command line ok = ok && argc >= 1 && argc <= 3; if (remoteCmd[0]) { ok = ok && remoteName[5] && !doRemoteReload && !doRemoteRaise && !doRemoteQuit && argc == 1; } if (doRemoteReload) { ok = ok && remoteName[5] && !doRemoteQuit && argc == 1; } if (doRemoteRaise) { ok = ok && remoteName[5] && !doRemoteQuit; } if (doRemoteQuit) { ok = ok && remoteName[5] && argc == 1; } if (!ok || printVersion || printHelp) { fprintf(stderr, "xpdf version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("xpdf", "[ [ | +]]", argDesc); } exitCode = 99; goto done1; } if (argc >= 2) { fileName = new GString(argv[1]); } else { fileName = NULL; } pg = 1; destName = NULL; if (argc == 3) { if (argv[2][0] == '+') { destName = new GString(&argv[2][1]); } else { pg = atoi(argv[2]); if (pg < 0) { fprintf(stderr, "Invalid page number (%d)\n", pg); exitCode = 99; goto done2; } } } // handle remote server stuff if (remoteName[5]) { app->setRemoteName(remoteName); if (app->remoteServerRunning()) { if (fileName) { if (destName) { app->remoteOpenAtDest(fileName, destName, doRemoteRaise); } else { app->remoteOpen(fileName, pg, doRemoteRaise); } } else if (remoteCmd[0]) { app->remoteExec(remoteCmd); } else if (doRemoteReload) { app->remoteReload(doRemoteRaise); } else if (doRemoteRaise) { app->remoteRaise(); } else if (doRemoteQuit) { app->remoteQuit(); } goto done2; } if (doRemoteQuit) { goto done2; } } // set options app->setFullScreen(fullScreen); // check for password string(s) ownerPasswordStr = ownerPassword[0] != '\001' ? new GString(ownerPassword) : (GString *)NULL; userPasswordStr = userPassword[0] != '\001' ? new GString(userPassword) : (GString *)NULL; // open the file and run the main loop if (destName) { if (!app->openAtDest(fileName, destName, ownerPasswordStr, userPasswordStr)) { exitCode = 1; goto done2; } } else { if (!app->open(fileName, pg, ownerPasswordStr, userPasswordStr)) { exitCode = 1; goto done2; } } app->run(); exitCode = 0; // clean up done2: if (userPasswordStr) { delete userPasswordStr; } if (ownerPasswordStr) { delete ownerPasswordStr; } if (destName) { delete destName; } if (fileName) { delete fileName; } done1: delete app; delete globalParams; // check for memory leaks done0: Object::memCheck(stderr); gMemReport(stderr); return exitCode; } xpdf-3.04/xpdf/XRef.h0000644000076400007640000001026312341430012013661 0ustar dereknderekn//======================================================================== // // XRef.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef XREF_H #define XREF_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "gfile.h" #include "Object.h" class Dict; class Stream; class Parser; class ObjectStream; class XRefPosSet; //------------------------------------------------------------------------ // XRef //------------------------------------------------------------------------ enum XRefEntryType { xrefEntryFree, xrefEntryUncompressed, xrefEntryCompressed }; struct XRefEntry { GFileOffset offset; int gen; XRefEntryType type; }; struct XRefCacheEntry { int num; int gen; Object obj; }; #define xrefCacheSize 16 #define objStrCacheSize 4 class XRef { public: // Constructor. Read xref table from stream. XRef(BaseStream *strA, GBool repair); // Destructor. ~XRef(); // Is xref table valid? GBool isOk() { return ok; } // Get the error code (if isOk() returns false). int getErrorCode() { return errCode; } // Set the encryption parameters. void setEncryption(int permFlagsA, GBool ownerPasswordOkA, Guchar *fileKeyA, int keyLengthA, int encVersionA, CryptAlgorithm encAlgorithmA); // Is the file encrypted? GBool isEncrypted() { return encrypted; } // Check various permissions. GBool okToPrint(GBool ignoreOwnerPW = gFalse); GBool okToChange(GBool ignoreOwnerPW = gFalse); GBool okToCopy(GBool ignoreOwnerPW = gFalse); GBool okToAddNotes(GBool ignoreOwnerPW = gFalse); int getPermFlags() { return permFlags; } // Get catalog object. Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); } // Fetch an indirect reference. Object *fetch(int num, int gen, Object *obj, int recursion = 0); // Return the document's Info dictionary (if any). Object *getDocInfo(Object *obj); Object *getDocInfoNF(Object *obj); // Return the number of objects in the xref table. int getNumObjects() { return last + 1; } // Return the offset of the last xref table. GFileOffset getLastXRefPos() { return lastXRefPos; } // Return the catalog object reference. int getRootNum() { return rootNum; } int getRootGen() { return rootGen; } // Get end position for a stream in a damaged file. // Returns false if unknown or file is not damaged. GBool getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd); // Direct access. int getSize() { return size; } XRefEntry *getEntry(int i) { return &entries[i]; } Object *getTrailerDict() { return &trailerDict; } private: BaseStream *str; // input stream GFileOffset start; // offset in file (to allow for garbage // at beginning of file) XRefEntry *entries; // xref entries int size; // size of array int last; // last used index in int rootNum, rootGen; // catalog dict GBool ok; // true if xref table is valid int errCode; // error code (if is false) Object trailerDict; // trailer dictionary 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 * // cached object streams objStrs[objStrCacheSize]; GBool encrypted; // true if file is encrypted int permFlags; // permission bits GBool ownerPasswordOk; // true if owner password is correct Guchar fileKey[32]; // file decryption key int keyLength; // length of key, in bytes int encVersion; // encryption version CryptAlgorithm encAlgorithm; // encryption algorithm XRefCacheEntry // cache of recently accessed objects cache[xrefCacheSize]; 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, GFileOffset *pos); GBool constructXRef(); ObjectStream *getObjectStream(int objStrNum); GFileOffset strToFileOffset(char *s); }; #endif xpdf-3.04/xpdf/Parser.h0000644000076400007640000000303712341430012014252 0ustar dereknderekn//======================================================================== // // Parser.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PARSER_H #define PARSER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Lexer.h" //------------------------------------------------------------------------ // Parser //------------------------------------------------------------------------ class Parser { public: // Constructor. Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA); // Destructor. ~Parser(); // Get the next object from the input stream. If is // true, do not parse compound objects (arrays, dictionaries, or // streams). Object *getObj(Object *obj, GBool simpleOnly = gFalse, Guchar *fileKey = NULL, CryptAlgorithm encAlgorithm = cryptRC4, int keyLength = 0, int objNum = 0, int objGen = 0, int recursion = 0); // Get stream. Stream *getStream() { return lexer->getStream(); } // Get current position in file. GFileOffset getPos() { return lexer->getPos(); } private: XRef *xref; // the xref table for this PDF file Lexer *lexer; // input stream GBool allowStreams; // parse stream objects? Object buf1, buf2; // next two tokens int inlineImg; // set when inline image data is encountered Stream *makeStream(Object *dict, Guchar *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, int recursion); void shift(); }; #endif xpdf-3.04/xpdf/SecurityHandler.cc0000644000076400007640000002761312341430012016267 0ustar dereknderekn//======================================================================== // // SecurityHandler.cc // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "GString.h" #include "PDFDoc.h" #include "Decrypt.h" #include "Error.h" #include "GlobalParams.h" #include "PDFCore.h" #ifdef ENABLE_PLUGINS # include "XpdfPluginAPI.h" #endif #include "SecurityHandler.h" //------------------------------------------------------------------------ // SecurityHandler //------------------------------------------------------------------------ SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) { Object filterObj; SecurityHandler *secHdlr; #ifdef ENABLE_PLUGINS XpdfSecurityHandler *xsh; #endif encryptDictA->dictLookup("Filter", &filterObj); if (filterObj.isName("Standard")) { secHdlr = new StandardSecurityHandler(docA, encryptDictA); } else if (filterObj.isName()) { #ifdef ENABLE_PLUGINS if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) { secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh); } else { #endif error(errSyntaxError, -1, "Couldn't find the '{0:s}' security handler", filterObj.getName()); secHdlr = NULL; #ifdef ENABLE_PLUGINS } #endif } else { error(errSyntaxError, -1, "Missing or invalid 'Filter' entry in encryption dictionary"); secHdlr = NULL; } filterObj.free(); return secHdlr; } SecurityHandler::SecurityHandler(PDFDoc *docA) { doc = docA; } SecurityHandler::~SecurityHandler() { } GBool SecurityHandler::checkEncryption(GString *ownerPassword, GString *userPassword) { void *authData; GBool ok; int i; if (ownerPassword || userPassword) { authData = makeAuthData(ownerPassword, userPassword); } else { authData = NULL; } ok = authorize(authData); if (authData) { freeAuthData(authData); } for (i = 0; !ok && i < 3; ++i) { if (!(authData = getAuthData())) { break; } ok = authorize(authData); if (authData) { freeAuthData(authData); } } if (!ok) { error(errCommandLine, -1, "Incorrect password"); } return ok; } //------------------------------------------------------------------------ // StandardSecurityHandler //------------------------------------------------------------------------ class StandardAuthData { public: StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) { ownerPassword = ownerPasswordA; userPassword = userPasswordA; } ~StandardAuthData() { if (ownerPassword) { delete ownerPassword; } if (userPassword) { delete userPassword; } } GString *ownerPassword; GString *userPassword; }; StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA): SecurityHandler(docA) { Object versionObj, revisionObj, lengthObj; Object ownerKeyObj, userKeyObj, ownerEncObj, userEncObj; Object permObj, fileIDObj, fileIDObj1; Object cryptFiltersObj, streamFilterObj, stringFilterObj; Object cryptFilterObj, cfmObj, cfLengthObj; Object encryptMetadataObj; ok = gFalse; fileID = NULL; ownerKey = NULL; userKey = NULL; ownerEnc = NULL; userEnc = NULL; fileKeyLength = 0; encryptDictA->dictLookup("V", &versionObj); encryptDictA->dictLookup("R", &revisionObj); encryptDictA->dictLookup("Length", &lengthObj); encryptDictA->dictLookup("O", &ownerKeyObj); encryptDictA->dictLookup("U", &userKeyObj); encryptDictA->dictLookup("OE", &ownerEncObj); encryptDictA->dictLookup("UE", &userEncObj); encryptDictA->dictLookup("P", &permObj); doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj); if (versionObj.isInt() && revisionObj.isInt() && permObj.isInt() && ownerKeyObj.isString() && userKeyObj.isString()) { encVersion = versionObj.getInt(); encRevision = revisionObj.getInt(); if ((encRevision <= 4 && ownerKeyObj.getString()->getLength() == 32 && userKeyObj.getString()->getLength() == 32) || ((encRevision == 5 || encRevision == 6) && // the spec says 48 bytes, but Acrobat pads them out longer ownerKeyObj.getString()->getLength() >= 48 && userKeyObj.getString()->getLength() >= 48 && ownerEncObj.isString() && ownerEncObj.getString()->getLength() == 32 && userEncObj.isString() && userEncObj.getString()->getLength() == 32)) { encAlgorithm = cryptRC4; // revision 2 forces a 40-bit key - some buggy PDF generators // set the Length value incorrectly if (encRevision == 2 || !lengthObj.isInt()) { fileKeyLength = 5; } else { fileKeyLength = lengthObj.getInt() / 8; } encryptMetadata = gTrue; //~ this currently only handles a subset of crypt filter functionality //~ (in particular, it ignores the EFF entry in encryptDictA, and //~ 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 == 6)) { encryptDictA->dictLookup("CF", &cryptFiltersObj); encryptDictA->dictLookup("StmF", &streamFilterObj); encryptDictA->dictLookup("StrF", &stringFilterObj); if (cryptFiltersObj.isDict() && streamFilterObj.isName() && stringFilterObj.isName() && !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) { if (!strcmp(streamFilterObj.getName(), "Identity")) { // no encryption on streams or strings encVersion = encRevision = -1; } else { if (cryptFiltersObj.dictLookup(streamFilterObj.getName(), &cryptFilterObj)->isDict()) { cryptFilterObj.dictLookup("CFM", &cfmObj); if (cfmObj.isName("V2")) { encVersion = 2; encRevision = 3; if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) { //~ according to the spec, this should be cfLengthObj / 8 fileKeyLength = cfLengthObj.getInt(); } cfLengthObj.free(); } else if (cfmObj.isName("AESV2")) { encVersion = 2; encRevision = 3; encAlgorithm = cryptAES; if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) { //~ according to the spec, this should be cfLengthObj / 8 fileKeyLength = cfLengthObj.getInt(); } cfLengthObj.free(); } else if (cfmObj.isName("AESV3")) { encVersion = 5; if (encRevision != 5 && encRevision != 6) { encRevision = 6; } encAlgorithm = cryptAES256; if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) { //~ according to the spec, this should be cfLengthObj / 8 fileKeyLength = cfLengthObj.getInt(); } cfLengthObj.free(); } cfmObj.free(); } cryptFilterObj.free(); } } stringFilterObj.free(); streamFilterObj.free(); cryptFiltersObj.free(); if (encryptDictA->dictLookup("EncryptMetadata", &encryptMetadataObj)->isBool()) { encryptMetadata = encryptMetadataObj.getBool(); } encryptMetadataObj.free(); } permFlags = permObj.getInt(); ownerKey = ownerKeyObj.getString()->copy(); userKey = userKeyObj.getString()->copy(); if (encVersion >= 1 && encVersion <= 2 && encRevision >= 2 && encRevision <= 3) { if (fileIDObj.isArray()) { if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) { fileID = fileIDObj1.getString()->copy(); } else { fileID = new GString(); } fileIDObj1.free(); } else { fileID = new GString(); } if (fileKeyLength > 16 || fileKeyLength <= 0) { fileKeyLength = 16; } ok = gTrue; } 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) { fileKeyLength = 32; } ok = gTrue; } else if (!(encVersion == -1 && encRevision == -1)) { error(errUnimplemented, -1, "Unsupported version/revision ({0:d}/{1:d}) of Standard security handler", encVersion, encRevision); } } else { error(errSyntaxError, -1, "Invalid encryption key length"); } } else { error(errSyntaxError, -1, "Weird encryption info"); } fileIDObj.free(); permObj.free(); userEncObj.free(); ownerEncObj.free(); userKeyObj.free(); ownerKeyObj.free(); lengthObj.free(); revisionObj.free(); versionObj.free(); } StandardSecurityHandler::~StandardSecurityHandler() { if (fileID) { delete fileID; } if (ownerKey) { delete ownerKey; } if (userKey) { delete userKey; } if (ownerEnc) { delete ownerEnc; } if (userEnc) { delete userEnc; } } GBool StandardSecurityHandler::isUnencrypted() { return encVersion == -1 && encRevision == -1; } void *StandardSecurityHandler::makeAuthData(GString *ownerPassword, GString *userPassword) { return new StandardAuthData(ownerPassword ? ownerPassword->copy() : (GString *)NULL, userPassword ? userPassword->copy() : (GString *)NULL); } void *StandardSecurityHandler::getAuthData() { PDFCore *core; GString *password; if (!(core = doc->getCore()) || !(password = core->getPassword())) { return NULL; } return new StandardAuthData(password, password->copy()); } void StandardSecurityHandler::freeAuthData(void *authData) { delete (StandardAuthData *)authData; } GBool StandardSecurityHandler::authorize(void *authData) { GString *ownerPassword, *userPassword; if (!ok) { return gFalse; } if (authData) { ownerPassword = ((StandardAuthData *)authData)->ownerPassword; userPassword = ((StandardAuthData *)authData)->userPassword; } else { ownerPassword = NULL; userPassword = NULL; } if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, ownerKey, userKey, ownerEnc, userEnc, permFlags, fileID, ownerPassword, userPassword, fileKey, encryptMetadata, &ownerPasswordOk)) { return gFalse; } return gTrue; } #ifdef ENABLE_PLUGINS //------------------------------------------------------------------------ // ExternalSecurityHandler //------------------------------------------------------------------------ ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA, XpdfSecurityHandler *xshA): SecurityHandler(docA) { encryptDictA->copy(&encryptDict); xsh = xshA; encAlgorithm = cryptRC4; //~ this should be obtained via getKey ok = gFalse; if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA, (XpdfObject)encryptDictA, &docData)) { return; } ok = gTrue; } ExternalSecurityHandler::~ExternalSecurityHandler() { (*xsh->freeDoc)(xsh->handlerData, docData); encryptDict.free(); } void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword, GString *userPassword) { char *opw, *upw; void *authData; opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL; upw = userPassword ? userPassword->getCString() : (char *)NULL; if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) { return NULL; } return authData; } void *ExternalSecurityHandler::getAuthData() { void *authData; if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) { return NULL; } return authData; } void ExternalSecurityHandler::freeAuthData(void *authData) { (*xsh->freeAuthData)(xsh->handlerData, docData, authData); } GBool ExternalSecurityHandler::authorize(void *authData) { char *key; int length; if (!ok) { return gFalse; } permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData); if (!(permFlags & xpdfPermissionOpen)) { return gFalse; } if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) { return gFalse; } if ((fileKeyLength = length) > 16) { fileKeyLength = 16; } memcpy(fileKey, key, fileKeyLength); (*xsh->freeKey)(xsh->handlerData, docData, key, length); return gTrue; } #endif // ENABLE_PLUGINS xpdf-3.04/xpdf/CharCodeToUnicode.h0000644000076400007640000000642412341430012016303 0ustar dereknderekn//======================================================================== // // CharCodeToUnicode.h // // Mapping from character codes to Unicode. // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef CHARCODETOUNICODE_H #define CHARCODETOUNICODE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "CharTypes.h" #if MULTITHREADED #include "GMutex.h" #endif struct CharCodeToUnicodeString; //------------------------------------------------------------------------ class CharCodeToUnicode { public: // Create an identity mapping (Unicode = CharCode). static CharCodeToUnicode *makeIdentityMapping(); // Read the CID-to-Unicode mapping for from the file // specified by . Sets the initial reference count to 1. // Returns NULL on failure. static CharCodeToUnicode *parseCIDToUnicode(GString *fileName, GString *collection); // Create a Unicode-to-Unicode mapping from the file specified by // . Sets the initial reference count to 1. Returns NULL // on failure. static CharCodeToUnicode *parseUnicodeToUnicode(GString *fileName); // Create the CharCode-to-Unicode mapping for an 8-bit font. // is an array of 256 Unicode indexes. Sets the initial // reference count to 1. static CharCodeToUnicode *make8BitToUnicode(Unicode *toUnicode); // Parse a ToUnicode CMap for an 8- or 16-bit font. static CharCodeToUnicode *parseCMap(GString *buf, int nBits); // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into // . void mergeCMap(GString *buf, int nBits); ~CharCodeToUnicode(); void incRefCnt(); void decRefCnt(); // Return true if this mapping matches the specified . GBool match(GString *tagA); // Set the mapping for . void setMapping(CharCode c, Unicode *u, int len); // Map a CharCode to Unicode. int mapToUnicode(CharCode c, Unicode *u, int size); // Return the mapping's length, i.e., one more than the max char // code supported by the mapping. CharCode getLength() { return mapLen; } GBool isIdentity() { return !map; } private: void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits); void addMapping(CharCode code, char *uStr, int n, int offset); CharCodeToUnicode(); CharCodeToUnicode(GString *tagA); CharCodeToUnicode(GString *tagA, Unicode *mapA, CharCode mapLenA, GBool copyMap, CharCodeToUnicodeString *sMapA, int sMapLenA, int sMapSizeA); GString *tag; Unicode *map; CharCode mapLen; CharCodeToUnicodeString *sMap; int sMapLen, sMapSize; int refCnt; #if MULTITHREADED GMutex mutex; #endif }; //------------------------------------------------------------------------ class CharCodeToUnicodeCache { public: CharCodeToUnicodeCache(int sizeA); ~CharCodeToUnicodeCache(); // Get the CharCodeToUnicode object for . Increments its // reference count; there will be one reference for the cache plus // one for the caller of this function. Returns NULL on failure. CharCodeToUnicode *getCharCodeToUnicode(GString *tag); // Insert into the cache, in the most-recently-used position. void add(CharCodeToUnicode *ctu); private: CharCodeToUnicode **cache; int size; }; #endif xpdf-3.04/xpdf/PDFDocEncoding.cc0000644000076400007640000000467312341430012015671 0ustar dereknderekn//======================================================================== // // PDFDocEncoding.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include "PDFDocEncoding.h" Unicode pdfDocEncoding[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, // 10 0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000, 0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }; xpdf-3.04/xpdf/XPDFTree.h0000644000076400007640000000212112341430012014370 0ustar dereknderekn//======================================================================== // // XPDFTree.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef XPDFTREE_H #define XPDFTREE_H #include #include extern "C" { externalref WidgetClass xpdfTreeWidgetClass; typedef struct _XPDFTreeClassRec *XPDFTreeWidgetClass; typedef struct _XPDFTreeRec *XPDFTreeWidget; #ifndef XPDFIsTree #define XPDFIsTree(w) XtIsSubclass(w, xpdfTreeWidgetClass) #endif #define XPDFNentryParent "entryParent" #define XPDFNentryExpanded "entryExpanded" #define XPDFNentryPosition "entryPosition" #define XPDFNselectionCallback "selectionCallback" #define XPDFCentryParent "EntryParent" #define XPDFCentryExpanded "EntryExpanded" #define XPDFCentryPosition "EntryPosition" typedef struct { int reason; XEvent *event; Widget selectedItem; } XPDFTreeSelectCallbackStruct; extern Widget XPDFCreateTree(Widget parent, char *name, ArgList argList, Cardinal argCount); } // extern "C" #endif xpdf-3.04/xpdf/FontEncodingTables.h0000644000076400007640000000106412341430012016524 0ustar dereknderekn//======================================================================== // // FontEncodingTables.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef FONTENCODINGTABLES_H #define FONTENCODINGTABLES_H extern const char *macRomanEncoding[]; extern const char *macExpertEncoding[]; extern const char *winAnsiEncoding[]; extern const char *standardEncoding[]; extern const char *expertEncoding[]; extern const char *symbolEncoding[]; extern const char *zapfDingbatsEncoding[]; #endif xpdf-3.04/xpdf/Makefile.dep0000644000076400007640000000000012341430012015037 0ustar derekndereknxpdf-3.04/xpdf/OutputDev.h0000644000076400007640000002240112341430012014751 0ustar dereknderekn//======================================================================== // // OutputDev.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef OUTPUTDEV_H #define OUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "CharTypes.h" class GString; class Gfx; class GfxState; struct GfxColor; class GfxColorSpace; class GfxImageColorMap; class GfxFunctionShading; class GfxAxialShading; class GfxRadialShading; class Stream; class Links; class Link; class Catalog; class Page; class Function; //------------------------------------------------------------------------ // OutputDev //------------------------------------------------------------------------ class OutputDev { public: // Constructor. OutputDev() {} // Destructor. virtual ~OutputDev() {} //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() = 0; // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() = 0; // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual GBool useTilingPatternFill() { return gFalse; } // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. virtual GBool useShadedFills() { return gFalse; } // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). virtual GBool useDrawForm() { return gFalse; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() = 0; // Does this device need non-text content? virtual GBool needNonText() { return gTrue; } // Does this device require incCharCount to be called for text on // non-shown layers? virtual GBool needCharCount() { return gFalse; } //----- initialization and control // Set default transform matrix. virtual void setDefaultCTM(double *ctm); // Check to see if a page slice should be displayed. If this // returns false, the page display is aborted. Typically, an // OutputDev will use some alternate means to display the page // before returning false. virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL) { return gTrue; } // Start a page. virtual void startPage(int pageNum, GfxState *state) {} // End a page. virtual void endPage() {} // Dump page contents to display. virtual void dump() {} //----- coordinate conversion // Convert between device and user coordinates. virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy); virtual void cvtUserToDev(double ux, double uy, double *dx, double *dy); virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy); double *getDefCTM() { return defCTM; } double *getDefICTM() { return defICTM; } //----- save/restore graphics state virtual void saveState(GfxState *state) {} virtual void restoreState(GfxState *state) {} //----- update graphics state virtual void updateAll(GfxState *state); virtual void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) {} virtual void updateLineDash(GfxState *state) {} virtual void updateFlatness(GfxState *state) {} virtual void updateLineJoin(GfxState *state) {} virtual void updateLineCap(GfxState *state) {} virtual void updateMiterLimit(GfxState *state) {} virtual void updateLineWidth(GfxState *state) {} virtual void updateStrokeAdjust(GfxState *state) {} virtual void updateFillColorSpace(GfxState *state) {} virtual void updateStrokeColorSpace(GfxState *state) {} virtual void updateFillColor(GfxState *state) {} virtual void updateStrokeColor(GfxState *state) {} virtual void updateBlendMode(GfxState *state) {} virtual void updateFillOpacity(GfxState *state) {} virtual void updateStrokeOpacity(GfxState *state) {} virtual void updateFillOverprint(GfxState *state) {} virtual void updateStrokeOverprint(GfxState *state) {} virtual void updateOverprintMode(GfxState *state) {} virtual void updateTransfer(GfxState *state) {} //----- update text state virtual void updateFont(GfxState *state) {} virtual void updateTextMat(GfxState *state) {} virtual void updateCharSpace(GfxState *state) {} virtual void updateRender(GfxState *state) {} virtual void updateRise(GfxState *state) {} virtual void updateWordSpace(GfxState *state) {} virtual void updateHorizScaling(GfxState *state) {} virtual void updateTextPos(GfxState *state) {} virtual void updateTextShift(GfxState *state, double shift) {} virtual void saveTextPos(GfxState *state) {} virtual void restoreTextPos(GfxState *state) {} //----- path painting virtual void stroke(GfxState *state) {} virtual void fill(GfxState *state) {} virtual void eoFill(GfxState *state) {} 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, double xStep, double yStep) {} virtual GBool functionShadedFill(GfxState *state, GfxFunctionShading *shading) { return gFalse; } virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading) { return gFalse; } virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading) { return gFalse; } //----- path clipping virtual void clip(GfxState *state) {} virtual void eoClip(GfxState *state) {} virtual void clipToStrokePath(GfxState *state) {} //----- text drawing virtual void beginStringOp(GfxState *state) {} virtual void endStringOp(GfxState *state) {} virtual void beginString(GfxState *state, GString *s) {} virtual void endString(GfxState *state) {} virtual void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode *u, int uLen) {} virtual void drawString(GfxState *state, GString *s) {} virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen); virtual void endType3Char(GfxState *state) {} virtual void endTextObject(GfxState *state) {} virtual void incCharCount(int nChars) {} virtual void beginActualText(GfxState *state, Unicode *u, int uLen) {} virtual void endActualText(GfxState *state) {} //----- image drawing virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, 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); #if OPI_SUPPORT //----- OPI functions virtual void opiBegin(GfxState *state, Dict *opiDict); virtual void opiEnd(GfxState *state, Dict *opiDict); #endif //----- Type 3 font operators virtual void type3D0(GfxState *state, double wx, double wy) {} virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {} //----- form XObjects virtual void drawForm(Ref id) {} //----- PostScript XObjects virtual void psXObject(Stream *psStream, Stream *level1Stream) {} //----- transparency groups and soft masks virtual void beginTransparencyGroup(GfxState *state, double *bbox, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, GBool forSoftMask) {} virtual void endTransparencyGroup(GfxState *state) {} virtual void paintTransparencyGroup(GfxState *state, double *bbox) {} virtual void setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor) {} virtual void clearSoftMask(GfxState *state) {} //----- links virtual void processLink(Link *link) {} #if 1 //~tmp: turn off anti-aliasing temporarily virtual void setInShading(GBool sh) {} #endif private: double defCTM[6]; // default coordinate transform matrix double defICTM[6]; // inverse of default CTM }; #endif xpdf-3.04/xpdf/Annot.h0000644000076400007640000001053412341430012014075 0ustar dereknderekn//======================================================================== // // Annot.h // // Copyright 2000-2003 Glyph & Cog, LLC // //======================================================================== #ifndef ANNOT_H #define ANNOT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif class XRef; class Catalog; class Gfx; class GfxFontDict; class PDFDoc; //------------------------------------------------------------------------ // AnnotBorderStyle //------------------------------------------------------------------------ enum AnnotBorderType { annotBorderSolid, annotBorderDashed, annotBorderBeveled, annotBorderInset, annotBorderUnderlined }; class AnnotBorderStyle { public: AnnotBorderStyle(AnnotBorderType typeA, double widthA, double *dashA, int dashLengthA, double *colorA, int nColorCompsA); ~AnnotBorderStyle(); AnnotBorderType getType() { return type; } double getWidth() { return width; } void getDash(double **dashA, int *dashLengthA) { *dashA = dash; *dashLengthA = dashLength; } int getNumColorComps() { return nColorComps; } double *getColor() { return color; } private: AnnotBorderType type; double width; double *dash; int dashLength; double color[4]; int nColorComps; }; //------------------------------------------------------------------------ enum AnnotLineEndType { annotLineEndNone, annotLineEndSquare, annotLineEndCircle, annotLineEndDiamond, annotLineEndOpenArrow, annotLineEndClosedArrow, annotLineEndButt, annotLineEndROpenArrow, annotLineEndRClosedArrow, annotLineEndSlash }; //------------------------------------------------------------------------ // Annot //------------------------------------------------------------------------ class Annot { public: Annot(PDFDoc *docA, Dict *dict, Ref *refA); ~Annot(); GBool isOk() { return ok; } void draw(Gfx *gfx, GBool printing); GString *getType() { return type; } double getXMin() { return xMin; } double getYMin() { return yMin; } double getXMax() { return xMax; } double getYMax() { return yMax; } Object *getObject(Object *obj); // Get appearance object. Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); } AnnotBorderStyle *getBorderStyle() { return borderStyle; } GBool match(Ref *refA) { return ref.num == refA->num && ref.gen == refA->gen; } void generateAnnotAppearance(); private: void generateLineAppearance(); void generatePolyLineAppearance(); void generatePolygonAppearance(); void setLineStyle(AnnotBorderStyle *bs, double *lineWidth); void setStrokeColor(double *color, int nComps); GBool setFillColor(Object *colorObj); AnnotLineEndType parseLineEndType(Object *obj); void adjustLineEndpoint(AnnotLineEndType lineEnd, double x, double y, double dx, double dy, double w, double *tx, double *ty); void drawLineArrow(AnnotLineEndType lineEnd, double x, double y, double dx, double dy, double w, GBool fill); void drawCircle(double cx, double cy, double r, const char *cmd); void drawCircleTopLeft(double cx, double cy, double r); void drawCircleBottomRight(double cx, double cy, double r); PDFDoc *doc; XRef *xref; // the xref table for this PDF file Ref ref; // object ref identifying this annotation GString *type; // annotation type GString *appearanceState; // appearance state name Object appearance; // a reference to the Form XObject stream // for the normal appearance GString *appearBuf; double xMin, yMin, // annotation rectangle xMax, yMax; Guint flags; AnnotBorderStyle *borderStyle; Object ocObj; // optional content entry GBool ok; }; //------------------------------------------------------------------------ // Annots //------------------------------------------------------------------------ class Annots { public: // Build a list of Annot objects. Annots(PDFDoc *docA, Object *annotsObj); ~Annots(); // Iterate through list of annotations. int getNumAnnots() { return nAnnots; } Annot *getAnnot(int i) { return annots[i]; } // Generate an appearance stream for any non-form-field annotation // that is missing it. void generateAnnotAppearances(); private: void scanFieldAppearances(Dict *node, Ref *ref, Dict *parent, Dict *acroForm); Annot *findAnnot(Ref *ref); PDFDoc *doc; Annot **annots; int nAnnots; }; #endif xpdf-3.04/xpdf/UnicodeMap.h0000644000076400007640000000561412341430012015045 0ustar dereknderekn//======================================================================== // // UnicodeMap.h // // Mapping from Unicode to an encoding. // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef UNICODEMAP_H #define UNICODEMAP_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "CharTypes.h" #if MULTITHREADED #include "GMutex.h" #endif class GString; //------------------------------------------------------------------------ enum UnicodeMapKind { unicodeMapUser, // read from a file unicodeMapResident, // static list of ranges unicodeMapFunc // function pointer }; typedef int (*UnicodeMapFunc)(Unicode u, char *buf, int bufSize); struct UnicodeMapRange { Unicode start, end; // range of Unicode chars Guint code, nBytes; // first output code }; struct UnicodeMapExt; //------------------------------------------------------------------------ class UnicodeMap { public: // Create the UnicodeMap specified by . Sets the // initial reference count to 1. Returns NULL on failure. static UnicodeMap *parse(GString *encodingNameA); // Create a resident UnicodeMap. UnicodeMap(const char *encodingNameA, GBool unicodeOutA, UnicodeMapRange *rangesA, int lenA); // Create a resident UnicodeMap that uses a function instead of a // list of ranges. UnicodeMap(const char *encodingNameA, GBool unicodeOutA, UnicodeMapFunc funcA); ~UnicodeMap(); void incRefCnt(); void decRefCnt(); GString *getEncodingName() { return encodingName; } GBool isUnicode() { return unicodeOut; } // Return true if this UnicodeMap matches the specified // . GBool match(GString *encodingNameA); // Map Unicode to the target encoding. Fills in with the // output and returns the number of bytes used. Output will be // truncated at bytes. No string terminator is written. // Returns 0 if no mapping is found. int mapUnicode(Unicode u, char *buf, int bufSize); private: UnicodeMap(GString *encodingNameA); GString *encodingName; UnicodeMapKind kind; GBool unicodeOut; union { UnicodeMapRange *ranges; // (user, resident) UnicodeMapFunc func; // (func) }; int len; // (user, resident) UnicodeMapExt *eMaps; // (user) int eMapsLen; // (user) int refCnt; #if MULTITHREADED GMutex mutex; #endif }; //------------------------------------------------------------------------ #define unicodeMapCacheSize 4 class UnicodeMapCache { public: UnicodeMapCache(); ~UnicodeMapCache(); // Get the UnicodeMap for . Increments its reference // count; there will be one reference for the cache plus one for the // caller of this function. Returns NULL on failure. UnicodeMap *getUnicodeMap(GString *encodingName); private: UnicodeMap *cache[unicodeMapCacheSize]; }; #endif xpdf-3.04/xpdf/XPDFTreeP.h0000644000076400007640000000432312341430012014516 0ustar dereknderekn//======================================================================== // // XPDFTreeP.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef XPDFTREEP_H #define XPDFTREEP_H #include #include #include "XPDFTree.h" extern "C" { typedef void (*XPDFLayoutProc)(Widget widget, Widget instigator); typedef void (*XPDFCalcSizeProc)(Widget widget, Widget instigator, Dimension *totalWidth, Dimension *totalHeight); typedef Boolean (*XPDFNeedRelayoutProc)(Widget oldWidget, Widget newWidget); #define XPDFInheritCreateGC ((XtWidgetProc)_XtInherit) #define XPDFInheritDestroyGC ((XtWidgetProc)_XtInherit) #define XPDFInheritLayout ((XPDFLayoutProc)_XtInherit) #define XPDFInheritCalcSize ((XPDFCalcSizeProc)_XtInherit) #define XPDFInheritNeedRelayout ((XPDFNeedRelayoutProc)_XtInherit) typedef struct { XtWidgetProc createGC; XtWidgetProc destroyGC; XPDFLayoutProc layout; XPDFCalcSizeProc calcSize; XPDFNeedRelayoutProc needRelayout; XtPointer extension; } XPDFTreeClassPart; typedef struct _XPDFTreeClassRec { CoreClassPart coreClass; CompositeClassPart compositeClass; ConstraintClassPart constraintClass; XmManagerClassPart managerClass; XPDFTreeClassPart treeClass; } XPDFTreeClassRec; externalref XPDFTreeClassRec xpdfTreeClassRec; typedef struct _XPDFTreeEntry XPDFTreeEntry; typedef struct { Dimension marginWidth; Dimension marginHeight; XtCallbackList selectCallback; GC plainGC; GC dottedGC; XPDFTreeEntry *root; int redrawY; } XPDFTreePart; typedef struct _XPDFTreeRec { CorePart core; CompositePart composite; ConstraintPart constraint; XmManagerPart manager; XPDFTreePart tree; } XPDFTreeRec; #define XPDFTreeIndex (XmManagerIndex + 1) typedef struct _XPDFTreeConstraintPart { Widget entryParent; Boolean entryExpanded; int entryPosition; XPDFTreeEntry *e; } XPDFTreeConstraintPart, *XPDFTreeConstraint; typedef struct _XPDFTreeConstraintRec { XmManagerConstraintPart manager; XPDFTreeConstraintPart tree; } XPDFTreeConstraintRec, *XPDFTreeConstraintPtr; #define XPDFTreeCPart(w) \ (&((XPDFTreeConstraintPtr)(w)->core.constraints)->tree) } // extern "C" #endif xpdf-3.04/xpdf/dblLeftArrow.xbm0000644000076400007640000000045012341430012015740 0ustar dereknderekn#define dblLeftArrow_width 16 #define dblLeftArrow_height 15 static unsigned char dblLeftArrow_bits[] = { 0x80, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0xc0, 0x80, 0x80}; xpdf-3.04/xpdf/Form.h0000644000076400007640000000236212341430012013721 0ustar dereknderekn//======================================================================== // // Form.h // // Copyright 2012 Glyph & Cog, LLC // //======================================================================== #ifndef FORM_H #define FORM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" class Gfx; class FormField; //------------------------------------------------------------------------ class Form { public: static Form *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj); virtual ~Form(); virtual const char *getType() = 0; virtual void draw(int pageNum, Gfx *gfx, GBool printing) = 0; virtual int getNumFields() = 0; virtual FormField *getField(int idx) = 0; protected: Form(PDFDoc *docA); PDFDoc *doc; }; //------------------------------------------------------------------------ class FormField { public: FormField(); virtual ~FormField(); virtual const char *getType() = 0; virtual Unicode *getName(int *length) = 0; virtual Unicode *getValue(int *length) = 0; // Return the resource dictionaries used to draw this field. The // returned object must be either a dictionary or an array of // dictonaries. virtual Object *getResources(Object *res) = 0; }; #endif xpdf-3.04/xpdf/CMap.cc0000644000076400007640000002455112341430012014000 0ustar dereknderekn//======================================================================== // // CMap.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "gfile.h" #include "GString.h" #include "Error.h" #include "GlobalParams.h" #include "PSTokenizer.h" #include "Object.h" #include "Stream.h" #include "CMap.h" //------------------------------------------------------------------------ struct CMapVectorEntry { GBool isVector; union { CMapVectorEntry *vector; CID cid; }; }; //------------------------------------------------------------------------ static int getCharFromFile(void *data) { return fgetc((FILE *)data); } static int getCharFromStream(void *data) { return ((Stream *)data)->getChar(); } //------------------------------------------------------------------------ CMap *CMap::parse(CMapCache *cache, GString *collectionA, Object *obj) { CMap *cMap; GString *cMapNameA; if (obj->isName()) { cMapNameA = new GString(obj->getName()); if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) { error(errSyntaxError, -1, "Unknown CMap '{0:t}' for character collection '{1:t}'", cMapNameA, collectionA); } delete cMapNameA; } else if (obj->isStream()) { if (!(cMap = CMap::parse(NULL, collectionA, obj->getStream()))) { error(errSyntaxError, -1, "Invalid CMap in Type 0 font"); } } else { error(errSyntaxError, -1, "Invalid Encoding in Type 0 font"); return NULL; } return cMap; } CMap *CMap::parse(CMapCache *cache, GString *collectionA, GString *cMapNameA) { FILE *f; CMap *cMap; if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) { // Check for an identity CMap. if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) { return new CMap(collectionA->copy(), cMapNameA->copy(), 0); } if (!cMapNameA->cmp("Identity-V")) { return new CMap(collectionA->copy(), cMapNameA->copy(), 1); } error(errSyntaxError, -1, "Couldn't find '{0:t}' CMap file for '{1:t}' collection", cMapNameA, collectionA); return NULL; } cMap = new CMap(collectionA->copy(), cMapNameA->copy()); cMap->parse2(cache, &getCharFromFile, f); fclose(f); return cMap; } CMap *CMap::parse(CMapCache *cache, GString *collectionA, Stream *str) { Object obj1; CMap *cMap; cMap = new CMap(collectionA->copy(), NULL); if (!str->getDict()->lookup("UseCMap", &obj1)->isNull()) { cMap->useCMap(cache, &obj1); } obj1.free(); str->reset(); cMap->parse2(cache, &getCharFromStream, str); str->close(); return cMap; } void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data) { PSTokenizer *pst; char tok1[256], tok2[256], tok3[256]; int n1, n2, n3; Guint start, end, code; pst = new PSTokenizer(getCharFunc, data); pst->getToken(tok1, sizeof(tok1), &n1); while (pst->getToken(tok2, sizeof(tok2), &n2)) { if (!strcmp(tok2, "usecmap")) { if (tok1[0] == '/') { useCMap(cache, tok1 + 1); } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok1, "/WMode")) { wMode = atoi(tok2); pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "begincidchar")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endcidchar")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endcidchar")) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && n1 >= 4 && (n1 & 1) == 0)) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); continue; } tok1[n1 - 1] = '\0'; if (sscanf(tok1 + 1, "%x", &code) != 1) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); continue; } n1 = (n1 - 2) / 2; addCIDs(code, code, n1, (CID)atoi(tok2)); } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "begincidrange")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endcidrange")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endcidrange") || !pst->getToken(tok3, sizeof(tok3), &n3) || !strcmp(tok3, "endcidrange")) { error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap"); break; } if (tok1[0] == '<' && tok2[0] == '<' && n1 == n2 && n1 >= 4 && (n1 & 1) == 0) { tok1[n1 - 1] = tok2[n1 - 1] = '\0'; sscanf(tok1 + 1, "%x", &start); sscanf(tok2 + 1, "%x", &end); n1 = (n1 - 2) / 2; addCIDs(start, end, n1, (CID)atoi(tok3)); } } pst->getToken(tok1, sizeof(tok1), &n1); } else { strcpy(tok1, tok2); } } delete pst; } CMap::CMap(GString *collectionA, GString *cMapNameA) { int i; collection = collectionA; cMapName = cMapNameA; isIdent = gFalse; wMode = 0; vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); for (i = 0; i < 256; ++i) { vector[i].isVector = gFalse; vector[i].cid = 0; } refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) { collection = collectionA; cMapName = cMapNameA; isIdent = gTrue; wMode = wModeA; vector = NULL; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } void CMap::useCMap(CMapCache *cache, char *useName) { GString *useNameStr; CMap *subCMap; useNameStr = new GString(useName); // if cache is non-NULL, we already have a lock, and we can use // CMapCache::getCMap() directly; otherwise, we need to use // GlobalParams::getCMap() in order to acqure the lock need to use // GlobalParams::getCMap if (cache) { subCMap = cache->getCMap(collection, useNameStr); } else { subCMap = globalParams->getCMap(collection, useNameStr); } delete useNameStr; if (!subCMap) { return; } isIdent = subCMap->isIdent; if (subCMap->vector) { copyVector(vector, subCMap->vector); } subCMap->decRefCnt(); } void CMap::useCMap(CMapCache *cache, Object *obj) { CMap *subCMap; subCMap = CMap::parse(cache, collection, obj); if (!subCMap) { return; } isIdent = subCMap->isIdent; if (subCMap->vector) { copyVector(vector, subCMap->vector); } subCMap->decRefCnt(); } void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) { int i, j; for (i = 0; i < 256; ++i) { if (src[i].isVector) { if (!dest[i].isVector) { dest[i].isVector = gTrue; dest[i].vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); for (j = 0; j < 256; ++j) { dest[i].vector[j].isVector = gFalse; dest[i].vector[j].cid = 0; } } copyVector(dest[i].vector, src[i].vector); } else { if (dest[i].isVector) { error(errSyntaxError, -1, "Collision in usecmap"); } else { dest[i].cid = src[i].cid; } } } } void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) { CMapVectorEntry *vec; int byte, byte0, byte1; Guint start1, end1, i, j, k; start1 = start & 0xffffff00; end1 = end & 0xffffff00; for (i = start1; i <= end1; i += 0x100) { vec = vector; for (j = nBytes - 1; j >= 1; --j) { byte = (i >> (8 * j)) & 0xff; if (!vec[byte].isVector) { vec[byte].isVector = gTrue; vec[byte].vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); for (k = 0; k < 256; ++k) { vec[byte].vector[k].isVector = gFalse; vec[byte].vector[k].cid = 0; } } vec = vec[byte].vector; } byte0 = (i < start) ? (start & 0xff) : 0; byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff; for (byte = byte0; byte <= byte1; ++byte) { if (vec[byte].isVector) { error(errSyntaxError, -1, "Invalid CID ({0:x} [{1:d} bytes]) in CMap", i, nBytes); } else { vec[byte].cid = firstCID + ((i + byte) - start); } } } } CMap::~CMap() { delete collection; if (cMapName) { delete cMapName; } if (vector) { freeCMapVector(vector); } #if MULTITHREADED gDestroyMutex(&mutex); #endif } void CMap::freeCMapVector(CMapVectorEntry *vec) { int i; for (i = 0; i < 256; ++i) { if (vec[i].isVector) { freeCMapVector(vec[i].vector); } } gfree(vec); } void CMap::incRefCnt() { #if MULTITHREADED gLockMutex(&mutex); #endif ++refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif } void CMap::decRefCnt() { GBool done; #if MULTITHREADED gLockMutex(&mutex); #endif done = --refCnt == 0; #if MULTITHREADED gUnlockMutex(&mutex); #endif if (done) { delete this; } } GBool CMap::match(GString *collectionA, GString *cMapNameA) { return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA); } CID CMap::getCID(char *s, int len, CharCode *c, int *nUsed) { CMapVectorEntry *vec; CharCode cc; int n, i; vec = vector; cc = 0; n = 0; while (vec && n < len) { i = s[n++] & 0xff; cc = (cc << 8) | i; if (!vec[i].isVector) { *c = cc; *nUsed = n; return vec[i].cid; } vec = vec[i].vector; } if (isIdent && len >= 2) { // identity CMap *nUsed = 2; *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff); return cc; } *nUsed = 1; *c = s[0] & 0xff; return 0; } //------------------------------------------------------------------------ CMapCache::CMapCache() { int i; for (i = 0; i < cMapCacheSize; ++i) { cache[i] = NULL; } } CMapCache::~CMapCache() { int i; for (i = 0; i < cMapCacheSize; ++i) { if (cache[i]) { cache[i]->decRefCnt(); } } } CMap *CMapCache::getCMap(GString *collection, GString *cMapName) { CMap *cmap; int i, j; if (cache[0] && cache[0]->match(collection, cMapName)) { cache[0]->incRefCnt(); return cache[0]; } for (i = 1; i < cMapCacheSize; ++i) { if (cache[i] && cache[i]->match(collection, cMapName)) { cmap = cache[i]; for (j = i; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = cmap; cmap->incRefCnt(); return cmap; } } if ((cmap = CMap::parse(this, collection, cMapName))) { if (cache[cMapCacheSize - 1]) { cache[cMapCacheSize - 1]->decRefCnt(); } for (j = cMapCacheSize - 1; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = cmap; cmap->incRefCnt(); return cmap; } return NULL; } xpdf-3.04/xpdf/SplashOutputDev.cc0000644000076400007640000027417612341430012016304 0ustar dereknderekn//======================================================================== // // SplashOutputDev.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include "gfile.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Gfx.h" #include "GfxFont.h" #include "Link.h" #include "CharCodeToUnicode.h" #include "FontEncodingTables.h" #include "BuiltinFont.h" #include "BuiltinFontTables.h" #include "FoFiTrueType.h" #include "JPXStream.h" #include "SplashBitmap.h" #include "SplashGlyphBitmap.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashPath.h" #include "SplashState.h" #include "SplashErrorCodes.h" #include "SplashFontEngine.h" #include "SplashFont.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" #include "Splash.h" #include "SplashOutputDev.h" #ifdef VMS #if (__VMS_VER < 70000000) extern "C" int unlink(char *filename); #endif #endif //------------------------------------------------------------------------ // Type 3 font cache size parameters #define type3FontCacheAssoc 8 #define type3FontCacheMaxSets 8 #define type3FontCacheSize (128*1024) //------------------------------------------------------------------------ // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } //------------------------------------------------------------------------ // Blend functions //------------------------------------------------------------------------ static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = (dest[i] * src[i]) / 255; } } static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; } } static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < 0x80 ? (src[i] * 2 * dest[i]) / 255 : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255; } } static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < src[i] ? dest[i] : src[i]; } } static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] > src[i] ? dest[i] : src[i]; } } static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (dest[i] == 0) { blend[i] = 0; } else if (src[i] == 255) { blend[i] = 255; } else { x = (dest[i] * 255) / (255 - src[i]); blend[i] = x <= 255 ? x : 255; } } } static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (dest[i] == 255) { blend[i] = 255; } else if (src[i] == 0) { blend[i] = 0; } else { x = ((255 - dest[i]) * 255) / src[i]; blend[i] = x <= 255 ? 255 - x : 0; } } } static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = src[i] < 0x80 ? (dest[i] * 2 * src[i]) / 255 : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255; } } static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] < 0x80) { blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255); } else { if (dest[i] < 0x40) { x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255; } else { x = (int)sqrt(255.0 * dest[i]); } blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255; } } } static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i]; } } static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255; } } static int getLum(int r, int g, int b) { return (int)(0.3 * r + 0.59 * g + 0.11 * b); } static int getSat(int r, int g, int b) { int rgbMin, rgbMax; rgbMin = rgbMax = r; if (g < rgbMin) { rgbMin = g; } else if (g > rgbMax) { rgbMax = g; } if (b < rgbMin) { rgbMin = b; } else if (b > rgbMax) { rgbMax = b; } return rgbMax - rgbMin; } static void clipColor(int rIn, int gIn, int bIn, Guchar *rOut, Guchar *gOut, Guchar *bOut) { int lum, rgbMin, rgbMax; lum = getLum(rIn, gIn, bIn); rgbMin = rgbMax = rIn; if (gIn < rgbMin) { rgbMin = gIn; } else if (gIn > rgbMax) { rgbMax = gIn; } if (bIn < rgbMin) { rgbMin = bIn; } else if (bIn > rgbMax) { rgbMax = bIn; } if (rgbMin < 0) { *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); } else if (rgbMax > 255) { *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); } else { *rOut = rIn; *gOut = gIn; *bOut = bIn; } } static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum, Guchar *rOut, Guchar *gOut, Guchar *bOut) { int d; d = lum - getLum(rIn, gIn, bIn); clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); } static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat, Guchar *rOut, Guchar *gOut, Guchar *bOut) { int rgbMin, rgbMid, rgbMax; Guchar *minOut, *midOut, *maxOut; if (rIn < gIn) { rgbMin = rIn; minOut = rOut; rgbMid = gIn; midOut = gOut; } else { rgbMin = gIn; minOut = gOut; rgbMid = rIn; midOut = rOut; } if (bIn > rgbMid) { rgbMax = bIn; maxOut = bOut; } else if (bIn > rgbMin) { rgbMax = rgbMid; maxOut = midOut; rgbMid = bIn; midOut = bOut; } else { rgbMax = rgbMid; maxOut = midOut; rgbMid = rgbMin; midOut = minOut; rgbMin = bIn; minOut = bOut; } if (rgbMax > rgbMin) { *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); *maxOut = (Guchar)sat; } else { *midOut = *maxOut = 0; } *minOut = 0; } static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { Guchar r0, g0, b0; #if SPLASH_CMYK Guchar r1, g1, b1; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeRGB8: case splashModeBGR8: setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; #if SPLASH_CMYK case splashModeCMYK8: // NB: inputs have already been converted to additive mode setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &r1, &g1, &b1); blend[0] = r1; blend[1] = g1; blend[2] = b1; blend[3] = dest[3]; break; #endif } } static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { Guchar r0, g0, b0; #if SPLASH_CMYK Guchar r1, g1, b1; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeRGB8: case splashModeBGR8: setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; #if SPLASH_CMYK case splashModeCMYK8: // NB: inputs have already been converted to additive mode setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &r1, &g1, &b1); blend[0] = r1; blend[1] = g1; blend[2] = b1; blend[3] = dest[3]; break; #endif } } static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { #if SPLASH_CMYK Guchar r, g, b; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeRGB8: case splashModeBGR8: setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; #if SPLASH_CMYK case splashModeCMYK8: // NB: inputs have already been converted to additive mode setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), &r, &g, &b); blend[0] = r; blend[1] = g; blend[2] = b; blend[3] = dest[3]; break; #endif } } static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { #if SPLASH_CMYK Guchar r, g, b; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeRGB8: case splashModeBGR8: setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), &blend[0], &blend[1], &blend[2]); break; #if SPLASH_CMYK case splashModeCMYK8: // NB: inputs have already been converted to additive mode setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), &r, &g, &b); blend[0] = r; blend[1] = g; blend[2] = b; blend[3] = src[3]; break; #endif } } // NB: This must match the GfxBlendMode enum defined in GfxState.h. SplashBlendFunc splashOutBlendFuncs[] = { NULL, &splashOutBlendMultiply, &splashOutBlendScreen, &splashOutBlendOverlay, &splashOutBlendDarken, &splashOutBlendLighten, &splashOutBlendColorDodge, &splashOutBlendColorBurn, &splashOutBlendHardLight, &splashOutBlendSoftLight, &splashOutBlendDifference, &splashOutBlendExclusion, &splashOutBlendHue, &splashOutBlendSaturation, &splashOutBlendColor, &splashOutBlendLuminosity }; //------------------------------------------------------------------------ // SplashOutFontFileID //------------------------------------------------------------------------ class SplashOutFontFileID: public SplashFontFileID { public: SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; oblique = 0; } ~SplashOutFontFileID() {} GBool matches(SplashFontFileID *id) { return ((SplashOutFontFileID *)id)->r.num == r.num && ((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; }; //------------------------------------------------------------------------ // T3FontCache //------------------------------------------------------------------------ struct T3FontCacheTag { Gushort code; Gushort mru; // valid bit (0x8000) and MRU index }; class T3FontCache { public: T3FontCache(Ref *fontID, double m11A, double m12A, double m21A, double m22A, int glyphXA, int glyphYA, int glyphWA, int glyphHA, GBool aa, GBool validBBoxA); ~T3FontCache(); GBool matches(Ref *idA, double m11A, double m12A, double m21A, double m22A) { return fontID.num == idA->num && fontID.gen == idA->gen && m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } Ref fontID; // PDF font ID double m11, m12, m21, m22; // transform matrix int glyphX, glyphY; // pixel offset of glyph bitmaps int glyphW, glyphH; // size of glyph bitmaps, in pixels GBool validBBox; // false if the bbox was [0 0 0 0] int glyphSize; // size of glyph bitmaps, in bytes int cacheSets; // number of sets in cache int cacheAssoc; // cache associativity (glyphs per set) Guchar *cacheData; // glyph pixmap cache T3FontCacheTag *cacheTags; // cache tags, i.e., char codes }; T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, double m21A, double m22A, int glyphXA, int glyphYA, int glyphWA, int glyphHA, GBool validBBoxA, GBool aa) { int i; fontID = *fontIDA; m11 = m11A; m12 = m12A; m21 = m21A; m22 = m22A; glyphX = glyphXA; glyphY = glyphYA; glyphW = glyphWA; glyphH = glyphHA; validBBox = validBBoxA; // sanity check for excessively large glyphs (which most likely // indicate an incorrect BBox) i = glyphW * glyphH; if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) { glyphW = glyphH = 100; validBBox = gFalse; } if (aa) { glyphSize = glyphW * glyphH; } else { glyphSize = ((glyphW + 7) >> 3) * glyphH; } cacheAssoc = type3FontCacheAssoc; for (cacheSets = type3FontCacheMaxSets; cacheSets > 1 && cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; cacheSets >>= 1) ; cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize); cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, sizeof(T3FontCacheTag)); for (i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = i & (cacheAssoc - 1); } } T3FontCache::~T3FontCache() { gfree(cacheData); gfree(cacheTags); } struct T3GlyphStack { Gushort code; // character code 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 Guchar *cacheData; // pointer to cache data for the glyph //----- saved state SplashBitmap *origBitmap; Splash *origSplash; double origCTM4, origCTM5; T3GlyphStack *next; // next object on stack }; //------------------------------------------------------------------------ // SplashTransparencyGroup //------------------------------------------------------------------------ struct SplashTransparencyGroup { int tx, ty; // translation coordinates SplashBitmap *tBitmap; // bitmap for transparency group GfxColorSpace *blendingColorSpace; GBool isolated; //----- saved state SplashBitmap *origBitmap; Splash *origSplash; SplashTransparencyGroup *next; }; //------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool bitmapTopDownA, GBool allowAntialiasA) { colorMode = colorModeA; bitmapRowPad = bitmapRowPadA; bitmapTopDown = bitmapTopDownA; bitmapUpsideDown = gFalse; noComposite = gFalse; allowAntialias = allowAntialiasA; vectorAntialias = allowAntialias && globalParams->getVectorAntialias() && colorMode != splashModeMono1; setupScreenParams(72.0, 72.0); reverseVideo = reverseVideoA; splashColorCopy(paperColor, paperColorA); skipHorizText = gFalse; skipRotatedText = gFalse; xref = NULL; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); splash->setStrokeAdjust(globalParams->getStrokeAdjust()); splash->clear(paperColor, 0); fontEngine = NULL; nT3Fonts = 0; t3GlyphStack = NULL; font = NULL; needFontUpdate = gFalse; textClipPath = NULL; transpGroupStack = NULL; nestCount = 0; } void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { screenParams.size = globalParams->getScreenSize(); screenParams.dotRadius = globalParams->getScreenDotRadius(); screenParams.gamma = (SplashCoord)globalParams->getScreenGamma(); screenParams.blackThreshold = (SplashCoord)globalParams->getScreenBlackThreshold(); screenParams.whiteThreshold = (SplashCoord)globalParams->getScreenWhiteThreshold(); switch (globalParams->getScreenType()) { case screenDispersed: screenParams.type = splashScreenDispersed; if (screenParams.size < 0) { screenParams.size = 4; } break; case screenClustered: screenParams.type = splashScreenClustered; if (screenParams.size < 0) { screenParams.size = 10; } break; case screenStochasticClustered: screenParams.type = splashScreenStochasticClustered; if (screenParams.size < 0) { screenParams.size = 64; } if (screenParams.dotRadius < 0) { screenParams.dotRadius = 2; } break; case screenUnset: default: // use clustered dithering for resolution >= 300 dpi // (compare to 299.9 to avoid floating point issues) if (hDPI > 299.9 && vDPI > 299.9) { screenParams.type = splashScreenStochasticClustered; if (screenParams.size < 0) { screenParams.size = 64; } if (screenParams.dotRadius < 0) { screenParams.dotRadius = 2; } } else { screenParams.type = splashScreenDispersed; if (screenParams.size < 0) { screenParams.size = 4; } } } } SplashOutputDev::~SplashOutputDev() { int i; for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } if (fontEngine) { delete fontEngine; } if (splash) { delete splash; } if (bitmap) { delete bitmap; } } void SplashOutputDev::startDoc(XRef *xrefA) { int i; xref = xrefA; if (fontEngine) { delete fontEngine; } fontEngine = new SplashFontEngine( #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H globalParams->getEnableFreeType(), globalParams->getDisableFreeTypeHinting() ? splashFTNoHinting : 0, #endif allowAntialias && globalParams->getAntialias() && colorMode != splashModeMono1); for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } nT3Fonts = 0; } void SplashOutputDev::startPage(int pageNum, GfxState *state) { int w, h; double *ctm; SplashCoord mat[6]; SplashColor color; if (state) { setupScreenParams(state->getHDPI(), state->getVDPI()); w = (int)(state->getPageWidth() + 0.5); if (w <= 0) { w = 1; } h = (int)(state->getPageHeight() + 0.5); if (h <= 0) { h = 1; } } else { w = h = 1; } if (splash) { delete splash; splash = NULL; } if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { if (bitmap) { delete bitmap; bitmap = NULL; } bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); } splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); if (state) { ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; mat[1] = (SplashCoord)ctm[1]; mat[2] = (SplashCoord)ctm[2]; mat[3] = (SplashCoord)ctm[3]; mat[4] = (SplashCoord)ctm[4]; mat[5] = (SplashCoord)ctm[5]; splash->setMatrix(mat); } switch (colorMode) { case splashModeMono1: case splashModeMono8: color[0] = 0; break; case splashModeRGB8: case splashModeBGR8: color[0] = color[1] = color[2] = 0; break; #if SPLASH_CMYK case splashModeCMYK8: color[0] = color[1] = color[2] = color[3] = 0; break; #endif } splash->setStrokePattern(new SplashSolidColor(color)); splash->setFillPattern(new SplashSolidColor(color)); splash->setLineCap(splashLineCapButt); splash->setLineJoin(splashLineJoinMiter); splash->setLineDash(NULL, 0, 0); splash->setMiterLimit(10); splash->setFlatness(1); // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true splash->setStrokeAdjust(globalParams->getStrokeAdjust()); splash->clear(paperColor, 0); } void SplashOutputDev::endPage() { if (colorMode != splashModeMono1 && !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) { updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); updateFlatness(state); updateMiterLimit(state); updateStrokeAdjust(state); updateFillColor(state); updateStrokeColor(state); needFontUpdate = gTrue; } void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { double *ctm; SplashCoord mat[6]; ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; mat[1] = (SplashCoord)ctm[1]; mat[2] = (SplashCoord)ctm[2]; mat[3] = (SplashCoord)ctm[3]; mat[4] = (SplashCoord)ctm[4]; mat[5] = (SplashCoord)ctm[5]; splash->setMatrix(mat); } void SplashOutputDev::updateLineDash(GfxState *state) { double *dashPattern; int dashLength; double dashStart; SplashCoord dash[20]; int i; state->getLineDash(&dashPattern, &dashLength, &dashStart); if (dashLength > 20) { dashLength = 20; } for (i = 0; i < dashLength; ++i) { dash[i] = (SplashCoord)dashPattern[i]; if (dash[i] < 0) { dash[i] = 0; } } splash->setLineDash(dash, dashLength, (SplashCoord)dashStart); } void SplashOutputDev::updateFlatness(GfxState *state) { #if 0 // Acrobat ignores the flatness setting, and always renders curves // with a fairly small flatness value splash->setFlatness(state->getFlatness()); #endif } void SplashOutputDev::updateLineJoin(GfxState *state) { splash->setLineJoin(state->getLineJoin()); } void SplashOutputDev::updateLineCap(GfxState *state) { splash->setLineCap(state->getLineCap()); } void SplashOutputDev::updateMiterLimit(GfxState *state) { splash->setMiterLimit(state->getMiterLimit()); } void SplashOutputDev::updateLineWidth(GfxState *state) { splash->setLineWidth(state->getLineWidth()); } void SplashOutputDev::updateStrokeAdjust(GfxState *state) { #if 0 // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true splash->setStrokeAdjust(state->getStrokeAdjust()); #endif } void SplashOutputDev::updateFillColor(GfxState *state) { GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif switch (colorMode) { case splashModeMono1: case splashModeMono8: state->getFillGray(&gray); splash->setFillPattern(getColor(gray)); break; case splashModeRGB8: case splashModeBGR8: state->getFillRGB(&rgb); splash->setFillPattern(getColor(&rgb)); break; #if SPLASH_CMYK case splashModeCMYK8: state->getFillCMYK(&cmyk); splash->setFillPattern(getColor(&cmyk)); break; #endif } } void SplashOutputDev::updateStrokeColor(GfxState *state) { GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif switch (colorMode) { case splashModeMono1: case splashModeMono8: state->getStrokeGray(&gray); splash->setStrokePattern(getColor(gray)); break; case splashModeRGB8: case splashModeBGR8: state->getStrokeRGB(&rgb); splash->setStrokePattern(getColor(&rgb)); break; #if SPLASH_CMYK case splashModeCMYK8: state->getStrokeCMYK(&cmyk); splash->setStrokePattern(getColor(&cmyk)); break; #endif } } SplashPattern *SplashOutputDev::getColor(GfxGray gray) { SplashColor color; if (reverseVideo) { gray = gfxColorComp1 - gray; } color[0] = colToByte(gray); return new SplashSolidColor(color); } SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { GfxColorComp r, g, b; SplashColor color; if (reverseVideo) { r = gfxColorComp1 - rgb->r; g = gfxColorComp1 - rgb->g; b = gfxColorComp1 - rgb->b; } else { r = rgb->r; g = rgb->g; b = rgb->b; } color[0] = colToByte(r); color[1] = colToByte(g); color[2] = colToByte(b); return new SplashSolidColor(color); } #if SPLASH_CMYK SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { SplashColor color; color[0] = colToByte(cmyk->c); color[1] = colToByte(cmyk->m); color[2] = colToByte(cmyk->y); color[3] = colToByte(cmyk->k); return new SplashSolidColor(color); } #endif void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, GBool overprintFlag, int overprintMode, GfxColor *singleColor) { #if SPLASH_CMYK Guint mask; GfxCMYK cmyk; 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->getCMYK(singleColor, &cmyk); if (cmyk.c == 0) { mask &= ~1; } if (cmyk.m == 0) { mask &= ~2; } if (cmyk.y == 0) { mask &= ~4; } if (cmyk.k == 0) { mask &= ~8; } } } else { mask = 0xffffffff; } splash->setOverprintMask(mask); #endif } void SplashOutputDev::updateBlendMode(GfxState *state) { splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]); } void SplashOutputDev::updateFillOpacity(GfxState *state) { splash->setFillAlpha((SplashCoord)state->getFillOpacity()); } void SplashOutputDev::updateStrokeOpacity(GfxState *state) { splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); } void SplashOutputDev::updateTransfer(GfxState *state) { Function **transfer; Guchar red[256], green[256], blue[256], gray[256]; double x, y; int i; transfer = state->getTransfer(); if (transfer[0] && transfer[0]->getInputSize() == 1 && transfer[0]->getOutputSize() == 1) { if (transfer[1] && transfer[1]->getInputSize() == 1 && transfer[1]->getOutputSize() == 1 && transfer[2] && transfer[2]->getInputSize() == 1 && transfer[2]->getOutputSize() == 1 && transfer[3] && transfer[3]->getInputSize() == 1 && transfer[3]->getOutputSize() == 1) { for (i = 0; i < 256; ++i) { x = i / 255.0; transfer[0]->transform(&x, &y); red[i] = (Guchar)(y * 255.0 + 0.5); transfer[1]->transform(&x, &y); green[i] = (Guchar)(y * 255.0 + 0.5); transfer[2]->transform(&x, &y); blue[i] = (Guchar)(y * 255.0 + 0.5); transfer[3]->transform(&x, &y); gray[i] = (Guchar)(y * 255.0 + 0.5); } } else { for (i = 0; i < 256; ++i) { x = i / 255.0; transfer[0]->transform(&x, &y); red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5); } } } else { for (i = 0; i < 256; ++i) { red[i] = green[i] = blue[i] = gray[i] = (Guchar)i; } } splash->setTransfer(red, green, blue, gray); } void SplashOutputDev::updateFont(GfxState *state) { needFontUpdate = gTrue; } void SplashOutputDev::doUpdateFont(GfxState *state) { GfxFont *gfxFont; GfxFontLoc *fontLoc; GfxFontType fontType; SplashOutFontFileID *id; SplashFontFile *fontFile; int fontNum; 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, oblique; double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale; Gushort ww; SplashCoord mat[4]; char *name; Unicode uBuf[8]; 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())) { goto err1; } fontType = gfxFont->getType(); if (fontType == fontType3) { goto err1; } // sanity-check the font size - skip anything larger than 20 inches // (this avoids problems allocating memory for the font cache) 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; } // check the font file cache id = new SplashOutFontFileID(gfxFont->getID()); if ((fontFile = fontEngine->getFontFile(id))) { delete id; } else { fontNum = 0; if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); goto err2; } // 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; goto err2; } 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(); fclose(tmpFile); delete fontLoc; goto err2; } strObj.streamReset(); 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 switch (fontLoc->fontType) { 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() : "(unnamed)"); delete fontLoc; goto err2; } break; 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() : "(unnamed)"); delete fontLoc; goto err2; } break; 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() : "(unnamed)"); delete fontLoc; goto err2; } break; case fontTrueType: case fontTrueTypeOT: #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; // if we're substituting for a non-TrueType font, we need to mark // all notdef codes as "do not draw" (rather than drawing TrueType // notdef glyphs) if (gfxFont->getType() != fontTrueType && gfxFont->getType() != fontTrueTypeOT) { for (i = 0; i < 256; ++i) { if (codeToGID[i] == 0) { codeToGID[i] = -1; } } } } else { codeToGID = NULL; n = 0; } if (!(fontFile = fontEngine->loadTrueTypeFont( id, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName->getCString(), fileName == tmpFileName, #endif fontNum, codeToGID, n, gfxFont->getEmbeddedFontName() ? gfxFont->getEmbeddedFontName()->getCString() : (char *)NULL))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; goto err2; } break; case fontCIDType0: case fontCIDType0C: if (!(fontFile = fontEngine->loadCIDFont( id, #if LOAD_FONTS_FROM_MEM fontBuf #else fileName->getCString(), fileName == tmpFileName #endif ))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; goto err2; } break; case fontCIDType0COT: if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); } else { codeToGID = NULL; n = 0; } 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() : "(unnamed)"); delete fontLoc; goto err2; } break; case fontCIDType2: case fontCIDType2OT: codeToGID = NULL; n = 0; if (fontLoc->locType == gfxFontLocEmbedded) { if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); codeToGID = (int *)gmallocn(n, sizeof(int)); 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())) { #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 && ff->getCmapEncoding(cmap) == 1) || ff->getCmapPlatform(cmap) == 0) { break; } } if (cmap < ff->getNumCmaps()) { // map CID -> Unicode -> GID 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) { codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]); } else { codeToGID[code] = -1; } } } delete ff; } ctu->decRefCnt(); } else { error(errSyntaxError, -1, "Couldn't find a mapping to Unicode for font '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); } } if (!(fontFile = fontEngine->loadTrueTypeFont( id, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName->getCString(), fileName == tmpFileName, #endif fontNum, codeToGID, n, gfxFont->getEmbeddedFontName() ? gfxFont->getEmbeddedFontName()->getCString() : (char *)NULL))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->getCString() : "(unnamed)"); delete fontLoc; goto err2; } break; default: // this shouldn't happen goto err2; } delete fontLoc; } // get the font matrix textMat = state->getTextMat(); fontSize = state->getFontSize(); 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 // and the substituted font substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx(); if (substIdx >= 0 && substIdx < 12) { fontScaleMin = 1; fontScaleAvg = 0; n = 0; for (code = 0; code < 256; ++code) { if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && name[0] && !name[1] && ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z') || (name[0] >= '0' && name[0] <= '9'))) { w = ((Gfx8BitFont *)gfxFont)->getWidth(code); builtinFontSubst[substIdx]->widths->getWidth(name, &ww); if (w > 0.01 && ww > 10) { w /= ww * 0.001; if (w < fontScaleMin) { fontScaleMin = w; } fontScaleAvg += w; ++n; } } } // if real font is narrower than substituted font, reduce the font // size accordingly -- this currently uses a scale factor halfway // between the minimum and average computed scale factors, which // is a bit of a kludge, but seems to produce mostly decent // results if (n) { fontScaleAvg /= n; if (fontScaleAvg < 1) { fontScale = 0.5 * (fontScaleMin + fontScaleAvg); m11 *= fontScale; m12 *= fontScale; } } } // create the scaled font mat[0] = m11; mat[1] = m12; 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; } void SplashOutputDev::stroke(GfxState *state) { SplashPath *path; if (state->getStrokeColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); path = convertPath(state, state->getPath(), gFalse); splash->stroke(path); delete path; } void SplashOutputDev::fill(GfxState *state) { SplashPath *path; if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); path = convertPath(state, state->getPath(), gTrue); splash->fill(path, gFalse); delete path; } void SplashOutputDev::eoFill(GfxState *state) { SplashPath *path; if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); path = convertPath(state, state->getPath(), gTrue); splash->fill(path, gTrue); delete path; } 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, double xStep, double yStep) { double tileXMin, tileYMin, tileXMax, tileYMax, tx, ty; int tileX0, tileY0, tileW, tileH, tileSize; SplashBitmap *origBitmap, *tileBitmap; Splash *origSplash; SplashColor color; double mat1[6]; double xa, ya, xb, yb, xc, yc; int x, y, xx, yy, i; // transform the four corners of the bbox from pattern space to // device space and compute the device space bbox state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4], bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5], &tx, &ty); tileXMin = tileXMax = tx; tileYMin = tileYMax = ty; state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4], bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5], &tx, &ty); if (tx < tileXMin) { tileXMin = tx; } else if (tx > tileXMax) { tileXMax = tx; } if (ty < tileYMin) { tileYMin = ty; } else if (ty > tileYMax) { tileYMax = ty; } state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4], bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5], &tx, &ty); if (tx < tileXMin) { tileXMin = tx; } else if (tx > tileXMax) { tileXMax = tx; } if (ty < tileYMin) { tileYMin = ty; } else if (ty > tileYMax) { tileYMax = ty; } state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4], bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5], &tx, &ty); if (tx < tileXMin) { tileXMin = tx; } else if (tx > tileXMax) { tileXMax = tx; } if (ty < tileYMin) { tileYMin = ty; } else if (ty > tileYMax) { tileYMax = ty; } if (tileXMin == tileXMax || tileYMin == tileYMax) { return; } tileX0 = (int)floor(tileXMin); tileY0 = (int)floor(tileYMin); tileW = (int)ceil(tileXMax) - tileX0; tileH = (int)ceil(tileYMax) - tileY0; // check for an excessively large tile size tileSize = tileW * tileH; if (tileSize > 1000000 || tileSize < 0) { mat1[0] = mat[0]; mat1[1] = mat[1]; mat1[2] = mat[2]; mat1[3] = mat[3]; for (y = y0; y < y1; ++y) { for (x = x0; x < x1; ++x) { xa = x * xStep; 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(strRef, resDict, mat1, bbox); } } return; } // create a temporary bitmap origBitmap = bitmap; origSplash = splash; bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad, 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; } splash->clear(color); ++nestCount; // copy the fill color (for uncolored tiling patterns) // (and stroke color, to handle buggy PDF files) splash->setFillPattern(origSplash->getFillPattern()->copy()); splash->setStrokePattern(origSplash->getStrokePattern()->copy()); // render the tile state->shiftCTM(-tileX0, -tileY0); updateCTM(state, 0, 0, 0, 0, 0, 0); gfx->drawForm(strRef, resDict, mat, bbox); state->shiftCTM(tileX0, tileY0); updateCTM(state, 0, 0, 0, 0, 0, 0); // restore the original bitmap --nestCount; delete splash; bitmap = origBitmap; splash = origSplash; splash->setOverprintMask(0xffffffff); // draw the tiles for (y = y0; y < y1; ++y) { for (x = x0; x < x1; ++x) { xa = x * xStep; ya = y * yStep; xb = xa * mat[0] + ya * mat[2]; yb = xa * mat[1] + ya * mat[3]; state->transformDelta(xb, yb, &xc, &yc); xx = (int)(xc + tileX0 + 0.5); yy = (int)(yc + tileY0 + 0.5); splash->composite(tileBitmap, 0, 0, xx, yy, tileW, tileH, gFalse, gFalse); } } delete tileBitmap; } void SplashOutputDev::clip(GfxState *state) { SplashPath *path; path = convertPath(state, state->getPath(), gTrue); splash->clipToPath(path, gFalse); delete path; } void SplashOutputDev::eoClip(GfxState *state) { SplashPath *path; path = convertPath(state, state->getPath(), gTrue); splash->clipToPath(path, gTrue); delete path; } void SplashOutputDev::clipToStrokePath(GfxState *state) { SplashPath *path, *path2; path = convertPath(state, state->getPath(), gFalse); path2 = splash->makeStrokePath(path, state->getLineWidth()); delete path; splash->clipToPath(path2, gFalse); delete path2; } SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path, GBool dropEmptySubpaths) { SplashPath *sPath; GfxSubpath *subpath; int n, i, j; n = dropEmptySubpaths ? 1 : 0; sPath = new SplashPath(); for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); if (subpath->getNumPoints() > n) { sPath->moveTo((SplashCoord)subpath->getX(0), (SplashCoord)subpath->getY(0)); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { sPath->curveTo((SplashCoord)subpath->getX(j), (SplashCoord)subpath->getY(j), (SplashCoord)subpath->getX(j+1), (SplashCoord)subpath->getY(j+1), (SplashCoord)subpath->getX(j+2), (SplashCoord)subpath->getY(j+2)); j += 3; } else { sPath->lineTo((SplashCoord)subpath->getX(j), (SplashCoord)subpath->getY(j)); ++j; } } if (subpath->isClosed()) { sPath->close(); } } } return sPath; } void SplashOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode *u, int uLen) { SplashPath *path; int render; GBool doFill, doStroke, doClip, strokeAdjust; double m[4]; GBool horiz; if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { return; } } // check for invisible text -- this is used by Acrobat Capture render = state->getRender(); if (render == 3) { return; } if (needFontUpdate) { doUpdateFont(state); } if (!font) { return; } x -= originX; y -= originY; doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); doStroke = ((render & 3) == 1 || (render & 3) == 2) && !state->getStrokeColorSpace()->isNonMarking(); doClip = render & 4; path = NULL; if (doStroke || doClip) { if ((path = font->getGlyphPath(code))) { path->offset((SplashCoord)x, (SplashCoord)y); } } // don't use stroke adjustment when stroking text -- the results // tend to be ugly (because characters with horizontal upper or // lower edges get misaligned relative to the other characters) strokeAdjust = gFalse; // make gcc happy if (doStroke) { strokeAdjust = splash->getStrokeAdjust(); splash->setStrokeAdjust(gFalse); } // fill and stroke if (doFill && doStroke) { if (path) { setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); splash->fill(path, gFalse); setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); splash->stroke(path); } // fill } else if (doFill) { setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); // stroke } else if (doStroke) { if (path) { setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); splash->stroke(path); } } // clip if (doClip) { if (path) { if (textClipPath) { textClipPath->append(path); } else { textClipPath = path; path = NULL; } } } if (doStroke) { splash->setStrokeAdjust(strokeAdjust); } if (path) { delete path; } } GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) { GfxFont *gfxFont; Ref *fontID; double *ctm, *bbox; T3FontCache *t3Font; T3GlyphStack *t3gs; GBool validBBox; double m[4]; GBool horiz; double x1, y1, xMin, yMin, xMax, yMax, xt, yt; int i, j; if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { return gTrue; } } if (!(gfxFont = state->getFont())) { return gFalse; } fontID = gfxFont->getID(); ctm = state->getCTM(); state->transform(0, 0, &xt, &yt); // is it the first (MRU) font in the cache? if (!(nT3Fonts > 0 && t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { // is the font elsewhere in the cache? for (i = 1; i < nT3Fonts; ++i) { if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { t3Font = t3FontCache[i]; for (j = i; j > 0; --j) { t3FontCache[j] = t3FontCache[j - 1]; } t3FontCache[0] = t3Font; break; } } if (i >= nT3Fonts) { // create new entry in the font cache if (nT3Fonts == splashOutT3FontCacheSize) { delete t3FontCache[nT3Fonts - 1]; --nT3Fonts; } for (j = nT3Fonts; j > 0; --j) { t3FontCache[j] = t3FontCache[j - 1]; } ++nT3Fonts; bbox = gfxFont->getFontBBox(); if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { // unspecified bounding box -- just take a guess xMin = xt - 5; xMax = xMin + 30; yMax = yt + 15; yMin = yMax - 45; validBBox = gFalse; } else { state->transform(bbox[0], bbox[1], &x1, &y1); xMin = xMax = x1; yMin = yMax = y1; state->transform(bbox[0], bbox[3], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(bbox[2], bbox[1], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(bbox[2], bbox[3], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } validBBox = gTrue; } t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], (int)floor(xMin - xt) - 2, (int)floor(yMin - yt) - 2, (int)ceil(xMax) - (int)floor(xMin) + 4, (int)ceil(yMax) - (int)floor(yMin) + 4, validBBox, colorMode != splashModeMono1); } } t3Font = t3FontCache[0]; // is the glyph in the cache? i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; for (j = 0; j < t3Font->cacheAssoc; ++j) { if ((t3Font->cacheTags[i+j].mru & 0x8000) && t3Font->cacheTags[i+j].code == code) { drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j], t3Font->cacheData + (i+j) * t3Font->glyphSize); return gTrue; } } // push a new Type 3 glyph record t3gs = new T3GlyphStack(); t3gs->next = t3GlyphStack; t3GlyphStack = t3gs; t3GlyphStack->code = code; t3GlyphStack->cache = t3Font; t3GlyphStack->cacheTag = NULL; t3GlyphStack->cacheData = NULL; t3GlyphStack->haveDx = gFalse; t3GlyphStack->doNotCache = gFalse; return gFalse; } void SplashOutputDev::endType3Char(GfxState *state) { T3GlyphStack *t3gs; double *ctm; if (t3GlyphStack->cacheTag) { --nestCount; memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), t3GlyphStack->cache->glyphSize); delete bitmap; delete splash; bitmap = t3GlyphStack->origBitmap; splash = t3GlyphStack->origSplash; ctm = state->getCTM(); state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); updateCTM(state, 0, 0, 0, 0, 0, 0); drawType3Glyph(state, t3GlyphStack->cache, t3GlyphStack->cacheTag, t3GlyphStack->cacheData); } t3gs = t3GlyphStack; t3GlyphStack = t3gs->next; delete t3gs; } void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { t3GlyphStack->haveDx = gTrue; } void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { double *ctm; T3FontCache *t3Font; SplashColor color; double xt, yt, xMin, xMax, yMin, yMax, x1, y1; int i, j; // ignore multiple d0/d1 operators if (t3GlyphStack->haveDx) { return; } t3GlyphStack->haveDx = gTrue; // don't cache if we got a gsave/grestore before the d1 if (t3GlyphStack->doNotCache) { return; } t3Font = t3GlyphStack->cache; // check for a valid bbox state->transform(0, 0, &xt, &yt); state->transform(llx, lly, &x1, &y1); xMin = xMax = x1; yMin = yMax = y1; state->transform(llx, ury, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(urx, lly, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(urx, ury, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } if (xMin - xt < t3Font->glyphX || yMin - yt < t3Font->glyphY || xMax - xt > t3Font->glyphX + t3Font->glyphW || yMax - yt > t3Font->glyphY + t3Font->glyphH) { if (t3Font->validBBox) { error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph"); } return; } // allocate a cache entry i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; for (j = 0; j < t3Font->cacheAssoc; ++j) { if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { t3Font->cacheTags[i+j].mru = 0x8000; t3Font->cacheTags[i+j].code = t3GlyphStack->code; t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; } else { ++t3Font->cacheTags[i+j].mru; } } // save state t3GlyphStack->origBitmap = bitmap; t3GlyphStack->origSplash = splash; ctm = state->getCTM(); t3GlyphStack->origCTM4 = ctm[4]; t3GlyphStack->origCTM5 = ctm[5]; // create the temporary bitmap if (colorMode == splashModeMono1) { bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono1, gFalse); splash = new Splash(bitmap, gFalse, t3GlyphStack->origSplash->getScreen()); color[0] = 0; splash->clear(color); color[0] = 0xff; } else { bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono8, gFalse); splash = new Splash(bitmap, vectorAntialias, t3GlyphStack->origSplash->getScreen()); color[0] = 0x00; splash->clear(color); color[0] = 0xff; } splash->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? //~ [this is likely the same situation as in beginTransparencyGroup()] state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], -t3Font->glyphX, -t3Font->glyphY); updateCTM(state, 0, 0, 0, 0, 0, 0); ++nestCount; } void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag *tag, Guchar *data) { SplashGlyphBitmap glyph; setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); glyph.x = -t3Font->glyphX; glyph.y = -t3Font->glyphY; glyph.w = t3Font->glyphW; glyph.h = t3Font->glyphH; glyph.aa = colorMode != splashModeMono1; glyph.data = data; glyph.freeData = gFalse; splash->fillGlyph(0, 0, &glyph); } void SplashOutputDev::endTextObject(GfxState *state) { if (textClipPath) { splash->clipToPath(textClipPath, gFalse); delete textClipPath; textClipPath = NULL; } } struct SplashOutImageMaskData { ImageStream *imgStr; GBool invert; int width, height, y; }; GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; Guchar *p; SplashColorPtr q; int x; if (imgMaskData->y == imgMaskData->height || !(p = imgMaskData->imgStr->getLine())) { memset(line, 0, imgMaskData->width); return gFalse; } for (x = 0, q = line; x < imgMaskData->width; ++x) { *q++ = *p++ ^ imgMaskData->invert; } ++imgMaskData->y; return gTrue; } void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; reduceImageResolution(str, ctm, &width, &height); imgMaskData.imgStr = new ImageStream(str, width, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? 0 : 1; imgMaskData.width = width; imgMaskData.height = height; imgMaskData.y = 0; splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL, interpolate); if (inlineImg) { while (imgMaskData.y < height) { imgMaskData.imgStr->getLine(); ++imgMaskData.y; } } delete imgMaskData.imgStr; str->close(); } void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; reduceImageResolution(str, ctm, &width, &height); imgMaskData.imgStr = new ImageStream(str, width, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? 0 : 1; imgMaskData.width = width; imgMaskData.height = height; imgMaskData.y = 0; maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); maskSplash = new Splash(maskBitmap, gTrue); 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, interpolate); delete imgMaskData.imgStr; str->close(); delete maskSplash; splash->setSoftMask(maskBitmap); } struct SplashOutImageData { ImageStream *imgStr; GfxImageColorMap *colorMap; SplashColorPtr lookup; int *maskColors; SplashColorMode colorMode; int width, height, y; }; GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, Guchar *alphaLine) { SplashOutImageData *imgData = (SplashOutImageData *)data; Guchar *p; SplashColorPtr q, col; int nComps, x; if (imgData->y == imgData->height || !(p = imgData->imgStr->getLine())) { memset(colorLine, 0, imgData->width * splashColorModeNComps[imgData->colorMode]); return gFalse; } nComps = imgData->colorMap->getNumPixelComps(); if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { *q++ = imgData->lookup[*p]; } break; case splashModeRGB8: case splashModeBGR8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; } break; #endif } } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width); break; case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width); break; #if SPLASH_CMYK case splashModeCMYK8: imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width); break; #endif } } ++imgData->y; return gTrue; } GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, Guchar *alphaLine) { SplashOutImageData *imgData = (SplashOutImageData *)data; Guchar *p, *aq; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; #if SPLASH_CMYK GfxCMYK cmyk; #endif Guchar alpha; int nComps, x, i; if (imgData->y == imgData->height || !(p = imgData->imgStr->getLine())) { memset(colorLine, 0, imgData->width * splashColorModeNComps[imgData->colorMode]); memset(alphaLine, 0, imgData->width); return gFalse; } nComps = imgData->colorMap->getNumPixelComps(); for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { alpha = 0; for (i = 0; i < nComps; ++i) { if (p[i] < imgData->maskColors[2*i] || p[i] > imgData->maskColors[2*i+1]) { alpha = 0xff; break; } } if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: *q++ = imgData->lookup[*p]; break; case splashModeRGB8: case splashModeBGR8: col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; break; #if SPLASH_CMYK case splashModeCMYK8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; break; #endif } *aq++ = alpha; } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); break; case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); break; #if SPLASH_CMYK case splashModeCMYK8: imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); break; #endif } *aq++ = alpha; } } ++imgData->y; return gTrue; } void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageData imgData; SplashColorMode srcMode; SplashImageSource src; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif Guchar pix; int n, i; setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), NULL); ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; reduceImageResolution(str, ctm, &width, &height); imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.maskColors = maskColors; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = NULL; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 3); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3*i] = colToByte(rgb.r); imgData.lookup[3*i+1] = colToByte(rgb.g); imgData.lookup[3*i+2] = colToByte(rgb.b); } break; #if SPLASH_CMYK case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } break; #endif } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else if (colorMode == splashModeBGR8) { srcMode = splashModeRGB8; } else { srcMode = colorMode; } src = maskColors ? &alphaImageSrc : &imageSrc; splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse, width, height, mat, interpolate); if (inlineImg) { while (imgData.y < height) { imgData.imgStr->getLine(); ++imgData.y; } } gfree(imgData.lookup); delete imgData.imgStr; str->close(); } struct SplashOutMaskedImageData { ImageStream *imgStr; GfxImageColorMap *colorMap; SplashBitmap *mask; SplashColorPtr lookup; SplashColorMode colorMode; int width, height, y; }; GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, Guchar *alphaLine) { SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; Guchar *p, *aq; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; #if SPLASH_CMYK GfxCMYK cmyk; #endif static Guchar bitToByte[2] = {0x00, 0xff}; Guchar alpha; Guchar *maskPtr; int maskShift; int nComps, x; if (imgData->y == imgData->height || !(p = imgData->imgStr->getLine())) { memset(colorLine, 0, imgData->width * splashColorModeNComps[imgData->colorMode]); memset(alphaLine, 0, imgData->width); return gFalse; } nComps = imgData->colorMap->getNumPixelComps(); maskPtr = imgData->mask->getDataPtr() + imgData->y * imgData->mask->getRowSize(); maskShift = 7; for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { alpha = bitToByte[(*maskPtr >> maskShift) & 1]; maskPtr += (8 - maskShift) >> 3; maskShift = (maskShift - 1) & 7; if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: *q++ = imgData->lookup[*p]; break; case splashModeRGB8: case splashModeBGR8: col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; break; #if SPLASH_CMYK case splashModeCMYK8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; break; #endif } *aq++ = alpha; } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); break; case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); break; #if SPLASH_CMYK case splashModeCMYK8: imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); break; #endif } *aq++ = alpha; } } ++imgData->y; return gTrue; } void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { GfxImageColorMap *maskColorMap; Object maskDecode, decodeLow, decodeHigh; double *ctm; SplashCoord mat[6]; SplashOutMaskedImageData imgData; SplashOutImageMaskData imgMaskData; SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif Guchar pix; int n, i; 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) { decodeLow.initInt(maskInvert ? 0 : 1); decodeHigh.initInt(maskInvert ? 1 : 0); maskDecode.initArray(xref); maskDecode.arrayAdd(&decodeLow); maskDecode.arrayAdd(&decodeHigh); maskColorMap = new GfxImageColorMap(1, &maskDecode, new GfxDeviceGrayColorSpace()); maskDecode.free(); drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap, interpolate); delete maskColorMap; } else { //----- scale the mask image to the same size as the source image mat[0] = (SplashCoord)width; mat[1] = 0; mat[2] = 0; mat[3] = (SplashCoord)height; mat[4] = 0; mat[5] = 0; imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = maskInvert ? 0 : 1; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse); maskSplash = new Splash(maskBitmap, gFalse); 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, gTrue, interpolate); delete imgMaskData.imgStr; maskStr->close(); delete maskSplash; //----- draw the source image mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.mask = maskBitmap; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = NULL; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 3); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3*i] = colToByte(rgb.r); imgData.lookup[3*i+1] = colToByte(rgb.g); imgData.lookup[3*i+2] = colToByte(rgb.b); } break; #if SPLASH_CMYK case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } break; #endif } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else if (colorMode == splashModeBGR8) { srcMode = splashModeRGB8; } else { srcMode = colorMode; } splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue, width, height, mat, interpolate); delete maskBitmap; gfree(imgData.lookup); delete imgData.imgStr; str->close(); } } void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, GBool interpolate) { double *ctm; SplashCoord mat[6]; SplashOutImageData imgData; SplashOutImageData imgMaskData; SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif Guchar pix; int n, i; setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), NULL); ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; reduceImageResolution(str, ctm, &width, &height); reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight); //----- set up the soft mask imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); imgMaskData.imgStr->reset(); imgMaskData.colorMap = maskColorMap; imgMaskData.maskColors = NULL; imgMaskData.colorMode = splashModeMono8; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; n = 1 << maskColorMap->getBits(); imgMaskData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (Guchar)i; maskColorMap->getGray(&pix, &gray); imgMaskData.lookup[i] = colToByte(gray); } maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); maskSplash = new Splash(maskBitmap, vectorAntialias); maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust()); clearMaskRegion(state, maskSplash, 0, 0, 1, 1); maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse, maskWidth, maskHeight, mat, interpolate); delete imgMaskData.imgStr; maskStr->close(); gfree(imgMaskData.lookup); delete maskSplash; splash->setSoftMask(maskBitmap); //----- draw the source image imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.maskColors = NULL; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = NULL; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 3); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3*i] = colToByte(rgb.r); imgData.lookup[3*i+1] = colToByte(rgb.g); imgData.lookup[3*i+2] = colToByte(rgb.b); } break; #if SPLASH_CMYK case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (Guchar)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } break; #endif } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else if (colorMode == splashModeBGR8) { srcMode = splashModeRGB8; } else { srcMode = colorMode; } splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat, interpolate); splash->setSoftMask(NULL); gfree(imgData.lookup); delete imgData.imgStr; 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, GBool forSoftMask) { SplashTransparencyGroup *transpGroup; SplashColor color; double xMin, yMin, xMax, yMax, x, y; int tx, ty, w, h, i; // transform the bbox state->transform(bbox[0], bbox[1], &x, &y); xMin = xMax = x; yMin = yMax = y; state->transform(bbox[0], bbox[3], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } state->transform(bbox[2], bbox[1], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } state->transform(bbox[2], bbox[3], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } tx = (int)floor(xMin); if (tx < 0) { tx = 0; } else if (tx >= bitmap->getWidth()) { tx = bitmap->getWidth() - 1; } ty = (int)floor(yMin); if (ty < 0) { ty = 0; } else if (ty >= bitmap->getHeight()) { ty = bitmap->getHeight() - 1; } w = (int)ceil(xMax) - tx + 1; if (tx + w > bitmap->getWidth()) { w = bitmap->getWidth() - tx; } if (w < 1) { w = 1; } h = (int)ceil(yMax) - ty + 1; if (ty + h > bitmap->getHeight()) { h = bitmap->getHeight() - ty; } if (h < 1) { h = 1; } // push a new stack entry transpGroup = new SplashTransparencyGroup(); transpGroup->tx = tx; transpGroup->ty = ty; transpGroup->blendingColorSpace = blendingColorSpace; transpGroup->isolated = isolated; transpGroup->next = transpGroupStack; transpGroupStack = transpGroup; // save state transpGroup->origBitmap = bitmap; transpGroup->origSplash = splash; //~ this handles the blendingColorSpace arg for soft masks, but //~ not yet for transparency groups // switch to the blending color space if (forSoftMask && isolated && !knockout && blendingColorSpace) { if (blendingColorSpace->getMode() == csDeviceGray || blendingColorSpace->getMode() == csCalGray || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 1)) { colorMode = splashModeMono8; } else if (blendingColorSpace->getMode() == csDeviceRGB || blendingColorSpace->getMode() == csCalRGB || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 3)) { //~ does this need to use BGR8? colorMode = splashModeRGB8; #if SPLASH_CMYK } else if (blendingColorSpace->getMode() == csDeviceCMYK || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 4)) { colorMode = splashModeCMYK8; #endif } } // create the temporary bitmap bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, bitmapTopDown); 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) //~ [this is likely the same situation as in type3D1()] splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); splash->setStrokePattern( transpGroup->origSplash->getStrokePattern()->copy()); if (isolated) { for (i = 0; i < splashMaxColorComps; ++i) { color[i] = 0; } splash->clear(color, 0); } else { splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); } splash->setInTransparencyGroup(transpGroup->origBitmap, tx, ty, !isolated, knockout); transpGroup->tBitmap = bitmap; state->shiftCTM(-tx, -ty); updateCTM(state, 0, 0, 0, 0, 0, 0); ++nestCount; } void SplashOutputDev::endTransparencyGroup(GfxState *state) { // restore state --nestCount; delete splash; bitmap = transpGroupStack->origBitmap; colorMode = bitmap->getMode(); splash = transpGroupStack->origSplash; state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty); updateCTM(state, 0, 0, 0, 0, 0, 0); } void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) { SplashBitmap *tBitmap; SplashTransparencyGroup *transpGroup; GBool isolated; int tx, ty; tx = transpGroupStack->tx; ty = transpGroupStack->ty; tBitmap = transpGroupStack->tBitmap; isolated = transpGroupStack->isolated; // paint the transparency group onto the parent bitmap // - the clip path was set in the parent's state) if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { splash->setOverprintMask(0xffffffff); splash->composite(tBitmap, 0, 0, tx, ty, tBitmap->getWidth(), tBitmap->getHeight(), gFalse, !isolated); } // pop the stack transpGroup = transpGroupStack; transpGroupStack = transpGroup->next; delete transpGroup; delete tBitmap; } void SplashOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor) { SplashBitmap *softMask, *tBitmap; Splash *tSplash; SplashTransparencyGroup *transpGroup; SplashColor color; SplashColorPtr p; GfxGray gray; GfxRGB rgb; #if SPLASH_CMYK GfxCMYK cmyk; #endif double backdrop, backdrop2, lum, lum2; int tx, ty, x, y; tx = transpGroupStack->tx; ty = transpGroupStack->ty; 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: // transparency is not supported in mono1 mode 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); tSplash->compositeBackground(color); break; #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); color[3] = colToByte(cmyk.k); tSplash->compositeBackground(color); break; #endif } delete tSplash; } } if (transferFunc) { transferFunc->transform(&backdrop, &backdrop2); } else { backdrop2 = backdrop; } softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse); 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; for (y = 0; y < tBitmap->getHeight(); ++y) { for (x = 0; x < tBitmap->getWidth(); ++x) { if (alpha) { lum = tBitmap->getAlpha(x, y) / 255.0; } else { tBitmap->getPixel(x, y, color); // convert to luminosity switch (tBitmap->getMode()) { case splashModeMono1: case splashModeMono8: lum = color[0] / 255.0; break; case splashModeRGB8: case splashModeBGR8: lum = (0.3 / 255.0) * color[0] + (0.59 / 255.0) * color[1] + (0.11 / 255.0) * color[2]; break; #if SPLASH_CMYK case splashModeCMYK8: lum = (1 - color[3] / 255.0) - (0.3 / 255.0) * color[0] - (0.59 / 255.0) * color[1] - (0.11 / 255.0) * color[2]; if (lum < 0) { lum = 0; } break; #endif } } if (transferFunc) { transferFunc->transform(&lum, &lum2); } else { lum2 = lum; } p[x] = (int)(lum2 * 255.0 + 0.5); } p += softMask->getRowSize(); } } splash->setSoftMask(softMask); // pop the stack transpGroup = transpGroupStack; transpGroupStack = transpGroup->next; delete transpGroup; delete tBitmap; } void SplashOutputDev::clearSoftMask(GfxState *state) { splash->setSoftMask(NULL); } void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) { splashColorCopy(paperColor, paperColorA); } int SplashOutputDev::getBitmapWidth() { return bitmap->getWidth(); } int SplashOutputDev::getBitmapHeight() { return bitmap->getHeight(); } SplashBitmap *SplashOutputDev::takeBitmap() { SplashBitmap *ret; ret = bitmap; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); return ret; } void SplashOutputDev::getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) { splash->getModRegion(xMin, yMin, xMax, yMax); } void SplashOutputDev::clearModRegion() { splash->clearModRegion(); } void SplashOutputDev::setFillColor(int r, int g, int b) { GfxRGB rgb; GfxGray gray; #if SPLASH_CMYK GfxCMYK cmyk; #endif rgb.r = byteToCol(r); rgb.g = byteToCol(g); rgb.b = byteToCol(b); switch (colorMode) { case splashModeMono1: case splashModeMono8: gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5); if (gray > gfxColorComp1) { gray = gfxColorComp1; } splash->setFillPattern(getColor(gray)); break; case splashModeRGB8: case splashModeBGR8: splash->setFillPattern(getColor(&rgb)); break; #if SPLASH_CMYK case splashModeCMYK8: cmyk.c = gfxColorComp1 - rgb.r; cmyk.m = gfxColorComp1 - rgb.g; cmyk.y = gfxColorComp1 - rgb.b; cmyk.k = 0; splash->setFillPattern(getColor(&cmyk)); break; #endif } } SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) { Ref ref; SplashOutFontFileID *id; GfxFontLoc *fontLoc; #if LOAD_FONTS_FROM_MEM GString *fontBuf; FILE *extFontFile; char blk[4096]; int n; #endif SplashFontFile *fontFile; SplashFont *fontObj; FoFiTrueType *ff; int *codeToGID; Unicode u; SplashCoord textMat[4]; SplashCoord oblique; int cmap, i; for (i = 0; i < nBuiltinFonts; ++i) { if (!name->cmp(builtinFonts[i].name)) { break; } } if (i == nBuiltinFonts) { return NULL; } ref.num = i; ref.gen = -1; id = new SplashOutFontFileID(&ref); // check the font file cache if ((fontFile = fontEngine->getFontFile(id))) { delete id; // load the font file } else { 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, #if LOAD_FONTS_FROM_MEM fontBuf, #else fontLoc->path->getCString(), gFalse, #endif winAnsiEncoding); } else if (fontLoc->fontType == fontTrueType) { #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; } for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) { if ((ff->getCmapPlatform(cmap) == 3 && ff->getCmapEncoding(cmap) == 1) || ff->getCmapPlatform(cmap) == 0) { break; } } if (cmap == ff->getNumCmaps()) { delete ff; delete fontLoc; delete id; return NULL; } codeToGID = (int *)gmallocn(256, sizeof(int)); for (i = 0; i < 256; ++i) { codeToGID[i] = 0; if (winAnsiEncoding[i] && (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) { codeToGID[i] = ff->mapCodeToGID(cmap, u); } } delete ff; fontFile = fontEngine->loadTrueTypeFont(id, #if LOAD_FONTS_FROM_MEM fontBuf, #else fontLoc->path->getCString(), gFalse, #endif fontLoc->fontNum, codeToGID, 256, NULL); } else { delete fontLoc; delete id; return NULL; } delete fontLoc; } if (!fontFile) { return NULL; } // create the scaled font oblique = (SplashCoord) ((SplashOutFontFileID *)fontFile->getID())->getOblique(); textMat[0] = (SplashCoord)textMatA[0]; textMat[1] = (SplashCoord)textMatA[1]; textMat[2] = oblique * textMatA[0] + textMatA[2]; textMat[3] = oblique * textMatA[1] + textMatA[3]; fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix()); return fontObj; } #if 1 //~tmp: turn off anti-aliasing temporarily void SplashOutputDev::setInShading(GBool sh) { splash->setInShading(sh); } #endif xpdf-3.04/xpdf/Function.h0000644000076400007640000001454212341430012014606 0ustar dereknderekn//======================================================================== // // Function.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef FUNCTION_H #define FUNCTION_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" class GList; class Dict; class Stream; struct PSCode; //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ #define funcMaxInputs 32 #define funcMaxOutputs 32 #define sampledFuncMaxInputs 16 class Function { public: Function(); virtual ~Function(); // Construct a function. Returns NULL if unsuccessful. static Function *parse(Object *funcObj, int recursion = 0); // Initialize the entries common to all function types. GBool init(Dict *dict); virtual Function *copy() = 0; // Return the function type: // -1 : identity // 0 : sampled // 2 : exponential // 3 : stitching // 4 : PostScript virtual int getType() = 0; // Return size of input and output tuples. int getInputSize() { return m; } int getOutputSize() { return n; } double getDomainMin(int i) { return domain[i][0]; } double getDomainMax(int i) { return domain[i][1]; } double getRangeMin(int i) { return range[i][0]; } double getRangeMax(int i) { return range[i][1]; } GBool getHasRange() { return hasRange; } // Transform an input tuple into an output tuple. virtual void transform(double *in, double *out) = 0; virtual GBool isOk() = 0; protected: int m, n; // size of input and output tuples double // min and max values for function domain domain[funcMaxInputs][2]; double // min and max values for function range range[funcMaxOutputs][2]; GBool hasRange; // set if range is defined }; //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ class IdentityFunction: public Function { public: IdentityFunction(); virtual ~IdentityFunction(); virtual Function *copy() { return new IdentityFunction(); } virtual int getType() { return -1; } virtual void transform(double *in, double *out); virtual GBool isOk() { return gTrue; } private: }; //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ class SampledFunction: public Function { public: SampledFunction(Object *funcObj, Dict *dict); virtual ~SampledFunction(); virtual Function *copy() { return new SampledFunction(this); } virtual int getType() { return 0; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getSampleSize(int i) { return sampleSize[i]; } double getEncodeMin(int i) { return encode[i][0]; } double getEncodeMax(int i) { return encode[i][1]; } double getDecodeMin(int i) { return decode[i][0]; } double getDecodeMax(int i) { return decode[i][1]; } double *getSamples() { return samples; } private: SampledFunction(SampledFunction *func); int // number of samples for each domain element sampleSize[funcMaxInputs]; double // min and max values for domain encoder encode[funcMaxInputs][2]; double // min and max values for range decoder decode[funcMaxOutputs][2]; double // input multipliers inputMul[funcMaxInputs]; int *idxOffset; double *samples; // the samples int nSamples; // size of the samples array double *sBuf; // buffer for the transform function double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ class ExponentialFunction: public Function { public: ExponentialFunction(Object *funcObj, Dict *dict); virtual ~ExponentialFunction(); virtual Function *copy() { return new ExponentialFunction(this); } virtual int getType() { return 2; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } double *getC0() { return c0; } double *getC1() { return c1; } double getE() { return e; } private: ExponentialFunction(ExponentialFunction *func); double c0[funcMaxOutputs]; double c1[funcMaxOutputs]; double e; GBool ok; }; //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ class StitchingFunction: public Function { public: StitchingFunction(Object *funcObj, Dict *dict, int recursion); virtual ~StitchingFunction(); virtual Function *copy() { return new StitchingFunction(this); } virtual int getType() { return 3; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getNumFuncs() { return k; } Function *getFunc(int i) { return funcs[i]; } double *getBounds() { return bounds; } double *getEncode() { return encode; } double *getScale() { return scale; } private: StitchingFunction(StitchingFunction *func); int k; Function **funcs; double *bounds; double *encode; double *scale; GBool ok; }; //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ class PostScriptFunction: public Function { public: PostScriptFunction(Object *funcObj, Dict *dict); virtual ~PostScriptFunction(); virtual Function *copy() { return new PostScriptFunction(this); } virtual int getType() { return 4; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } GString *getCodeString() { return codeString; } private: PostScriptFunction(PostScriptFunction *func); GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); void addCode(int *codePtr, int op); void addCodeI(int *codePtr, int op, int x); void addCodeD(int *codePtr, int op, double x); GString *getToken(Stream *str); int exec(double *stack, int sp0); GString *codeString; PSCode *code; int codeLen; int codeSize; double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; #endif xpdf-3.04/xpdf/NameToUnicodeTable.h0000644000076400007640000034445512341430012016474 0ustar dereknderekn//======================================================================== // // NameToUnicodeTable.h // // Copyright 2001-2004 Glyph & Cog, LLC // //======================================================================== static struct { Unicode u; const char *name; } nameToUnicodeTab[] = { {0x0021, "!"}, {0x0023, "#"}, {0x0024, "$"}, {0x0025, "%"}, {0x0026, "&"}, {0x0027, "'"}, {0x0028, "("}, {0x0029, ")"}, {0x002a, "*"}, {0x002b, "+"}, {0x002c, ","}, {0x002d, "-"}, {0x002e, "."}, {0x002f, "/"}, {0x0030, "0"}, {0x0031, "1"}, {0x0032, "2"}, {0x0033, "3"}, {0x0034, "4"}, {0x0035, "5"}, {0x0036, "6"}, {0x0037, "7"}, {0x0038, "8"}, {0x0039, "9"}, {0x003a, ":"}, {0x003b, ";"}, {0x003c, "<"}, {0x003d, "="}, {0x003e, ">"}, {0x003f, "?"}, {0x0040, "@"}, {0x0041, "A"}, {0x00c6, "AE"}, {0x01fc, "AEacute"}, {0x01e2, "AEmacron"}, {0xf7e6, "AEsmall"}, {0x00c1, "Aacute"}, {0xf7e1, "Aacutesmall"}, {0x0102, "Abreve"}, {0x1eae, "Abreveacute"}, {0x04d0, "Abrevecyrillic"}, {0x1eb6, "Abrevedotbelow"}, {0x1eb0, "Abrevegrave"}, {0x1eb2, "Abrevehookabove"}, {0x1eb4, "Abrevetilde"}, {0x01cd, "Acaron"}, {0x24b6, "Acircle"}, {0x00c2, "Acircumflex"}, {0x1ea4, "Acircumflexacute"}, {0x1eac, "Acircumflexdotbelow"}, {0x1ea6, "Acircumflexgrave"}, {0x1ea8, "Acircumflexhookabove"}, {0xf7e2, "Acircumflexsmall"}, {0x1eaa, "Acircumflextilde"}, {0xf6c9, "Acute"}, {0xf7b4, "Acutesmall"}, {0x0410, "Acyrillic"}, {0x0200, "Adblgrave"}, {0x00c4, "Adieresis"}, {0x04d2, "Adieresiscyrillic"}, {0x01de, "Adieresismacron"}, {0xf7e4, "Adieresissmall"}, {0x1ea0, "Adotbelow"}, {0x01e0, "Adotmacron"}, {0x00c0, "Agrave"}, {0xf7e0, "Agravesmall"}, {0x1ea2, "Ahookabove"}, {0x04d4, "Aiecyrillic"}, {0x0202, "Ainvertedbreve"}, {0x0391, "Alpha"}, {0x0386, "Alphatonos"}, {0x0100, "Amacron"}, {0xff21, "Amonospace"}, {0x0104, "Aogonek"}, {0x00c5, "Aring"}, {0x01fa, "Aringacute"}, {0x1e00, "Aringbelow"}, {0xf7e5, "Aringsmall"}, {0xf761, "Asmall"}, {0x00c3, "Atilde"}, {0xf7e3, "Atildesmall"}, {0x0531, "Aybarmenian"}, {0x0042, "B"}, {0x24b7, "Bcircle"}, {0x1e02, "Bdotaccent"}, {0x1e04, "Bdotbelow"}, {0x0411, "Becyrillic"}, {0x0532, "Benarmenian"}, {0x0392, "Beta"}, {0x0181, "Bhook"}, {0x1e06, "Blinebelow"}, {0xff22, "Bmonospace"}, {0xf6f4, "Brevesmall"}, {0xf762, "Bsmall"}, {0x0182, "Btopbar"}, {0x0043, "C"}, {0x053e, "Caarmenian"}, {0x0106, "Cacute"}, {0xf6ca, "Caron"}, {0xf6f5, "Caronsmall"}, {0x010c, "Ccaron"}, {0x00c7, "Ccedilla"}, {0x1e08, "Ccedillaacute"}, {0xf7e7, "Ccedillasmall"}, {0x24b8, "Ccircle"}, {0x0108, "Ccircumflex"}, {0x010a, "Cdot"}, {0x010a, "Cdotaccent"}, {0xf7b8, "Cedillasmall"}, {0x0549, "Chaarmenian"}, {0x04bc, "Cheabkhasiancyrillic"}, {0x0427, "Checyrillic"}, {0x04be, "Chedescenderabkhasiancyrillic"}, {0x04b6, "Chedescendercyrillic"}, {0x04f4, "Chedieresiscyrillic"}, {0x0543, "Cheharmenian"}, {0x04cb, "Chekhakassiancyrillic"}, {0x04b8, "Cheverticalstrokecyrillic"}, {0x03a7, "Chi"}, {0x0187, "Chook"}, {0xf6f6, "Circumflexsmall"}, {0xff23, "Cmonospace"}, {0x0551, "Coarmenian"}, {0xf763, "Csmall"}, {0x0044, "D"}, {0x01f1, "DZ"}, {0x01c4, "DZcaron"}, {0x0534, "Daarmenian"}, {0x0189, "Dafrican"}, {0x010e, "Dcaron"}, {0x1e10, "Dcedilla"}, {0x24b9, "Dcircle"}, {0x1e12, "Dcircumflexbelow"}, {0x0110, "Dcroat"}, {0x1e0a, "Ddotaccent"}, {0x1e0c, "Ddotbelow"}, {0x0414, "Decyrillic"}, {0x03ee, "Deicoptic"}, {0x2206, "Delta"}, {0x0394, "Deltagreek"}, {0x018a, "Dhook"}, {0xf6cb, "Dieresis"}, {0xf6cc, "DieresisAcute"}, {0xf6cd, "DieresisGrave"}, {0xf7a8, "Dieresissmall"}, {0x03dc, "Digammagreek"}, {0x0402, "Djecyrillic"}, {0x1e0e, "Dlinebelow"}, {0xff24, "Dmonospace"}, {0xf6f7, "Dotaccentsmall"}, {0x0110, "Dslash"}, {0xf764, "Dsmall"}, {0x018b, "Dtopbar"}, {0x01f2, "Dz"}, {0x01c5, "Dzcaron"}, {0x04e0, "Dzeabkhasiancyrillic"}, {0x0405, "Dzecyrillic"}, {0x040f, "Dzhecyrillic"}, {0x0045, "E"}, {0x00c9, "Eacute"}, {0xf7e9, "Eacutesmall"}, {0x0114, "Ebreve"}, {0x011a, "Ecaron"}, {0x1e1c, "Ecedillabreve"}, {0x0535, "Echarmenian"}, {0x24ba, "Ecircle"}, {0x00ca, "Ecircumflex"}, {0x1ebe, "Ecircumflexacute"}, {0x1e18, "Ecircumflexbelow"}, {0x1ec6, "Ecircumflexdotbelow"}, {0x1ec0, "Ecircumflexgrave"}, {0x1ec2, "Ecircumflexhookabove"}, {0xf7ea, "Ecircumflexsmall"}, {0x1ec4, "Ecircumflextilde"}, {0x0404, "Ecyrillic"}, {0x0204, "Edblgrave"}, {0x00cb, "Edieresis"}, {0xf7eb, "Edieresissmall"}, {0x0116, "Edot"}, {0x0116, "Edotaccent"}, {0x1eb8, "Edotbelow"}, {0x0424, "Efcyrillic"}, {0x00c8, "Egrave"}, {0xf7e8, "Egravesmall"}, {0x0537, "Eharmenian"}, {0x1eba, "Ehookabove"}, {0x2167, "Eightroman"}, {0x0206, "Einvertedbreve"}, {0x0464, "Eiotifiedcyrillic"}, {0x041b, "Elcyrillic"}, {0x216a, "Elevenroman"}, {0x0112, "Emacron"}, {0x1e16, "Emacronacute"}, {0x1e14, "Emacrongrave"}, {0x041c, "Emcyrillic"}, {0xff25, "Emonospace"}, {0x041d, "Encyrillic"}, {0x04a2, "Endescendercyrillic"}, {0x014a, "Eng"}, {0x04a4, "Enghecyrillic"}, {0x04c7, "Enhookcyrillic"}, {0x0118, "Eogonek"}, {0x0190, "Eopen"}, {0x0395, "Epsilon"}, {0x0388, "Epsilontonos"}, {0x0420, "Ercyrillic"}, {0x018e, "Ereversed"}, {0x042d, "Ereversedcyrillic"}, {0x0421, "Escyrillic"}, {0x04aa, "Esdescendercyrillic"}, {0x01a9, "Esh"}, {0xf765, "Esmall"}, {0x0397, "Eta"}, {0x0538, "Etarmenian"}, {0x0389, "Etatonos"}, {0x00d0, "Eth"}, {0xf7f0, "Ethsmall"}, {0x1ebc, "Etilde"}, {0x1e1a, "Etildebelow"}, {0x20ac, "Euro"}, {0x01b7, "Ezh"}, {0x01ee, "Ezhcaron"}, {0x01b8, "Ezhreversed"}, {0x0046, "F"}, {0x24bb, "Fcircle"}, {0x1e1e, "Fdotaccent"}, {0x0556, "Feharmenian"}, {0x03e4, "Feicoptic"}, {0x0191, "Fhook"}, {0x0472, "Fitacyrillic"}, {0x2164, "Fiveroman"}, {0xff26, "Fmonospace"}, {0x2163, "Fourroman"}, {0xf766, "Fsmall"}, {0x0047, "G"}, {0x3387, "GBsquare"}, {0x01f4, "Gacute"}, {0x0393, "Gamma"}, {0x0194, "Gammaafrican"}, {0x03ea, "Gangiacoptic"}, {0x011e, "Gbreve"}, {0x01e6, "Gcaron"}, {0x0122, "Gcedilla"}, {0x24bc, "Gcircle"}, {0x011c, "Gcircumflex"}, {0x0122, "Gcommaaccent"}, {0x0120, "Gdot"}, {0x0120, "Gdotaccent"}, {0x0413, "Gecyrillic"}, {0x0542, "Ghadarmenian"}, {0x0494, "Ghemiddlehookcyrillic"}, {0x0492, "Ghestrokecyrillic"}, {0x0490, "Gheupturncyrillic"}, {0x0193, "Ghook"}, {0x0533, "Gimarmenian"}, {0x0403, "Gjecyrillic"}, {0x1e20, "Gmacron"}, {0xff27, "Gmonospace"}, {0xf6ce, "Grave"}, {0xf760, "Gravesmall"}, {0xf767, "Gsmall"}, {0x029b, "Gsmallhook"}, {0x01e4, "Gstroke"}, {0x0048, "H"}, {0x25cf, "H18533"}, {0x25aa, "H18543"}, {0x25ab, "H18551"}, {0x25a1, "H22073"}, {0x33cb, "HPsquare"}, {0x04a8, "Haabkhasiancyrillic"}, {0x04b2, "Hadescendercyrillic"}, {0x042a, "Hardsigncyrillic"}, {0x0126, "Hbar"}, {0x1e2a, "Hbrevebelow"}, {0x1e28, "Hcedilla"}, {0x24bd, "Hcircle"}, {0x0124, "Hcircumflex"}, {0x1e26, "Hdieresis"}, {0x1e22, "Hdotaccent"}, {0x1e24, "Hdotbelow"}, {0xff28, "Hmonospace"}, {0x0540, "Hoarmenian"}, {0x03e8, "Horicoptic"}, {0xf768, "Hsmall"}, {0xf6cf, "Hungarumlaut"}, {0xf6f8, "Hungarumlautsmall"}, {0x3390, "Hzsquare"}, {0x0049, "I"}, {0x042f, "IAcyrillic"}, {0x0132, "IJ"}, {0x042e, "IUcyrillic"}, {0x00cd, "Iacute"}, {0xf7ed, "Iacutesmall"}, {0x012c, "Ibreve"}, {0x01cf, "Icaron"}, {0x24be, "Icircle"}, {0x00ce, "Icircumflex"}, {0xf7ee, "Icircumflexsmall"}, {0x0406, "Icyrillic"}, {0x0208, "Idblgrave"}, {0x00cf, "Idieresis"}, {0x1e2e, "Idieresisacute"}, {0x04e4, "Idieresiscyrillic"}, {0xf7ef, "Idieresissmall"}, {0x0130, "Idot"}, {0x0130, "Idotaccent"}, {0x1eca, "Idotbelow"}, {0x04d6, "Iebrevecyrillic"}, {0x0415, "Iecyrillic"}, {0x2111, "Ifraktur"}, {0x00cc, "Igrave"}, {0xf7ec, "Igravesmall"}, {0x1ec8, "Ihookabove"}, {0x0418, "Iicyrillic"}, {0x020a, "Iinvertedbreve"}, {0x0419, "Iishortcyrillic"}, {0x012a, "Imacron"}, {0x04e2, "Imacroncyrillic"}, {0xff29, "Imonospace"}, {0x053b, "Iniarmenian"}, {0x0401, "Iocyrillic"}, {0x012e, "Iogonek"}, {0x0399, "Iota"}, {0x0196, "Iotaafrican"}, {0x03aa, "Iotadieresis"}, {0x038a, "Iotatonos"}, {0xf769, "Ismall"}, {0x0197, "Istroke"}, {0x0128, "Itilde"}, {0x1e2c, "Itildebelow"}, {0x0474, "Izhitsacyrillic"}, {0x0476, "Izhitsadblgravecyrillic"}, {0x004a, "J"}, {0x0541, "Jaarmenian"}, {0x24bf, "Jcircle"}, {0x0134, "Jcircumflex"}, {0x0408, "Jecyrillic"}, {0x054b, "Jheharmenian"}, {0xff2a, "Jmonospace"}, {0xf76a, "Jsmall"}, {0x004b, "K"}, {0x3385, "KBsquare"}, {0x33cd, "KKsquare"}, {0x04a0, "Kabashkircyrillic"}, {0x1e30, "Kacute"}, {0x041a, "Kacyrillic"}, {0x049a, "Kadescendercyrillic"}, {0x04c3, "Kahookcyrillic"}, {0x039a, "Kappa"}, {0x049e, "Kastrokecyrillic"}, {0x049c, "Kaverticalstrokecyrillic"}, {0x01e8, "Kcaron"}, {0x0136, "Kcedilla"}, {0x24c0, "Kcircle"}, {0x0136, "Kcommaaccent"}, {0x1e32, "Kdotbelow"}, {0x0554, "Keharmenian"}, {0x053f, "Kenarmenian"}, {0x0425, "Khacyrillic"}, {0x03e6, "Kheicoptic"}, {0x0198, "Khook"}, {0x040c, "Kjecyrillic"}, {0x1e34, "Klinebelow"}, {0xff2b, "Kmonospace"}, {0x0480, "Koppacyrillic"}, {0x03de, "Koppagreek"}, {0x046e, "Ksicyrillic"}, {0xf76b, "Ksmall"}, {0x004c, "L"}, {0x01c7, "LJ"}, {0xf6bf, "LL"}, {0x0139, "Lacute"}, {0x039b, "Lambda"}, {0x013d, "Lcaron"}, {0x013b, "Lcedilla"}, {0x24c1, "Lcircle"}, {0x1e3c, "Lcircumflexbelow"}, {0x013b, "Lcommaaccent"}, {0x013f, "Ldot"}, {0x013f, "Ldotaccent"}, {0x1e36, "Ldotbelow"}, {0x1e38, "Ldotbelowmacron"}, {0x053c, "Liwnarmenian"}, {0x01c8, "Lj"}, {0x0409, "Ljecyrillic"}, {0x1e3a, "Llinebelow"}, {0xff2c, "Lmonospace"}, {0x0141, "Lslash"}, {0xf6f9, "Lslashsmall"}, {0xf76c, "Lsmall"}, {0x004d, "M"}, {0x3386, "MBsquare"}, {0xf6d0, "Macron"}, {0xf7af, "Macronsmall"}, {0x1e3e, "Macute"}, {0x24c2, "Mcircle"}, {0x1e40, "Mdotaccent"}, {0x1e42, "Mdotbelow"}, {0x0544, "Menarmenian"}, {0xff2d, "Mmonospace"}, {0xf76d, "Msmall"}, {0x019c, "Mturned"}, {0x039c, "Mu"}, {0x004e, "N"}, {0x01ca, "NJ"}, {0x0143, "Nacute"}, {0x0147, "Ncaron"}, {0x0145, "Ncedilla"}, {0x24c3, "Ncircle"}, {0x1e4a, "Ncircumflexbelow"}, {0x0145, "Ncommaaccent"}, {0x1e44, "Ndotaccent"}, {0x1e46, "Ndotbelow"}, {0x019d, "Nhookleft"}, {0x2168, "Nineroman"}, {0x01cb, "Nj"}, {0x040a, "Njecyrillic"}, {0x1e48, "Nlinebelow"}, {0xff2e, "Nmonospace"}, {0x0546, "Nowarmenian"}, {0xf76e, "Nsmall"}, {0x00d1, "Ntilde"}, {0xf7f1, "Ntildesmall"}, {0x039d, "Nu"}, {0x004f, "O"}, {0x0152, "OE"}, {0xf6fa, "OEsmall"}, {0x00d3, "Oacute"}, {0xf7f3, "Oacutesmall"}, {0x04e8, "Obarredcyrillic"}, {0x04ea, "Obarreddieresiscyrillic"}, {0x014e, "Obreve"}, {0x01d1, "Ocaron"}, {0x019f, "Ocenteredtilde"}, {0x24c4, "Ocircle"}, {0x00d4, "Ocircumflex"}, {0x1ed0, "Ocircumflexacute"}, {0x1ed8, "Ocircumflexdotbelow"}, {0x1ed2, "Ocircumflexgrave"}, {0x1ed4, "Ocircumflexhookabove"}, {0xf7f4, "Ocircumflexsmall"}, {0x1ed6, "Ocircumflextilde"}, {0x041e, "Ocyrillic"}, {0x0150, "Odblacute"}, {0x020c, "Odblgrave"}, {0x00d6, "Odieresis"}, {0x04e6, "Odieresiscyrillic"}, {0xf7f6, "Odieresissmall"}, {0x1ecc, "Odotbelow"}, {0xf6fb, "Ogoneksmall"}, {0x00d2, "Ograve"}, {0xf7f2, "Ogravesmall"}, {0x0555, "Oharmenian"}, {0x2126, "Ohm"}, {0x1ece, "Ohookabove"}, {0x01a0, "Ohorn"}, {0x1eda, "Ohornacute"}, {0x1ee2, "Ohorndotbelow"}, {0x1edc, "Ohorngrave"}, {0x1ede, "Ohornhookabove"}, {0x1ee0, "Ohorntilde"}, {0x0150, "Ohungarumlaut"}, {0x01a2, "Oi"}, {0x020e, "Oinvertedbreve"}, {0x014c, "Omacron"}, {0x1e52, "Omacronacute"}, {0x1e50, "Omacrongrave"}, {0x2126, "Omega"}, {0x0460, "Omegacyrillic"}, {0x03a9, "Omegagreek"}, {0x047a, "Omegaroundcyrillic"}, {0x047c, "Omegatitlocyrillic"}, {0x038f, "Omegatonos"}, {0x039f, "Omicron"}, {0x038c, "Omicrontonos"}, {0xff2f, "Omonospace"}, {0x2160, "Oneroman"}, {0x01ea, "Oogonek"}, {0x01ec, "Oogonekmacron"}, {0x0186, "Oopen"}, {0x00d8, "Oslash"}, {0x01fe, "Oslashacute"}, {0xf7f8, "Oslashsmall"}, {0xf76f, "Osmall"}, {0x01fe, "Ostrokeacute"}, {0x047e, "Otcyrillic"}, {0x00d5, "Otilde"}, {0x1e4c, "Otildeacute"}, {0x1e4e, "Otildedieresis"}, {0xf7f5, "Otildesmall"}, {0x0050, "P"}, {0x1e54, "Pacute"}, {0x24c5, "Pcircle"}, {0x1e56, "Pdotaccent"}, {0x041f, "Pecyrillic"}, {0x054a, "Peharmenian"}, {0x04a6, "Pemiddlehookcyrillic"}, {0x03a6, "Phi"}, {0x01a4, "Phook"}, {0x03a0, "Pi"}, {0x0553, "Piwrarmenian"}, {0xff30, "Pmonospace"}, {0x03a8, "Psi"}, {0x0470, "Psicyrillic"}, {0xf770, "Psmall"}, {0x0051, "Q"}, {0x24c6, "Qcircle"}, {0xff31, "Qmonospace"}, {0xf771, "Qsmall"}, {0x0052, "R"}, {0x054c, "Raarmenian"}, {0x0154, "Racute"}, {0x0158, "Rcaron"}, {0x0156, "Rcedilla"}, {0x24c7, "Rcircle"}, {0x0156, "Rcommaaccent"}, {0x0210, "Rdblgrave"}, {0x1e58, "Rdotaccent"}, {0x1e5a, "Rdotbelow"}, {0x1e5c, "Rdotbelowmacron"}, {0x0550, "Reharmenian"}, {0x211c, "Rfraktur"}, {0x03a1, "Rho"}, {0xf6fc, "Ringsmall"}, {0x0212, "Rinvertedbreve"}, {0x1e5e, "Rlinebelow"}, {0xff32, "Rmonospace"}, {0xf772, "Rsmall"}, {0x0281, "Rsmallinverted"}, {0x02b6, "Rsmallinvertedsuperior"}, {0x0053, "S"}, {0x250c, "SF010000"}, {0x2514, "SF020000"}, {0x2510, "SF030000"}, {0x2518, "SF040000"}, {0x253c, "SF050000"}, {0x252c, "SF060000"}, {0x2534, "SF070000"}, {0x251c, "SF080000"}, {0x2524, "SF090000"}, {0x2500, "SF100000"}, {0x2502, "SF110000"}, {0x2561, "SF190000"}, {0x2562, "SF200000"}, {0x2556, "SF210000"}, {0x2555, "SF220000"}, {0x2563, "SF230000"}, {0x2551, "SF240000"}, {0x2557, "SF250000"}, {0x255d, "SF260000"}, {0x255c, "SF270000"}, {0x255b, "SF280000"}, {0x255e, "SF360000"}, {0x255f, "SF370000"}, {0x255a, "SF380000"}, {0x2554, "SF390000"}, {0x2569, "SF400000"}, {0x2566, "SF410000"}, {0x2560, "SF420000"}, {0x2550, "SF430000"}, {0x256c, "SF440000"}, {0x2567, "SF450000"}, {0x2568, "SF460000"}, {0x2564, "SF470000"}, {0x2565, "SF480000"}, {0x2559, "SF490000"}, {0x2558, "SF500000"}, {0x2552, "SF510000"}, {0x2553, "SF520000"}, {0x256b, "SF530000"}, {0x256a, "SF540000"}, {0x015a, "Sacute"}, {0x1e64, "Sacutedotaccent"}, {0x03e0, "Sampigreek"}, {0x0160, "Scaron"}, {0x1e66, "Scarondotaccent"}, {0xf6fd, "Scaronsmall"}, {0x015e, "Scedilla"}, {0x018f, "Schwa"}, {0x04d8, "Schwacyrillic"}, {0x04da, "Schwadieresiscyrillic"}, {0x24c8, "Scircle"}, {0x015c, "Scircumflex"}, {0x0218, "Scommaaccent"}, {0x1e60, "Sdotaccent"}, {0x1e62, "Sdotbelow"}, {0x1e68, "Sdotbelowdotaccent"}, {0x054d, "Seharmenian"}, {0x2166, "Sevenroman"}, {0x0547, "Shaarmenian"}, {0x0428, "Shacyrillic"}, {0x0429, "Shchacyrillic"}, {0x03e2, "Sheicoptic"}, {0x04ba, "Shhacyrillic"}, {0x03ec, "Shimacoptic"}, {0x03a3, "Sigma"}, {0x2165, "Sixroman"}, {0xff33, "Smonospace"}, {0x042c, "Softsigncyrillic"}, {0xf773, "Ssmall"}, {0x03da, "Stigmagreek"}, {0x0054, "T"}, {0x03a4, "Tau"}, {0x0166, "Tbar"}, {0x0164, "Tcaron"}, {0x0162, "Tcedilla"}, {0x24c9, "Tcircle"}, {0x1e70, "Tcircumflexbelow"}, {0x0162, "Tcommaaccent"}, {0x1e6a, "Tdotaccent"}, {0x1e6c, "Tdotbelow"}, {0x0422, "Tecyrillic"}, {0x04ac, "Tedescendercyrillic"}, {0x2169, "Tenroman"}, {0x04b4, "Tetsecyrillic"}, {0x0398, "Theta"}, {0x01ac, "Thook"}, {0x00de, "Thorn"}, {0xf7fe, "Thornsmall"}, {0x2162, "Threeroman"}, {0xf6fe, "Tildesmall"}, {0x054f, "Tiwnarmenian"}, {0x1e6e, "Tlinebelow"}, {0xff34, "Tmonospace"}, {0x0539, "Toarmenian"}, {0x01bc, "Tonefive"}, {0x0184, "Tonesix"}, {0x01a7, "Tonetwo"}, {0x01ae, "Tretroflexhook"}, {0x0426, "Tsecyrillic"}, {0x040b, "Tshecyrillic"}, {0xf774, "Tsmall"}, {0x216b, "Twelveroman"}, {0x2161, "Tworoman"}, {0x0055, "U"}, {0x00da, "Uacute"}, {0xf7fa, "Uacutesmall"}, {0x016c, "Ubreve"}, {0x01d3, "Ucaron"}, {0x24ca, "Ucircle"}, {0x00db, "Ucircumflex"}, {0x1e76, "Ucircumflexbelow"}, {0xf7fb, "Ucircumflexsmall"}, {0x0423, "Ucyrillic"}, {0x0170, "Udblacute"}, {0x0214, "Udblgrave"}, {0x00dc, "Udieresis"}, {0x01d7, "Udieresisacute"}, {0x1e72, "Udieresisbelow"}, {0x01d9, "Udieresiscaron"}, {0x04f0, "Udieresiscyrillic"}, {0x01db, "Udieresisgrave"}, {0x01d5, "Udieresismacron"}, {0xf7fc, "Udieresissmall"}, {0x1ee4, "Udotbelow"}, {0x00d9, "Ugrave"}, {0xf7f9, "Ugravesmall"}, {0x1ee6, "Uhookabove"}, {0x01af, "Uhorn"}, {0x1ee8, "Uhornacute"}, {0x1ef0, "Uhorndotbelow"}, {0x1eea, "Uhorngrave"}, {0x1eec, "Uhornhookabove"}, {0x1eee, "Uhorntilde"}, {0x0170, "Uhungarumlaut"}, {0x04f2, "Uhungarumlautcyrillic"}, {0x0216, "Uinvertedbreve"}, {0x0478, "Ukcyrillic"}, {0x016a, "Umacron"}, {0x04ee, "Umacroncyrillic"}, {0x1e7a, "Umacrondieresis"}, {0xff35, "Umonospace"}, {0x0172, "Uogonek"}, {0x03a5, "Upsilon"}, {0x03d2, "Upsilon1"}, {0x03d3, "Upsilonacutehooksymbolgreek"}, {0x01b1, "Upsilonafrican"}, {0x03ab, "Upsilondieresis"}, {0x03d4, "Upsilondieresishooksymbolgreek"}, {0x03d2, "Upsilonhooksymbol"}, {0x038e, "Upsilontonos"}, {0x016e, "Uring"}, {0x040e, "Ushortcyrillic"}, {0xf775, "Usmall"}, {0x04ae, "Ustraightcyrillic"}, {0x04b0, "Ustraightstrokecyrillic"}, {0x0168, "Utilde"}, {0x1e78, "Utildeacute"}, {0x1e74, "Utildebelow"}, {0x0056, "V"}, {0x24cb, "Vcircle"}, {0x1e7e, "Vdotbelow"}, {0x0412, "Vecyrillic"}, {0x054e, "Vewarmenian"}, {0x01b2, "Vhook"}, {0xff36, "Vmonospace"}, {0x0548, "Voarmenian"}, {0xf776, "Vsmall"}, {0x1e7c, "Vtilde"}, {0x0057, "W"}, {0x1e82, "Wacute"}, {0x24cc, "Wcircle"}, {0x0174, "Wcircumflex"}, {0x1e84, "Wdieresis"}, {0x1e86, "Wdotaccent"}, {0x1e88, "Wdotbelow"}, {0x1e80, "Wgrave"}, {0xff37, "Wmonospace"}, {0xf777, "Wsmall"}, {0x0058, "X"}, {0x24cd, "Xcircle"}, {0x1e8c, "Xdieresis"}, {0x1e8a, "Xdotaccent"}, {0x053d, "Xeharmenian"}, {0x039e, "Xi"}, {0xff38, "Xmonospace"}, {0xf778, "Xsmall"}, {0x0059, "Y"}, {0x00dd, "Yacute"}, {0xf7fd, "Yacutesmall"}, {0x0462, "Yatcyrillic"}, {0x24ce, "Ycircle"}, {0x0176, "Ycircumflex"}, {0x0178, "Ydieresis"}, {0xf7ff, "Ydieresissmall"}, {0x1e8e, "Ydotaccent"}, {0x1ef4, "Ydotbelow"}, {0x042b, "Yericyrillic"}, {0x04f8, "Yerudieresiscyrillic"}, {0x1ef2, "Ygrave"}, {0x01b3, "Yhook"}, {0x1ef6, "Yhookabove"}, {0x0545, "Yiarmenian"}, {0x0407, "Yicyrillic"}, {0x0552, "Yiwnarmenian"}, {0xff39, "Ymonospace"}, {0xf779, "Ysmall"}, {0x1ef8, "Ytilde"}, {0x046a, "Yusbigcyrillic"}, {0x046c, "Yusbigiotifiedcyrillic"}, {0x0466, "Yuslittlecyrillic"}, {0x0468, "Yuslittleiotifiedcyrillic"}, {0x005a, "Z"}, {0x0536, "Zaarmenian"}, {0x0179, "Zacute"}, {0x017d, "Zcaron"}, {0xf6ff, "Zcaronsmall"}, {0x24cf, "Zcircle"}, {0x1e90, "Zcircumflex"}, {0x017b, "Zdot"}, {0x017b, "Zdotaccent"}, {0x1e92, "Zdotbelow"}, {0x0417, "Zecyrillic"}, {0x0498, "Zedescendercyrillic"}, {0x04de, "Zedieresiscyrillic"}, {0x0396, "Zeta"}, {0x053a, "Zhearmenian"}, {0x04c1, "Zhebrevecyrillic"}, {0x0416, "Zhecyrillic"}, {0x0496, "Zhedescendercyrillic"}, {0x04dc, "Zhedieresiscyrillic"}, {0x1e94, "Zlinebelow"}, {0xff3a, "Zmonospace"}, {0xf77a, "Zsmall"}, {0x01b5, "Zstroke"}, {0x0022, "\""}, {0x005c, "\\"}, {0x005d, "]"}, {0x005e, "^"}, {0x005f, "_"}, {0x0060, "`"}, {0x0061, "a"}, {0x0986, "aabengali"}, {0x00e1, "aacute"}, {0x0906, "aadeva"}, {0x0a86, "aagujarati"}, {0x0a06, "aagurmukhi"}, {0x0a3e, "aamatragurmukhi"}, {0x3303, "aarusquare"}, {0x09be, "aavowelsignbengali"}, {0x093e, "aavowelsigndeva"}, {0x0abe, "aavowelsigngujarati"}, {0x055f, "abbreviationmarkarmenian"}, {0x0970, "abbreviationsigndeva"}, {0x0985, "abengali"}, {0x311a, "abopomofo"}, {0x0103, "abreve"}, {0x1eaf, "abreveacute"}, {0x04d1, "abrevecyrillic"}, {0x1eb7, "abrevedotbelow"}, {0x1eb1, "abrevegrave"}, {0x1eb3, "abrevehookabove"}, {0x1eb5, "abrevetilde"}, {0x01ce, "acaron"}, {0x24d0, "acircle"}, {0x00e2, "acircumflex"}, {0x1ea5, "acircumflexacute"}, {0x1ead, "acircumflexdotbelow"}, {0x1ea7, "acircumflexgrave"}, {0x1ea9, "acircumflexhookabove"}, {0x1eab, "acircumflextilde"}, {0x00b4, "acute"}, {0x0317, "acutebelowcmb"}, {0x0301, "acutecmb"}, {0x0301, "acutecomb"}, {0x0954, "acutedeva"}, {0x02cf, "acutelowmod"}, {0x0341, "acutetonecmb"}, {0x0430, "acyrillic"}, {0x0201, "adblgrave"}, {0x0a71, "addakgurmukhi"}, {0x0905, "adeva"}, {0x00e4, "adieresis"}, {0x04d3, "adieresiscyrillic"}, {0x01df, "adieresismacron"}, {0x1ea1, "adotbelow"}, {0x01e1, "adotmacron"}, {0x00e6, "ae"}, {0x01fd, "aeacute"}, {0x3150, "aekorean"}, {0x01e3, "aemacron"}, {0x2015, "afii00208"}, {0x20a4, "afii08941"}, {0x0410, "afii10017"}, {0x0411, "afii10018"}, {0x0412, "afii10019"}, {0x0413, "afii10020"}, {0x0414, "afii10021"}, {0x0415, "afii10022"}, {0x0401, "afii10023"}, {0x0416, "afii10024"}, {0x0417, "afii10025"}, {0x0418, "afii10026"}, {0x0419, "afii10027"}, {0x041a, "afii10028"}, {0x041b, "afii10029"}, {0x041c, "afii10030"}, {0x041d, "afii10031"}, {0x041e, "afii10032"}, {0x041f, "afii10033"}, {0x0420, "afii10034"}, {0x0421, "afii10035"}, {0x0422, "afii10036"}, {0x0423, "afii10037"}, {0x0424, "afii10038"}, {0x0425, "afii10039"}, {0x0426, "afii10040"}, {0x0427, "afii10041"}, {0x0428, "afii10042"}, {0x0429, "afii10043"}, {0x042a, "afii10044"}, {0x042b, "afii10045"}, {0x042c, "afii10046"}, {0x042d, "afii10047"}, {0x042e, "afii10048"}, {0x042f, "afii10049"}, {0x0490, "afii10050"}, {0x0402, "afii10051"}, {0x0403, "afii10052"}, {0x0404, "afii10053"}, {0x0405, "afii10054"}, {0x0406, "afii10055"}, {0x0407, "afii10056"}, {0x0408, "afii10057"}, {0x0409, "afii10058"}, {0x040a, "afii10059"}, {0x040b, "afii10060"}, {0x040c, "afii10061"}, {0x040e, "afii10062"}, {0xf6c4, "afii10063"}, {0xf6c5, "afii10064"}, {0x0430, "afii10065"}, {0x0431, "afii10066"}, {0x0432, "afii10067"}, {0x0433, "afii10068"}, {0x0434, "afii10069"}, {0x0435, "afii10070"}, {0x0451, "afii10071"}, {0x0436, "afii10072"}, {0x0437, "afii10073"}, {0x0438, "afii10074"}, {0x0439, "afii10075"}, {0x043a, "afii10076"}, {0x043b, "afii10077"}, {0x043c, "afii10078"}, {0x043d, "afii10079"}, {0x043e, "afii10080"}, {0x043f, "afii10081"}, {0x0440, "afii10082"}, {0x0441, "afii10083"}, {0x0442, "afii10084"}, {0x0443, "afii10085"}, {0x0444, "afii10086"}, {0x0445, "afii10087"}, {0x0446, "afii10088"}, {0x0447, "afii10089"}, {0x0448, "afii10090"}, {0x0449, "afii10091"}, {0x044a, "afii10092"}, {0x044b, "afii10093"}, {0x044c, "afii10094"}, {0x044d, "afii10095"}, {0x044e, "afii10096"}, {0x044f, "afii10097"}, {0x0491, "afii10098"}, {0x0452, "afii10099"}, {0x0453, "afii10100"}, {0x0454, "afii10101"}, {0x0455, "afii10102"}, {0x0456, "afii10103"}, {0x0457, "afii10104"}, {0x0458, "afii10105"}, {0x0459, "afii10106"}, {0x045a, "afii10107"}, {0x045b, "afii10108"}, {0x045c, "afii10109"}, {0x045e, "afii10110"}, {0x040f, "afii10145"}, {0x0462, "afii10146"}, {0x0472, "afii10147"}, {0x0474, "afii10148"}, {0xf6c6, "afii10192"}, {0x045f, "afii10193"}, {0x0463, "afii10194"}, {0x0473, "afii10195"}, {0x0475, "afii10196"}, {0xf6c7, "afii10831"}, {0xf6c8, "afii10832"}, {0x04d9, "afii10846"}, {0x200e, "afii299"}, {0x200f, "afii300"}, {0x200d, "afii301"}, {0x066a, "afii57381"}, {0x060c, "afii57388"}, {0x0660, "afii57392"}, {0x0661, "afii57393"}, {0x0662, "afii57394"}, {0x0663, "afii57395"}, {0x0664, "afii57396"}, {0x0665, "afii57397"}, {0x0666, "afii57398"}, {0x0667, "afii57399"}, {0x0668, "afii57400"}, {0x0669, "afii57401"}, {0x061b, "afii57403"}, {0x061f, "afii57407"}, {0x0621, "afii57409"}, {0x0622, "afii57410"}, {0x0623, "afii57411"}, {0x0624, "afii57412"}, {0x0625, "afii57413"}, {0x0626, "afii57414"}, {0x0627, "afii57415"}, {0x0628, "afii57416"}, {0x0629, "afii57417"}, {0x062a, "afii57418"}, {0x062b, "afii57419"}, {0x062c, "afii57420"}, {0x062d, "afii57421"}, {0x062e, "afii57422"}, {0x062f, "afii57423"}, {0x0630, "afii57424"}, {0x0631, "afii57425"}, {0x0632, "afii57426"}, {0x0633, "afii57427"}, {0x0634, "afii57428"}, {0x0635, "afii57429"}, {0x0636, "afii57430"}, {0x0637, "afii57431"}, {0x0638, "afii57432"}, {0x0639, "afii57433"}, {0x063a, "afii57434"}, {0x0640, "afii57440"}, {0x0641, "afii57441"}, {0x0642, "afii57442"}, {0x0643, "afii57443"}, {0x0644, "afii57444"}, {0x0645, "afii57445"}, {0x0646, "afii57446"}, {0x0648, "afii57448"}, {0x0649, "afii57449"}, {0x064a, "afii57450"}, {0x064b, "afii57451"}, {0x064c, "afii57452"}, {0x064d, "afii57453"}, {0x064e, "afii57454"}, {0x064f, "afii57455"}, {0x0650, "afii57456"}, {0x0651, "afii57457"}, {0x0652, "afii57458"}, {0x0647, "afii57470"}, {0x06a4, "afii57505"}, {0x067e, "afii57506"}, {0x0686, "afii57507"}, {0x0698, "afii57508"}, {0x06af, "afii57509"}, {0x0679, "afii57511"}, {0x0688, "afii57512"}, {0x0691, "afii57513"}, {0x06ba, "afii57514"}, {0x06d2, "afii57519"}, {0x06d5, "afii57534"}, {0x20aa, "afii57636"}, {0x05be, "afii57645"}, {0x05c3, "afii57658"}, {0x05d0, "afii57664"}, {0x05d1, "afii57665"}, {0x05d2, "afii57666"}, {0x05d3, "afii57667"}, {0x05d4, "afii57668"}, {0x05d5, "afii57669"}, {0x05d6, "afii57670"}, {0x05d7, "afii57671"}, {0x05d8, "afii57672"}, {0x05d9, "afii57673"}, {0x05da, "afii57674"}, {0x05db, "afii57675"}, {0x05dc, "afii57676"}, {0x05dd, "afii57677"}, {0x05de, "afii57678"}, {0x05df, "afii57679"}, {0x05e0, "afii57680"}, {0x05e1, "afii57681"}, {0x05e2, "afii57682"}, {0x05e3, "afii57683"}, {0x05e4, "afii57684"}, {0x05e5, "afii57685"}, {0x05e6, "afii57686"}, {0x05e7, "afii57687"}, {0x05e8, "afii57688"}, {0x05e9, "afii57689"}, {0x05ea, "afii57690"}, {0xfb2a, "afii57694"}, {0xfb2b, "afii57695"}, {0xfb4b, "afii57700"}, {0xfb1f, "afii57705"}, {0x05f0, "afii57716"}, {0x05f1, "afii57717"}, {0x05f2, "afii57718"}, {0xfb35, "afii57723"}, {0x05b4, "afii57793"}, {0x05b5, "afii57794"}, {0x05b6, "afii57795"}, {0x05bb, "afii57796"}, {0x05b8, "afii57797"}, {0x05b7, "afii57798"}, {0x05b0, "afii57799"}, {0x05b2, "afii57800"}, {0x05b1, "afii57801"}, {0x05b3, "afii57802"}, {0x05c2, "afii57803"}, {0x05c1, "afii57804"}, {0x05b9, "afii57806"}, {0x05bc, "afii57807"}, {0x05bd, "afii57839"}, {0x05bf, "afii57841"}, {0x05c0, "afii57842"}, {0x02bc, "afii57929"}, {0x2105, "afii61248"}, {0x2113, "afii61289"}, {0x2116, "afii61352"}, {0x202c, "afii61573"}, {0x202d, "afii61574"}, {0x202e, "afii61575"}, {0x200c, "afii61664"}, {0x066d, "afii63167"}, {0x02bd, "afii64937"}, {0x00e0, "agrave"}, {0x0a85, "agujarati"}, {0x0a05, "agurmukhi"}, {0x3042, "ahiragana"}, {0x1ea3, "ahookabove"}, {0x0990, "aibengali"}, {0x311e, "aibopomofo"}, {0x0910, "aideva"}, {0x04d5, "aiecyrillic"}, {0x0a90, "aigujarati"}, {0x0a10, "aigurmukhi"}, {0x0a48, "aimatragurmukhi"}, {0x0639, "ainarabic"}, {0xfeca, "ainfinalarabic"}, {0xfecb, "aininitialarabic"}, {0xfecc, "ainmedialarabic"}, {0x0203, "ainvertedbreve"}, {0x09c8, "aivowelsignbengali"}, {0x0948, "aivowelsigndeva"}, {0x0ac8, "aivowelsigngujarati"}, {0x30a2, "akatakana"}, {0xff71, "akatakanahalfwidth"}, {0x314f, "akorean"}, {0x05d0, "alef"}, {0x0627, "alefarabic"}, {0xfb30, "alefdageshhebrew"}, {0xfe8e, "aleffinalarabic"}, {0x0623, "alefhamzaabovearabic"}, {0xfe84, "alefhamzaabovefinalarabic"}, {0x0625, "alefhamzabelowarabic"}, {0xfe88, "alefhamzabelowfinalarabic"}, {0x05d0, "alefhebrew"}, {0xfb4f, "aleflamedhebrew"}, {0x0622, "alefmaddaabovearabic"}, {0xfe82, "alefmaddaabovefinalarabic"}, {0x0649, "alefmaksuraarabic"}, {0xfef0, "alefmaksurafinalarabic"}, {0xfef3, "alefmaksurainitialarabic"}, {0xfef4, "alefmaksuramedialarabic"}, {0xfb2e, "alefpatahhebrew"}, {0xfb2f, "alefqamatshebrew"}, {0x2135, "aleph"}, {0x224c, "allequal"}, {0x03b1, "alpha"}, {0x03ac, "alphatonos"}, {0x0101, "amacron"}, {0xff41, "amonospace"}, {0x0026, "ampersand"}, {0xff06, "ampersandmonospace"}, {0xf726, "ampersandsmall"}, {0x33c2, "amsquare"}, {0x3122, "anbopomofo"}, {0x3124, "angbopomofo"}, {0x0e5a, "angkhankhuthai"}, {0x2220, "angle"}, {0x3008, "anglebracketleft"}, {0xfe3f, "anglebracketleftvertical"}, {0x3009, "anglebracketright"}, {0xfe40, "anglebracketrightvertical"}, {0x2329, "angleleft"}, {0x232a, "angleright"}, {0x212b, "angstrom"}, {0x0387, "anoteleia"}, {0x0952, "anudattadeva"}, {0x0982, "anusvarabengali"}, {0x0902, "anusvaradeva"}, {0x0a82, "anusvaragujarati"}, {0x0105, "aogonek"}, {0x3300, "apaatosquare"}, {0x249c, "aparen"}, {0x055a, "apostrophearmenian"}, {0x02bc, "apostrophemod"}, {0xf8ff, "apple"}, {0x2250, "approaches"}, {0x2248, "approxequal"}, {0x2252, "approxequalorimage"}, {0x2245, "approximatelyequal"}, {0x318e, "araeaekorean"}, {0x318d, "araeakorean"}, {0x2312, "arc"}, {0x1e9a, "arighthalfring"}, {0x00e5, "aring"}, {0x01fb, "aringacute"}, {0x1e01, "aringbelow"}, {0x2194, "arrowboth"}, {0x21e3, "arrowdashdown"}, {0x21e0, "arrowdashleft"}, {0x21e2, "arrowdashright"}, {0x21e1, "arrowdashup"}, {0x21d4, "arrowdblboth"}, {0x21d3, "arrowdbldown"}, {0x21d0, "arrowdblleft"}, {0x21d2, "arrowdblright"}, {0x21d1, "arrowdblup"}, {0x2193, "arrowdown"}, {0x2199, "arrowdownleft"}, {0x2198, "arrowdownright"}, {0x21e9, "arrowdownwhite"}, {0x02c5, "arrowheaddownmod"}, {0x02c2, "arrowheadleftmod"}, {0x02c3, "arrowheadrightmod"}, {0x02c4, "arrowheadupmod"}, {0xf8e7, "arrowhorizex"}, {0x2190, "arrowleft"}, {0x21d0, "arrowleftdbl"}, {0x21cd, "arrowleftdblstroke"}, {0x21c6, "arrowleftoverright"}, {0x21e6, "arrowleftwhite"}, {0x2192, "arrowright"}, {0x21cf, "arrowrightdblstroke"}, {0x279e, "arrowrightheavy"}, {0x21c4, "arrowrightoverleft"}, {0x21e8, "arrowrightwhite"}, {0x21e4, "arrowtableft"}, {0x21e5, "arrowtabright"}, {0x2191, "arrowup"}, {0x2195, "arrowupdn"}, {0x21a8, "arrowupdnbse"}, {0x21a8, "arrowupdownbase"}, {0x2196, "arrowupleft"}, {0x21c5, "arrowupleftofdown"}, {0x2197, "arrowupright"}, {0x21e7, "arrowupwhite"}, {0xf8e6, "arrowvertex"}, {0x005e, "asciicircum"}, {0xff3e, "asciicircummonospace"}, {0x007e, "asciitilde"}, {0xff5e, "asciitildemonospace"}, {0x0251, "ascript"}, {0x0252, "ascriptturned"}, {0x3041, "asmallhiragana"}, {0x30a1, "asmallkatakana"}, {0xff67, "asmallkatakanahalfwidth"}, {0x002a, "asterisk"}, {0x066d, "asteriskaltonearabic"}, {0x066d, "asteriskarabic"}, {0x2217, "asteriskmath"}, {0xff0a, "asteriskmonospace"}, {0xfe61, "asterisksmall"}, {0x2042, "asterism"}, {0xf6e9, "asuperior"}, {0x2243, "asymptoticallyequal"}, {0x0040, "at"}, {0x00e3, "atilde"}, {0xff20, "atmonospace"}, {0xfe6b, "atsmall"}, {0x0250, "aturned"}, {0x0994, "aubengali"}, {0x3120, "aubopomofo"}, {0x0914, "audeva"}, {0x0a94, "augujarati"}, {0x0a14, "augurmukhi"}, {0x09d7, "aulengthmarkbengali"}, {0x0a4c, "aumatragurmukhi"}, {0x09cc, "auvowelsignbengali"}, {0x094c, "auvowelsigndeva"}, {0x0acc, "auvowelsigngujarati"}, {0x093d, "avagrahadeva"}, {0x0561, "aybarmenian"}, {0x05e2, "ayin"}, {0xfb20, "ayinaltonehebrew"}, {0x05e2, "ayinhebrew"}, {0x0062, "b"}, {0x09ac, "babengali"}, {0x005c, "backslash"}, {0xff3c, "backslashmonospace"}, {0x092c, "badeva"}, {0x0aac, "bagujarati"}, {0x0a2c, "bagurmukhi"}, {0x3070, "bahiragana"}, {0x0e3f, "bahtthai"}, {0x30d0, "bakatakana"}, {0x007c, "bar"}, {0xff5c, "barmonospace"}, {0x3105, "bbopomofo"}, {0x24d1, "bcircle"}, {0x1e03, "bdotaccent"}, {0x1e05, "bdotbelow"}, {0x266c, "beamedsixteenthnotes"}, {0x2235, "because"}, {0x0431, "becyrillic"}, {0x0628, "beharabic"}, {0xfe90, "behfinalarabic"}, {0xfe91, "behinitialarabic"}, {0x3079, "behiragana"}, {0xfe92, "behmedialarabic"}, {0xfc9f, "behmeeminitialarabic"}, {0xfc08, "behmeemisolatedarabic"}, {0xfc6d, "behnoonfinalarabic"}, {0x30d9, "bekatakana"}, {0x0562, "benarmenian"}, {0x05d1, "bet"}, {0x03b2, "beta"}, {0x03d0, "betasymbolgreek"}, {0xfb31, "betdagesh"}, {0xfb31, "betdageshhebrew"}, {0x05d1, "bethebrew"}, {0xfb4c, "betrafehebrew"}, {0x09ad, "bhabengali"}, {0x092d, "bhadeva"}, {0x0aad, "bhagujarati"}, {0x0a2d, "bhagurmukhi"}, {0x0253, "bhook"}, {0x3073, "bihiragana"}, {0x30d3, "bikatakana"}, {0x0298, "bilabialclick"}, {0x0a02, "bindigurmukhi"}, {0x3331, "birusquare"}, {0x25cf, "blackcircle"}, {0x25c6, "blackdiamond"}, {0x25bc, "blackdownpointingtriangle"}, {0x25c4, "blackleftpointingpointer"}, {0x25c0, "blackleftpointingtriangle"}, {0x3010, "blacklenticularbracketleft"}, {0xfe3b, "blacklenticularbracketleftvertical"}, {0x3011, "blacklenticularbracketright"}, {0xfe3c, "blacklenticularbracketrightvertical"}, {0x25e3, "blacklowerlefttriangle"}, {0x25e2, "blacklowerrighttriangle"}, {0x25ac, "blackrectangle"}, {0x25ba, "blackrightpointingpointer"}, {0x25b6, "blackrightpointingtriangle"}, {0x25aa, "blacksmallsquare"}, {0x263b, "blacksmilingface"}, {0x25a0, "blacksquare"}, {0x2605, "blackstar"}, {0x25e4, "blackupperlefttriangle"}, {0x25e5, "blackupperrighttriangle"}, {0x25b4, "blackuppointingsmalltriangle"}, {0x25b2, "blackuppointingtriangle"}, {0x2423, "blank"}, {0x1e07, "blinebelow"}, {0x2588, "block"}, {0xff42, "bmonospace"}, {0x0e1a, "bobaimaithai"}, {0x307c, "bohiragana"}, {0x30dc, "bokatakana"}, {0x249d, "bparen"}, {0x33c3, "bqsquare"}, {0xf8f4, "braceex"}, {0x007b, "braceleft"}, {0xf8f3, "braceleftbt"}, {0xf8f2, "braceleftmid"}, {0xff5b, "braceleftmonospace"}, {0xfe5b, "braceleftsmall"}, {0xf8f1, "bracelefttp"}, {0xfe37, "braceleftvertical"}, {0x007d, "braceright"}, {0xf8fe, "bracerightbt"}, {0xf8fd, "bracerightmid"}, {0xff5d, "bracerightmonospace"}, {0xfe5c, "bracerightsmall"}, {0xf8fc, "bracerighttp"}, {0xfe38, "bracerightvertical"}, {0x005b, "bracketleft"}, {0xf8f0, "bracketleftbt"}, {0xf8ef, "bracketleftex"}, {0xff3b, "bracketleftmonospace"}, {0xf8ee, "bracketlefttp"}, {0x005d, "bracketright"}, {0xf8fb, "bracketrightbt"}, {0xf8fa, "bracketrightex"}, {0xff3d, "bracketrightmonospace"}, {0xf8f9, "bracketrighttp"}, {0x02d8, "breve"}, {0x032e, "brevebelowcmb"}, {0x0306, "brevecmb"}, {0x032f, "breveinvertedbelowcmb"}, {0x0311, "breveinvertedcmb"}, {0x0361, "breveinverteddoublecmb"}, {0x032a, "bridgebelowcmb"}, {0x033a, "bridgeinvertedbelowcmb"}, {0x00a6, "brokenbar"}, {0x0180, "bstroke"}, {0xf6ea, "bsuperior"}, {0x0183, "btopbar"}, {0x3076, "buhiragana"}, {0x30d6, "bukatakana"}, {0x2022, "bullet"}, {0x25d8, "bulletinverse"}, {0x2219, "bulletoperator"}, {0x25ce, "bullseye"}, {0x0063, "c"}, {0x056e, "caarmenian"}, {0x099a, "cabengali"}, {0x0107, "cacute"}, {0x091a, "cadeva"}, {0x0a9a, "cagujarati"}, {0x0a1a, "cagurmukhi"}, {0x3388, "calsquare"}, {0x0981, "candrabindubengali"}, {0x0310, "candrabinducmb"}, {0x0901, "candrabindudeva"}, {0x0a81, "candrabindugujarati"}, {0x21ea, "capslock"}, {0x2105, "careof"}, {0x02c7, "caron"}, {0x032c, "caronbelowcmb"}, {0x030c, "caroncmb"}, {0x21b5, "carriagereturn"}, {0x3118, "cbopomofo"}, {0x010d, "ccaron"}, {0x00e7, "ccedilla"}, {0x1e09, "ccedillaacute"}, {0x24d2, "ccircle"}, {0x0109, "ccircumflex"}, {0x0255, "ccurl"}, {0x010b, "cdot"}, {0x010b, "cdotaccent"}, {0x33c5, "cdsquare"}, {0x00b8, "cedilla"}, {0x0327, "cedillacmb"}, {0x00a2, "cent"}, {0x2103, "centigrade"}, {0xf6df, "centinferior"}, {0xffe0, "centmonospace"}, {0xf7a2, "centoldstyle"}, {0xf6e0, "centsuperior"}, {0x0579, "chaarmenian"}, {0x099b, "chabengali"}, {0x091b, "chadeva"}, {0x0a9b, "chagujarati"}, {0x0a1b, "chagurmukhi"}, {0x3114, "chbopomofo"}, {0x04bd, "cheabkhasiancyrillic"}, {0x2713, "checkmark"}, {0x0447, "checyrillic"}, {0x04bf, "chedescenderabkhasiancyrillic"}, {0x04b7, "chedescendercyrillic"}, {0x04f5, "chedieresiscyrillic"}, {0x0573, "cheharmenian"}, {0x04cc, "chekhakassiancyrillic"}, {0x04b9, "cheverticalstrokecyrillic"}, {0x03c7, "chi"}, {0x3277, "chieuchacirclekorean"}, {0x3217, "chieuchaparenkorean"}, {0x3269, "chieuchcirclekorean"}, {0x314a, "chieuchkorean"}, {0x3209, "chieuchparenkorean"}, {0x0e0a, "chochangthai"}, {0x0e08, "chochanthai"}, {0x0e09, "chochingthai"}, {0x0e0c, "chochoethai"}, {0x0188, "chook"}, {0x3276, "cieucacirclekorean"}, {0x3216, "cieucaparenkorean"}, {0x3268, "cieuccirclekorean"}, {0x3148, "cieuckorean"}, {0x3208, "cieucparenkorean"}, {0x321c, "cieucuparenkorean"}, {0x25cb, "circle"}, {0x2297, "circlemultiply"}, {0x2299, "circleot"}, {0x2295, "circleplus"}, {0x3036, "circlepostalmark"}, {0x25d0, "circlewithlefthalfblack"}, {0x25d1, "circlewithrighthalfblack"}, {0x02c6, "circumflex"}, {0x032d, "circumflexbelowcmb"}, {0x0302, "circumflexcmb"}, {0x2327, "clear"}, {0x01c2, "clickalveolar"}, {0x01c0, "clickdental"}, {0x01c1, "clicklateral"}, {0x01c3, "clickretroflex"}, {0x2663, "club"}, {0x2663, "clubsuitblack"}, {0x2667, "clubsuitwhite"}, {0x33a4, "cmcubedsquare"}, {0xff43, "cmonospace"}, {0x33a0, "cmsquaredsquare"}, {0x0581, "coarmenian"}, {0x003a, "colon"}, {0x20a1, "colonmonetary"}, {0xff1a, "colonmonospace"}, {0x20a1, "colonsign"}, {0xfe55, "colonsmall"}, {0x02d1, "colontriangularhalfmod"}, {0x02d0, "colontriangularmod"}, {0x002c, "comma"}, {0x0313, "commaabovecmb"}, {0x0315, "commaaboverightcmb"}, {0xf6c3, "commaaccent"}, {0x060c, "commaarabic"}, {0x055d, "commaarmenian"}, {0xf6e1, "commainferior"}, {0xff0c, "commamonospace"}, {0x0314, "commareversedabovecmb"}, {0x02bd, "commareversedmod"}, {0xfe50, "commasmall"}, {0xf6e2, "commasuperior"}, {0x0312, "commaturnedabovecmb"}, {0x02bb, "commaturnedmod"}, {0x263c, "compass"}, {0x2245, "congruent"}, {0x222e, "contourintegral"}, {0x2303, "control"}, {0x0006, "controlACK"}, {0x0007, "controlBEL"}, {0x0008, "controlBS"}, {0x0018, "controlCAN"}, {0x000d, "controlCR"}, {0x0011, "controlDC1"}, {0x0012, "controlDC2"}, {0x0013, "controlDC3"}, {0x0014, "controlDC4"}, {0x007f, "controlDEL"}, {0x0010, "controlDLE"}, {0x0019, "controlEM"}, {0x0005, "controlENQ"}, {0x0004, "controlEOT"}, {0x001b, "controlESC"}, {0x0017, "controlETB"}, {0x0003, "controlETX"}, {0x000c, "controlFF"}, {0x001c, "controlFS"}, {0x001d, "controlGS"}, {0x0009, "controlHT"}, {0x000a, "controlLF"}, {0x0015, "controlNAK"}, {0x001e, "controlRS"}, {0x000f, "controlSI"}, {0x000e, "controlSO"}, {0x0002, "controlSOT"}, {0x0001, "controlSTX"}, {0x001a, "controlSUB"}, {0x0016, "controlSYN"}, {0x001f, "controlUS"}, {0x000b, "controlVT"}, {0x00a9, "copyright"}, {0x00a9, "copyrightsans"}, {0x00a9, "copyrightserif"}, {0x300c, "cornerbracketleft"}, {0xff62, "cornerbracketlefthalfwidth"}, {0xfe41, "cornerbracketleftvertical"}, {0x300d, "cornerbracketright"}, {0xff63, "cornerbracketrighthalfwidth"}, {0xfe42, "cornerbracketrightvertical"}, {0x337f, "corporationsquare"}, {0x33c7, "cosquare"}, {0x33c6, "coverkgsquare"}, {0x249e, "cparen"}, {0x20a2, "cruzeiro"}, {0x0297, "cstretched"}, {0x22cf, "curlyand"}, {0x22ce, "curlyor"}, {0x00a4, "currency"}, {0xf6d1, "cyrBreve"}, {0xf6d2, "cyrFlex"}, {0xf6d4, "cyrbreve"}, {0xf6d5, "cyrflex"}, {0x0064, "d"}, {0x0564, "daarmenian"}, {0x09a6, "dabengali"}, {0x0636, "dadarabic"}, {0x0926, "dadeva"}, {0xfebe, "dadfinalarabic"}, {0xfebf, "dadinitialarabic"}, {0xfec0, "dadmedialarabic"}, {0x05bc, "dagesh"}, {0x05bc, "dageshhebrew"}, {0x2020, "dagger"}, {0x2021, "daggerdbl"}, {0x0aa6, "dagujarati"}, {0x0a26, "dagurmukhi"}, {0x3060, "dahiragana"}, {0x30c0, "dakatakana"}, {0x062f, "dalarabic"}, {0x05d3, "dalet"}, {0xfb33, "daletdagesh"}, {0xfb33, "daletdageshhebrew"}, {0x05d3, "dalethebrew"}, {0xfeaa, "dalfinalarabic"}, {0x064f, "dammaarabic"}, {0x064f, "dammalowarabic"}, {0x064c, "dammatanaltonearabic"}, {0x064c, "dammatanarabic"}, {0x0964, "danda"}, {0x05a7, "dargahebrew"}, {0x05a7, "dargalefthebrew"}, {0x0485, "dasiapneumatacyrilliccmb"}, {0xf6d3, "dblGrave"}, {0x300a, "dblanglebracketleft"}, {0xfe3d, "dblanglebracketleftvertical"}, {0x300b, "dblanglebracketright"}, {0xfe3e, "dblanglebracketrightvertical"}, {0x032b, "dblarchinvertedbelowcmb"}, {0x21d4, "dblarrowleft"}, {0x21d2, "dblarrowright"}, {0x0965, "dbldanda"}, {0xf6d6, "dblgrave"}, {0x030f, "dblgravecmb"}, {0x222c, "dblintegral"}, {0x2017, "dbllowline"}, {0x0333, "dbllowlinecmb"}, {0x033f, "dbloverlinecmb"}, {0x02ba, "dblprimemod"}, {0x2016, "dblverticalbar"}, {0x030e, "dblverticallineabovecmb"}, {0x3109, "dbopomofo"}, {0x33c8, "dbsquare"}, {0x010f, "dcaron"}, {0x1e11, "dcedilla"}, {0x24d3, "dcircle"}, {0x1e13, "dcircumflexbelow"}, {0x0111, "dcroat"}, {0x09a1, "ddabengali"}, {0x0921, "ddadeva"}, {0x0aa1, "ddagujarati"}, {0x0a21, "ddagurmukhi"}, {0x0688, "ddalarabic"}, {0xfb89, "ddalfinalarabic"}, {0x095c, "dddhadeva"}, {0x09a2, "ddhabengali"}, {0x0922, "ddhadeva"}, {0x0aa2, "ddhagujarati"}, {0x0a22, "ddhagurmukhi"}, {0x1e0b, "ddotaccent"}, {0x1e0d, "ddotbelow"}, {0x066b, "decimalseparatorarabic"}, {0x066b, "decimalseparatorpersian"}, {0x0434, "decyrillic"}, {0x00b0, "degree"}, {0x05ad, "dehihebrew"}, {0x3067, "dehiragana"}, {0x03ef, "deicoptic"}, {0x30c7, "dekatakana"}, {0x232b, "deleteleft"}, {0x2326, "deleteright"}, {0x03b4, "delta"}, {0x018d, "deltaturned"}, {0x09f8, "denominatorminusonenumeratorbengali"}, {0x02a4, "dezh"}, {0x09a7, "dhabengali"}, {0x0927, "dhadeva"}, {0x0aa7, "dhagujarati"}, {0x0a27, "dhagurmukhi"}, {0x0257, "dhook"}, {0x0385, "dialytikatonos"}, {0x0344, "dialytikatonoscmb"}, {0x2666, "diamond"}, {0x2662, "diamondsuitwhite"}, {0x00a8, "dieresis"}, {0xf6d7, "dieresisacute"}, {0x0324, "dieresisbelowcmb"}, {0x0308, "dieresiscmb"}, {0xf6d8, "dieresisgrave"}, {0x0385, "dieresistonos"}, {0x3062, "dihiragana"}, {0x30c2, "dikatakana"}, {0x3003, "dittomark"}, {0x00f7, "divide"}, {0x2223, "divides"}, {0x2215, "divisionslash"}, {0x0452, "djecyrillic"}, {0x2593, "dkshade"}, {0x1e0f, "dlinebelow"}, {0x3397, "dlsquare"}, {0x0111, "dmacron"}, {0xff44, "dmonospace"}, {0x2584, "dnblock"}, {0x0e0e, "dochadathai"}, {0x0e14, "dodekthai"}, {0x3069, "dohiragana"}, {0x30c9, "dokatakana"}, {0x0024, "dollar"}, {0xf6e3, "dollarinferior"}, {0xff04, "dollarmonospace"}, {0xf724, "dollaroldstyle"}, {0xfe69, "dollarsmall"}, {0xf6e4, "dollarsuperior"}, {0x20ab, "dong"}, {0x3326, "dorusquare"}, {0x02d9, "dotaccent"}, {0x0307, "dotaccentcmb"}, {0x0323, "dotbelowcmb"}, {0x0323, "dotbelowcomb"}, {0x30fb, "dotkatakana"}, {0x0131, "dotlessi"}, {0xf6be, "dotlessj"}, {0x0284, "dotlessjstrokehook"}, {0x22c5, "dotmath"}, {0x25cc, "dottedcircle"}, {0xfb1f, "doubleyodpatah"}, {0xfb1f, "doubleyodpatahhebrew"}, {0x031e, "downtackbelowcmb"}, {0x02d5, "downtackmod"}, {0x249f, "dparen"}, {0xf6eb, "dsuperior"}, {0x0256, "dtail"}, {0x018c, "dtopbar"}, {0x3065, "duhiragana"}, {0x30c5, "dukatakana"}, {0x01f3, "dz"}, {0x02a3, "dzaltone"}, {0x01c6, "dzcaron"}, {0x02a5, "dzcurl"}, {0x04e1, "dzeabkhasiancyrillic"}, {0x0455, "dzecyrillic"}, {0x045f, "dzhecyrillic"}, {0x0065, "e"}, {0x00e9, "eacute"}, {0x2641, "earth"}, {0x098f, "ebengali"}, {0x311c, "ebopomofo"}, {0x0115, "ebreve"}, {0x090d, "ecandradeva"}, {0x0a8d, "ecandragujarati"}, {0x0945, "ecandravowelsigndeva"}, {0x0ac5, "ecandravowelsigngujarati"}, {0x011b, "ecaron"}, {0x1e1d, "ecedillabreve"}, {0x0565, "echarmenian"}, {0x0587, "echyiwnarmenian"}, {0x24d4, "ecircle"}, {0x00ea, "ecircumflex"}, {0x1ebf, "ecircumflexacute"}, {0x1e19, "ecircumflexbelow"}, {0x1ec7, "ecircumflexdotbelow"}, {0x1ec1, "ecircumflexgrave"}, {0x1ec3, "ecircumflexhookabove"}, {0x1ec5, "ecircumflextilde"}, {0x0454, "ecyrillic"}, {0x0205, "edblgrave"}, {0x090f, "edeva"}, {0x00eb, "edieresis"}, {0x0117, "edot"}, {0x0117, "edotaccent"}, {0x1eb9, "edotbelow"}, {0x0a0f, "eegurmukhi"}, {0x0a47, "eematragurmukhi"}, {0x0444, "efcyrillic"}, {0x00e8, "egrave"}, {0x0a8f, "egujarati"}, {0x0567, "eharmenian"}, {0x311d, "ehbopomofo"}, {0x3048, "ehiragana"}, {0x1ebb, "ehookabove"}, {0x311f, "eibopomofo"}, {0x0038, "eight"}, {0x0668, "eightarabic"}, {0x09ee, "eightbengali"}, {0x2467, "eightcircle"}, {0x2791, "eightcircleinversesansserif"}, {0x096e, "eightdeva"}, {0x2471, "eighteencircle"}, {0x2485, "eighteenparen"}, {0x2499, "eighteenperiod"}, {0x0aee, "eightgujarati"}, {0x0a6e, "eightgurmukhi"}, {0x0668, "eighthackarabic"}, {0x3028, "eighthangzhou"}, {0x266b, "eighthnotebeamed"}, {0x3227, "eightideographicparen"}, {0x2088, "eightinferior"}, {0xff18, "eightmonospace"}, {0xf738, "eightoldstyle"}, {0x247b, "eightparen"}, {0x248f, "eightperiod"}, {0x06f8, "eightpersian"}, {0x2177, "eightroman"}, {0x2078, "eightsuperior"}, {0x0e58, "eightthai"}, {0x0207, "einvertedbreve"}, {0x0465, "eiotifiedcyrillic"}, {0x30a8, "ekatakana"}, {0xff74, "ekatakanahalfwidth"}, {0x0a74, "ekonkargurmukhi"}, {0x3154, "ekorean"}, {0x043b, "elcyrillic"}, {0x2208, "element"}, {0x246a, "elevencircle"}, {0x247e, "elevenparen"}, {0x2492, "elevenperiod"}, {0x217a, "elevenroman"}, {0x2026, "ellipsis"}, {0x22ee, "ellipsisvertical"}, {0x0113, "emacron"}, {0x1e17, "emacronacute"}, {0x1e15, "emacrongrave"}, {0x043c, "emcyrillic"}, {0x2014, "emdash"}, {0xfe31, "emdashvertical"}, {0xff45, "emonospace"}, {0x055b, "emphasismarkarmenian"}, {0x2205, "emptyset"}, {0x3123, "enbopomofo"}, {0x043d, "encyrillic"}, {0x2013, "endash"}, {0xfe32, "endashvertical"}, {0x04a3, "endescendercyrillic"}, {0x014b, "eng"}, {0x3125, "engbopomofo"}, {0x04a5, "enghecyrillic"}, {0x04c8, "enhookcyrillic"}, {0x2002, "enspace"}, {0x0119, "eogonek"}, {0x3153, "eokorean"}, {0x025b, "eopen"}, {0x029a, "eopenclosed"}, {0x025c, "eopenreversed"}, {0x025e, "eopenreversedclosed"}, {0x025d, "eopenreversedhook"}, {0x24a0, "eparen"}, {0x03b5, "epsilon"}, {0x03ad, "epsilontonos"}, {0x003d, "equal"}, {0xff1d, "equalmonospace"}, {0xfe66, "equalsmall"}, {0x207c, "equalsuperior"}, {0x2261, "equivalence"}, {0x3126, "erbopomofo"}, {0x0440, "ercyrillic"}, {0x0258, "ereversed"}, {0x044d, "ereversedcyrillic"}, {0x0441, "escyrillic"}, {0x04ab, "esdescendercyrillic"}, {0x0283, "esh"}, {0x0286, "eshcurl"}, {0x090e, "eshortdeva"}, {0x0946, "eshortvowelsigndeva"}, {0x01aa, "eshreversedloop"}, {0x0285, "eshsquatreversed"}, {0x3047, "esmallhiragana"}, {0x30a7, "esmallkatakana"}, {0xff6a, "esmallkatakanahalfwidth"}, {0x212e, "estimated"}, {0xf6ec, "esuperior"}, {0x03b7, "eta"}, {0x0568, "etarmenian"}, {0x03ae, "etatonos"}, {0x00f0, "eth"}, {0x1ebd, "etilde"}, {0x1e1b, "etildebelow"}, {0x0591, "etnahtafoukhhebrew"}, {0x0591, "etnahtafoukhlefthebrew"}, {0x0591, "etnahtahebrew"}, {0x0591, "etnahtalefthebrew"}, {0x01dd, "eturned"}, {0x3161, "eukorean"}, {0x20ac, "euro"}, {0x09c7, "evowelsignbengali"}, {0x0947, "evowelsigndeva"}, {0x0ac7, "evowelsigngujarati"}, {0x0021, "exclam"}, {0x055c, "exclamarmenian"}, {0x203c, "exclamdbl"}, {0x00a1, "exclamdown"}, {0xf7a1, "exclamdownsmall"}, {0x0021, "exclamleft"}, {0xff01, "exclammonospace"}, {0xf721, "exclamsmall"}, {0x2203, "existential"}, {0x0292, "ezh"}, {0x01ef, "ezhcaron"}, {0x0293, "ezhcurl"}, {0x01b9, "ezhreversed"}, {0x01ba, "ezhtail"}, {0x0066, "f"}, {0x095e, "fadeva"}, {0x0a5e, "fagurmukhi"}, {0x2109, "fahrenheit"}, {0x064e, "fathaarabic"}, {0x064e, "fathalowarabic"}, {0x064b, "fathatanarabic"}, {0x3108, "fbopomofo"}, {0x24d5, "fcircle"}, {0x1e1f, "fdotaccent"}, {0x0641, "feharabic"}, {0x0586, "feharmenian"}, {0xfed2, "fehfinalarabic"}, {0xfed3, "fehinitialarabic"}, {0xfed4, "fehmedialarabic"}, {0x03e5, "feicoptic"}, {0x2640, "female"}, {0xfb00, "ff"}, {0xfb03, "ffi"}, {0xfb04, "ffl"}, {0xfb01, "fi"}, {0x246e, "fifteencircle"}, {0x2482, "fifteenparen"}, {0x2496, "fifteenperiod"}, {0x2012, "figuredash"}, {0x25a0, "filledbox"}, {0x25ac, "filledrect"}, {0x05da, "finalkaf"}, {0xfb3a, "finalkafdagesh"}, {0xfb3a, "finalkafdageshhebrew"}, {0x05da, "finalkafhebrew"}, {0x05dd, "finalmem"}, {0x05dd, "finalmemhebrew"}, {0x05df, "finalnun"}, {0x05df, "finalnunhebrew"}, {0x05e3, "finalpe"}, {0x05e3, "finalpehebrew"}, {0x05e5, "finaltsadi"}, {0x05e5, "finaltsadihebrew"}, {0x02c9, "firsttonechinese"}, {0x25c9, "fisheye"}, {0x0473, "fitacyrillic"}, {0x0035, "five"}, {0x0665, "fivearabic"}, {0x09eb, "fivebengali"}, {0x2464, "fivecircle"}, {0x278e, "fivecircleinversesansserif"}, {0x096b, "fivedeva"}, {0x215d, "fiveeighths"}, {0x0aeb, "fivegujarati"}, {0x0a6b, "fivegurmukhi"}, {0x0665, "fivehackarabic"}, {0x3025, "fivehangzhou"}, {0x3224, "fiveideographicparen"}, {0x2085, "fiveinferior"}, {0xff15, "fivemonospace"}, {0xf735, "fiveoldstyle"}, {0x2478, "fiveparen"}, {0x248c, "fiveperiod"}, {0x06f5, "fivepersian"}, {0x2174, "fiveroman"}, {0x2075, "fivesuperior"}, {0x0e55, "fivethai"}, {0xfb02, "fl"}, {0x0192, "florin"}, {0xff46, "fmonospace"}, {0x3399, "fmsquare"}, {0x0e1f, "fofanthai"}, {0x0e1d, "fofathai"}, {0x0e4f, "fongmanthai"}, {0x2200, "forall"}, {0x0034, "four"}, {0x0664, "fourarabic"}, {0x09ea, "fourbengali"}, {0x2463, "fourcircle"}, {0x278d, "fourcircleinversesansserif"}, {0x096a, "fourdeva"}, {0x0aea, "fourgujarati"}, {0x0a6a, "fourgurmukhi"}, {0x0664, "fourhackarabic"}, {0x3024, "fourhangzhou"}, {0x3223, "fourideographicparen"}, {0x2084, "fourinferior"}, {0xff14, "fourmonospace"}, {0x09f7, "fournumeratorbengali"}, {0xf734, "fouroldstyle"}, {0x2477, "fourparen"}, {0x248b, "fourperiod"}, {0x06f4, "fourpersian"}, {0x2173, "fourroman"}, {0x2074, "foursuperior"}, {0x246d, "fourteencircle"}, {0x2481, "fourteenparen"}, {0x2495, "fourteenperiod"}, {0x0e54, "fourthai"}, {0x02cb, "fourthtonechinese"}, {0x24a1, "fparen"}, {0x2044, "fraction"}, {0x20a3, "franc"}, {0x0067, "g"}, {0x0997, "gabengali"}, {0x01f5, "gacute"}, {0x0917, "gadeva"}, {0x06af, "gafarabic"}, {0xfb93, "gaffinalarabic"}, {0xfb94, "gafinitialarabic"}, {0xfb95, "gafmedialarabic"}, {0x0a97, "gagujarati"}, {0x0a17, "gagurmukhi"}, {0x304c, "gahiragana"}, {0x30ac, "gakatakana"}, {0x03b3, "gamma"}, {0x0263, "gammalatinsmall"}, {0x02e0, "gammasuperior"}, {0x03eb, "gangiacoptic"}, {0x310d, "gbopomofo"}, {0x011f, "gbreve"}, {0x01e7, "gcaron"}, {0x0123, "gcedilla"}, {0x24d6, "gcircle"}, {0x011d, "gcircumflex"}, {0x0123, "gcommaaccent"}, {0x0121, "gdot"}, {0x0121, "gdotaccent"}, {0x0433, "gecyrillic"}, {0x3052, "gehiragana"}, {0x30b2, "gekatakana"}, {0x2251, "geometricallyequal"}, {0x059c, "gereshaccenthebrew"}, {0x05f3, "gereshhebrew"}, {0x059d, "gereshmuqdamhebrew"}, {0x00df, "germandbls"}, {0x059e, "gershayimaccenthebrew"}, {0x05f4, "gershayimhebrew"}, {0x3013, "getamark"}, {0x0998, "ghabengali"}, {0x0572, "ghadarmenian"}, {0x0918, "ghadeva"}, {0x0a98, "ghagujarati"}, {0x0a18, "ghagurmukhi"}, {0x063a, "ghainarabic"}, {0xfece, "ghainfinalarabic"}, {0xfecf, "ghaininitialarabic"}, {0xfed0, "ghainmedialarabic"}, {0x0495, "ghemiddlehookcyrillic"}, {0x0493, "ghestrokecyrillic"}, {0x0491, "gheupturncyrillic"}, {0x095a, "ghhadeva"}, {0x0a5a, "ghhagurmukhi"}, {0x0260, "ghook"}, {0x3393, "ghzsquare"}, {0x304e, "gihiragana"}, {0x30ae, "gikatakana"}, {0x0563, "gimarmenian"}, {0x05d2, "gimel"}, {0xfb32, "gimeldagesh"}, {0xfb32, "gimeldageshhebrew"}, {0x05d2, "gimelhebrew"}, {0x0453, "gjecyrillic"}, {0x01be, "glottalinvertedstroke"}, {0x0294, "glottalstop"}, {0x0296, "glottalstopinverted"}, {0x02c0, "glottalstopmod"}, {0x0295, "glottalstopreversed"}, {0x02c1, "glottalstopreversedmod"}, {0x02e4, "glottalstopreversedsuperior"}, {0x02a1, "glottalstopstroke"}, {0x02a2, "glottalstopstrokereversed"}, {0x1e21, "gmacron"}, {0xff47, "gmonospace"}, {0x3054, "gohiragana"}, {0x30b4, "gokatakana"}, {0x24a2, "gparen"}, {0x33ac, "gpasquare"}, {0x2207, "gradient"}, {0x0060, "grave"}, {0x0316, "gravebelowcmb"}, {0x0300, "gravecmb"}, {0x0300, "gravecomb"}, {0x0953, "gravedeva"}, {0x02ce, "gravelowmod"}, {0xff40, "gravemonospace"}, {0x0340, "gravetonecmb"}, {0x003e, "greater"}, {0x2265, "greaterequal"}, {0x22db, "greaterequalorless"}, {0xff1e, "greatermonospace"}, {0x2273, "greaterorequivalent"}, {0x2277, "greaterorless"}, {0x2267, "greateroverequal"}, {0xfe65, "greatersmall"}, {0x0261, "gscript"}, {0x01e5, "gstroke"}, {0x3050, "guhiragana"}, {0x00ab, "guillemotleft"}, {0x00bb, "guillemotright"}, {0x2039, "guilsinglleft"}, {0x203a, "guilsinglright"}, {0x30b0, "gukatakana"}, {0x3318, "guramusquare"}, {0x33c9, "gysquare"}, {0x0068, "h"}, {0x04a9, "haabkhasiancyrillic"}, {0x06c1, "haaltonearabic"}, {0x09b9, "habengali"}, {0x04b3, "hadescendercyrillic"}, {0x0939, "hadeva"}, {0x0ab9, "hagujarati"}, {0x0a39, "hagurmukhi"}, {0x062d, "haharabic"}, {0xfea2, "hahfinalarabic"}, {0xfea3, "hahinitialarabic"}, {0x306f, "hahiragana"}, {0xfea4, "hahmedialarabic"}, {0x332a, "haitusquare"}, {0x30cf, "hakatakana"}, {0xff8a, "hakatakanahalfwidth"}, {0x0a4d, "halantgurmukhi"}, {0x0621, "hamzaarabic"}, {0x0621, "hamzalowarabic"}, {0x3164, "hangulfiller"}, {0x044a, "hardsigncyrillic"}, {0x21bc, "harpoonleftbarbup"}, {0x21c0, "harpoonrightbarbup"}, {0x33ca, "hasquare"}, {0x05b2, "hatafpatah"}, {0x05b2, "hatafpatah16"}, {0x05b2, "hatafpatah23"}, {0x05b2, "hatafpatah2f"}, {0x05b2, "hatafpatahhebrew"}, {0x05b2, "hatafpatahnarrowhebrew"}, {0x05b2, "hatafpatahquarterhebrew"}, {0x05b2, "hatafpatahwidehebrew"}, {0x05b3, "hatafqamats"}, {0x05b3, "hatafqamats1b"}, {0x05b3, "hatafqamats28"}, {0x05b3, "hatafqamats34"}, {0x05b3, "hatafqamatshebrew"}, {0x05b3, "hatafqamatsnarrowhebrew"}, {0x05b3, "hatafqamatsquarterhebrew"}, {0x05b3, "hatafqamatswidehebrew"}, {0x05b1, "hatafsegol"}, {0x05b1, "hatafsegol17"}, {0x05b1, "hatafsegol24"}, {0x05b1, "hatafsegol30"}, {0x05b1, "hatafsegolhebrew"}, {0x05b1, "hatafsegolnarrowhebrew"}, {0x05b1, "hatafsegolquarterhebrew"}, {0x05b1, "hatafsegolwidehebrew"}, {0x0127, "hbar"}, {0x310f, "hbopomofo"}, {0x1e2b, "hbrevebelow"}, {0x1e29, "hcedilla"}, {0x24d7, "hcircle"}, {0x0125, "hcircumflex"}, {0x1e27, "hdieresis"}, {0x1e23, "hdotaccent"}, {0x1e25, "hdotbelow"}, {0x05d4, "he"}, {0x2665, "heart"}, {0x2665, "heartsuitblack"}, {0x2661, "heartsuitwhite"}, {0xfb34, "hedagesh"}, {0xfb34, "hedageshhebrew"}, {0x06c1, "hehaltonearabic"}, {0x0647, "heharabic"}, {0x05d4, "hehebrew"}, {0xfba7, "hehfinalaltonearabic"}, {0xfeea, "hehfinalalttwoarabic"}, {0xfeea, "hehfinalarabic"}, {0xfba5, "hehhamzaabovefinalarabic"}, {0xfba4, "hehhamzaaboveisolatedarabic"}, {0xfba8, "hehinitialaltonearabic"}, {0xfeeb, "hehinitialarabic"}, {0x3078, "hehiragana"}, {0xfba9, "hehmedialaltonearabic"}, {0xfeec, "hehmedialarabic"}, {0x337b, "heiseierasquare"}, {0x30d8, "hekatakana"}, {0xff8d, "hekatakanahalfwidth"}, {0x3336, "hekutaarusquare"}, {0x0267, "henghook"}, {0x3339, "herutusquare"}, {0x05d7, "het"}, {0x05d7, "hethebrew"}, {0x0266, "hhook"}, {0x02b1, "hhooksuperior"}, {0x327b, "hieuhacirclekorean"}, {0x321b, "hieuhaparenkorean"}, {0x326d, "hieuhcirclekorean"}, {0x314e, "hieuhkorean"}, {0x320d, "hieuhparenkorean"}, {0x3072, "hihiragana"}, {0x30d2, "hikatakana"}, {0xff8b, "hikatakanahalfwidth"}, {0x05b4, "hiriq"}, {0x05b4, "hiriq14"}, {0x05b4, "hiriq21"}, {0x05b4, "hiriq2d"}, {0x05b4, "hiriqhebrew"}, {0x05b4, "hiriqnarrowhebrew"}, {0x05b4, "hiriqquarterhebrew"}, {0x05b4, "hiriqwidehebrew"}, {0x1e96, "hlinebelow"}, {0xff48, "hmonospace"}, {0x0570, "hoarmenian"}, {0x0e2b, "hohipthai"}, {0x307b, "hohiragana"}, {0x30db, "hokatakana"}, {0xff8e, "hokatakanahalfwidth"}, {0x05b9, "holam"}, {0x05b9, "holam19"}, {0x05b9, "holam26"}, {0x05b9, "holam32"}, {0x05b9, "holamhebrew"}, {0x05b9, "holamnarrowhebrew"}, {0x05b9, "holamquarterhebrew"}, {0x05b9, "holamwidehebrew"}, {0x0e2e, "honokhukthai"}, {0x0309, "hookabovecomb"}, {0x0309, "hookcmb"}, {0x0321, "hookpalatalizedbelowcmb"}, {0x0322, "hookretroflexbelowcmb"}, {0x3342, "hoonsquare"}, {0x03e9, "horicoptic"}, {0x2015, "horizontalbar"}, {0x031b, "horncmb"}, {0x2668, "hotsprings"}, {0x2302, "house"}, {0x24a3, "hparen"}, {0x02b0, "hsuperior"}, {0x0265, "hturned"}, {0x3075, "huhiragana"}, {0x3333, "huiitosquare"}, {0x30d5, "hukatakana"}, {0xff8c, "hukatakanahalfwidth"}, {0x02dd, "hungarumlaut"}, {0x030b, "hungarumlautcmb"}, {0x0195, "hv"}, {0x002d, "hyphen"}, {0xf6e5, "hypheninferior"}, {0xff0d, "hyphenmonospace"}, {0xfe63, "hyphensmall"}, {0xf6e6, "hyphensuperior"}, {0x2010, "hyphentwo"}, {0x0069, "i"}, {0x00ed, "iacute"}, {0x044f, "iacyrillic"}, {0x0987, "ibengali"}, {0x3127, "ibopomofo"}, {0x012d, "ibreve"}, {0x01d0, "icaron"}, {0x24d8, "icircle"}, {0x00ee, "icircumflex"}, {0x0456, "icyrillic"}, {0x0209, "idblgrave"}, {0x328f, "ideographearthcircle"}, {0x328b, "ideographfirecircle"}, {0x323f, "ideographicallianceparen"}, {0x323a, "ideographiccallparen"}, {0x32a5, "ideographiccentrecircle"}, {0x3006, "ideographicclose"}, {0x3001, "ideographiccomma"}, {0xff64, "ideographiccommaleft"}, {0x3237, "ideographiccongratulationparen"}, {0x32a3, "ideographiccorrectcircle"}, {0x322f, "ideographicearthparen"}, {0x323d, "ideographicenterpriseparen"}, {0x329d, "ideographicexcellentcircle"}, {0x3240, "ideographicfestivalparen"}, {0x3296, "ideographicfinancialcircle"}, {0x3236, "ideographicfinancialparen"}, {0x322b, "ideographicfireparen"}, {0x3232, "ideographichaveparen"}, {0x32a4, "ideographichighcircle"}, {0x3005, "ideographiciterationmark"}, {0x3298, "ideographiclaborcircle"}, {0x3238, "ideographiclaborparen"}, {0x32a7, "ideographicleftcircle"}, {0x32a6, "ideographiclowcircle"}, {0x32a9, "ideographicmedicinecircle"}, {0x322e, "ideographicmetalparen"}, {0x322a, "ideographicmoonparen"}, {0x3234, "ideographicnameparen"}, {0x3002, "ideographicperiod"}, {0x329e, "ideographicprintcircle"}, {0x3243, "ideographicreachparen"}, {0x3239, "ideographicrepresentparen"}, {0x323e, "ideographicresourceparen"}, {0x32a8, "ideographicrightcircle"}, {0x3299, "ideographicsecretcircle"}, {0x3242, "ideographicselfparen"}, {0x3233, "ideographicsocietyparen"}, {0x3000, "ideographicspace"}, {0x3235, "ideographicspecialparen"}, {0x3231, "ideographicstockparen"}, {0x323b, "ideographicstudyparen"}, {0x3230, "ideographicsunparen"}, {0x323c, "ideographicsuperviseparen"}, {0x322c, "ideographicwaterparen"}, {0x322d, "ideographicwoodparen"}, {0x3007, "ideographiczero"}, {0x328e, "ideographmetalcircle"}, {0x328a, "ideographmooncircle"}, {0x3294, "ideographnamecircle"}, {0x3290, "ideographsuncircle"}, {0x328c, "ideographwatercircle"}, {0x328d, "ideographwoodcircle"}, {0x0907, "ideva"}, {0x00ef, "idieresis"}, {0x1e2f, "idieresisacute"}, {0x04e5, "idieresiscyrillic"}, {0x1ecb, "idotbelow"}, {0x04d7, "iebrevecyrillic"}, {0x0435, "iecyrillic"}, {0x3275, "ieungacirclekorean"}, {0x3215, "ieungaparenkorean"}, {0x3267, "ieungcirclekorean"}, {0x3147, "ieungkorean"}, {0x3207, "ieungparenkorean"}, {0x00ec, "igrave"}, {0x0a87, "igujarati"}, {0x0a07, "igurmukhi"}, {0x3044, "ihiragana"}, {0x1ec9, "ihookabove"}, {0x0988, "iibengali"}, {0x0438, "iicyrillic"}, {0x0908, "iideva"}, {0x0a88, "iigujarati"}, {0x0a08, "iigurmukhi"}, {0x0a40, "iimatragurmukhi"}, {0x020b, "iinvertedbreve"}, {0x0439, "iishortcyrillic"}, {0x09c0, "iivowelsignbengali"}, {0x0940, "iivowelsigndeva"}, {0x0ac0, "iivowelsigngujarati"}, {0x0133, "ij"}, {0x30a4, "ikatakana"}, {0xff72, "ikatakanahalfwidth"}, {0x3163, "ikorean"}, {0x02dc, "ilde"}, {0x05ac, "iluyhebrew"}, {0x012b, "imacron"}, {0x04e3, "imacroncyrillic"}, {0x2253, "imageorapproximatelyequal"}, {0x0a3f, "imatragurmukhi"}, {0xff49, "imonospace"}, {0x2206, "increment"}, {0x221e, "infinity"}, {0x056b, "iniarmenian"}, {0x222b, "integral"}, {0x2321, "integralbottom"}, {0x2321, "integralbt"}, {0xf8f5, "integralex"}, {0x2320, "integraltop"}, {0x2320, "integraltp"}, {0x2229, "intersection"}, {0x3305, "intisquare"}, {0x25d8, "invbullet"}, {0x25d9, "invcircle"}, {0x263b, "invsmileface"}, {0x0451, "iocyrillic"}, {0x012f, "iogonek"}, {0x03b9, "iota"}, {0x03ca, "iotadieresis"}, {0x0390, "iotadieresistonos"}, {0x0269, "iotalatin"}, {0x03af, "iotatonos"}, {0x24a4, "iparen"}, {0x0a72, "irigurmukhi"}, {0x3043, "ismallhiragana"}, {0x30a3, "ismallkatakana"}, {0xff68, "ismallkatakanahalfwidth"}, {0x09fa, "issharbengali"}, {0x0268, "istroke"}, {0xf6ed, "isuperior"}, {0x309d, "iterationhiragana"}, {0x30fd, "iterationkatakana"}, {0x0129, "itilde"}, {0x1e2d, "itildebelow"}, {0x3129, "iubopomofo"}, {0x044e, "iucyrillic"}, {0x09bf, "ivowelsignbengali"}, {0x093f, "ivowelsigndeva"}, {0x0abf, "ivowelsigngujarati"}, {0x0475, "izhitsacyrillic"}, {0x0477, "izhitsadblgravecyrillic"}, {0x006a, "j"}, {0x0571, "jaarmenian"}, {0x099c, "jabengali"}, {0x091c, "jadeva"}, {0x0a9c, "jagujarati"}, {0x0a1c, "jagurmukhi"}, {0x3110, "jbopomofo"}, {0x01f0, "jcaron"}, {0x24d9, "jcircle"}, {0x0135, "jcircumflex"}, {0x029d, "jcrossedtail"}, {0x025f, "jdotlessstroke"}, {0x0458, "jecyrillic"}, {0x062c, "jeemarabic"}, {0xfe9e, "jeemfinalarabic"}, {0xfe9f, "jeeminitialarabic"}, {0xfea0, "jeemmedialarabic"}, {0x0698, "jeharabic"}, {0xfb8b, "jehfinalarabic"}, {0x099d, "jhabengali"}, {0x091d, "jhadeva"}, {0x0a9d, "jhagujarati"}, {0x0a1d, "jhagurmukhi"}, {0x057b, "jheharmenian"}, {0x3004, "jis"}, {0xff4a, "jmonospace"}, {0x24a5, "jparen"}, {0x02b2, "jsuperior"}, {0x006b, "k"}, {0x04a1, "kabashkircyrillic"}, {0x0995, "kabengali"}, {0x1e31, "kacute"}, {0x043a, "kacyrillic"}, {0x049b, "kadescendercyrillic"}, {0x0915, "kadeva"}, {0x05db, "kaf"}, {0x0643, "kafarabic"}, {0xfb3b, "kafdagesh"}, {0xfb3b, "kafdageshhebrew"}, {0xfeda, "kaffinalarabic"}, {0x05db, "kafhebrew"}, {0xfedb, "kafinitialarabic"}, {0xfedc, "kafmedialarabic"}, {0xfb4d, "kafrafehebrew"}, {0x0a95, "kagujarati"}, {0x0a15, "kagurmukhi"}, {0x304b, "kahiragana"}, {0x04c4, "kahookcyrillic"}, {0x30ab, "kakatakana"}, {0xff76, "kakatakanahalfwidth"}, {0x03ba, "kappa"}, {0x03f0, "kappasymbolgreek"}, {0x3171, "kapyeounmieumkorean"}, {0x3184, "kapyeounphieuphkorean"}, {0x3178, "kapyeounpieupkorean"}, {0x3179, "kapyeounssangpieupkorean"}, {0x330d, "karoriisquare"}, {0x0640, "kashidaautoarabic"}, {0x0640, "kashidaautonosidebearingarabic"}, {0x30f5, "kasmallkatakana"}, {0x3384, "kasquare"}, {0x0650, "kasraarabic"}, {0x064d, "kasratanarabic"}, {0x049f, "kastrokecyrillic"}, {0xff70, "katahiraprolongmarkhalfwidth"}, {0x049d, "kaverticalstrokecyrillic"}, {0x310e, "kbopomofo"}, {0x3389, "kcalsquare"}, {0x01e9, "kcaron"}, {0x0137, "kcedilla"}, {0x24da, "kcircle"}, {0x0137, "kcommaaccent"}, {0x1e33, "kdotbelow"}, {0x0584, "keharmenian"}, {0x3051, "kehiragana"}, {0x30b1, "kekatakana"}, {0xff79, "kekatakanahalfwidth"}, {0x056f, "kenarmenian"}, {0x30f6, "kesmallkatakana"}, {0x0138, "kgreenlandic"}, {0x0996, "khabengali"}, {0x0445, "khacyrillic"}, {0x0916, "khadeva"}, {0x0a96, "khagujarati"}, {0x0a16, "khagurmukhi"}, {0x062e, "khaharabic"}, {0xfea6, "khahfinalarabic"}, {0xfea7, "khahinitialarabic"}, {0xfea8, "khahmedialarabic"}, {0x03e7, "kheicoptic"}, {0x0959, "khhadeva"}, {0x0a59, "khhagurmukhi"}, {0x3278, "khieukhacirclekorean"}, {0x3218, "khieukhaparenkorean"}, {0x326a, "khieukhcirclekorean"}, {0x314b, "khieukhkorean"}, {0x320a, "khieukhparenkorean"}, {0x0e02, "khokhaithai"}, {0x0e05, "khokhonthai"}, {0x0e03, "khokhuatthai"}, {0x0e04, "khokhwaithai"}, {0x0e5b, "khomutthai"}, {0x0199, "khook"}, {0x0e06, "khorakhangthai"}, {0x3391, "khzsquare"}, {0x304d, "kihiragana"}, {0x30ad, "kikatakana"}, {0xff77, "kikatakanahalfwidth"}, {0x3315, "kiroguramusquare"}, {0x3316, "kiromeetorusquare"}, {0x3314, "kirosquare"}, {0x326e, "kiyeokacirclekorean"}, {0x320e, "kiyeokaparenkorean"}, {0x3260, "kiyeokcirclekorean"}, {0x3131, "kiyeokkorean"}, {0x3200, "kiyeokparenkorean"}, {0x3133, "kiyeoksioskorean"}, {0x045c, "kjecyrillic"}, {0x1e35, "klinebelow"}, {0x3398, "klsquare"}, {0x33a6, "kmcubedsquare"}, {0xff4b, "kmonospace"}, {0x33a2, "kmsquaredsquare"}, {0x3053, "kohiragana"}, {0x33c0, "kohmsquare"}, {0x0e01, "kokaithai"}, {0x30b3, "kokatakana"}, {0xff7a, "kokatakanahalfwidth"}, {0x331e, "kooposquare"}, {0x0481, "koppacyrillic"}, {0x327f, "koreanstandardsymbol"}, {0x0343, "koroniscmb"}, {0x24a6, "kparen"}, {0x33aa, "kpasquare"}, {0x046f, "ksicyrillic"}, {0x33cf, "ktsquare"}, {0x029e, "kturned"}, {0x304f, "kuhiragana"}, {0x30af, "kukatakana"}, {0xff78, "kukatakanahalfwidth"}, {0x33b8, "kvsquare"}, {0x33be, "kwsquare"}, {0x006c, "l"}, {0x09b2, "labengali"}, {0x013a, "lacute"}, {0x0932, "ladeva"}, {0x0ab2, "lagujarati"}, {0x0a32, "lagurmukhi"}, {0x0e45, "lakkhangyaothai"}, {0xfefc, "lamaleffinalarabic"}, {0xfef8, "lamalefhamzaabovefinalarabic"}, {0xfef7, "lamalefhamzaaboveisolatedarabic"}, {0xfefa, "lamalefhamzabelowfinalarabic"}, {0xfef9, "lamalefhamzabelowisolatedarabic"}, {0xfefb, "lamalefisolatedarabic"}, {0xfef6, "lamalefmaddaabovefinalarabic"}, {0xfef5, "lamalefmaddaaboveisolatedarabic"}, {0x0644, "lamarabic"}, {0x03bb, "lambda"}, {0x019b, "lambdastroke"}, {0x05dc, "lamed"}, {0xfb3c, "lameddagesh"}, {0xfb3c, "lameddageshhebrew"}, {0x05dc, "lamedhebrew"}, {0xfede, "lamfinalarabic"}, {0xfcca, "lamhahinitialarabic"}, {0xfedf, "laminitialarabic"}, {0xfcc9, "lamjeeminitialarabic"}, {0xfccb, "lamkhahinitialarabic"}, {0xfdf2, "lamlamhehisolatedarabic"}, {0xfee0, "lammedialarabic"}, {0xfd88, "lammeemhahinitialarabic"}, {0xfccc, "lammeeminitialarabic"}, {0x25ef, "largecircle"}, {0x019a, "lbar"}, {0x026c, "lbelt"}, {0x310c, "lbopomofo"}, {0x013e, "lcaron"}, {0x013c, "lcedilla"}, {0x24db, "lcircle"}, {0x1e3d, "lcircumflexbelow"}, {0x013c, "lcommaaccent"}, {0x0140, "ldot"}, {0x0140, "ldotaccent"}, {0x1e37, "ldotbelow"}, {0x1e39, "ldotbelowmacron"}, {0x031a, "leftangleabovecmb"}, {0x0318, "lefttackbelowcmb"}, {0x003c, "less"}, {0x2264, "lessequal"}, {0x22da, "lessequalorgreater"}, {0xff1c, "lessmonospace"}, {0x2272, "lessorequivalent"}, {0x2276, "lessorgreater"}, {0x2266, "lessoverequal"}, {0xfe64, "lesssmall"}, {0x026e, "lezh"}, {0x258c, "lfblock"}, {0x026d, "lhookretroflex"}, {0x20a4, "lira"}, {0x056c, "liwnarmenian"}, {0x01c9, "lj"}, {0x0459, "ljecyrillic"}, {0xf6c0, "ll"}, {0x0933, "lladeva"}, {0x0ab3, "llagujarati"}, {0x1e3b, "llinebelow"}, {0x0934, "llladeva"}, {0x09e1, "llvocalicbengali"}, {0x0961, "llvocalicdeva"}, {0x09e3, "llvocalicvowelsignbengali"}, {0x0963, "llvocalicvowelsigndeva"}, {0x026b, "lmiddletilde"}, {0xff4c, "lmonospace"}, {0x33d0, "lmsquare"}, {0x0e2c, "lochulathai"}, {0x2227, "logicaland"}, {0x00ac, "logicalnot"}, {0x2310, "logicalnotreversed"}, {0x2228, "logicalor"}, {0x0e25, "lolingthai"}, {0x017f, "longs"}, {0xfe4e, "lowlinecenterline"}, {0x0332, "lowlinecmb"}, {0xfe4d, "lowlinedashed"}, {0x25ca, "lozenge"}, {0x24a7, "lparen"}, {0x0142, "lslash"}, {0x2113, "lsquare"}, {0xf6ee, "lsuperior"}, {0x2591, "ltshade"}, {0x0e26, "luthai"}, {0x098c, "lvocalicbengali"}, {0x090c, "lvocalicdeva"}, {0x09e2, "lvocalicvowelsignbengali"}, {0x0962, "lvocalicvowelsigndeva"}, {0x33d3, "lxsquare"}, {0x006d, "m"}, {0x09ae, "mabengali"}, {0x00af, "macron"}, {0x0331, "macronbelowcmb"}, {0x0304, "macroncmb"}, {0x02cd, "macronlowmod"}, {0xffe3, "macronmonospace"}, {0x1e3f, "macute"}, {0x092e, "madeva"}, {0x0aae, "magujarati"}, {0x0a2e, "magurmukhi"}, {0x05a4, "mahapakhhebrew"}, {0x05a4, "mahapakhlefthebrew"}, {0x307e, "mahiragana"}, {0xf895, "maichattawalowleftthai"}, {0xf894, "maichattawalowrightthai"}, {0x0e4b, "maichattawathai"}, {0xf893, "maichattawaupperleftthai"}, {0xf88c, "maieklowleftthai"}, {0xf88b, "maieklowrightthai"}, {0x0e48, "maiekthai"}, {0xf88a, "maiekupperleftthai"}, {0xf884, "maihanakatleftthai"}, {0x0e31, "maihanakatthai"}, {0xf889, "maitaikhuleftthai"}, {0x0e47, "maitaikhuthai"}, {0xf88f, "maitholowleftthai"}, {0xf88e, "maitholowrightthai"}, {0x0e49, "maithothai"}, {0xf88d, "maithoupperleftthai"}, {0xf892, "maitrilowleftthai"}, {0xf891, "maitrilowrightthai"}, {0x0e4a, "maitrithai"}, {0xf890, "maitriupperleftthai"}, {0x0e46, "maiyamokthai"}, {0x30de, "makatakana"}, {0xff8f, "makatakanahalfwidth"}, {0x2642, "male"}, {0x3347, "mansyonsquare"}, {0x05be, "maqafhebrew"}, {0x2642, "mars"}, {0x05af, "masoracirclehebrew"}, {0x3383, "masquare"}, {0x3107, "mbopomofo"}, {0x33d4, "mbsquare"}, {0x24dc, "mcircle"}, {0x33a5, "mcubedsquare"}, {0x1e41, "mdotaccent"}, {0x1e43, "mdotbelow"}, {0x0645, "meemarabic"}, {0xfee2, "meemfinalarabic"}, {0xfee3, "meeminitialarabic"}, {0xfee4, "meemmedialarabic"}, {0xfcd1, "meemmeeminitialarabic"}, {0xfc48, "meemmeemisolatedarabic"}, {0x334d, "meetorusquare"}, {0x3081, "mehiragana"}, {0x337e, "meizierasquare"}, {0x30e1, "mekatakana"}, {0xff92, "mekatakanahalfwidth"}, {0x05de, "mem"}, {0xfb3e, "memdagesh"}, {0xfb3e, "memdageshhebrew"}, {0x05de, "memhebrew"}, {0x0574, "menarmenian"}, {0x05a5, "merkhahebrew"}, {0x05a6, "merkhakefulahebrew"}, {0x05a6, "merkhakefulalefthebrew"}, {0x05a5, "merkhalefthebrew"}, {0x0271, "mhook"}, {0x3392, "mhzsquare"}, {0xff65, "middledotkatakanahalfwidth"}, {0x00b7, "middot"}, {0x3272, "mieumacirclekorean"}, {0x3212, "mieumaparenkorean"}, {0x3264, "mieumcirclekorean"}, {0x3141, "mieumkorean"}, {0x3170, "mieumpansioskorean"}, {0x3204, "mieumparenkorean"}, {0x316e, "mieumpieupkorean"}, {0x316f, "mieumsioskorean"}, {0x307f, "mihiragana"}, {0x30df, "mikatakana"}, {0xff90, "mikatakanahalfwidth"}, {0x2212, "minus"}, {0x0320, "minusbelowcmb"}, {0x2296, "minuscircle"}, {0x02d7, "minusmod"}, {0x2213, "minusplus"}, {0x2032, "minute"}, {0x334a, "miribaarusquare"}, {0x3349, "mirisquare"}, {0x0270, "mlonglegturned"}, {0x3396, "mlsquare"}, {0x33a3, "mmcubedsquare"}, {0xff4d, "mmonospace"}, {0x339f, "mmsquaredsquare"}, {0x3082, "mohiragana"}, {0x33c1, "mohmsquare"}, {0x30e2, "mokatakana"}, {0xff93, "mokatakanahalfwidth"}, {0x33d6, "molsquare"}, {0x0e21, "momathai"}, {0x33a7, "moverssquare"}, {0x33a8, "moverssquaredsquare"}, {0x24a8, "mparen"}, {0x33ab, "mpasquare"}, {0x33b3, "mssquare"}, {0xf6ef, "msuperior"}, {0x026f, "mturned"}, {0x00b5, "mu"}, {0x00b5, "mu1"}, {0x3382, "muasquare"}, {0x226b, "muchgreater"}, {0x226a, "muchless"}, {0x338c, "mufsquare"}, {0x03bc, "mugreek"}, {0x338d, "mugsquare"}, {0x3080, "muhiragana"}, {0x30e0, "mukatakana"}, {0xff91, "mukatakanahalfwidth"}, {0x3395, "mulsquare"}, {0x00d7, "multiply"}, {0x339b, "mumsquare"}, {0x05a3, "munahhebrew"}, {0x05a3, "munahlefthebrew"}, {0x266a, "musicalnote"}, {0x266b, "musicalnotedbl"}, {0x266d, "musicflatsign"}, {0x266f, "musicsharpsign"}, {0x33b2, "mussquare"}, {0x33b6, "muvsquare"}, {0x33bc, "muwsquare"}, {0x33b9, "mvmegasquare"}, {0x33b7, "mvsquare"}, {0x33bf, "mwmegasquare"}, {0x33bd, "mwsquare"}, {0x006e, "n"}, {0x09a8, "nabengali"}, {0x2207, "nabla"}, {0x0144, "nacute"}, {0x0928, "nadeva"}, {0x0aa8, "nagujarati"}, {0x0a28, "nagurmukhi"}, {0x306a, "nahiragana"}, {0x30ca, "nakatakana"}, {0xff85, "nakatakanahalfwidth"}, {0x0149, "napostrophe"}, {0x3381, "nasquare"}, {0x310b, "nbopomofo"}, {0x00a0, "nbspace"}, {0x0148, "ncaron"}, {0x0146, "ncedilla"}, {0x24dd, "ncircle"}, {0x1e4b, "ncircumflexbelow"}, {0x0146, "ncommaaccent"}, {0x1e45, "ndotaccent"}, {0x1e47, "ndotbelow"}, {0x306d, "nehiragana"}, {0x30cd, "nekatakana"}, {0xff88, "nekatakanahalfwidth"}, {0x20aa, "newsheqelsign"}, {0x338b, "nfsquare"}, {0x0999, "ngabengali"}, {0x0919, "ngadeva"}, {0x0a99, "ngagujarati"}, {0x0a19, "ngagurmukhi"}, {0x0e07, "ngonguthai"}, {0x3093, "nhiragana"}, {0x0272, "nhookleft"}, {0x0273, "nhookretroflex"}, {0x326f, "nieunacirclekorean"}, {0x320f, "nieunaparenkorean"}, {0x3135, "nieuncieuckorean"}, {0x3261, "nieuncirclekorean"}, {0x3136, "nieunhieuhkorean"}, {0x3134, "nieunkorean"}, {0x3168, "nieunpansioskorean"}, {0x3201, "nieunparenkorean"}, {0x3167, "nieunsioskorean"}, {0x3166, "nieuntikeutkorean"}, {0x306b, "nihiragana"}, {0x30cb, "nikatakana"}, {0xff86, "nikatakanahalfwidth"}, {0xf899, "nikhahitleftthai"}, {0x0e4d, "nikhahitthai"}, {0x0039, "nine"}, {0x0669, "ninearabic"}, {0x09ef, "ninebengali"}, {0x2468, "ninecircle"}, {0x2792, "ninecircleinversesansserif"}, {0x096f, "ninedeva"}, {0x0aef, "ninegujarati"}, {0x0a6f, "ninegurmukhi"}, {0x0669, "ninehackarabic"}, {0x3029, "ninehangzhou"}, {0x3228, "nineideographicparen"}, {0x2089, "nineinferior"}, {0xff19, "ninemonospace"}, {0xf739, "nineoldstyle"}, {0x247c, "nineparen"}, {0x2490, "nineperiod"}, {0x06f9, "ninepersian"}, {0x2178, "nineroman"}, {0x2079, "ninesuperior"}, {0x2472, "nineteencircle"}, {0x2486, "nineteenparen"}, {0x249a, "nineteenperiod"}, {0x0e59, "ninethai"}, {0x01cc, "nj"}, {0x045a, "njecyrillic"}, {0x30f3, "nkatakana"}, {0xff9d, "nkatakanahalfwidth"}, {0x019e, "nlegrightlong"}, {0x1e49, "nlinebelow"}, {0xff4e, "nmonospace"}, {0x339a, "nmsquare"}, {0x09a3, "nnabengali"}, {0x0923, "nnadeva"}, {0x0aa3, "nnagujarati"}, {0x0a23, "nnagurmukhi"}, {0x0929, "nnnadeva"}, {0x306e, "nohiragana"}, {0x30ce, "nokatakana"}, {0xff89, "nokatakanahalfwidth"}, {0x00a0, "nonbreakingspace"}, {0x0e13, "nonenthai"}, {0x0e19, "nonuthai"}, {0x0646, "noonarabic"}, {0xfee6, "noonfinalarabic"}, {0x06ba, "noonghunnaarabic"}, {0xfb9f, "noonghunnafinalarabic"}, {0xfee7, "nooninitialarabic"}, {0xfcd2, "noonjeeminitialarabic"}, {0xfc4b, "noonjeemisolatedarabic"}, {0xfee8, "noonmedialarabic"}, {0xfcd5, "noonmeeminitialarabic"}, {0xfc4e, "noonmeemisolatedarabic"}, {0xfc8d, "noonnoonfinalarabic"}, {0x220c, "notcontains"}, {0x2209, "notelement"}, {0x2209, "notelementof"}, {0x2260, "notequal"}, {0x226f, "notgreater"}, {0x2271, "notgreaternorequal"}, {0x2279, "notgreaternorless"}, {0x2262, "notidentical"}, {0x226e, "notless"}, {0x2270, "notlessnorequal"}, {0x2226, "notparallel"}, {0x2280, "notprecedes"}, {0x2284, "notsubset"}, {0x2281, "notsucceeds"}, {0x2285, "notsuperset"}, {0x0576, "nowarmenian"}, {0x24a9, "nparen"}, {0x33b1, "nssquare"}, {0x207f, "nsuperior"}, {0x00f1, "ntilde"}, {0x03bd, "nu"}, {0x306c, "nuhiragana"}, {0x30cc, "nukatakana"}, {0xff87, "nukatakanahalfwidth"}, {0x09bc, "nuktabengali"}, {0x093c, "nuktadeva"}, {0x0abc, "nuktagujarati"}, {0x0a3c, "nuktagurmukhi"}, {0x0023, "numbersign"}, {0xff03, "numbersignmonospace"}, {0xfe5f, "numbersignsmall"}, {0x0374, "numeralsigngreek"}, {0x0375, "numeralsignlowergreek"}, {0x2116, "numero"}, {0x05e0, "nun"}, {0xfb40, "nundagesh"}, {0xfb40, "nundageshhebrew"}, {0x05e0, "nunhebrew"}, {0x33b5, "nvsquare"}, {0x33bb, "nwsquare"}, {0x099e, "nyabengali"}, {0x091e, "nyadeva"}, {0x0a9e, "nyagujarati"}, {0x0a1e, "nyagurmukhi"}, {0x006f, "o"}, {0x00f3, "oacute"}, {0x0e2d, "oangthai"}, {0x0275, "obarred"}, {0x04e9, "obarredcyrillic"}, {0x04eb, "obarreddieresiscyrillic"}, {0x0993, "obengali"}, {0x311b, "obopomofo"}, {0x014f, "obreve"}, {0x0911, "ocandradeva"}, {0x0a91, "ocandragujarati"}, {0x0949, "ocandravowelsigndeva"}, {0x0ac9, "ocandravowelsigngujarati"}, {0x01d2, "ocaron"}, {0x24de, "ocircle"}, {0x00f4, "ocircumflex"}, {0x1ed1, "ocircumflexacute"}, {0x1ed9, "ocircumflexdotbelow"}, {0x1ed3, "ocircumflexgrave"}, {0x1ed5, "ocircumflexhookabove"}, {0x1ed7, "ocircumflextilde"}, {0x043e, "ocyrillic"}, {0x0151, "odblacute"}, {0x020d, "odblgrave"}, {0x0913, "odeva"}, {0x00f6, "odieresis"}, {0x04e7, "odieresiscyrillic"}, {0x1ecd, "odotbelow"}, {0x0153, "oe"}, {0x315a, "oekorean"}, {0x02db, "ogonek"}, {0x0328, "ogonekcmb"}, {0x00f2, "ograve"}, {0x0a93, "ogujarati"}, {0x0585, "oharmenian"}, {0x304a, "ohiragana"}, {0x1ecf, "ohookabove"}, {0x01a1, "ohorn"}, {0x1edb, "ohornacute"}, {0x1ee3, "ohorndotbelow"}, {0x1edd, "ohorngrave"}, {0x1edf, "ohornhookabove"}, {0x1ee1, "ohorntilde"}, {0x0151, "ohungarumlaut"}, {0x01a3, "oi"}, {0x020f, "oinvertedbreve"}, {0x30aa, "okatakana"}, {0xff75, "okatakanahalfwidth"}, {0x3157, "okorean"}, {0x05ab, "olehebrew"}, {0x014d, "omacron"}, {0x1e53, "omacronacute"}, {0x1e51, "omacrongrave"}, {0x0950, "omdeva"}, {0x03c9, "omega"}, {0x03d6, "omega1"}, {0x0461, "omegacyrillic"}, {0x0277, "omegalatinclosed"}, {0x047b, "omegaroundcyrillic"}, {0x047d, "omegatitlocyrillic"}, {0x03ce, "omegatonos"}, {0x0ad0, "omgujarati"}, {0x03bf, "omicron"}, {0x03cc, "omicrontonos"}, {0xff4f, "omonospace"}, {0x0031, "one"}, {0x0661, "onearabic"}, {0x09e7, "onebengali"}, {0x2460, "onecircle"}, {0x278a, "onecircleinversesansserif"}, {0x0967, "onedeva"}, {0x2024, "onedotenleader"}, {0x215b, "oneeighth"}, {0xf6dc, "onefitted"}, {0x0ae7, "onegujarati"}, {0x0a67, "onegurmukhi"}, {0x0661, "onehackarabic"}, {0x00bd, "onehalf"}, {0x3021, "onehangzhou"}, {0x3220, "oneideographicparen"}, {0x2081, "oneinferior"}, {0xff11, "onemonospace"}, {0x09f4, "onenumeratorbengali"}, {0xf731, "oneoldstyle"}, {0x2474, "oneparen"}, {0x2488, "oneperiod"}, {0x06f1, "onepersian"}, {0x00bc, "onequarter"}, {0x2170, "oneroman"}, {0x00b9, "onesuperior"}, {0x0e51, "onethai"}, {0x2153, "onethird"}, {0x01eb, "oogonek"}, {0x01ed, "oogonekmacron"}, {0x0a13, "oogurmukhi"}, {0x0a4b, "oomatragurmukhi"}, {0x0254, "oopen"}, {0x24aa, "oparen"}, {0x25e6, "openbullet"}, {0x2325, "option"}, {0x00aa, "ordfeminine"}, {0x00ba, "ordmasculine"}, {0x221f, "orthogonal"}, {0x0912, "oshortdeva"}, {0x094a, "oshortvowelsigndeva"}, {0x00f8, "oslash"}, {0x01ff, "oslashacute"}, {0x3049, "osmallhiragana"}, {0x30a9, "osmallkatakana"}, {0xff6b, "osmallkatakanahalfwidth"}, {0x01ff, "ostrokeacute"}, {0xf6f0, "osuperior"}, {0x047f, "otcyrillic"}, {0x00f5, "otilde"}, {0x1e4d, "otildeacute"}, {0x1e4f, "otildedieresis"}, {0x3121, "oubopomofo"}, {0x203e, "overline"}, {0xfe4a, "overlinecenterline"}, {0x0305, "overlinecmb"}, {0xfe49, "overlinedashed"}, {0xfe4c, "overlinedblwavy"}, {0xfe4b, "overlinewavy"}, {0x00af, "overscore"}, {0x09cb, "ovowelsignbengali"}, {0x094b, "ovowelsigndeva"}, {0x0acb, "ovowelsigngujarati"}, {0x0070, "p"}, {0x3380, "paampssquare"}, {0x332b, "paasentosquare"}, {0x09aa, "pabengali"}, {0x1e55, "pacute"}, {0x092a, "padeva"}, {0x21df, "pagedown"}, {0x21de, "pageup"}, {0x0aaa, "pagujarati"}, {0x0a2a, "pagurmukhi"}, {0x3071, "pahiragana"}, {0x0e2f, "paiyannoithai"}, {0x30d1, "pakatakana"}, {0x0484, "palatalizationcyrilliccmb"}, {0x04c0, "palochkacyrillic"}, {0x317f, "pansioskorean"}, {0x00b6, "paragraph"}, {0x2225, "parallel"}, {0x0028, "parenleft"}, {0xfd3e, "parenleftaltonearabic"}, {0xf8ed, "parenleftbt"}, {0xf8ec, "parenleftex"}, {0x208d, "parenleftinferior"}, {0xff08, "parenleftmonospace"}, {0xfe59, "parenleftsmall"}, {0x207d, "parenleftsuperior"}, {0xf8eb, "parenlefttp"}, {0xfe35, "parenleftvertical"}, {0x0029, "parenright"}, {0xfd3f, "parenrightaltonearabic"}, {0xf8f8, "parenrightbt"}, {0xf8f7, "parenrightex"}, {0x208e, "parenrightinferior"}, {0xff09, "parenrightmonospace"}, {0xfe5a, "parenrightsmall"}, {0x207e, "parenrightsuperior"}, {0xf8f6, "parenrighttp"}, {0xfe36, "parenrightvertical"}, {0x2202, "partialdiff"}, {0x05c0, "paseqhebrew"}, {0x0599, "pashtahebrew"}, {0x33a9, "pasquare"}, {0x05b7, "patah"}, {0x05b7, "patah11"}, {0x05b7, "patah1d"}, {0x05b7, "patah2a"}, {0x05b7, "patahhebrew"}, {0x05b7, "patahnarrowhebrew"}, {0x05b7, "patahquarterhebrew"}, {0x05b7, "patahwidehebrew"}, {0x05a1, "pazerhebrew"}, {0x3106, "pbopomofo"}, {0x24df, "pcircle"}, {0x1e57, "pdotaccent"}, {0x05e4, "pe"}, {0x043f, "pecyrillic"}, {0xfb44, "pedagesh"}, {0xfb44, "pedageshhebrew"}, {0x333b, "peezisquare"}, {0xfb43, "pefinaldageshhebrew"}, {0x067e, "peharabic"}, {0x057a, "peharmenian"}, {0x05e4, "pehebrew"}, {0xfb57, "pehfinalarabic"}, {0xfb58, "pehinitialarabic"}, {0x307a, "pehiragana"}, {0xfb59, "pehmedialarabic"}, {0x30da, "pekatakana"}, {0x04a7, "pemiddlehookcyrillic"}, {0xfb4e, "perafehebrew"}, {0x0025, "percent"}, {0x066a, "percentarabic"}, {0xff05, "percentmonospace"}, {0xfe6a, "percentsmall"}, {0x002e, "period"}, {0x0589, "periodarmenian"}, {0x00b7, "periodcentered"}, {0xff61, "periodhalfwidth"}, {0xf6e7, "periodinferior"}, {0xff0e, "periodmonospace"}, {0xfe52, "periodsmall"}, {0xf6e8, "periodsuperior"}, {0x0342, "perispomenigreekcmb"}, {0x22a5, "perpendicular"}, {0x2030, "perthousand"}, {0x20a7, "peseta"}, {0x338a, "pfsquare"}, {0x09ab, "phabengali"}, {0x092b, "phadeva"}, {0x0aab, "phagujarati"}, {0x0a2b, "phagurmukhi"}, {0x03c6, "phi"}, {0x03d5, "phi1"}, {0x327a, "phieuphacirclekorean"}, {0x321a, "phieuphaparenkorean"}, {0x326c, "phieuphcirclekorean"}, {0x314d, "phieuphkorean"}, {0x320c, "phieuphparenkorean"}, {0x0278, "philatin"}, {0x0e3a, "phinthuthai"}, {0x03d5, "phisymbolgreek"}, {0x01a5, "phook"}, {0x0e1e, "phophanthai"}, {0x0e1c, "phophungthai"}, {0x0e20, "phosamphaothai"}, {0x03c0, "pi"}, {0x3273, "pieupacirclekorean"}, {0x3213, "pieupaparenkorean"}, {0x3176, "pieupcieuckorean"}, {0x3265, "pieupcirclekorean"}, {0x3172, "pieupkiyeokkorean"}, {0x3142, "pieupkorean"}, {0x3205, "pieupparenkorean"}, {0x3174, "pieupsioskiyeokkorean"}, {0x3144, "pieupsioskorean"}, {0x3175, "pieupsiostikeutkorean"}, {0x3177, "pieupthieuthkorean"}, {0x3173, "pieuptikeutkorean"}, {0x3074, "pihiragana"}, {0x30d4, "pikatakana"}, {0x03d6, "pisymbolgreek"}, {0x0583, "piwrarmenian"}, {0x002b, "plus"}, {0x031f, "plusbelowcmb"}, {0x2295, "pluscircle"}, {0x00b1, "plusminus"}, {0x02d6, "plusmod"}, {0xff0b, "plusmonospace"}, {0xfe62, "plussmall"}, {0x207a, "plussuperior"}, {0xff50, "pmonospace"}, {0x33d8, "pmsquare"}, {0x307d, "pohiragana"}, {0x261f, "pointingindexdownwhite"}, {0x261c, "pointingindexleftwhite"}, {0x261e, "pointingindexrightwhite"}, {0x261d, "pointingindexupwhite"}, {0x30dd, "pokatakana"}, {0x0e1b, "poplathai"}, {0x3012, "postalmark"}, {0x3020, "postalmarkface"}, {0x24ab, "pparen"}, {0x227a, "precedes"}, {0x211e, "prescription"}, {0x02b9, "primemod"}, {0x2035, "primereversed"}, {0x220f, "product"}, {0x2305, "projective"}, {0x30fc, "prolongedkana"}, {0x2318, "propellor"}, {0x2282, "propersubset"}, {0x2283, "propersuperset"}, {0x2237, "proportion"}, {0x221d, "proportional"}, {0x03c8, "psi"}, {0x0471, "psicyrillic"}, {0x0486, "psilipneumatacyrilliccmb"}, {0x33b0, "pssquare"}, {0x3077, "puhiragana"}, {0x30d7, "pukatakana"}, {0x33b4, "pvsquare"}, {0x33ba, "pwsquare"}, {0x0071, "q"}, {0x0958, "qadeva"}, {0x05a8, "qadmahebrew"}, {0x0642, "qafarabic"}, {0xfed6, "qaffinalarabic"}, {0xfed7, "qafinitialarabic"}, {0xfed8, "qafmedialarabic"}, {0x05b8, "qamats"}, {0x05b8, "qamats10"}, {0x05b8, "qamats1a"}, {0x05b8, "qamats1c"}, {0x05b8, "qamats27"}, {0x05b8, "qamats29"}, {0x05b8, "qamats33"}, {0x05b8, "qamatsde"}, {0x05b8, "qamatshebrew"}, {0x05b8, "qamatsnarrowhebrew"}, {0x05b8, "qamatsqatanhebrew"}, {0x05b8, "qamatsqatannarrowhebrew"}, {0x05b8, "qamatsqatanquarterhebrew"}, {0x05b8, "qamatsqatanwidehebrew"}, {0x05b8, "qamatsquarterhebrew"}, {0x05b8, "qamatswidehebrew"}, {0x059f, "qarneyparahebrew"}, {0x3111, "qbopomofo"}, {0x24e0, "qcircle"}, {0x02a0, "qhook"}, {0xff51, "qmonospace"}, {0x05e7, "qof"}, {0xfb47, "qofdagesh"}, {0xfb47, "qofdageshhebrew"}, {0x05e7, "qofhebrew"}, {0x24ac, "qparen"}, {0x2669, "quarternote"}, {0x05bb, "qubuts"}, {0x05bb, "qubuts18"}, {0x05bb, "qubuts25"}, {0x05bb, "qubuts31"}, {0x05bb, "qubutshebrew"}, {0x05bb, "qubutsnarrowhebrew"}, {0x05bb, "qubutsquarterhebrew"}, {0x05bb, "qubutswidehebrew"}, {0x003f, "question"}, {0x061f, "questionarabic"}, {0x055e, "questionarmenian"}, {0x00bf, "questiondown"}, {0xf7bf, "questiondownsmall"}, {0x037e, "questiongreek"}, {0xff1f, "questionmonospace"}, {0xf73f, "questionsmall"}, {0x0022, "quotedbl"}, {0x201e, "quotedblbase"}, {0x201c, "quotedblleft"}, {0xff02, "quotedblmonospace"}, {0x301e, "quotedblprime"}, {0x301d, "quotedblprimereversed"}, {0x201d, "quotedblright"}, {0x2018, "quoteleft"}, {0x201b, "quoteleftreversed"}, {0x201b, "quotereversed"}, {0x2019, "quoteright"}, {0x0149, "quoterightn"}, {0x201a, "quotesinglbase"}, {0x0027, "quotesingle"}, {0xff07, "quotesinglemonospace"}, {0x0072, "r"}, {0x057c, "raarmenian"}, {0x09b0, "rabengali"}, {0x0155, "racute"}, {0x0930, "radeva"}, {0x221a, "radical"}, {0xf8e5, "radicalex"}, {0x33ae, "radoverssquare"}, {0x33af, "radoverssquaredsquare"}, {0x33ad, "radsquare"}, {0x05bf, "rafe"}, {0x05bf, "rafehebrew"}, {0x0ab0, "ragujarati"}, {0x0a30, "ragurmukhi"}, {0x3089, "rahiragana"}, {0x30e9, "rakatakana"}, {0xff97, "rakatakanahalfwidth"}, {0x09f1, "ralowerdiagonalbengali"}, {0x09f0, "ramiddlediagonalbengali"}, {0x0264, "ramshorn"}, {0x2236, "ratio"}, {0x3116, "rbopomofo"}, {0x0159, "rcaron"}, {0x0157, "rcedilla"}, {0x24e1, "rcircle"}, {0x0157, "rcommaaccent"}, {0x0211, "rdblgrave"}, {0x1e59, "rdotaccent"}, {0x1e5b, "rdotbelow"}, {0x1e5d, "rdotbelowmacron"}, {0x203b, "referencemark"}, {0x2286, "reflexsubset"}, {0x2287, "reflexsuperset"}, {0x00ae, "registered"}, {0x00ae, "registersans"}, {0x00ae, "registerserif"}, {0x0631, "reharabic"}, {0x0580, "reharmenian"}, {0xfeae, "rehfinalarabic"}, {0x308c, "rehiragana"}, {0x30ec, "rekatakana"}, {0xff9a, "rekatakanahalfwidth"}, {0x05e8, "resh"}, {0xfb48, "reshdageshhebrew"}, {0x05e8, "reshhebrew"}, {0x223d, "reversedtilde"}, {0x0597, "reviahebrew"}, {0x0597, "reviamugrashhebrew"}, {0x2310, "revlogicalnot"}, {0x027e, "rfishhook"}, {0x027f, "rfishhookreversed"}, {0x09dd, "rhabengali"}, {0x095d, "rhadeva"}, {0x03c1, "rho"}, {0x027d, "rhook"}, {0x027b, "rhookturned"}, {0x02b5, "rhookturnedsuperior"}, {0x03f1, "rhosymbolgreek"}, {0x02de, "rhotichookmod"}, {0x3271, "rieulacirclekorean"}, {0x3211, "rieulaparenkorean"}, {0x3263, "rieulcirclekorean"}, {0x3140, "rieulhieuhkorean"}, {0x313a, "rieulkiyeokkorean"}, {0x3169, "rieulkiyeoksioskorean"}, {0x3139, "rieulkorean"}, {0x313b, "rieulmieumkorean"}, {0x316c, "rieulpansioskorean"}, {0x3203, "rieulparenkorean"}, {0x313f, "rieulphieuphkorean"}, {0x313c, "rieulpieupkorean"}, {0x316b, "rieulpieupsioskorean"}, {0x313d, "rieulsioskorean"}, {0x313e, "rieulthieuthkorean"}, {0x316a, "rieultikeutkorean"}, {0x316d, "rieulyeorinhieuhkorean"}, {0x221f, "rightangle"}, {0x0319, "righttackbelowcmb"}, {0x22bf, "righttriangle"}, {0x308a, "rihiragana"}, {0x30ea, "rikatakana"}, {0xff98, "rikatakanahalfwidth"}, {0x02da, "ring"}, {0x0325, "ringbelowcmb"}, {0x030a, "ringcmb"}, {0x02bf, "ringhalfleft"}, {0x0559, "ringhalfleftarmenian"}, {0x031c, "ringhalfleftbelowcmb"}, {0x02d3, "ringhalfleftcentered"}, {0x02be, "ringhalfright"}, {0x0339, "ringhalfrightbelowcmb"}, {0x02d2, "ringhalfrightcentered"}, {0x0213, "rinvertedbreve"}, {0x3351, "rittorusquare"}, {0x1e5f, "rlinebelow"}, {0x027c, "rlongleg"}, {0x027a, "rlonglegturned"}, {0xff52, "rmonospace"}, {0x308d, "rohiragana"}, {0x30ed, "rokatakana"}, {0xff9b, "rokatakanahalfwidth"}, {0x0e23, "roruathai"}, {0x24ad, "rparen"}, {0x09dc, "rrabengali"}, {0x0931, "rradeva"}, {0x0a5c, "rragurmukhi"}, {0x0691, "rreharabic"}, {0xfb8d, "rrehfinalarabic"}, {0x09e0, "rrvocalicbengali"}, {0x0960, "rrvocalicdeva"}, {0x0ae0, "rrvocalicgujarati"}, {0x09c4, "rrvocalicvowelsignbengali"}, {0x0944, "rrvocalicvowelsigndeva"}, {0x0ac4, "rrvocalicvowelsigngujarati"}, {0xf6f1, "rsuperior"}, {0x2590, "rtblock"}, {0x0279, "rturned"}, {0x02b4, "rturnedsuperior"}, {0x308b, "ruhiragana"}, {0x30eb, "rukatakana"}, {0xff99, "rukatakanahalfwidth"}, {0x09f2, "rupeemarkbengali"}, {0x09f3, "rupeesignbengali"}, {0xf6dd, "rupiah"}, {0x0e24, "ruthai"}, {0x098b, "rvocalicbengali"}, {0x090b, "rvocalicdeva"}, {0x0a8b, "rvocalicgujarati"}, {0x09c3, "rvocalicvowelsignbengali"}, {0x0943, "rvocalicvowelsigndeva"}, {0x0ac3, "rvocalicvowelsigngujarati"}, {0x0073, "s"}, {0x09b8, "sabengali"}, {0x015b, "sacute"}, {0x1e65, "sacutedotaccent"}, {0x0635, "sadarabic"}, {0x0938, "sadeva"}, {0xfeba, "sadfinalarabic"}, {0xfebb, "sadinitialarabic"}, {0xfebc, "sadmedialarabic"}, {0x0ab8, "sagujarati"}, {0x0a38, "sagurmukhi"}, {0x3055, "sahiragana"}, {0x30b5, "sakatakana"}, {0xff7b, "sakatakanahalfwidth"}, {0xfdfa, "sallallahoualayhewasallamarabic"}, {0x05e1, "samekh"}, {0xfb41, "samekhdagesh"}, {0xfb41, "samekhdageshhebrew"}, {0x05e1, "samekhhebrew"}, {0x0e32, "saraaathai"}, {0x0e41, "saraaethai"}, {0x0e44, "saraaimaimalaithai"}, {0x0e43, "saraaimaimuanthai"}, {0x0e33, "saraamthai"}, {0x0e30, "saraathai"}, {0x0e40, "saraethai"}, {0xf886, "saraiileftthai"}, {0x0e35, "saraiithai"}, {0xf885, "saraileftthai"}, {0x0e34, "saraithai"}, {0x0e42, "saraothai"}, {0xf888, "saraueeleftthai"}, {0x0e37, "saraueethai"}, {0xf887, "saraueleftthai"}, {0x0e36, "sarauethai"}, {0x0e38, "sarauthai"}, {0x0e39, "sarauuthai"}, {0x3119, "sbopomofo"}, {0x0161, "scaron"}, {0x1e67, "scarondotaccent"}, {0x015f, "scedilla"}, {0x0259, "schwa"}, {0x04d9, "schwacyrillic"}, {0x04db, "schwadieresiscyrillic"}, {0x025a, "schwahook"}, {0x24e2, "scircle"}, {0x015d, "scircumflex"}, {0x0219, "scommaaccent"}, {0x1e61, "sdotaccent"}, {0x1e63, "sdotbelow"}, {0x1e69, "sdotbelowdotaccent"}, {0x033c, "seagullbelowcmb"}, {0x2033, "second"}, {0x02ca, "secondtonechinese"}, {0x00a7, "section"}, {0x0633, "seenarabic"}, {0xfeb2, "seenfinalarabic"}, {0xfeb3, "seeninitialarabic"}, {0xfeb4, "seenmedialarabic"}, {0x05b6, "segol"}, {0x05b6, "segol13"}, {0x05b6, "segol1f"}, {0x05b6, "segol2c"}, {0x05b6, "segolhebrew"}, {0x05b6, "segolnarrowhebrew"}, {0x05b6, "segolquarterhebrew"}, {0x0592, "segoltahebrew"}, {0x05b6, "segolwidehebrew"}, {0x057d, "seharmenian"}, {0x305b, "sehiragana"}, {0x30bb, "sekatakana"}, {0xff7e, "sekatakanahalfwidth"}, {0x003b, "semicolon"}, {0x061b, "semicolonarabic"}, {0xff1b, "semicolonmonospace"}, {0xfe54, "semicolonsmall"}, {0x309c, "semivoicedmarkkana"}, {0xff9f, "semivoicedmarkkanahalfwidth"}, {0x3322, "sentisquare"}, {0x3323, "sentosquare"}, {0x0037, "seven"}, {0x0667, "sevenarabic"}, {0x09ed, "sevenbengali"}, {0x2466, "sevencircle"}, {0x2790, "sevencircleinversesansserif"}, {0x096d, "sevendeva"}, {0x215e, "seveneighths"}, {0x0aed, "sevengujarati"}, {0x0a6d, "sevengurmukhi"}, {0x0667, "sevenhackarabic"}, {0x3027, "sevenhangzhou"}, {0x3226, "sevenideographicparen"}, {0x2087, "seveninferior"}, {0xff17, "sevenmonospace"}, {0xf737, "sevenoldstyle"}, {0x247a, "sevenparen"}, {0x248e, "sevenperiod"}, {0x06f7, "sevenpersian"}, {0x2176, "sevenroman"}, {0x2077, "sevensuperior"}, {0x2470, "seventeencircle"}, {0x2484, "seventeenparen"}, {0x2498, "seventeenperiod"}, {0x0e57, "seventhai"}, {0x00ad, "sfthyphen"}, {0x0577, "shaarmenian"}, {0x09b6, "shabengali"}, {0x0448, "shacyrillic"}, {0x0651, "shaddaarabic"}, {0xfc61, "shaddadammaarabic"}, {0xfc5e, "shaddadammatanarabic"}, {0xfc60, "shaddafathaarabic"}, {0xfc62, "shaddakasraarabic"}, {0xfc5f, "shaddakasratanarabic"}, {0x2592, "shade"}, {0x2593, "shadedark"}, {0x2591, "shadelight"}, {0x2592, "shademedium"}, {0x0936, "shadeva"}, {0x0ab6, "shagujarati"}, {0x0a36, "shagurmukhi"}, {0x0593, "shalshelethebrew"}, {0x3115, "shbopomofo"}, {0x0449, "shchacyrillic"}, {0x0634, "sheenarabic"}, {0xfeb6, "sheenfinalarabic"}, {0xfeb7, "sheeninitialarabic"}, {0xfeb8, "sheenmedialarabic"}, {0x03e3, "sheicoptic"}, {0x20aa, "sheqel"}, {0x20aa, "sheqelhebrew"}, {0x05b0, "sheva"}, {0x05b0, "sheva115"}, {0x05b0, "sheva15"}, {0x05b0, "sheva22"}, {0x05b0, "sheva2e"}, {0x05b0, "shevahebrew"}, {0x05b0, "shevanarrowhebrew"}, {0x05b0, "shevaquarterhebrew"}, {0x05b0, "shevawidehebrew"}, {0x04bb, "shhacyrillic"}, {0x03ed, "shimacoptic"}, {0x05e9, "shin"}, {0xfb49, "shindagesh"}, {0xfb49, "shindageshhebrew"}, {0xfb2c, "shindageshshindot"}, {0xfb2c, "shindageshshindothebrew"}, {0xfb2d, "shindageshsindot"}, {0xfb2d, "shindageshsindothebrew"}, {0x05c1, "shindothebrew"}, {0x05e9, "shinhebrew"}, {0xfb2a, "shinshindot"}, {0xfb2a, "shinshindothebrew"}, {0xfb2b, "shinsindot"}, {0xfb2b, "shinsindothebrew"}, {0x0282, "shook"}, {0x03c3, "sigma"}, {0x03c2, "sigma1"}, {0x03c2, "sigmafinal"}, {0x03f2, "sigmalunatesymbolgreek"}, {0x3057, "sihiragana"}, {0x30b7, "sikatakana"}, {0xff7c, "sikatakanahalfwidth"}, {0x05bd, "siluqhebrew"}, {0x05bd, "siluqlefthebrew"}, {0x223c, "similar"}, {0x05c2, "sindothebrew"}, {0x3274, "siosacirclekorean"}, {0x3214, "siosaparenkorean"}, {0x317e, "sioscieuckorean"}, {0x3266, "sioscirclekorean"}, {0x317a, "sioskiyeokkorean"}, {0x3145, "sioskorean"}, {0x317b, "siosnieunkorean"}, {0x3206, "siosparenkorean"}, {0x317d, "siospieupkorean"}, {0x317c, "siostikeutkorean"}, {0x0036, "six"}, {0x0666, "sixarabic"}, {0x09ec, "sixbengali"}, {0x2465, "sixcircle"}, {0x278f, "sixcircleinversesansserif"}, {0x096c, "sixdeva"}, {0x0aec, "sixgujarati"}, {0x0a6c, "sixgurmukhi"}, {0x0666, "sixhackarabic"}, {0x3026, "sixhangzhou"}, {0x3225, "sixideographicparen"}, {0x2086, "sixinferior"}, {0xff16, "sixmonospace"}, {0xf736, "sixoldstyle"}, {0x2479, "sixparen"}, {0x248d, "sixperiod"}, {0x06f6, "sixpersian"}, {0x2175, "sixroman"}, {0x2076, "sixsuperior"}, {0x246f, "sixteencircle"}, {0x09f9, "sixteencurrencydenominatorbengali"}, {0x2483, "sixteenparen"}, {0x2497, "sixteenperiod"}, {0x0e56, "sixthai"}, {0x002f, "slash"}, {0xff0f, "slashmonospace"}, {0x017f, "slong"}, {0x1e9b, "slongdotaccent"}, {0x263a, "smileface"}, {0xff53, "smonospace"}, {0x05c3, "sofpasuqhebrew"}, {0x00ad, "softhyphen"}, {0x044c, "softsigncyrillic"}, {0x305d, "sohiragana"}, {0x30bd, "sokatakana"}, {0xff7f, "sokatakanahalfwidth"}, {0x0338, "soliduslongoverlaycmb"}, {0x0337, "solidusshortoverlaycmb"}, {0x0e29, "sorusithai"}, {0x0e28, "sosalathai"}, {0x0e0b, "sosothai"}, {0x0e2a, "sosuathai"}, {0x0020, "space"}, {0x0020, "spacehackarabic"}, {0x2660, "spade"}, {0x2660, "spadesuitblack"}, {0x2664, "spadesuitwhite"}, {0x24ae, "sparen"}, {0x033b, "squarebelowcmb"}, {0x33c4, "squarecc"}, {0x339d, "squarecm"}, {0x25a9, "squarediagonalcrosshatchfill"}, {0x25a4, "squarehorizontalfill"}, {0x338f, "squarekg"}, {0x339e, "squarekm"}, {0x33ce, "squarekmcapital"}, {0x33d1, "squareln"}, {0x33d2, "squarelog"}, {0x338e, "squaremg"}, {0x33d5, "squaremil"}, {0x339c, "squaremm"}, {0x33a1, "squaremsquared"}, {0x25a6, "squareorthogonalcrosshatchfill"}, {0x25a7, "squareupperlefttolowerrightfill"}, {0x25a8, "squareupperrighttolowerleftfill"}, {0x25a5, "squareverticalfill"}, {0x25a3, "squarewhitewithsmallblack"}, {0x33db, "srsquare"}, {0x09b7, "ssabengali"}, {0x0937, "ssadeva"}, {0x0ab7, "ssagujarati"}, {0x3149, "ssangcieuckorean"}, {0x3185, "ssanghieuhkorean"}, {0x3180, "ssangieungkorean"}, {0x3132, "ssangkiyeokkorean"}, {0x3165, "ssangnieunkorean"}, {0x3143, "ssangpieupkorean"}, {0x3146, "ssangsioskorean"}, {0x3138, "ssangtikeutkorean"}, {0xf6f2, "ssuperior"}, {0x00a3, "sterling"}, {0xffe1, "sterlingmonospace"}, {0x0336, "strokelongoverlaycmb"}, {0x0335, "strokeshortoverlaycmb"}, {0x2282, "subset"}, {0x228a, "subsetnotequal"}, {0x2286, "subsetorequal"}, {0x227b, "succeeds"}, {0x220b, "suchthat"}, {0x3059, "suhiragana"}, {0x30b9, "sukatakana"}, {0xff7d, "sukatakanahalfwidth"}, {0x0652, "sukunarabic"}, {0x2211, "summation"}, {0x263c, "sun"}, {0x2283, "superset"}, {0x228b, "supersetnotequal"}, {0x2287, "supersetorequal"}, {0x33dc, "svsquare"}, {0x337c, "syouwaerasquare"}, {0x0074, "t"}, {0x09a4, "tabengali"}, {0x22a4, "tackdown"}, {0x22a3, "tackleft"}, {0x0924, "tadeva"}, {0x0aa4, "tagujarati"}, {0x0a24, "tagurmukhi"}, {0x0637, "taharabic"}, {0xfec2, "tahfinalarabic"}, {0xfec3, "tahinitialarabic"}, {0x305f, "tahiragana"}, {0xfec4, "tahmedialarabic"}, {0x337d, "taisyouerasquare"}, {0x30bf, "takatakana"}, {0xff80, "takatakanahalfwidth"}, {0x0640, "tatweelarabic"}, {0x03c4, "tau"}, {0x05ea, "tav"}, {0xfb4a, "tavdages"}, {0xfb4a, "tavdagesh"}, {0xfb4a, "tavdageshhebrew"}, {0x05ea, "tavhebrew"}, {0x0167, "tbar"}, {0x310a, "tbopomofo"}, {0x0165, "tcaron"}, {0x02a8, "tccurl"}, {0x0163, "tcedilla"}, {0x0686, "tcheharabic"}, {0xfb7b, "tchehfinalarabic"}, {0xfb7c, "tchehinitialarabic"}, {0xfb7d, "tchehmedialarabic"}, {0x24e3, "tcircle"}, {0x1e71, "tcircumflexbelow"}, {0x0163, "tcommaaccent"}, {0x1e97, "tdieresis"}, {0x1e6b, "tdotaccent"}, {0x1e6d, "tdotbelow"}, {0x0442, "tecyrillic"}, {0x04ad, "tedescendercyrillic"}, {0x062a, "teharabic"}, {0xfe96, "tehfinalarabic"}, {0xfca2, "tehhahinitialarabic"}, {0xfc0c, "tehhahisolatedarabic"}, {0xfe97, "tehinitialarabic"}, {0x3066, "tehiragana"}, {0xfca1, "tehjeeminitialarabic"}, {0xfc0b, "tehjeemisolatedarabic"}, {0x0629, "tehmarbutaarabic"}, {0xfe94, "tehmarbutafinalarabic"}, {0xfe98, "tehmedialarabic"}, {0xfca4, "tehmeeminitialarabic"}, {0xfc0e, "tehmeemisolatedarabic"}, {0xfc73, "tehnoonfinalarabic"}, {0x30c6, "tekatakana"}, {0xff83, "tekatakanahalfwidth"}, {0x2121, "telephone"}, {0x260e, "telephoneblack"}, {0x05a0, "telishagedolahebrew"}, {0x05a9, "telishaqetanahebrew"}, {0x2469, "tencircle"}, {0x3229, "tenideographicparen"}, {0x247d, "tenparen"}, {0x2491, "tenperiod"}, {0x2179, "tenroman"}, {0x02a7, "tesh"}, {0x05d8, "tet"}, {0xfb38, "tetdagesh"}, {0xfb38, "tetdageshhebrew"}, {0x05d8, "tethebrew"}, {0x04b5, "tetsecyrillic"}, {0x059b, "tevirhebrew"}, {0x059b, "tevirlefthebrew"}, {0x09a5, "thabengali"}, {0x0925, "thadeva"}, {0x0aa5, "thagujarati"}, {0x0a25, "thagurmukhi"}, {0x0630, "thalarabic"}, {0xfeac, "thalfinalarabic"}, {0xf898, "thanthakhatlowleftthai"}, {0xf897, "thanthakhatlowrightthai"}, {0x0e4c, "thanthakhatthai"}, {0xf896, "thanthakhatupperleftthai"}, {0x062b, "theharabic"}, {0xfe9a, "thehfinalarabic"}, {0xfe9b, "thehinitialarabic"}, {0xfe9c, "thehmedialarabic"}, {0x2203, "thereexists"}, {0x2234, "therefore"}, {0x03b8, "theta"}, {0x03d1, "theta1"}, {0x03d1, "thetasymbolgreek"}, {0x3279, "thieuthacirclekorean"}, {0x3219, "thieuthaparenkorean"}, {0x326b, "thieuthcirclekorean"}, {0x314c, "thieuthkorean"}, {0x320b, "thieuthparenkorean"}, {0x246c, "thirteencircle"}, {0x2480, "thirteenparen"}, {0x2494, "thirteenperiod"}, {0x0e11, "thonangmonthothai"}, {0x01ad, "thook"}, {0x0e12, "thophuthaothai"}, {0x00fe, "thorn"}, {0x0e17, "thothahanthai"}, {0x0e10, "thothanthai"}, {0x0e18, "thothongthai"}, {0x0e16, "thothungthai"}, {0x0482, "thousandcyrillic"}, {0x066c, "thousandsseparatorarabic"}, {0x066c, "thousandsseparatorpersian"}, {0x0033, "three"}, {0x0663, "threearabic"}, {0x09e9, "threebengali"}, {0x2462, "threecircle"}, {0x278c, "threecircleinversesansserif"}, {0x0969, "threedeva"}, {0x215c, "threeeighths"}, {0x0ae9, "threegujarati"}, {0x0a69, "threegurmukhi"}, {0x0663, "threehackarabic"}, {0x3023, "threehangzhou"}, {0x3222, "threeideographicparen"}, {0x2083, "threeinferior"}, {0xff13, "threemonospace"}, {0x09f6, "threenumeratorbengali"}, {0xf733, "threeoldstyle"}, {0x2476, "threeparen"}, {0x248a, "threeperiod"}, {0x06f3, "threepersian"}, {0x00be, "threequarters"}, {0xf6de, "threequartersemdash"}, {0x2172, "threeroman"}, {0x00b3, "threesuperior"}, {0x0e53, "threethai"}, {0x3394, "thzsquare"}, {0x3061, "tihiragana"}, {0x30c1, "tikatakana"}, {0xff81, "tikatakanahalfwidth"}, {0x3270, "tikeutacirclekorean"}, {0x3210, "tikeutaparenkorean"}, {0x3262, "tikeutcirclekorean"}, {0x3137, "tikeutkorean"}, {0x3202, "tikeutparenkorean"}, {0x02dc, "tilde"}, {0x0330, "tildebelowcmb"}, {0x0303, "tildecmb"}, {0x0303, "tildecomb"}, {0x0360, "tildedoublecmb"}, {0x223c, "tildeoperator"}, {0x0334, "tildeoverlaycmb"}, {0x033e, "tildeverticalcmb"}, {0x2297, "timescircle"}, {0x0596, "tipehahebrew"}, {0x0596, "tipehalefthebrew"}, {0x0a70, "tippigurmukhi"}, {0x0483, "titlocyrilliccmb"}, {0x057f, "tiwnarmenian"}, {0x1e6f, "tlinebelow"}, {0xff54, "tmonospace"}, {0x0569, "toarmenian"}, {0x3068, "tohiragana"}, {0x30c8, "tokatakana"}, {0xff84, "tokatakanahalfwidth"}, {0x02e5, "tonebarextrahighmod"}, {0x02e9, "tonebarextralowmod"}, {0x02e6, "tonebarhighmod"}, {0x02e8, "tonebarlowmod"}, {0x02e7, "tonebarmidmod"}, {0x01bd, "tonefive"}, {0x0185, "tonesix"}, {0x01a8, "tonetwo"}, {0x0384, "tonos"}, {0x3327, "tonsquare"}, {0x0e0f, "topatakthai"}, {0x3014, "tortoiseshellbracketleft"}, {0xfe5d, "tortoiseshellbracketleftsmall"}, {0xfe39, "tortoiseshellbracketleftvertical"}, {0x3015, "tortoiseshellbracketright"}, {0xfe5e, "tortoiseshellbracketrightsmall"}, {0xfe3a, "tortoiseshellbracketrightvertical"}, {0x0e15, "totaothai"}, {0x01ab, "tpalatalhook"}, {0x24af, "tparen"}, {0x2122, "trademark"}, {0x2122, "trademarksans"}, {0x2122, "trademarkserif"}, {0x0288, "tretroflexhook"}, {0x25bc, "triagdn"}, {0x25c4, "triaglf"}, {0x25ba, "triagrt"}, {0x25b2, "triagup"}, {0x02a6, "ts"}, {0x05e6, "tsadi"}, {0xfb46, "tsadidagesh"}, {0xfb46, "tsadidageshhebrew"}, {0x05e6, "tsadihebrew"}, {0x0446, "tsecyrillic"}, {0x05b5, "tsere"}, {0x05b5, "tsere12"}, {0x05b5, "tsere1e"}, {0x05b5, "tsere2b"}, {0x05b5, "tserehebrew"}, {0x05b5, "tserenarrowhebrew"}, {0x05b5, "tserequarterhebrew"}, {0x05b5, "tserewidehebrew"}, {0x045b, "tshecyrillic"}, {0xf6f3, "tsuperior"}, {0x099f, "ttabengali"}, {0x091f, "ttadeva"}, {0x0a9f, "ttagujarati"}, {0x0a1f, "ttagurmukhi"}, {0x0679, "tteharabic"}, {0xfb67, "ttehfinalarabic"}, {0xfb68, "ttehinitialarabic"}, {0xfb69, "ttehmedialarabic"}, {0x09a0, "tthabengali"}, {0x0920, "tthadeva"}, {0x0aa0, "tthagujarati"}, {0x0a20, "tthagurmukhi"}, {0x0287, "tturned"}, {0x3064, "tuhiragana"}, {0x30c4, "tukatakana"}, {0xff82, "tukatakanahalfwidth"}, {0x3063, "tusmallhiragana"}, {0x30c3, "tusmallkatakana"}, {0xff6f, "tusmallkatakanahalfwidth"}, {0x246b, "twelvecircle"}, {0x247f, "twelveparen"}, {0x2493, "twelveperiod"}, {0x217b, "twelveroman"}, {0x2473, "twentycircle"}, {0x5344, "twentyhangzhou"}, {0x2487, "twentyparen"}, {0x249b, "twentyperiod"}, {0x0032, "two"}, {0x0662, "twoarabic"}, {0x09e8, "twobengali"}, {0x2461, "twocircle"}, {0x278b, "twocircleinversesansserif"}, {0x0968, "twodeva"}, {0x2025, "twodotenleader"}, {0x2025, "twodotleader"}, {0xfe30, "twodotleadervertical"}, {0x0ae8, "twogujarati"}, {0x0a68, "twogurmukhi"}, {0x0662, "twohackarabic"}, {0x3022, "twohangzhou"}, {0x3221, "twoideographicparen"}, {0x2082, "twoinferior"}, {0xff12, "twomonospace"}, {0x09f5, "twonumeratorbengali"}, {0xf732, "twooldstyle"}, {0x2475, "twoparen"}, {0x2489, "twoperiod"}, {0x06f2, "twopersian"}, {0x2171, "tworoman"}, {0x01bb, "twostroke"}, {0x00b2, "twosuperior"}, {0x0e52, "twothai"}, {0x2154, "twothirds"}, {0x0075, "u"}, {0x00fa, "uacute"}, {0x0289, "ubar"}, {0x0989, "ubengali"}, {0x3128, "ubopomofo"}, {0x016d, "ubreve"}, {0x01d4, "ucaron"}, {0x24e4, "ucircle"}, {0x00fb, "ucircumflex"}, {0x1e77, "ucircumflexbelow"}, {0x0443, "ucyrillic"}, {0x0951, "udattadeva"}, {0x0171, "udblacute"}, {0x0215, "udblgrave"}, {0x0909, "udeva"}, {0x00fc, "udieresis"}, {0x01d8, "udieresisacute"}, {0x1e73, "udieresisbelow"}, {0x01da, "udieresiscaron"}, {0x04f1, "udieresiscyrillic"}, {0x01dc, "udieresisgrave"}, {0x01d6, "udieresismacron"}, {0x1ee5, "udotbelow"}, {0x00f9, "ugrave"}, {0x0a89, "ugujarati"}, {0x0a09, "ugurmukhi"}, {0x3046, "uhiragana"}, {0x1ee7, "uhookabove"}, {0x01b0, "uhorn"}, {0x1ee9, "uhornacute"}, {0x1ef1, "uhorndotbelow"}, {0x1eeb, "uhorngrave"}, {0x1eed, "uhornhookabove"}, {0x1eef, "uhorntilde"}, {0x0171, "uhungarumlaut"}, {0x04f3, "uhungarumlautcyrillic"}, {0x0217, "uinvertedbreve"}, {0x30a6, "ukatakana"}, {0xff73, "ukatakanahalfwidth"}, {0x0479, "ukcyrillic"}, {0x315c, "ukorean"}, {0x016b, "umacron"}, {0x04ef, "umacroncyrillic"}, {0x1e7b, "umacrondieresis"}, {0x0a41, "umatragurmukhi"}, {0xff55, "umonospace"}, {0x005f, "underscore"}, {0x2017, "underscoredbl"}, {0xff3f, "underscoremonospace"}, {0xfe33, "underscorevertical"}, {0xfe4f, "underscorewavy"}, {0x222a, "union"}, {0x2200, "universal"}, {0x0173, "uogonek"}, {0x24b0, "uparen"}, {0x2580, "upblock"}, {0x05c4, "upperdothebrew"}, {0x03c5, "upsilon"}, {0x03cb, "upsilondieresis"}, {0x03b0, "upsilondieresistonos"}, {0x028a, "upsilonlatin"}, {0x03cd, "upsilontonos"}, {0x031d, "uptackbelowcmb"}, {0x02d4, "uptackmod"}, {0x0a73, "uragurmukhi"}, {0x016f, "uring"}, {0x045e, "ushortcyrillic"}, {0x3045, "usmallhiragana"}, {0x30a5, "usmallkatakana"}, {0xff69, "usmallkatakanahalfwidth"}, {0x04af, "ustraightcyrillic"}, {0x04b1, "ustraightstrokecyrillic"}, {0x0169, "utilde"}, {0x1e79, "utildeacute"}, {0x1e75, "utildebelow"}, {0x098a, "uubengali"}, {0x090a, "uudeva"}, {0x0a8a, "uugujarati"}, {0x0a0a, "uugurmukhi"}, {0x0a42, "uumatragurmukhi"}, {0x09c2, "uuvowelsignbengali"}, {0x0942, "uuvowelsigndeva"}, {0x0ac2, "uuvowelsigngujarati"}, {0x09c1, "uvowelsignbengali"}, {0x0941, "uvowelsigndeva"}, {0x0ac1, "uvowelsigngujarati"}, {0x0076, "v"}, {0x0935, "vadeva"}, {0x0ab5, "vagujarati"}, {0x0a35, "vagurmukhi"}, {0x30f7, "vakatakana"}, {0x05d5, "vav"}, {0xfb35, "vavdagesh"}, {0xfb35, "vavdagesh65"}, {0xfb35, "vavdageshhebrew"}, {0x05d5, "vavhebrew"}, {0xfb4b, "vavholam"}, {0xfb4b, "vavholamhebrew"}, {0x05f0, "vavvavhebrew"}, {0x05f1, "vavyodhebrew"}, {0x24e5, "vcircle"}, {0x1e7f, "vdotbelow"}, {0x0432, "vecyrillic"}, {0x06a4, "veharabic"}, {0xfb6b, "vehfinalarabic"}, {0xfb6c, "vehinitialarabic"}, {0xfb6d, "vehmedialarabic"}, {0x30f9, "vekatakana"}, {0x2640, "venus"}, {0x007c, "verticalbar"}, {0x030d, "verticallineabovecmb"}, {0x0329, "verticallinebelowcmb"}, {0x02cc, "verticallinelowmod"}, {0x02c8, "verticallinemod"}, {0x057e, "vewarmenian"}, {0x028b, "vhook"}, {0x30f8, "vikatakana"}, {0x09cd, "viramabengali"}, {0x094d, "viramadeva"}, {0x0acd, "viramagujarati"}, {0x0983, "visargabengali"}, {0x0903, "visargadeva"}, {0x0a83, "visargagujarati"}, {0xff56, "vmonospace"}, {0x0578, "voarmenian"}, {0x309e, "voicediterationhiragana"}, {0x30fe, "voicediterationkatakana"}, {0x309b, "voicedmarkkana"}, {0xff9e, "voicedmarkkanahalfwidth"}, {0x30fa, "vokatakana"}, {0x24b1, "vparen"}, {0x1e7d, "vtilde"}, {0x028c, "vturned"}, {0x3094, "vuhiragana"}, {0x30f4, "vukatakana"}, {0x0077, "w"}, {0x1e83, "wacute"}, {0x3159, "waekorean"}, {0x308f, "wahiragana"}, {0x30ef, "wakatakana"}, {0xff9c, "wakatakanahalfwidth"}, {0x3158, "wakorean"}, {0x308e, "wasmallhiragana"}, {0x30ee, "wasmallkatakana"}, {0x3357, "wattosquare"}, {0x301c, "wavedash"}, {0xfe34, "wavyunderscorevertical"}, {0x0648, "wawarabic"}, {0xfeee, "wawfinalarabic"}, {0x0624, "wawhamzaabovearabic"}, {0xfe86, "wawhamzaabovefinalarabic"}, {0x33dd, "wbsquare"}, {0x24e6, "wcircle"}, {0x0175, "wcircumflex"}, {0x1e85, "wdieresis"}, {0x1e87, "wdotaccent"}, {0x1e89, "wdotbelow"}, {0x3091, "wehiragana"}, {0x2118, "weierstrass"}, {0x30f1, "wekatakana"}, {0x315e, "wekorean"}, {0x315d, "weokorean"}, {0x1e81, "wgrave"}, {0x25e6, "whitebullet"}, {0x25cb, "whitecircle"}, {0x25d9, "whitecircleinverse"}, {0x300e, "whitecornerbracketleft"}, {0xfe43, "whitecornerbracketleftvertical"}, {0x300f, "whitecornerbracketright"}, {0xfe44, "whitecornerbracketrightvertical"}, {0x25c7, "whitediamond"}, {0x25c8, "whitediamondcontainingblacksmalldiamond"}, {0x25bf, "whitedownpointingsmalltriangle"}, {0x25bd, "whitedownpointingtriangle"}, {0x25c3, "whiteleftpointingsmalltriangle"}, {0x25c1, "whiteleftpointingtriangle"}, {0x3016, "whitelenticularbracketleft"}, {0x3017, "whitelenticularbracketright"}, {0x25b9, "whiterightpointingsmalltriangle"}, {0x25b7, "whiterightpointingtriangle"}, {0x25ab, "whitesmallsquare"}, {0x263a, "whitesmilingface"}, {0x25a1, "whitesquare"}, {0x2606, "whitestar"}, {0x260f, "whitetelephone"}, {0x3018, "whitetortoiseshellbracketleft"}, {0x3019, "whitetortoiseshellbracketright"}, {0x25b5, "whiteuppointingsmalltriangle"}, {0x25b3, "whiteuppointingtriangle"}, {0x3090, "wihiragana"}, {0x30f0, "wikatakana"}, {0x315f, "wikorean"}, {0xff57, "wmonospace"}, {0x3092, "wohiragana"}, {0x30f2, "wokatakana"}, {0xff66, "wokatakanahalfwidth"}, {0x20a9, "won"}, {0xffe6, "wonmonospace"}, {0x0e27, "wowaenthai"}, {0x24b2, "wparen"}, {0x1e98, "wring"}, {0x02b7, "wsuperior"}, {0x028d, "wturned"}, {0x01bf, "wynn"}, {0x0078, "x"}, {0x033d, "xabovecmb"}, {0x3112, "xbopomofo"}, {0x24e7, "xcircle"}, {0x1e8d, "xdieresis"}, {0x1e8b, "xdotaccent"}, {0x056d, "xeharmenian"}, {0x03be, "xi"}, {0xff58, "xmonospace"}, {0x24b3, "xparen"}, {0x02e3, "xsuperior"}, {0x0079, "y"}, {0x334e, "yaadosquare"}, {0x09af, "yabengali"}, {0x00fd, "yacute"}, {0x092f, "yadeva"}, {0x3152, "yaekorean"}, {0x0aaf, "yagujarati"}, {0x0a2f, "yagurmukhi"}, {0x3084, "yahiragana"}, {0x30e4, "yakatakana"}, {0xff94, "yakatakanahalfwidth"}, {0x3151, "yakorean"}, {0x0e4e, "yamakkanthai"}, {0x3083, "yasmallhiragana"}, {0x30e3, "yasmallkatakana"}, {0xff6c, "yasmallkatakanahalfwidth"}, {0x0463, "yatcyrillic"}, {0x24e8, "ycircle"}, {0x0177, "ycircumflex"}, {0x00ff, "ydieresis"}, {0x1e8f, "ydotaccent"}, {0x1ef5, "ydotbelow"}, {0x064a, "yeharabic"}, {0x06d2, "yehbarreearabic"}, {0xfbaf, "yehbarreefinalarabic"}, {0xfef2, "yehfinalarabic"}, {0x0626, "yehhamzaabovearabic"}, {0xfe8a, "yehhamzaabovefinalarabic"}, {0xfe8b, "yehhamzaaboveinitialarabic"}, {0xfe8c, "yehhamzaabovemedialarabic"}, {0xfef3, "yehinitialarabic"}, {0xfef4, "yehmedialarabic"}, {0xfcdd, "yehmeeminitialarabic"}, {0xfc58, "yehmeemisolatedarabic"}, {0xfc94, "yehnoonfinalarabic"}, {0x06d1, "yehthreedotsbelowarabic"}, {0x3156, "yekorean"}, {0x00a5, "yen"}, {0xffe5, "yenmonospace"}, {0x3155, "yeokorean"}, {0x3186, "yeorinhieuhkorean"}, {0x05aa, "yerahbenyomohebrew"}, {0x05aa, "yerahbenyomolefthebrew"}, {0x044b, "yericyrillic"}, {0x04f9, "yerudieresiscyrillic"}, {0x3181, "yesieungkorean"}, {0x3183, "yesieungpansioskorean"}, {0x3182, "yesieungsioskorean"}, {0x059a, "yetivhebrew"}, {0x1ef3, "ygrave"}, {0x01b4, "yhook"}, {0x1ef7, "yhookabove"}, {0x0575, "yiarmenian"}, {0x0457, "yicyrillic"}, {0x3162, "yikorean"}, {0x262f, "yinyang"}, {0x0582, "yiwnarmenian"}, {0xff59, "ymonospace"}, {0x05d9, "yod"}, {0xfb39, "yoddagesh"}, {0xfb39, "yoddageshhebrew"}, {0x05d9, "yodhebrew"}, {0x05f2, "yodyodhebrew"}, {0xfb1f, "yodyodpatahhebrew"}, {0x3088, "yohiragana"}, {0x3189, "yoikorean"}, {0x30e8, "yokatakana"}, {0xff96, "yokatakanahalfwidth"}, {0x315b, "yokorean"}, {0x3087, "yosmallhiragana"}, {0x30e7, "yosmallkatakana"}, {0xff6e, "yosmallkatakanahalfwidth"}, {0x03f3, "yotgreek"}, {0x3188, "yoyaekorean"}, {0x3187, "yoyakorean"}, {0x0e22, "yoyakthai"}, {0x0e0d, "yoyingthai"}, {0x24b4, "yparen"}, {0x037a, "ypogegrammeni"}, {0x0345, "ypogegrammenigreekcmb"}, {0x01a6, "yr"}, {0x1e99, "yring"}, {0x02b8, "ysuperior"}, {0x1ef9, "ytilde"}, {0x028e, "yturned"}, {0x3086, "yuhiragana"}, {0x318c, "yuikorean"}, {0x30e6, "yukatakana"}, {0xff95, "yukatakanahalfwidth"}, {0x3160, "yukorean"}, {0x046b, "yusbigcyrillic"}, {0x046d, "yusbigiotifiedcyrillic"}, {0x0467, "yuslittlecyrillic"}, {0x0469, "yuslittleiotifiedcyrillic"}, {0x3085, "yusmallhiragana"}, {0x30e5, "yusmallkatakana"}, {0xff6d, "yusmallkatakanahalfwidth"}, {0x318b, "yuyekorean"}, {0x318a, "yuyeokorean"}, {0x09df, "yyabengali"}, {0x095f, "yyadeva"}, {0x007a, "z"}, {0x0566, "zaarmenian"}, {0x017a, "zacute"}, {0x095b, "zadeva"}, {0x0a5b, "zagurmukhi"}, {0x0638, "zaharabic"}, {0xfec6, "zahfinalarabic"}, {0xfec7, "zahinitialarabic"}, {0x3056, "zahiragana"}, {0xfec8, "zahmedialarabic"}, {0x0632, "zainarabic"}, {0xfeb0, "zainfinalarabic"}, {0x30b6, "zakatakana"}, {0x0595, "zaqefgadolhebrew"}, {0x0594, "zaqefqatanhebrew"}, {0x0598, "zarqahebrew"}, {0x05d6, "zayin"}, {0xfb36, "zayindagesh"}, {0xfb36, "zayindageshhebrew"}, {0x05d6, "zayinhebrew"}, {0x3117, "zbopomofo"}, {0x017e, "zcaron"}, {0x24e9, "zcircle"}, {0x1e91, "zcircumflex"}, {0x0291, "zcurl"}, {0x017c, "zdot"}, {0x017c, "zdotaccent"}, {0x1e93, "zdotbelow"}, {0x0437, "zecyrillic"}, {0x0499, "zedescendercyrillic"}, {0x04df, "zedieresiscyrillic"}, {0x305c, "zehiragana"}, {0x30bc, "zekatakana"}, {0x0030, "zero"}, {0x0660, "zeroarabic"}, {0x09e6, "zerobengali"}, {0x0966, "zerodeva"}, {0x0ae6, "zerogujarati"}, {0x0a66, "zerogurmukhi"}, {0x0660, "zerohackarabic"}, {0x2080, "zeroinferior"}, {0xff10, "zeromonospace"}, {0xf730, "zerooldstyle"}, {0x06f0, "zeropersian"}, {0x2070, "zerosuperior"}, {0x0e50, "zerothai"}, {0xfeff, "zerowidthjoiner"}, {0x200c, "zerowidthnonjoiner"}, {0x200b, "zerowidthspace"}, {0x03b6, "zeta"}, {0x3113, "zhbopomofo"}, {0x056a, "zhearmenian"}, {0x04c2, "zhebrevecyrillic"}, {0x0436, "zhecyrillic"}, {0x0497, "zhedescendercyrillic"}, {0x04dd, "zhedieresiscyrillic"}, {0x3058, "zihiragana"}, {0x30b8, "zikatakana"}, {0x05ae, "zinorhebrew"}, {0x1e95, "zlinebelow"}, {0xff5a, "zmonospace"}, {0x305e, "zohiragana"}, {0x30be, "zokatakana"}, {0x24b5, "zparen"}, {0x0290, "zretroflexhook"}, {0x01b6, "zstroke"}, {0x305a, "zuhiragana"}, {0x30ba, "zukatakana"}, {0x007b, "{"}, {0x007c, "|"}, {0x007d, "}"}, {0x007e, "~"}, { 0, NULL } }; xpdf-3.04/xpdf/PSTokenizer.cc0000644000076400007640000000626312341430012015375 0ustar dereknderekn//======================================================================== // // PSTokenizer.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "PSTokenizer.h" //------------------------------------------------------------------------ // A '1' in this array means the character is white space. A '1' or // '2' means the character ends a name or command. static char specialChars[256] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx }; //------------------------------------------------------------------------ PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) { getCharFunc = getCharFuncA; data = dataA; charBuf = -1; } PSTokenizer::~PSTokenizer() { } GBool PSTokenizer::getToken(char *buf, int size, int *length) { GBool comment, backslash; int c; int i; // skip whitespace and comments comment = gFalse; while (1) { if ((c = getChar()) == EOF) { buf[0] = '\0'; *length = 0; return gFalse; } if (comment) { if (c == '\x0a' || c == '\x0d') { comment = gFalse; } } else if (c == '%') { comment = gTrue; } else if (specialChars[c] != 1) { break; } } // read a token i = 0; buf[i++] = c; if (c == '(') { backslash = gFalse; while ((c = lookChar()) != EOF) { if (i < size - 1) { buf[i++] = c; } getChar(); if (c == '\\') { backslash = gTrue; } else if (!backslash && c == ')') { break; } else { backslash = gFalse; } } } else if (c == '<') { while ((c = lookChar()) != EOF) { getChar(); if (i < size - 1 && specialChars[c] != 1) { buf[i++] = c; } if (c == '>') { break; } } } else if (c != '[' && c != ']') { while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (i < size - 1) { buf[i++] = c; } } } buf[i] = '\0'; *length = i; return gTrue; } int PSTokenizer::lookChar() { if (charBuf < 0) { charBuf = (*getCharFunc)(data); } return charBuf; } int PSTokenizer::getChar() { int c; if (charBuf < 0) { charBuf = (*getCharFunc)(data); } c = charBuf; charBuf = -1; return c; } xpdf-3.04/xpdf/pdftopng.cc0000644000076400007640000001751612341430012015004 0ustar dereknderekn//======================================================================== // // 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); } xpdf-3.04/xpdf/XPDFCore.cc0000644000076400007640000013475712341430012014544 0ustar dereknderekn//======================================================================== // // XPDFCore.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include "gmem.h" #include "GString.h" #include "GList.h" #include "Error.h" #include "GlobalParams.h" #include "PDFDoc.h" #include "Link.h" #include "ErrorCodes.h" #include "GfxState.h" #include "CoreOutputDev.h" #include "PSOutputDev.h" #include "TextOutputDev.h" #include "SplashBitmap.h" #include "SplashPattern.h" #include "XPDFApp.h" #include "XPDFCore.h" // these macro defns conflict with xpdf's Object class #ifdef LESSTIF_VERSION #undef XtDisplay #undef XtScreen #undef XtWindow #undef XtParent #undef XtIsRealized #endif //------------------------------------------------------------------------ // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } //------------------------------------------------------------------------ GString *XPDFCore::currentSelection = NULL; XPDFCore *XPDFCore::currentSelectionOwner = NULL; Atom XPDFCore::targetsAtom; //------------------------------------------------------------------------ // XPDFCoreTile //------------------------------------------------------------------------ class XPDFCoreTile: public PDFCoreTile { public: XPDFCoreTile(int xDestA, int yDestA); virtual ~XPDFCoreTile(); XImage *image; }; XPDFCoreTile::XPDFCoreTile(int xDestA, int yDestA): PDFCoreTile(xDestA, yDestA) { image = NULL; } XPDFCoreTile::~XPDFCoreTile() { if (image) { gfree(image->data); image->data = NULL; XDestroyImage(image); } } //------------------------------------------------------------------------ // XPDFCore //------------------------------------------------------------------------ XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA, SplashColorPtr paperColorA, Gulong paperPixelA, Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA, GBool installCmap, int rgbCubeSizeA): PDFCore(splashModeRGB8, 4, reverseVideoA, paperColorA, !fullScreenA) { GString *initialZoom; shell = shellA; parentWidget = parentWidgetA; display = XtDisplay(parentWidget); screenNum = XScreenNumberOfScreen(XtScreen(parentWidget)); targetsAtom = XInternAtom(display, "TARGETS", False); paperPixel = paperPixelA; mattePixel = mattePixelA; fullScreen = fullScreenA; setupX(installCmap, rgbCubeSizeA); scrolledWin = NULL; hScrollBar = NULL; vScrollBar = NULL; drawAreaFrame = NULL; drawArea = NULL; // get the initial zoom value if (fullScreen) { zoom = zoomPage; } else { initialZoom = globalParams->getInitialZoom(); if (!initialZoom->cmp("page")) { zoom = zoomPage; } else if (!initialZoom->cmp("width")) { zoom = zoomWidth; } else { zoom = atoi(initialZoom->getCString()); if (zoom <= 0) { zoom = defZoom; } } delete initialZoom; } linkAction = NULL; panning = gFalse; updateCbk = NULL; actionCbk = NULL; keyPressCbk = NULL; mouseCbk = NULL; // optional features default to on hyperlinksEnabled = gTrue; selectEnabled = gTrue; // do X-specific initialization and create the widgets initWindow(); initPasswordDialog(); } XPDFCore::~XPDFCore() { if (currentSelectionOwner == this && currentSelection) { delete currentSelection; currentSelection = NULL; currentSelectionOwner = NULL; } if (drawAreaGC) { XFreeGC(display, drawAreaGC); } if (scrolledWin) { XtDestroyWidget(scrolledWin); } if (busyCursor) { XFreeCursor(display, busyCursor); } if (linkCursor) { XFreeCursor(display, linkCursor); } if (selectCursor) { XFreeCursor(display, selectCursor); } } //------------------------------------------------------------------------ // loadFile / displayPage / displayDest //------------------------------------------------------------------------ int XPDFCore::loadFile(GString *fileName, GString *ownerPassword, GString *userPassword) { int err; err = PDFCore::loadFile(fileName, ownerPassword, userPassword); if (err == errNone) { // save the modification time modTime = getModTime(doc->getFileName()->getCString()); // update the parent window if (updateCbk) { (*updateCbk)(updateCbkData, doc->getFileName(), -1, doc->getNumPages(), NULL); } } return err; } int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword, GString *userPassword) { int err; err = PDFCore::loadFile(stream, ownerPassword, userPassword); if (err == errNone) { // no file modTime = 0; // update the parent window if (updateCbk) { (*updateCbk)(updateCbkData, doc->getFileName(), -1, doc->getNumPages(), NULL); } } return err; } void XPDFCore::loadDoc(PDFDoc *docA) { PDFCore::loadDoc(docA); // save the modification time if (doc->getFileName()) { modTime = getModTime(doc->getFileName()->getCString()); } // update the parent window if (updateCbk) { (*updateCbk)(updateCbkData, doc->getFileName(), -1, doc->getNumPages(), NULL); } } void XPDFCore::resizeToPage(int pg) { Dimension width, height; double width1, height1; Dimension topW, topH, topBorder, daW, daH; Dimension displayW, displayH; displayW = DisplayWidth(display, screenNum); displayH = DisplayHeight(display, screenNum); if (fullScreen) { width = displayW; height = displayH; } else { if (!doc || pg <= 0 || pg > doc->getNumPages()) { width1 = 612; height1 = 792; } else if (doc->getPageRotate(pg) == 90 || doc->getPageRotate(pg) == 270) { width1 = doc->getPageCropHeight(pg); height1 = doc->getPageCropWidth(pg); } else { width1 = doc->getPageCropWidth(pg); height1 = doc->getPageCropHeight(pg); } if (zoom == zoomPage || zoom == zoomWidth) { width = (Dimension)(width1 * 0.01 * defZoom + 0.5); height = (Dimension)(height1 * 0.01 * defZoom + 0.5); } else { width = (Dimension)(width1 * 0.01 * zoom + 0.5); height = (Dimension)(height1 * 0.01 * zoom + 0.5); } if (continuousMode) { height += continuousModePageSpacing; } if (width > displayW - 100) { width = displayW - 100; } if (height > displayH - 100) { height = displayH - 100; } } if (XtIsRealized(shell)) { XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH, XmNborderWidth, &topBorder, NULL); XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL); XtVaSetValues(shell, XmNwidth, width + (topW - daW), XmNheight, height + (topH - daH), NULL); } else { XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL); } } void XPDFCore::update(int topPageA, int scrollXA, int scrollYA, double zoomA, int rotateA, GBool force, GBool addToHist, GBool adjustScrollX) { int oldPage; oldPage = topPage; PDFCore::update(topPageA, scrollXA, scrollYA, zoomA, rotateA, force, addToHist, adjustScrollX); linkAction = NULL; if (doc && topPage != oldPage) { if (updateCbk) { (*updateCbk)(updateCbkData, NULL, topPage, -1, ""); } } } GBool XPDFCore::checkForNewFile() { time_t newModTime; if (doc->getFileName()) { newModTime = getModTime(doc->getFileName()->getCString()); if (newModTime != modTime) { modTime = newModTime; return gTrue; } } return gFalse; } //------------------------------------------------------------------------ // page/position changes //------------------------------------------------------------------------ GBool XPDFCore::gotoNextPage(int inc, GBool top) { if (!PDFCore::gotoNextPage(inc, top)) { XBell(display, 0); return gFalse; } return gTrue; } GBool XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) { if (!PDFCore::gotoPrevPage(dec, top, bottom)) { XBell(display, 0); return gFalse; } return gTrue; } GBool XPDFCore::goForward() { if (!PDFCore::goForward()) { XBell(display, 0); return gFalse; } return gTrue; } GBool XPDFCore::goBackward() { if (!PDFCore::goBackward()) { XBell(display, 0); return gFalse; } return gTrue; } void XPDFCore::startPan(int wx, int wy) { panning = gTrue; panMX = wx; panMY = wy; } void XPDFCore::endPan(int wx, int wy) { panning = gFalse; } //------------------------------------------------------------------------ // selection //------------------------------------------------------------------------ void XPDFCore::startSelection(int wx, int wy) { int pg, x, y; takeFocus(); if (doc && doc->getNumPages() > 0) { if (selectEnabled) { if (cvtWindowToDev(wx, wy, &pg, &x, &y)) { setSelection(pg, x, y, x, y); setCursor(selectCursor); dragging = gTrue; } } } } void XPDFCore::endSelection(int wx, int wy) { int pg, x, y; GBool ok; if (doc && doc->getNumPages() > 0) { ok = cvtWindowToDev(wx, wy, &pg, &x, &y); if (dragging) { dragging = gFalse; setCursor(None); if (ok) { moveSelection(pg, x, y); } #ifndef NO_TEXT_SELECT if (selectULX != selectLRX && selectULY != selectLRY) { if (doc->okToCopy()) { copySelection(); } else { error(errNotAllowed, -1, "Copying of text from this document is not allowed."); } } #endif } } } // X's copy-and-paste mechanism is brain damaged. Xt doesn't help // any, but doesn't make it too much worse, either. Motif, on the // other hand, adds significant complexity to the mess. So here we // blow off the Motif junk and stick to plain old Xt. The next two // functions (copySelection and convertSelectionCbk) implement the // magic needed to deal with Xt's mechanism. Note that this requires // global variables (currentSelection and currentSelectionOwner). void XPDFCore::copySelection() { int pg; double ulx, uly, lrx, lry; if (!doc->okToCopy()) { return; } if (getSelection(&pg, &ulx, &uly, &lrx, &lry)) { //~ for multithreading: need a mutex here if (currentSelection) { delete currentSelection; } currentSelection = extractText(pg, ulx, uly, lrx, lry); currentSelectionOwner = this; XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display), &convertSelectionCbk, NULL, NULL); } } Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection, Atom *target, Atom *type, XtPointer *value, unsigned long *length, int *format) { Atom *array; // send back a list of supported conversion targets if (*target == targetsAtom) { if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) { return False; } array[0] = XA_STRING; *value = (XtPointer)array; *type = XA_ATOM; *format = 32; *length = 1; return True; // send the selected text } else if (*target == XA_STRING) { //~ for multithreading: need a mutex here *value = XtNewString(currentSelection->getCString()); *length = currentSelection->getLength(); *type = XA_STRING; *format = 8; // 8-bit elements return True; } return False; } //------------------------------------------------------------------------ // hyperlinks //------------------------------------------------------------------------ void XPDFCore::doAction(LinkAction *action) { LinkActionKind kind; LinkDest *dest; GString *namedDest; char *s; GString *fileName, *fileName2; GString *cmd; GString *actionName; Object movieAnnot, obj1, obj2; GString *msg; int i; switch (kind = action->getKind()) { // GoTo / GoToR action case actionGoTo: case actionGoToR: if (kind == actionGoTo) { dest = NULL; namedDest = NULL; if ((dest = ((LinkGoTo *)action)->getDest())) { dest = dest->copy(); } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) { namedDest = namedDest->copy(); } } else { dest = NULL; namedDest = NULL; if ((dest = ((LinkGoToR *)action)->getDest())) { dest = dest->copy(); } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) { namedDest = namedDest->copy(); } s = ((LinkGoToR *)action)->getFileName()->getCString(); //~ translate path name for VMS (deal with '/') if (isAbsolutePath(s) || !doc->getFileName()) { fileName = new GString(s); } else { fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s); } if (loadFile(fileName) != errNone) { if (dest) { delete dest; } if (namedDest) { delete namedDest; } delete fileName; return; } delete fileName; } if (namedDest) { dest = doc->findDest(namedDest); delete namedDest; } if (dest) { displayDest(dest, zoom, rotate, gTrue); delete dest; } else { if (kind == actionGoToR) { displayPage(1, zoom, 0, gFalse, gTrue); } } break; // Launch action case actionLaunch: fileName = ((LinkLaunch *)action)->getFileName(); s = fileName->getCString(); if (!strcmp(s + fileName->getLength() - 4, ".pdf") || !strcmp(s + fileName->getLength() - 4, ".PDF")) { //~ translate path name for VMS (deal with '/') if (isAbsolutePath(s) || !doc->getFileName()) { fileName = fileName->copy(); } else { fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s); } if (loadFile(fileName) != errNone) { delete fileName; return; } delete fileName; displayPage(1, zoom, rotate, gFalse, gTrue); } else { fileName = fileName->copy(); if (((LinkLaunch *)action)->getParams()) { fileName->append(' '); fileName->append(((LinkLaunch *)action)->getParams()); } #ifdef VMS fileName->insert(0, "spawn/nowait "); #elif defined(__EMX__) fileName->insert(0, "start /min /n "); #else fileName->append(" &"); #endif if (globalParams->getLaunchCommand()) { fileName->insert(0, ' '); fileName->insert(0, globalParams->getLaunchCommand()); system(fileName->getCString()); } else { msg = new GString("About to execute the command:\n"); msg->append(fileName); if (doQuestionDialog("Launching external application", msg)) { system(fileName->getCString()); } delete msg; } delete fileName; } break; // URI action case actionURI: if (!(cmd = globalParams->getURLCommand())) { error(errConfig, -1, "No urlCommand defined in config file"); break; } runCommand(cmd, ((LinkURI *)action)->getURI()); break; // Named action case actionNamed: actionName = ((LinkNamed *)action)->getName(); if (!actionName->cmp("NextPage")) { gotoNextPage(1, gTrue); } else if (!actionName->cmp("PrevPage")) { gotoPrevPage(1, gTrue, gFalse); } else if (!actionName->cmp("FirstPage")) { if (topPage != 1) { displayPage(1, zoom, rotate, gTrue, gTrue); } } else if (!actionName->cmp("LastPage")) { if (topPage != doc->getNumPages()) { displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue); } } else if (!actionName->cmp("GoBack")) { goBackward(); } else if (!actionName->cmp("GoForward")) { goForward(); } else if (!actionName->cmp("Quit")) { if (actionCbk) { (*actionCbk)(actionCbkData, actionName->getCString()); } } else { error(errSyntaxError, -1, "Unknown named action: '{0:t}'", actionName); } break; // Movie action case actionMovie: if (!(cmd = globalParams->getMovieCommand())) { error(errConfig, -1, "No movieCommand defined in config file"); break; } if (((LinkMovie *)action)->hasAnnotRef()) { doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num, ((LinkMovie *)action)->getAnnotRef()->gen, &movieAnnot); } else { //~ need to use the correct page num here doc->getCatalog()->getPage(topPage)->getAnnots(&obj1); if (obj1.isArray()) { for (i = 0; i < obj1.arrayGetLength(); ++i) { if (obj1.arrayGet(i, &movieAnnot)->isDict()) { if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) { obj2.free(); break; } obj2.free(); } movieAnnot.free(); } obj1.free(); } } if (movieAnnot.isDict()) { if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) { if (obj1.dictLookup("F", &obj2)) { if ((fileName = LinkAction::getFileSpecName(&obj2))) { if (!isAbsolutePath(fileName->getCString()) && doc->getFileName()) { fileName2 = appendToPath( grabPath(doc->getFileName()->getCString()), fileName->getCString()); delete fileName; fileName = fileName2; } runCommand(cmd, fileName); delete fileName; } obj2.free(); } obj1.free(); } } 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}'", ((LinkUnknown *)action)->getAction()); break; } } // Run a command, given a string with one '%s' in it, and an // string to insert in place of the '%s'. void XPDFCore::runCommand(GString *cmdFmt, GString *arg) { GString *cmd; char *s; if ((s = strstr(cmdFmt->getCString(), "%s"))) { cmd = mungeURL(arg); cmd->insert(0, cmdFmt->getCString(), s - cmdFmt->getCString()); cmd->append(s + 2); } else { cmd = cmdFmt->copy(); } #ifdef VMS cmd->insert(0, "spawn/nowait "); #elif defined(__EMX__) cmd->insert(0, "start /min /n "); #else cmd->append(" &"); #endif system(cmd->getCString()); delete cmd; } // Escape any characters in a URL which might cause problems when // calling system(). GString *XPDFCore::mungeURL(GString *url) { static const char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "-_.~/?:@&=+,#%"; GString *newURL; char c; char buf[4]; int i; newURL = new GString(); for (i = 0; i < url->getLength(); ++i) { c = url->getChar(i); if (strchr(allowed, c)) { newURL->append(c); } else { sprintf(buf, "%%%02x", c & 0xff); newURL->append(buf); } } return newURL; } //------------------------------------------------------------------------ // find //------------------------------------------------------------------------ GBool XPDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly) { if (!PDFCore::find(s, caseSensitive, next, backward, wholeWord, onePageOnly)) { XBell(display, 0); return gFalse; } #ifndef NO_TEXT_SELECT copySelection(); #endif return gTrue; } GBool XPDFCore::findU(Unicode *u, int len, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly) { if (!PDFCore::findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly)) { XBell(display, 0); return gFalse; } #ifndef NO_TEXT_SELECT copySelection(); #endif return gTrue; } //------------------------------------------------------------------------ // misc access //------------------------------------------------------------------------ void XPDFCore::setBusyCursor(GBool busy) { setCursor(busy ? busyCursor : None); } void XPDFCore::takeFocus() { XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT); } //------------------------------------------------------------------------ // GUI code //------------------------------------------------------------------------ void XPDFCore::setupX(GBool installCmap, int rgbCubeSizeA) { XVisualInfo visualTempl; XVisualInfo *visualList; Gulong mask; int nVisuals; XColor xcolor; XColor *xcolors; int r, g, b, n, m; GBool ok; // for some reason, querying XmNvisual doesn't work (even if done // after the window is mapped) visual = DefaultVisual(display, screenNum); XtVaGetValues(shell, XmNcolormap, &colormap, NULL); // check for TrueColor visual //~ this should scan the list, not just look at the first one visualTempl.visualid = XVisualIDFromVisual(visual); visualList = XGetVisualInfo(display, VisualIDMask, &visualTempl, &nVisuals); if (nVisuals < 1) { // this shouldn't happen XFree((XPointer)visualList); visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl, &nVisuals); } depth = visualList->depth; if (visualList->c_class == TrueColor) { trueColor = gTrue; for (mask = visualList->red_mask, rShift = 0; mask && !(mask & 1); mask >>= 1, ++rShift) ; for (rDiv = 8; mask; mask >>= 1, --rDiv) ; for (mask = visualList->green_mask, gShift = 0; mask && !(mask & 1); mask >>= 1, ++gShift) ; for (gDiv = 8; mask; mask >>= 1, --gDiv) ; for (mask = visualList->blue_mask, bShift = 0; mask && !(mask & 1); mask >>= 1, ++bShift) ; for (bDiv = 8; mask; mask >>= 1, --bDiv) ; } else { trueColor = gFalse; } XFree((XPointer)visualList); // allocate a color cube if (!trueColor) { // set colors in private colormap if (installCmap) { for (rgbCubeSize = xMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) { m = rgbCubeSize * rgbCubeSize * rgbCubeSize; if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) { break; } } if (rgbCubeSize >= 2) { m = rgbCubeSize * rgbCubeSize * rgbCubeSize; xcolors = (XColor *)gmallocn(m, sizeof(XColor)); n = 0; for (r = 0; r < rgbCubeSize; ++r) { for (g = 0; g < rgbCubeSize; ++g) { for (b = 0; b < rgbCubeSize; ++b) { xcolors[n].pixel = colors[n]; xcolors[n].red = (r * 65535) / (rgbCubeSize - 1); xcolors[n].green = (g * 65535) / (rgbCubeSize - 1); xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1); xcolors[n].flags = DoRed | DoGreen | DoBlue; ++n; } } } XStoreColors(display, colormap, xcolors, m); gfree(xcolors); } else { rgbCubeSize = 1; colors[0] = BlackPixel(display, screenNum); colors[1] = WhitePixel(display, screenNum); } // allocate colors in shared colormap } else { if (rgbCubeSize > xMaxRGBCube) { rgbCubeSize = xMaxRGBCube; } ok = gFalse; for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) { ok = gTrue; n = 0; for (r = 0; r < rgbCubeSize && ok; ++r) { for (g = 0; g < rgbCubeSize && ok; ++g) { for (b = 0; b < rgbCubeSize && ok; ++b) { if (n == 0) { colors[n] = BlackPixel(display, screenNum); ++n; } else { xcolor.red = (r * 65535) / (rgbCubeSize - 1); xcolor.green = (g * 65535) / (rgbCubeSize - 1); xcolor.blue = (b * 65535) / (rgbCubeSize - 1); if (XAllocColor(display, colormap, &xcolor)) { colors[n++] = xcolor.pixel; } else { ok = gFalse; } } } } } if (ok) { break; } XFreeColors(display, colormap, &colors[1], n-1, 0); } if (!ok) { rgbCubeSize = 1; colors[0] = BlackPixel(display, screenNum); colors[1] = WhitePixel(display, screenNum); } } } } void XPDFCore::initWindow() { Arg args[20]; int n; // create the cursors busyCursor = XCreateFontCursor(display, XC_watch); linkCursor = XCreateFontCursor(display, XC_hand2); selectCursor = XCreateFontCursor(display, XC_cross); currentCursor = 0; // create the scrolled window and scrollbars n = 0; XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n; XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n; scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n); XtManageChild(scrolledWin); n = 0; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; XtSetArg(args[n], XmNminimum, 0); ++n; XtSetArg(args[n], XmNmaximum, 1); ++n; XtSetArg(args[n], XmNsliderSize, 1); ++n; XtSetArg(args[n], XmNvalue, 0); ++n; XtSetArg(args[n], XmNincrement, 1); ++n; XtSetArg(args[n], XmNpageIncrement, 1); ++n; hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n); if (!fullScreen) { XtManageChild(hScrollBar); } XtAddCallback(hScrollBar, XmNvalueChangedCallback, &hScrollChangeCbk, (XtPointer)this); #ifndef DISABLE_SMOOTH_SCROLL XtAddCallback(hScrollBar, XmNdragCallback, &hScrollDragCbk, (XtPointer)this); #endif n = 0; XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n; XtSetArg(args[n], XmNminimum, 0); ++n; XtSetArg(args[n], XmNmaximum, 1); ++n; XtSetArg(args[n], XmNsliderSize, 1); ++n; XtSetArg(args[n], XmNvalue, 0); ++n; XtSetArg(args[n], XmNincrement, 1); ++n; XtSetArg(args[n], XmNpageIncrement, 1); ++n; vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n); if (!fullScreen) { XtManageChild(vScrollBar); } XtAddCallback(vScrollBar, XmNvalueChangedCallback, &vScrollChangeCbk, (XtPointer)this); #ifndef DISABLE_SMOOTH_SCROLL XtAddCallback(vScrollBar, XmNdragCallback, &vScrollDragCbk, (XtPointer)this); #endif // create the drawing area n = 0; XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n; XtSetArg(args[n], XmNmarginWidth, 0); ++n; XtSetArg(args[n], XmNmarginHeight, 0); ++n; if (fullScreen) { XtSetArg(args[n], XmNshadowThickness, 0); ++n; } drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n); XtManageChild(drawAreaFrame); n = 0; XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n; XtSetArg(args[n], XmNwidth, 700); ++n; XtSetArg(args[n], XmNheight, 500); ++n; drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n); XtManageChild(drawArea); XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this); XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this); XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this); resizeCbk(drawArea, this, NULL); // set up mouse motion translations XtOverrideTranslations(drawArea, XtParseTranslationTable( ":DrawingAreaInput()\n" ":DrawingAreaInput()\n" ":DrawingAreaInput()\n" ":DrawingAreaInput()")); // can't create a GC until the window gets mapped drawAreaGC = NULL; } void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; core->scrollTo(data->value, core->scrollY); } void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; core->scrollTo(data->value, core->scrollY); } void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; core->scrollTo(core->scrollX, data->value); } void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData; core->scrollTo(core->scrollX, data->value); } void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XEvent event; Widget top; Window rootWin; int x1, y1; Guint w1, h1, bw1, depth1; Arg args[2]; int n; Dimension w, h; int sx, sy; // find the top-most widget which has an associated window, and look // for a pending ConfigureNotify in the event queue -- if there is // one, and it specifies a different width or height, that means // we're still resizing, and we want to skip the current event for (top = core->parentWidget; XtParent(top) && XtWindow(XtParent(top)); top = XtParent(top)) ; if (XCheckTypedWindowEvent(core->display, XtWindow(top), ConfigureNotify, &event)) { XPutBackEvent(core->display, &event); XGetGeometry(core->display, event.xconfigure.window, &rootWin, &x1, &y1, &w1, &h1, &bw1, &depth1); if ((Guint)event.xconfigure.width != w1 || (Guint)event.xconfigure.height != h1) { return; } } n = 0; XtSetArg(args[n], XmNwidth, &w); ++n; XtSetArg(args[n], XmNheight, &h); ++n; XtGetValues(core->drawArea, args, n); core->drawAreaWidth = (int)w; core->drawAreaHeight = (int)h; if (core->zoom == zoomPage || core->zoom == zoomWidth) { sx = sy = -1; } else { sx = core->scrollX; sy = core->scrollY; } core->update(core->topPage, sx, sy, core->zoom, core->rotate, gTrue, gFalse, gFalse); } void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData; int x, y, w, h; if (data->reason == XmCR_EXPOSE) { x = data->event->xexpose.x; y = data->event->xexpose.y; w = data->event->xexpose.width; h = data->event->xexpose.height; } else { x = 0; y = 0; w = core->drawAreaWidth; h = core->drawAreaHeight; } core->redrawWindow(x, y, w, h, gFalse); } void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData; LinkAction *action; int pg, x, y; double xu, yu; const char *s; KeySym key; GBool ok; switch (data->event->type) { case ButtonPress: if (*core->mouseCbk) { (*core->mouseCbk)(core->mouseCbkData, data->event); } break; case ButtonRelease: if (*core->mouseCbk) { (*core->mouseCbk)(core->mouseCbkData, data->event); } break; case MotionNotify: if (core->doc && core->doc->getNumPages() > 0) { ok = core->cvtWindowToDev(data->event->xmotion.x, data->event->xmotion.y, &pg, &x, &y); if (core->dragging) { if (ok) { core->moveSelection(pg, x, y); } } else if (core->hyperlinksEnabled) { core->cvtDevToUser(pg, x, y, &xu, &yu); if (ok && (action = core->findLink(pg, xu, yu))) { core->setCursor(core->linkCursor); if (action != core->linkAction) { core->linkAction = action; if (core->updateCbk) { s = ""; switch (action->getKind()) { case actionGoTo: s = "[internal link]"; break; case actionGoToR: s = ((LinkGoToR *)action)->getFileName()->getCString(); break; case actionLaunch: s = ((LinkLaunch *)action)->getFileName()->getCString(); break; case actionURI: s = ((LinkURI *)action)->getURI()->getCString(); break; case actionNamed: s = ((LinkNamed *)action)->getName()->getCString(); break; case actionMovie: s = "[movie]"; break; case actionJavaScript: case actionSubmitForm: case actionHide: case actionUnknown: s = "[unknown link]"; break; } (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s); } } } else { core->setCursor(None); if (core->linkAction) { core->linkAction = NULL; if (core->updateCbk) { (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, ""); } } } } } if (core->panning) { core->scrollTo(core->scrollX - (data->event->xmotion.x - core->panMX), core->scrollY - (data->event->xmotion.y - core->panMY)); core->panMX = data->event->xmotion.x; core->panMY = data->event->xmotion.y; } break; case KeyPress: if (core->keyPressCbk) { key = XLookupKeysym(&data->event->xkey, (data->event->xkey.state & ShiftMask) ? 1 : 0); (*core->keyPressCbk)(core->keyPressCbkData, key, data->event->xkey.state, data->event); } break; } } PDFCoreTile *XPDFCore::newTile(int xDestA, int yDestA) { return new XPDFCoreTile(xDestA, yDestA); } void XPDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc, int width, int height, GBool composited) { XPDFCoreTile *tile = (XPDFCoreTile *)tileA; XImage *image; SplashColorPtr dataPtr, p; Gulong pixel; Guchar *ap; Guchar alpha, alpha1; int w, h, bw, x, y, r, g, b, gray; int *errDownR, *errDownG, *errDownB; int errRightR, errRightG, errRightB; int errDownRightR, errDownRightG, errDownRightB; int r0, g0, b0, re, ge, be; if (!tile->image) { w = tile->xMax - tile->xMin; h = tile->yMax - tile->yMin; image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, w, h, 8, 0); image->data = (char *)gmallocn(h, image->bytes_per_line); tile->image = image; } else { image = (XImage *)tile->image; } //~ optimize for known XImage formats bw = tile->bitmap->getRowSize(); dataPtr = tile->bitmap->getDataPtr(); if (trueColor) { for (y = 0; y < height; ++y) { p = dataPtr + (ySrc + y) * bw + xSrc * 3; if (!composited && tile->bitmap->getAlphaPtr()) { ap = tile->bitmap->getAlphaPtr() + (ySrc + y) * tile->bitmap->getWidth() + xSrc; } else { ap = NULL; } for (x = 0; x < width; ++x) { r = splashRGB8R(p); g = splashRGB8G(p); b = splashRGB8B(p); if (ap) { alpha = *ap++; alpha1 = 255 - alpha; r = div255(alpha1 * paperColor[0] + alpha * r); g = div255(alpha1 * paperColor[1] + alpha * g); b = div255(alpha1 * paperColor[2] + alpha * b); } r >>= rDiv; g >>= gDiv; b >>= bDiv; pixel = ((Gulong)r << rShift) + ((Gulong)g << gShift) + ((Gulong)b << bShift); XPutPixel(image, xSrc + x, ySrc + y, pixel); p += 3; } } } else if (rgbCubeSize == 1) { //~ this should really use splashModeMono, with non-clustered dithering for (y = 0; y < height; ++y) { p = dataPtr + (ySrc + y) * bw + xSrc * 3; if (!composited && tile->bitmap->getAlphaPtr()) { ap = tile->bitmap->getAlphaPtr() + (ySrc + y) * tile->bitmap->getWidth() + xSrc; } else { ap = NULL; } for (x = 0; x < width; ++x) { r = splashRGB8R(p); g = splashRGB8G(p); b = splashRGB8B(p); if (ap) { alpha = *ap++; alpha1 = 255 - alpha; r = div255(alpha1 * paperColor[0] + alpha * r); g = div255(alpha1 * paperColor[1] + alpha * g); b = div255(alpha1 * paperColor[2] + alpha * b); } gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5); if (gray < 128) { pixel = colors[0]; } else { pixel = colors[1]; } XPutPixel(image, xSrc + x, ySrc + y, pixel); p += 3; } } } else { // do Floyd-Steinberg dithering on the whole bitmap errDownR = (int *)gmallocn(width + 2, sizeof(int)); errDownG = (int *)gmallocn(width + 2, sizeof(int)); errDownB = (int *)gmallocn(width + 2, sizeof(int)); errRightR = errRightG = errRightB = 0; errDownRightR = errDownRightG = errDownRightB = 0; memset(errDownR, 0, (width + 2) * sizeof(int)); memset(errDownG, 0, (width + 2) * sizeof(int)); memset(errDownB, 0, (width + 2) * sizeof(int)); for (y = 0; y < height; ++y) { p = dataPtr + (ySrc + y) * bw + xSrc * 3; if (!composited && tile->bitmap->getAlphaPtr()) { ap = tile->bitmap->getAlphaPtr() + (ySrc + y) * tile->bitmap->getWidth() + xSrc; } else { ap = NULL; } for (x = 0; x < width; ++x) { r = splashRGB8R(p); g = splashRGB8G(p); b = splashRGB8B(p); if (ap) { alpha = *ap++; alpha1 = 255 - alpha; r = div255(alpha1 * paperColor[0] + alpha * r); g = div255(alpha1 * paperColor[1] + alpha * g); b = div255(alpha1 * paperColor[2] + alpha * b); } r0 = r + errRightR + errDownR[x+1]; g0 = g + errRightG + errDownG[x+1]; b0 = b + errRightB + errDownB[x+1]; if (r0 < 0) { r = 0; } else if (r0 >= 255) { r = rgbCubeSize - 1; } else { r = div255(r0 * (rgbCubeSize - 1)); } if (g0 < 0) { g = 0; } else if (g0 >= 255) { g = rgbCubeSize - 1; } else { g = div255(g0 * (rgbCubeSize - 1)); } if (b0 < 0) { b = 0; } else if (b0 >= 255) { b = rgbCubeSize - 1; } else { b = div255(b0 * (rgbCubeSize - 1)); } re = r0 - ((r << 8) - r) / (rgbCubeSize - 1); ge = g0 - ((g << 8) - g) / (rgbCubeSize - 1); be = b0 - ((b << 8) - b) / (rgbCubeSize - 1); errRightR = (re * 7) >> 4; errRightG = (ge * 7) >> 4; errRightB = (be * 7) >> 4; errDownR[x] += (re * 3) >> 4; errDownG[x] += (ge * 3) >> 4; errDownB[x] += (be * 3) >> 4; errDownR[x+1] = ((re * 5) >> 4) + errDownRightR; errDownG[x+1] = ((ge * 5) >> 4) + errDownRightG; errDownB[x+1] = ((be * 5) >> 4) + errDownRightB; errDownRightR = re >> 4; errDownRightG = ge >> 4; errDownRightB = be >> 4; pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b]; XPutPixel(image, xSrc + x, ySrc + y, pixel); p += 3; } } gfree(errDownR); gfree(errDownG); gfree(errDownB); } } void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc, int xDest, int yDest, int width, int height, GBool composited) { XPDFCoreTile *tile = (XPDFCoreTile *)tileA; Window drawAreaWin; XGCValues gcValues; // create a GC for the drawing area drawAreaWin = XtWindow(drawArea); if (!drawAreaGC) { gcValues.foreground = mattePixel; drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues); } // draw the document if (tile) { XPutImage(display, drawAreaWin, drawAreaGC, tile->image, xSrc, ySrc, xDest, yDest, width, height); // draw the background } else { XFillRectangle(display, drawAreaWin, drawAreaGC, xDest, yDest, width, height); } XFlush(display); } void XPDFCore::updateScrollbars() { Arg args[20]; int n; int maxPos; if (pages->getLength() > 0) { if (continuousMode) { maxPos = maxPageW; } else { maxPos = ((PDFCorePage *)pages->get(0))->w; } } else { maxPos = 1; } if (maxPos < drawAreaWidth) { maxPos = drawAreaWidth; } n = 0; XtSetArg(args[n], XmNvalue, scrollX); ++n; XtSetArg(args[n], XmNmaximum, maxPos); ++n; XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n; XtSetArg(args[n], XmNincrement, 16); ++n; XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n; XtSetValues(hScrollBar, args, n); if (pages->getLength() > 0) { if (continuousMode) { maxPos = totalDocH; } else { maxPos = ((PDFCorePage *)pages->get(0))->h; } } else { maxPos = 1; } if (maxPos < drawAreaHeight) { maxPos = drawAreaHeight; } n = 0; XtSetArg(args[n], XmNvalue, scrollY); ++n; XtSetArg(args[n], XmNmaximum, maxPos); ++n; XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n; XtSetArg(args[n], XmNincrement, 16); ++n; XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n; XtSetValues(vScrollBar, args, n); } void XPDFCore::setCursor(Cursor cursor) { Window topWin; if (cursor == currentCursor) { return; } if (!(topWin = XtWindow(shell))) { return; } if (cursor == None) { XUndefineCursor(display, topWin); } else { XDefineCursor(display, topWin, cursor); } XFlush(display); currentCursor = cursor; } GBool XPDFCore::doQuestionDialog(const char *title, GString *msg) { return doDialog(XmDIALOG_QUESTION, gTrue, title, msg); } void XPDFCore::doInfoDialog(const char *title, GString *msg) { doDialog(XmDIALOG_INFORMATION, gFalse, title, msg); } void XPDFCore::doErrorDialog(const char *title, GString *msg) { doDialog(XmDIALOG_ERROR, gFalse, title, msg); } GBool XPDFCore::doDialog(int type, GBool hasCancel, const char *title, GString *msg) { Widget dialog, scroll, text; XtAppContext appContext; Arg args[20]; int n; XmString s1, s2; XEvent event; n = 0; XtSetArg(args[n], XmNdialogType, type); ++n; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; s1 = XmStringCreateLocalized((char *)title); XtSetArg(args[n], XmNdialogTitle, s1); ++n; s2 = NULL; // make gcc happy if (msg->getLength() <= 80) { s2 = XmStringCreateLocalized(msg->getCString()); XtSetArg(args[n], XmNmessageString, s2); ++n; } dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n); XmStringFree(s1); if (msg->getLength() <= 80) { XmStringFree(s2); } else { n = 0; XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n; if (drawAreaWidth > 300) { XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n; } scroll = XmCreateScrolledWindow(dialog, "scroll", args, n); XtManageChild(scroll); n = 0; XtSetArg(args[n], XmNeditable, False); ++n; XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n; XtSetArg(args[n], XmNvalue, msg->getCString()); ++n; XtSetArg(args[n], XmNshadowThickness, 0); ++n; text = XmCreateText(scroll, "text", args, n); XtManageChild(text); } XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON)); XtAddCallback(dialog, XmNokCallback, &dialogOkCbk, (XtPointer)this); if (hasCancel) { XtAddCallback(dialog, XmNcancelCallback, &dialogCancelCbk, (XtPointer)this); } else { XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON)); } XtManageChild(dialog); appContext = XtWidgetToApplicationContext(dialog); dialogDone = 0; do { XtAppNextEvent(appContext, &event); XtDispatchEvent(&event); } while (!dialogDone); XtUnmanageChild(dialog); XtDestroyWidget(dialog); return dialogDone > 0; } void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; core->dialogDone = 1; } void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; core->dialogDone = -1; } //------------------------------------------------------------------------ // password dialog //------------------------------------------------------------------------ void XPDFCore::initPasswordDialog() { Widget row, label, okBtn, cancelBtn; Arg args[20]; int n; XmString s; //----- dialog n = 0; s = XmStringCreateLocalized(xpdfAppName ": Password"); XtSetArg(args[n], XmNdialogTitle, s); ++n; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n; passwordDialog = XmCreateFormDialog(drawArea, "passwordDialog", args, n); XmStringFree(s); //----- message n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNtopOffset, 4); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; s = XmStringCreateLocalized("This document requires a password."); XtSetArg(args[n], XmNlabelString, s); ++n; label = XmCreateLabel(passwordDialog, "msg", args, n); XmStringFree(s); XtManageChild(label); //----- label and password entry n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, label); ++n; XtSetArg(args[n], XmNtopOffset, 4); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n; XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n; row = XmCreateRowColumn(passwordDialog, "row", args, n); XtManageChild(row); n = 0; s = XmStringCreateLocalized("Password: "); XtSetArg(args[n], XmNlabelString, s); ++n; label = XmCreateLabel(row, "label", args, n); XmStringFree(s); XtManageChild(label); n = 0; XtSetArg(args[n], XmNcolumns, 16); ++n; passwordText = XmCreateTextField(row, "text", args, n); XtManageChild(passwordText); XtAddCallback(passwordText, XmNmodifyVerifyCallback, &passwordTextVerifyCbk, this); //----- "Ok" and "Cancel" buttons n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, row); ++n; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNleftOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n; okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n); XtManageChild(okBtn); XtAddCallback(okBtn, XmNactivateCallback, &passwordOkCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n; XtSetArg(args[n], XmNtopWidget, row); ++n; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNrightOffset, 4); ++n; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n; XtSetArg(args[n], XmNbottomOffset, 4); ++n; XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n; cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n); XtManageChild(cancelBtn); XtAddCallback(cancelBtn, XmNactivateCallback, &passwordCancelCbk, (XtPointer)this); n = 0; XtSetArg(args[n], XmNdefaultButton, okBtn); ++n; XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n; #if XmVersion > 1001 XtSetArg(args[n], XmNinitialFocus, passwordText); ++n; #endif XtSetValues(passwordDialog, args, n); } void XPDFCore::passwordTextVerifyCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; XmTextVerifyCallbackStruct *data = (XmTextVerifyCallbackStruct *)callData; int i, n; i = (int)data->startPos; n = (int)data->endPos - i; if (i > core->password->getLength()) { i = core->password->getLength(); } if (i + n > core->password->getLength()) { n = core->password->getLength() - i; } core->password->del(i, n); core->password->insert(i, data->text->ptr, data->text->length); for (i = 0; i < data->text->length; ++i) { data->text->ptr[i] = '*'; } data->doit = True; } void XPDFCore::passwordOkCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; core->dialogDone = 1; } void XPDFCore::passwordCancelCbk(Widget widget, XtPointer ptr, XtPointer callData) { XPDFCore *core = (XPDFCore *)ptr; core->dialogDone = -1; } GString *XPDFCore::getPassword() { XtAppContext appContext; XEvent event; // NB: set before calling XmTextFieldSetString, because // SetString will trigger a call to passwordTextVerifyCbk, which // expects to be valid password = new GString(); XmTextFieldSetString(passwordText, ""); XtManageChild(passwordDialog); appContext = XtWidgetToApplicationContext(passwordDialog); dialogDone = 0; do { XtAppNextEvent(appContext, &event); XtDispatchEvent(&event); } while (!dialogDone); XtUnmanageChild(passwordDialog); if (dialogDone < 0) { delete password; return NULL; } return password; } xpdf-3.04/xpdf/Lexer.cc0000644000076400007640000002352212341430012014234 0ustar dereknderekn//======================================================================== // // Lexer.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "Lexer.h" #include "Error.h" //------------------------------------------------------------------------ // A '1' in this array means the character is white space. A '1' or // '2' means the character ends a name or command. static char specialChars[256] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx }; //------------------------------------------------------------------------ // Lexer //------------------------------------------------------------------------ Lexer::Lexer(XRef *xref, Stream *str) { Object obj; curStr.initStream(str); streams = new Array(xref); streams->add(curStr.copy(&obj)); strPtr = 0; freeArray = gTrue; curStr.streamReset(); } Lexer::Lexer(XRef *xref, Object *obj) { Object obj2; if (obj->isStream()) { streams = new Array(xref); freeArray = gTrue; streams->add(obj->copy(&obj2)); } else { streams = obj->getArray(); freeArray = gFalse; } strPtr = 0; if (streams->getLength() > 0) { streams->get(strPtr, &curStr); curStr.streamReset(); } } Lexer::~Lexer() { if (!curStr.isNone()) { curStr.streamClose(); curStr.free(); } if (freeArray) { delete streams; } } int Lexer::getChar() { int c; c = EOF; while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) { curStr.streamClose(); curStr.free(); ++strPtr; if (strPtr < streams->getLength()) { streams->get(strPtr, &curStr); curStr.streamReset(); } } return c; } int Lexer::lookChar() { if (curStr.isNone()) { return EOF; } return curStr.streamLookChar(); } Object *Lexer::getObj(Object *obj) { char *p; int c, c2; GBool comment, neg, done; int numParen; int xi; double xf, scale; GString *s; int n, m; // skip whitespace and comments comment = gFalse; while (1) { if ((c = getChar()) == EOF) { return obj->initEOF(); } if (comment) { if (c == '\r' || c == '\n') comment = gFalse; } else if (c == '%') { comment = gTrue; } else if (specialChars[c] != 1) { break; } } // start reading token switch (c) { // number case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '.': neg = gFalse; xf = xi = 0; if (c == '-') { neg = gTrue; } else if (c == '.') { goto doReal; } else { xf = xi = c - '0'; } while (1) { c = lookChar(); if (isdigit(c)) { getChar(); xi = xi * 10 + (c - '0'); xf = xf * 10 + (c - '0'); } else if (c == '.') { getChar(); goto doReal; } else { break; } } if (neg) { xi = -xi; } obj->initInt(xi); break; doReal: scale = 0.1; while (1) { c = lookChar(); if (c == '-') { // ignore minus signs in the middle of numbers to match // Adobe's behavior error(errSyntaxWarning, getPos(), "Badly formatted number"); getChar(); continue; } if (!isdigit(c)) { break; } getChar(); xf = xf + scale * (c - '0'); scale *= 0.1; } if (neg) { xf = -xf; } obj->initReal(xf); break; // string case '(': p = tokBuf; n = 0; numParen = 1; done = gFalse; s = NULL; do { c2 = EOF; switch (c = getChar()) { case EOF: #if 0 // This breaks some PDF files, e.g., ones from Photoshop. case '\r': case '\n': #endif error(errSyntaxError, getPos(), "Unterminated string"); done = gTrue; break; case '(': ++numParen; c2 = c; break; case ')': if (--numParen == 0) { done = gTrue; } else { c2 = c; } break; case '\\': switch (c = getChar()) { case 'n': c2 = '\n'; break; case 'r': c2 = '\r'; break; case 't': c2 = '\t'; break; case 'b': c2 = '\b'; break; case 'f': c2 = '\f'; break; case '\\': case '(': case ')': c2 = c; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c2 = c - '0'; c = lookChar(); if (c >= '0' && c <= '7') { getChar(); c2 = (c2 << 3) + (c - '0'); c = lookChar(); if (c >= '0' && c <= '7') { getChar(); c2 = (c2 << 3) + (c - '0'); } } break; case '\r': c = lookChar(); if (c == '\n') { getChar(); } break; case '\n': break; case EOF: error(errSyntaxError, getPos(), "Unterminated string"); done = gTrue; break; default: c2 = c; break; } break; default: c2 = c; break; } if (c2 != EOF) { if (n == tokBufSize) { if (!s) s = new GString(tokBuf, tokBufSize); else s->append(tokBuf, tokBufSize); p = tokBuf; n = 0; } *p++ = (char)c2; ++n; } } while (!done); if (!s) s = new GString(tokBuf, n); else s->append(tokBuf, n); obj->initString(s); break; // name case '/': p = tokBuf; n = 0; s = NULL; while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (c == '#') { c2 = lookChar(); if (c2 >= '0' && c2 <= '9') { c = c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { c = c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { c = c2 - 'a' + 10; } else { goto notEscChar; } getChar(); c <<= 4; c2 = getChar(); if (c2 >= '0' && c2 <= '9') { c += c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { c += c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { c += c2 - 'a' + 10; } else { error(errSyntaxError, getPos(), "Illegal digit in hex char in name"); } } notEscChar: // the PDF spec claims that names are limited to 127 chars, but // Distiller 8 will produce longer names, and Acrobat 8 will // accept longer names ++n; if (n < tokBufSize) { *p++ = c; } else if (n == tokBufSize) { *p = c; s = new GString(tokBuf, n); } else { s->append((char)c); } } if (n < tokBufSize) { *p = '\0'; obj->initName(tokBuf); } else { obj->initName(s->getCString()); delete s; } break; // array punctuation case '[': case ']': tokBuf[0] = c; tokBuf[1] = '\0'; obj->initCmd(tokBuf); break; // hex string or dict punctuation case '<': c = lookChar(); // dict punctuation if (c == '<') { getChar(); tokBuf[0] = tokBuf[1] = '<'; tokBuf[2] = '\0'; obj->initCmd(tokBuf); // hex string } else { p = tokBuf; m = n = 0; c2 = 0; s = NULL; while (1) { c = getChar(); if (c == '>') { break; } else if (c == EOF) { error(errSyntaxError, getPos(), "Unterminated hex string"); break; } else if (specialChars[c] != 1) { c2 = c2 << 4; if (c >= '0' && c <= '9') c2 += c - '0'; else if (c >= 'A' && c <= 'F') c2 += c - 'A' + 10; else if (c >= 'a' && c <= 'f') c2 += c - 'a' + 10; else error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in hex string", c); if (++m == 2) { if (n == tokBufSize) { if (!s) s = new GString(tokBuf, tokBufSize); else s->append(tokBuf, tokBufSize); p = tokBuf; n = 0; } *p++ = (char)c2; ++n; c2 = 0; m = 0; } } } if (!s) s = new GString(tokBuf, n); else s->append(tokBuf, n); if (m == 1) s->append((char)(c2 << 4)); obj->initString(s); } break; // dict punctuation case '>': c = lookChar(); if (c == '>') { getChar(); tokBuf[0] = tokBuf[1] = '>'; tokBuf[2] = '\0'; obj->initCmd(tokBuf); } else { error(errSyntaxError, getPos(), "Illegal character '>'"); obj->initError(); } break; // error case ')': case '{': case '}': error(errSyntaxError, getPos(), "Illegal character '{0:c}'", c); obj->initError(); break; // command default: p = tokBuf; *p++ = c; n = 1; while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (++n == tokBufSize) { error(errSyntaxError, getPos(), "Command token too long"); break; } *p++ = c; } *p = '\0'; if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) { obj->initBool(gTrue); } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) { obj->initBool(gFalse); } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) { obj->initNull(); } else { obj->initCmd(tokBuf); } break; } return obj; } void Lexer::skipToNextLine() { int c; while (1) { c = getChar(); if (c == EOF || c == '\n') { return; } if (c == '\r') { if ((c = lookChar()) == '\n') { getChar(); } return; } } } GBool Lexer::isSpace(int c) { return c >= 0 && c <= 0xff && specialChars[c] == 1; } xpdf-3.04/xpdf/PDFDoc.cc0000644000076400007640000003145012341430012014213 0ustar dereknderekn//======================================================================== // // PDFDoc.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #ifdef _WIN32 # include #endif #include "GString.h" #include "config.h" #include "GlobalParams.h" #include "Page.h" #include "Catalog.h" #include "Stream.h" #include "XRef.h" #include "Link.h" #include "OutputDev.h" #include "Error.h" #include "ErrorCodes.h" #include "Lexer.h" #include "Parser.h" #include "SecurityHandler.h" #ifndef DISABLE_OUTLINE #include "Outline.h" #endif #include "OptionalContent.h" #include "PDFDoc.h" //------------------------------------------------------------------------ #define headerSearchSize 1024 // read this many bytes at beginning of // file to look for '%PDF' //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword, GString *userPassword, PDFCore *coreA) { Object obj; GString *fileName1, *fileName2; #ifdef _WIN32 int n, i; #endif ok = gFalse; errCode = errNone; core = coreA; file = NULL; str = NULL; xref = NULL; catalog = NULL; #ifndef DISABLE_OUTLINE outline = NULL; #endif optContent = NULL; fileName = fileNameA; #ifdef _WIN32 n = fileName->getLength(); fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t)); for (i = 0; i < n; ++i) { fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff); } fileNameU[n] = L'\0'; #endif fileName1 = fileName; // try to open file fileName2 = NULL; #ifdef VMS if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName1); errCode = errOpenFile; return; } #else if (!(file = fopen(fileName1->getCString(), "rb"))) { fileName2 = fileName->copy(); fileName2->lowerCase(); if (!(file = fopen(fileName2->getCString(), "rb"))) { fileName2->upperCase(); if (!(file = fopen(fileName2->getCString(), "rb"))) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName); delete fileName2; errCode = errOpenFile; return; } } delete fileName2; } #endif // create stream obj.initNull(); str = new FileStream(file, 0, gFalse, 0, &obj); ok = setup(ownerPassword, userPassword); } #ifdef _WIN32 PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword, GString *userPassword, PDFCore *coreA) { OSVERSIONINFO version; Object obj; int i; ok = gFalse; errCode = errNone; core = coreA; file = NULL; str = NULL; xref = NULL; catalog = NULL; #ifndef DISABLE_OUTLINE outline = NULL; #endif optContent = NULL; // save both Unicode and 8-bit copies of the file name fileName = new GString(); fileNameU = (wchar_t *)gmallocn(fileNameLen + 1, sizeof(wchar_t)); for (i = 0; i < fileNameLen; ++i) { fileName->append((char)fileNameA[i]); fileNameU[i] = fileNameA[i]; } fileNameU[fileNameLen] = L'\0'; // try to open file // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { file = _wfopen(fileNameU, L"rb"); } else { file = fopen(fileName->getCString(), "rb"); } if (!file) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName); errCode = errOpenFile; return; } // create stream obj.initNull(); str = new FileStream(file, 0, gFalse, 0, &obj); ok = setup(ownerPassword, userPassword); } #endif PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword, GString *userPassword, PDFCore *coreA) { #ifdef _WIN32 int n, i; #endif ok = gFalse; errCode = errNone; core = coreA; if (strA->getFileName()) { fileName = strA->getFileName()->copy(); #ifdef _WIN32 n = fileName->getLength(); fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t)); for (i = 0; i < n; ++i) { fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff); } fileNameU[n] = L'\0'; #endif } else { fileName = NULL; #ifdef _WIN32 fileNameU = NULL; #endif } file = NULL; str = strA; xref = NULL; catalog = NULL; #ifndef DISABLE_OUTLINE outline = NULL; #endif optContent = NULL; ok = setup(ownerPassword, userPassword); } GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) { str->reset(); // check header checkHeader(); // read the xref and catalog if (!PDFDoc::setup2(ownerPassword, userPassword, gFalse)) { if (errCode == errDamaged || errCode == errBadCatalog) { // try repairing the xref table error(errSyntaxWarning, -1, "PDF file is damaged - attempting to reconstruct xref table..."); if (!PDFDoc::setup2(ownerPassword, userPassword, gTrue)) { return gFalse; } } else { return gFalse; } } #ifndef DISABLE_OUTLINE // read outline outline = new Outline(catalog->getOutline(), xref); #endif // read the optional content info optContent = new OptionalContent(this); // done return gTrue; } GBool PDFDoc::setup2(GString *ownerPassword, GString *userPassword, GBool repairXRef) { // read xref table xref = new XRef(str, repairXRef); if (!xref->isOk()) { error(errSyntaxError, -1, "Couldn't read xref table"); errCode = xref->getErrorCode(); delete xref; xref = NULL; return gFalse; } // check for encryption if (!checkEncryption(ownerPassword, userPassword)) { errCode = errEncrypted; delete xref; xref = NULL; return gFalse; } // read catalog catalog = new Catalog(this); if (!catalog->isOk()) { error(errSyntaxError, -1, "Couldn't read page catalog"); errCode = errBadCatalog; delete catalog; catalog = NULL; delete xref; xref = NULL; return gFalse; } return gTrue; } PDFDoc::~PDFDoc() { if (optContent) { delete optContent; } #ifndef DISABLE_OUTLINE if (outline) { delete outline; } #endif if (catalog) { delete catalog; } if (xref) { delete xref; } if (str) { delete str; } if (file) { fclose(file); } if (fileName) { delete fileName; } #ifdef _WIN32 if (fileNameU) { gfree(fileNameU); } #endif } // Check for a PDF header on this stream. Skip past some garbage // if necessary. void PDFDoc::checkHeader() { char hdrBuf[headerSearchSize+1]; char *p; int i; pdfVersion = 0; memset(hdrBuf, 0, headerSearchSize + 1); str->getBlock(hdrBuf, headerSearchSize); for (i = 0; i < headerSearchSize - 5; ++i) { if (!strncmp(&hdrBuf[i], "%PDF-", 5)) { break; } } if (i >= headerSearchSize - 5) { error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)"); return; } str->moveStart(i); if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) { error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)"); return; } pdfVersion = atof(p); if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') || pdfVersion > supportedPDFVersionNum + 0.0001) { error(errSyntaxWarning, -1, "PDF version {0:s} -- xpdf supports version {1:s} (continuing anyway)", p, supportedPDFVersionStr); } } GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) { Object encrypt; GBool encrypted; SecurityHandler *secHdlr; GBool ret; xref->getTrailerDict()->dictLookup("Encrypt", &encrypt); if ((encrypted = encrypt.isDict())) { if ((secHdlr = SecurityHandler::make(this, &encrypt))) { if (secHdlr->isUnencrypted()) { // no encryption ret = gTrue; } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) { // authorization succeeded xref->setEncryption(secHdlr->getPermissionFlags(), secHdlr->getOwnerPasswordOk(), secHdlr->getFileKey(), secHdlr->getFileKeyLength(), secHdlr->getEncVersion(), secHdlr->getEncAlgorithm()); ret = gTrue; } else { // authorization failed ret = gFalse; } delete secHdlr; } else { // couldn't find the matching security handler ret = gFalse; } } else { // document is not encrypted ret = gTrue; } encrypt.free(); return ret; } void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { if (globalParams->getPrintCommands()) { printf("***** page %d *****\n", page); } catalog->getPage(page)->display(out, hDPI, vDPI, rotate, useMediaBox, crop, printing, abortCheckCbk, abortCheckCbkData); } void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { int page; for (page = firstPage; page <= lastPage; ++page) { displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing, abortCheckCbk, abortCheckCbkData); catalog->doneWithPage(page); } } void PDFDoc::displayPageSlice(OutputDev *out, int page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, int sliceX, int sliceY, int sliceW, int sliceH, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { catalog->getPage(page)->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData); } Links *PDFDoc::getLinks(int page) { return catalog->getPage(page)->getLinks(); } void PDFDoc::processLinks(OutputDev *out, int page) { catalog->getPage(page)->processLinks(out); } GBool PDFDoc::isLinearized() { Parser *parser; Object obj1, obj2, obj3, obj4, obj5; GBool lin; lin = gFalse; obj1.initNull(); parser = new Parser(xref, new Lexer(xref, str->makeSubStream(str->getStart(), gFalse, 0, &obj1)), gTrue); parser->getObj(&obj1); parser->getObj(&obj2); parser->getObj(&obj3); parser->getObj(&obj4); if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") && obj4.isDict()) { obj4.dictLookup("Linearized", &obj5); if (obj5.isNum() && obj5.getNum() > 0) { lin = gTrue; } obj5.free(); } obj4.free(); obj3.free(); obj2.free(); obj1.free(); delete parser; return lin; } GBool PDFDoc::saveAs(GString *name) { FILE *f; 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 ((n = str->getBlock(buf, sizeof(buf))) > 0) { fwrite(buf, 1, n, f); } str->close(); fclose(f); return gTrue; } GBool PDFDoc::saveEmbeddedFile(int idx, char *path) { FILE *f; GBool ret; if (!(f = fopen(path, "wb"))) { return gFalse; } ret = saveEmbeddedFile2(idx, f); fclose(f); return ret; } #ifdef _WIN32 GBool PDFDoc::saveEmbeddedFile(int idx, wchar_t *path, int pathLen) { FILE *f; OSVERSIONINFO version; wchar_t path2w[_MAX_PATH + 1]; char path2c[_MAX_PATH + 1]; int i; GBool ret; // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { for (i = 0; i < pathLen && i < _MAX_PATH; ++i) { path2w[i] = path[i]; } path2w[i] = 0; f = _wfopen(path2w, L"wb"); } else { for (i = 0; i < pathLen && i < _MAX_PATH; ++i) { path2c[i] = (char)path[i]; } path2c[i] = 0; f = fopen(path2c, "wb"); } if (!f) { return gFalse; } ret = saveEmbeddedFile2(idx, f); fclose(f); return ret; } #endif GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) { Object strObj; char buf[4096]; int n; if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) { return gFalse; } strObj.streamReset(); while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) { fwrite(buf, 1, n, f); } strObj.streamClose(); strObj.free(); return gTrue; } char *PDFDoc::getEmbeddedFileMem(int idx, int *size) { Object strObj; char *buf; int bufSize, sizeInc, n; if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) { return NULL; } strObj.streamReset(); 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 = (char *)grealloc(buf, bufSize + sizeInc); n = strObj.streamGetBlock(buf + bufSize, sizeInc); bufSize += n; } while (n == sizeInc); strObj.streamClose(); strObj.free(); *size = bufSize; return buf; } xpdf-3.04/xpdf/XPDFViewer.h0000644000076400007640000003115412341430012014742 0ustar dereknderekn//======================================================================== // // XPDFViewer.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef XPDFVIEWER_H #define XPDFVIEWER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #define Object XtObject #include #undef Object #include "gtypes.h" #include "XPDFCore.h" #if (XmVERSION <= 1) && !defined(__sgi) #define DISABLE_OUTLINE #endif #if (XmVERSION >= 2 && !defined(LESSTIF_VERSION)) # define USE_COMBO_BOX 1 #else # undef USE_COMBO_BOX #endif class GString; class GList; class UnicodeMap; class LinkDest; class XPDFApp; class XPDFViewer; //------------------------------------------------------------------------ // NB: this must match the defn of zoomMenuBtnInfo in XPDFViewer.cc #define nZoomMenuItems 10 //------------------------------------------------------------------------ struct XPDFViewerCmd { const char *name; int nArgs; GBool requiresDoc; GBool requiresEvent; void (XPDFViewer::*func)(GString *args[], int nArgs, XEvent *event); }; //------------------------------------------------------------------------ // XPDFViewer //------------------------------------------------------------------------ class XPDFViewer { public: XPDFViewer(XPDFApp *appA, GString *fileName, int pageA, GString *destName, GBool fullScreen, GString *ownerPassword, GString *userPassword); XPDFViewer(XPDFApp *appA, PDFDoc *doc, int pageA, GString *destName, GBool fullScreen); GBool isOk() { return ok; } ~XPDFViewer(); void open(GString *fileName, int pageA, GString *destName); void clear(); void reloadFile(); void execCmd(GString *cmd, XEvent *event); Widget getWindow() { return win; } private: //----- load / display GBool loadFile(GString *fileName, GString *ownerPassword = NULL, GString *userPassword = NULL); void displayPage(int pageA, double zoomA, int rotateA, GBool scrollToTop, GBool addToHist); void displayDest(LinkDest *dest, double zoomA, int rotateA, GBool addToHist); void getPageAndDest(int pageA, GString *destName, int *pageOut, LinkDest **destOut); //----- hyperlinks / actions void doLink(int wx, int wy, GBool onlyIfNoSelection, GBool newWin); static void actionCbk(void *data, char *action); //----- keyboard/mouse input static void keyPressCbk(void *data, KeySym key, Guint modifiers, XEvent *event); static void mouseCbk(void *data, XEvent *event); int getModifiers(Guint modifiers); int getContext(Guint modifiers); //----- command functions void cmdAbout(GString *args[], int nArgs, XEvent *event); void cmdCloseOutline(GString *args[], int nArgs, XEvent *event); void cmdCloseWindow(GString *args[], int nArgs, XEvent *event); void 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); void cmdFind(GString *args[], int nArgs, XEvent *event); void cmdFindNext(GString *args[], int nArgs, XEvent *event); void cmdFocusToDocWin(GString *args[], int nArgs, XEvent *event); void cmdFocusToPageNum(GString *args[], int nArgs, XEvent *event); void cmdFollowLink(GString *args[], int nArgs, XEvent *event); void cmdFollowLinkInNewWin(GString *args[], int nArgs, XEvent *event); void cmdFollowLinkInNewWinNoSel(GString *args[], int nArgs, XEvent *event); void cmdFollowLinkNoSel(GString *args[], int nArgs, XEvent *event); void cmdFullScreenMode(GString *args[], int nArgs, XEvent *event); void cmdGoBackward(GString *args[], int nArgs, XEvent *event); void cmdGoForward(GString *args[], int nArgs, XEvent *event); void cmdGotoDest(GString *args[], int nArgs, XEvent *event); void cmdGotoLastPage(GString *args[], int nArgs, XEvent *event); void cmdGotoLastPageNoScroll(GString *args[], int nArgs, XEvent *event); void cmdGotoPage(GString *args[], int nArgs, XEvent *event); void cmdGotoPageNoScroll(GString *args[], int nArgs, XEvent *event); void cmdNextPage(GString *args[], int nArgs, XEvent *event); void cmdNextPageNoScroll(GString *args[], int nArgs, XEvent *event); void cmdOpen(GString *args[], int nArgs, XEvent *event); void cmdOpenFile(GString *args[], int nArgs, XEvent *event); void cmdOpenFileAtDest(GString *args[], int nArgs, XEvent *event); void cmdOpenFileAtDestInNewWin(GString *args[], int nArgs, XEvent *event); void cmdOpenFileAtPage(GString *args[], int nArgs, XEvent *event); void cmdOpenFileAtPageInNewWin(GString *args[], int nArgs, XEvent *event); void cmdOpenFileInNewWin(GString *args[], int nArgs, XEvent *event); void cmdOpenInNewWin(GString *args[], int nArgs, XEvent *event); void cmdOpenOutline(GString *args[], int nArgs, XEvent *event); void cmdPageDown(GString *args[], int nArgs, XEvent *event); void cmdPageUp(GString *args[], int nArgs, XEvent *event); void cmdPostPopupMenu(GString *args[], int nArgs, XEvent *event); void cmdPrevPage(GString *args[], int nArgs, XEvent *event); void cmdPrevPageNoScroll(GString *args[], int nArgs, XEvent *event); void cmdPrint(GString *args[], int nArgs, XEvent *event); void cmdQuit(GString *args[], int nArgs, XEvent *event); void cmdRaise(GString *args[], int nArgs, XEvent *event); void cmdRedraw(GString *args[], int nArgs, XEvent *event); void cmdReload(GString *args[], int nArgs, XEvent *event); void cmdRotateCCW(GString *args[], int nArgs, XEvent *event); void cmdRotateCW(GString *args[], int nArgs, XEvent *event); void cmdRun(GString *args[], int nArgs, XEvent *event); void cmdScrollDown(GString *args[], int nArgs, XEvent *event); void cmdScrollDownNextPage(GString *args[], int nArgs, XEvent *event); void cmdScrollLeft(GString *args[], int nArgs, XEvent *event); void cmdScrollOutlineDown(GString *args[], int nArgs, XEvent *event); void cmdScrollOutlineUp(GString *args[], int nArgs, XEvent *event); void cmdScrollRight(GString *args[], int nArgs, XEvent *event); void cmdScrollToBottomEdge(GString *args[], int nArgs, XEvent *event); void cmdScrollToBottomRight(GString *args[], int nArgs, XEvent *event); void cmdScrollToLeftEdge(GString *args[], int nArgs, XEvent *event); void cmdScrollToRightEdge(GString *args[], int nArgs, XEvent *event); void cmdScrollToTopEdge(GString *args[], int nArgs, XEvent *event); void cmdScrollToTopLeft(GString *args[], int nArgs, XEvent *event); void cmdScrollUp(GString *args[], int nArgs, XEvent *event); void cmdScrollUpPrevPage(GString *args[], int nArgs, XEvent *event); void cmdSetSelection(GString *args[], int nArgs, XEvent *event); void cmdSinglePageMode(GString *args[], int nArgs, XEvent *event); void cmdStartPan(GString *args[], int nArgs, XEvent *event); void cmdStartSelection(GString *args[], int nArgs, XEvent *event); void cmdToggleContinuousMode(GString *args[], int nArgs, XEvent *event); void cmdToggleFullScreenMode(GString *args[], int nArgs, XEvent *event); void cmdToggleOutline(GString *args[], int nArgs, XEvent *event); void cmdWindowMode(GString *args[], int nArgs, XEvent *event); void cmdZoomFitPage(GString *args[], int nArgs, XEvent *event); void cmdZoomFitWidth(GString *args[], int nArgs, XEvent *event); void cmdZoomIn(GString *args[], int nArgs, XEvent *event); void cmdZoomOut(GString *args[], int nArgs, XEvent *event); void cmdZoomPercent(GString *args[], int nArgs, XEvent *event); void cmdZoomToSelection(GString *args[], int nArgs, XEvent *event); //----- GUI code: main window void initWindow(GBool fullScreen); void initToolbar(Widget parent); #ifndef DISABLE_OUTLINE void initPanedWin(Widget parent); #endif void initCore(Widget parent, GBool fullScreen); void initPopupMenu(); void addToolTip(Widget widget, char *text); void mapWindow(); void closeWindow(); int getZoomIdx(); void setZoomIdx(int idx); void setZoomVal(double z); static void prevPageCbk(Widget widget, XtPointer ptr, XtPointer callData); static void prevTenPageCbk(Widget widget, XtPointer ptr, XtPointer callData); static void nextPageCbk(Widget widget, XtPointer ptr, XtPointer callData); static void nextTenPageCbk(Widget widget, XtPointer ptr, XtPointer callData); static void backCbk(Widget widget, XtPointer ptr, XtPointer callData); static void forwardCbk(Widget widget, XtPointer ptr, XtPointer callData); #if USE_COMBO_BOX static void zoomComboBoxCbk(Widget widget, XtPointer ptr, XtPointer callData); #else static void zoomMenuCbk(Widget widget, XtPointer ptr, XtPointer callData); #endif static void findCbk(Widget widget, XtPointer ptr, XtPointer callData); static void printCbk(Widget widget, XtPointer ptr, XtPointer callData); static void aboutCbk(Widget widget, XtPointer ptr, XtPointer callData); static void quitCbk(Widget widget, XtPointer ptr, XtPointer callData); static void openCbk(Widget widget, XtPointer ptr, XtPointer callData); static void openInNewWindowCbk(Widget widget, XtPointer ptr, XtPointer callData); static void reloadCbk(Widget widget, XtPointer ptr, XtPointer callData); static void saveAsCbk(Widget widget, XtPointer ptr, XtPointer callData); static void continuousModeToggleCbk(Widget widget, XtPointer ptr, XtPointer callData); static void fullScreenToggleCbk(Widget widget, XtPointer ptr, XtPointer callData); static void rotateCCWCbk(Widget widget, XtPointer ptr, XtPointer callData); static void rotateCWCbk(Widget widget, XtPointer ptr, XtPointer callData); static void zoomToSelectionCbk(Widget widget, XtPointer ptr, XtPointer callData); static void closeCbk(Widget widget, XtPointer ptr, XtPointer callData); static void closeMsgCbk(Widget widget, XtPointer ptr, XtPointer callData); static void pageNumCbk(Widget widget, XtPointer ptr, XtPointer callData); static void updateCbk(void *data, GString *fileName, int pageNum, int numPages, const char *linkString); //----- GUI code: outline #ifndef DISABLE_OUTLINE void setupOutline(); void setupOutlineItems(GList *items, Widget parent, UnicodeMap *uMap); static void outlineSelectCbk(Widget widget, XtPointer ptr, XtPointer callData); #endif //----- GUI code: "about" dialog void initAboutDialog(); //----- GUI code: "open" dialog void initOpenDialog(); void mapOpenDialog(GBool openInNewWindowA); static void openOkCbk(Widget widget, XtPointer ptr, XtPointer callData); //----- GUI code: "find" dialog void initFindDialog(); static void findFindCbk(Widget widget, XtPointer ptr, XtPointer callData); void mapFindDialog(); void doFind(GBool next); static void findCloseCbk(Widget widget, XtPointer ptr, XtPointer callData); //----- GUI code: "save as" dialog void initSaveAsDialog(); void mapSaveAsDialog(); static void saveAsOkCbk(Widget widget, XtPointer ptr, XtPointer callData); //----- GUI code: "print" dialog void initPrintDialog(); void setupPrintDialog(); static void printWithCmdBtnCbk(Widget widget, XtPointer ptr, XtPointer callData); static void printToFileBtnCbk(Widget widget, XtPointer ptr, XtPointer callData); static void printPrintCbk(Widget widget, XtPointer ptr, XtPointer callData); //----- Motif support XmFontList createFontList(char *xlfd); static XPDFViewerCmd cmdTab[]; XPDFApp *app; GBool ok; Display *display; int screenNum; Widget win; // top-level window Widget form; Widget panedWin; #ifndef DISABLE_OUTLINE Widget outlineScroll; Widget outlineTree; Widget *outlineLabels; int outlineLabelsLength; int outlineLabelsSize; Dimension outlinePaneWidth; #endif XPDFCore *core; Widget toolBar; Widget backBtn; Widget prevTenPageBtn; Widget prevPageBtn; Widget nextPageBtn; Widget nextTenPageBtn; Widget forwardBtn; Widget pageNumText; Widget pageCountLabel; #if USE_COMBO_BOX Widget zoomComboBox; #else Widget zoomMenu; Widget zoomMenuBtns[nZoomMenuItems]; #endif Widget zoomWidget; Widget findBtn; Widget printBtn; Widget aboutBtn; Widget linkLabel; Widget quitBtn; Widget popupMenu; Widget aboutDialog; XmFontList aboutBigFont, aboutVersionFont, aboutFixedFont; Widget openDialog; GBool openInNewWindow; Widget findDialog; Widget findText; Widget findBackwardToggle; Widget findCaseSensitiveToggle; Widget findWholeWordToggle; Widget saveAsDialog; Widget printDialog; Widget printWithCmdBtn; Widget printToFileBtn; Widget printCmdText; Widget printFileText; Widget printFirstPage; Widget printLastPage; }; #endif xpdf-3.04/xpdf/XPDFApp.cc0000644000076400007640000003307612341430012014364 0ustar dereknderekn//======================================================================== // // XPDFApp.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "GString.h" #include "GList.h" #include "Error.h" #include "XPDFViewer.h" #include "XPDFApp.h" #include "config.h" // these macro defns conflict with xpdf's Object class #ifdef LESSTIF_VERSION #undef XtDisplay #undef XtScreen #undef XtWindow #undef XtParent #undef XtIsRealized #endif //------------------------------------------------------------------------ #define remoteCmdSize 512 //------------------------------------------------------------------------ static String fallbackResources[] = { "*.zoomComboBox*fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1", "*XmTextField.fontList: -*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1", "*.fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1", "*XmTextField.translations: #override\\n" " Ctrla:beginning-of-line()\\n" " Ctrlb:backward-character()\\n" " Ctrld:delete-next-character()\\n" " Ctrle:end-of-line()\\n" " Ctrlf:forward-character()\\n" " Ctrlu:beginning-of-line()delete-to-end-of-line()\\n" " Ctrlk:delete-to-end-of-line()\\n", "*.toolTipEnable: True", "*.toolTipPostDelay: 1500", "*.toolTipPostDuration: 0", "*.TipLabel.foreground: black", "*.TipLabel.background: LightYellow", "*.TipShell.borderWidth: 1", "*.TipShell.borderColor: black", NULL }; static XrmOptionDescRec xOpts[] = { {"-display", ".display", XrmoptionSepArg, NULL}, {"-foreground", "*Foreground", XrmoptionSepArg, NULL}, {"-fg", "*Foreground", XrmoptionSepArg, NULL}, {"-background", "*Background", XrmoptionSepArg, NULL}, {"-bg", "*Background", XrmoptionSepArg, NULL}, {"-geometry", ".geometry", XrmoptionSepArg, NULL}, {"-g", ".geometry", XrmoptionSepArg, NULL}, {"-font", "*.fontList", XrmoptionSepArg, NULL}, {"-fn", "*.fontList", XrmoptionSepArg, NULL}, {"-title", ".title", XrmoptionSepArg, NULL}, {"-cmap", ".installCmap", XrmoptionNoArg, (XPointer)"on"}, {"-rgb", ".rgbCubeSize", XrmoptionSepArg, NULL}, {"-rv", ".reverseVideo", XrmoptionNoArg, (XPointer)"true"}, {"-papercolor", ".paperColor", XrmoptionSepArg, NULL}, {"-mattecolor", ".matteColor", XrmoptionSepArg, NULL}, {"-z", ".initialZoom", XrmoptionSepArg, NULL} }; #define nXOpts (sizeof(xOpts) / sizeof(XrmOptionDescRec)) struct XPDFAppResources { String geometry; String title; Bool installCmap; int rgbCubeSize; Bool reverseVideo; String paperColor; String matteColor; String fullScreenMatteColor; String initialZoom; }; static Bool defInstallCmap = False; static int defRGBCubeSize = defaultRGBCube; static Bool defReverseVideo = False; static XtResource xResources[] = { { "geometry", "Geometry", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, geometry), XtRString, (XtPointer)NULL }, { "title", "Title", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, title), XtRString, (XtPointer)NULL }, { "installCmap", "InstallCmap", XtRBool, sizeof(Bool), XtOffsetOf(XPDFAppResources, installCmap), XtRBool, (XtPointer)&defInstallCmap }, { "rgbCubeSize", "RgbCubeSize", XtRInt, sizeof(int), XtOffsetOf(XPDFAppResources, rgbCubeSize), XtRInt, (XtPointer)&defRGBCubeSize }, { "reverseVideo", "ReverseVideo", XtRBool, sizeof(Bool), XtOffsetOf(XPDFAppResources, reverseVideo), XtRBool, (XtPointer)&defReverseVideo }, { "paperColor", "PaperColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, paperColor), XtRString, (XtPointer)NULL }, { "matteColor", "MatteColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, matteColor), XtRString, (XtPointer)"gray50" }, { "fullScreenMatteColor", "FullScreenMatteColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, fullScreenMatteColor), XtRString, (XtPointer)"black" }, { "initialZoom", "InitialZoom", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, initialZoom), XtRString, (XtPointer)NULL } }; #define nXResources (sizeof(xResources) / sizeof(XtResource)) //------------------------------------------------------------------------ // XPDFApp //------------------------------------------------------------------------ #if 0 //~ for debugging static int xErrorHandler(Display *display, XErrorEvent *ev) { printf("X error:\n"); printf(" resource ID = %08lx\n", ev->resourceid); printf(" serial = %lu\n", ev->serial); printf(" error_code = %d\n", ev->error_code); printf(" request_code = %d\n", ev->request_code); printf(" minor_code = %d\n", ev->minor_code); fflush(stdout); abort(); } #endif XPDFApp::XPDFApp(int *argc, char *argv[]) { appShell = XtAppInitialize(&appContext, xpdfAppName, xOpts, nXOpts, argc, argv, fallbackResources, NULL, 0); display = XtDisplay(appShell); screenNum = XScreenNumberOfScreen(XtScreen(appShell)); #if XmVERSION > 1 XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)), XmNenableButtonTab, True, NULL); #endif #if XmVERSION > 1 // Drag-and-drop appears to be buggy -- I'm seeing weird crashes // deep in the Motif code when I destroy widgets in the XpdfForms // code. Xpdf doesn't use it, so just turn it off. XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)), XmNdragInitiatorProtocolStyle, XmDRAG_NONE, XmNdragReceiverProtocolStyle, XmDRAG_NONE, NULL); #endif #if 0 //~ for debugging XSynchronize(display, True); XSetErrorHandler(&xErrorHandler); #endif fullScreen = gFalse; remoteAtom = None; remoteViewer = NULL; remoteWin = None; getResources(); viewers = new GList(); } void XPDFApp::getResources() { XPDFAppResources resources; XColor xcol, xcol2; Colormap colormap; XtGetApplicationResources(appShell, &resources, xResources, nXResources, NULL, 0); geometry = resources.geometry ? new GString(resources.geometry) : (GString *)NULL; title = resources.title ? new GString(resources.title) : (GString *)NULL; installCmap = (GBool)resources.installCmap; rgbCubeSize = resources.rgbCubeSize; reverseVideo = (GBool)resources.reverseVideo; if (reverseVideo) { paperRGB[0] = paperRGB[1] = paperRGB[2] = 0; paperPixel = BlackPixel(display, screenNum); } else { paperRGB[0] = paperRGB[1] = paperRGB[2] = 0xff; paperPixel = WhitePixel(display, screenNum); } XtVaGetValues(appShell, XmNcolormap, &colormap, NULL); if (resources.paperColor) { if (XAllocNamedColor(display, colormap, resources.paperColor, &xcol, &xcol2)) { paperRGB[0] = xcol.red >> 8; paperRGB[1] = xcol.green >> 8; paperRGB[2] = xcol.blue >> 8; paperPixel = xcol.pixel; } else { error(errIO, -1, "Couldn't allocate color '{0:s}'", resources.paperColor); } } if (XAllocNamedColor(display, colormap, resources.matteColor, &xcol, &xcol2)) { mattePixel = xcol.pixel; } else { mattePixel = paperPixel; } if (XAllocNamedColor(display, colormap, resources.fullScreenMatteColor, &xcol, &xcol2)) { fullScreenMattePixel = xcol.pixel; } else { fullScreenMattePixel = paperPixel; } initialZoom = resources.initialZoom ? new GString(resources.initialZoom) : (GString *)NULL; } XPDFApp::~XPDFApp() { deleteGList(viewers, XPDFViewer); if (geometry) { delete geometry; } if (title) { delete title; } if (initialZoom) { delete initialZoom; } } XPDFViewer *XPDFApp::open(GString *fileName, int page, GString *ownerPassword, GString *userPassword) { XPDFViewer *viewer; viewer = new XPDFViewer(this, fileName, page, NULL, fullScreen, ownerPassword, userPassword); if (!viewer->isOk()) { delete viewer; return NULL; } if (remoteAtom != None) { remoteViewer = viewer; remoteWin = viewer->getWindow(); XtAddEventHandler(remoteWin, PropertyChangeMask, False, &remoteMsgCbk, this); XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime); } viewers->append(viewer); return viewer; } XPDFViewer *XPDFApp::openAtDest(GString *fileName, GString *dest, GString *ownerPassword, GString *userPassword) { XPDFViewer *viewer; viewer = new XPDFViewer(this, fileName, 1, dest, fullScreen, ownerPassword, userPassword); if (!viewer->isOk()) { delete viewer; return NULL; } if (remoteAtom != None) { remoteViewer = viewer; remoteWin = viewer->getWindow(); XtAddEventHandler(remoteWin, PropertyChangeMask, False, &remoteMsgCbk, this); XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime); } viewers->append(viewer); return viewer; } XPDFViewer *XPDFApp::reopen(XPDFViewer *viewer, PDFDoc *doc, int page, GBool fullScreenA) { int i; for (i = 0; i < viewers->getLength(); ++i) { if (((XPDFViewer *)viewers->get(i)) == viewer) { viewers->del(i); delete viewer; } } viewer = new XPDFViewer(this, doc, page, NULL, fullScreenA); if (!viewer->isOk()) { delete viewer; return NULL; } if (remoteAtom != None) { remoteViewer = viewer; remoteWin = viewer->getWindow(); XtAddEventHandler(remoteWin, PropertyChangeMask, False, &remoteMsgCbk, this); XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime); } viewers->append(viewer); return viewer; } void XPDFApp::close(XPDFViewer *viewer, GBool closeLast) { int i; if (viewers->getLength() == 1) { if (viewer != (XPDFViewer *)viewers->get(0)) { return; } if (closeLast) { quit(); } else { viewer->clear(); } } else { for (i = 0; i < viewers->getLength(); ++i) { if (((XPDFViewer *)viewers->get(i)) == viewer) { viewers->del(i); if (remoteAtom != None && remoteViewer == viewer) { remoteViewer = (XPDFViewer *)viewers->get(viewers->getLength() - 1); remoteWin = remoteViewer->getWindow(); XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime); } delete viewer; return; } } } } void XPDFApp::quit() { if (remoteAtom != None) { XSetSelectionOwner(display, remoteAtom, None, CurrentTime); } while (viewers->getLength() > 0) { delete (XPDFViewer *)viewers->del(0); } #if HAVE_XTAPPSETEXITFLAG XtAppSetExitFlag(appContext); #else exit(0); #endif } void XPDFApp::run() { XtAppMainLoop(appContext); } void XPDFApp::setRemoteName(char *remoteName) { remoteAtom = XInternAtom(display, remoteName, False); remoteXWin = XGetSelectionOwner(display, remoteAtom); } GBool XPDFApp::remoteServerRunning() { return remoteXWin != None; } void XPDFApp::remoteExec(char *cmd) { char cmd2[remoteCmdSize]; int n; n = strlen(cmd); if (n > remoteCmdSize - 2) { n = remoteCmdSize - 2; } memcpy(cmd2, cmd, n); cmd2[n] = '\n'; cmd2[n+1] = '\0'; XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)cmd2, n + 2); XFlush(display); } void XPDFApp::remoteOpen(GString *fileName, int page, GBool raise) { char cmd[remoteCmdSize]; sprintf(cmd, "openFileAtPage(%.200s,%d)\n", fileName->getCString(), page); if (raise) { strcat(cmd, "raise\n"); } XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1); XFlush(display); } void XPDFApp::remoteOpenAtDest(GString *fileName, GString *dest, GBool raise) { char cmd[remoteCmdSize]; sprintf(cmd, "openFileAtDest(%.200s,%.256s)\n", fileName->getCString(), dest->getCString()); if (raise) { strcat(cmd, "raise\n"); } XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1); XFlush(display); } void XPDFApp::remoteReload(GBool raise) { char cmd[remoteCmdSize]; strcpy(cmd, "reload\n"); if (raise) { strcat(cmd, "raise\n"); } XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1); XFlush(display); } void XPDFApp::remoteRaise() { XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)"raise\n", 7); XFlush(display); } void XPDFApp::remoteQuit() { XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8, PropModeReplace, (Guchar *)"quit\n", 6); XFlush(display); } void XPDFApp::remoteMsgCbk(Widget widget, XtPointer ptr, XEvent *event, Boolean *cont) { XPDFApp *app = (XPDFApp *)ptr; char *cmd, *p0, *p1; Atom type; int format; Gulong size, remain; GString *cmdStr; if (event->xproperty.atom != app->remoteAtom) { *cont = True; return; } *cont = False; if (XGetWindowProperty(app->display, XtWindow(app->remoteWin), app->remoteAtom, 0, remoteCmdSize/4, True, app->remoteAtom, &type, &format, &size, &remain, (Guchar **)&cmd) != Success) { return; } if (!cmd) { return; } p0 = cmd; while (*p0 && (p1 = strchr(p0, '\n'))) { cmdStr = new GString(p0, p1 - p0); app->remoteViewer->execCmd(cmdStr, NULL); delete cmdStr; p0 = p1 + 1; } XFree((XPointer)cmd); } xpdf-3.04/xpdf/Outline.cc0000644000076400007640000000757412341430012014605 0ustar dereknderekn//======================================================================== // // Outline.cc // // Copyright 2002-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "gmem.h" #include "GString.h" #include "GList.h" #include "Error.h" #include "Link.h" #include "TextString.h" #include "Outline.h" //------------------------------------------------------------------------ Outline::Outline(Object *outlineObj, XRef *xref) { Object first, last; items = NULL; if (!outlineObj->isDict()) { return; } outlineObj->dictLookupNF("First", &first); outlineObj->dictLookupNF("Last", &last); if (first.isRef() && last.isRef()) { items = OutlineItem::readItemList(&first, &last, NULL, xref); } first.free(); last.free(); } Outline::~Outline() { if (items) { deleteGList(items, OutlineItem); } } //------------------------------------------------------------------------ OutlineItem::OutlineItem(Object *itemRefA, Dict *dict, OutlineItem *parentA, XRef *xrefA) { Object obj1; xref = xrefA; title = NULL; action = NULL; kids = NULL; parent = parentA; if (dict->lookup("Title", &obj1)->isString()) { title = new TextString(obj1.getString()); } obj1.free(); if (!dict->lookup("Dest", &obj1)->isNull()) { action = LinkAction::parseDest(&obj1); } else { obj1.free(); if (!dict->lookup("A", &obj1)->isNull()) { action = LinkAction::parseAction(&obj1); } } obj1.free(); itemRefA->copy(&itemRef); dict->lookupNF("First", &firstRef); dict->lookupNF("Last", &lastRef); dict->lookupNF("Next", &nextRef); startsOpen = gFalse; if (dict->lookup("Count", &obj1)->isInt()) { if (obj1.getInt() > 0) { startsOpen = gTrue; } } obj1.free(); } OutlineItem::~OutlineItem() { close(); if (title) { delete title; } if (action) { delete action; } itemRef.free(); firstRef.free(); lastRef.free(); nextRef.free(); } GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef, OutlineItem *parentA, XRef *xrefA) { GList *items; OutlineItem *item, *sibling; Object obj; Object *p; OutlineItem *ancestor; int i; items = new GList(); if (!firstItemRef->isRef() || !lastItemRef->isRef()) { return items; } p = firstItemRef; do { if (!p->fetch(xrefA, &obj)->isDict()) { obj.free(); break; } 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) { break; } p = &item->nextRef; if (!p->isRef()) { break; } } while (p); return items; } void OutlineItem::open() { if (!kids) { kids = readItemList(&firstRef, &lastRef, this, xref); } } void OutlineItem::close() { if (kids) { deleteGList(kids, OutlineItem); kids = NULL; } } Unicode *OutlineItem::getTitle() { return title ? title->getUnicode() : (Unicode *)NULL; } int OutlineItem::getTitleLength() { return title ? title->getLength() : 0; } xpdf-3.04/xpdf/XPDFCore.h0000644000076400007640000001742712341430012014400 0ustar dereknderekn//======================================================================== // // XPDFCore.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef XPDFCORE_H #define XPDFCORE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #define Object XtObject #include #undef Object #include "gtypes.h" #include "gfile.h" // for time_t #include "SplashTypes.h" #include "PDFCore.h" class GString; class BaseStream; class PDFDoc; class LinkAction; //------------------------------------------------------------------------ #define xMaxRGBCube 6 // max size of RGB color cube //------------------------------------------------------------------------ // callbacks //------------------------------------------------------------------------ typedef void (*XPDFUpdateCbk)(void *data, GString *fileName, int pageNum, int numPages, const char *linkLabel); typedef void (*XPDFActionCbk)(void *data, char *action); typedef void (*XPDFKeyPressCbk)(void *data, KeySym key, Guint modifiers, XEvent *event); typedef void (*XPDFMouseCbk)(void *data, XEvent *event); //------------------------------------------------------------------------ // XPDFCore //------------------------------------------------------------------------ class XPDFCore: public PDFCore { public: // Create viewer core inside . XPDFCore(Widget shellA, Widget parentWidgetA, SplashColorPtr paperColorA, Gulong paperPixelA, Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA, GBool installCmap, int rgbCubeSizeA); ~XPDFCore(); //----- loadFile / displayPage / displayDest // Load a new file. Returns pdfOk or error code. virtual int loadFile(GString *fileName, GString *ownerPassword = NULL, GString *userPassword = NULL); // Load a new file, via a Stream instead of a file name. Returns // pdfOk or error code. virtual int loadFile(BaseStream *stream, GString *ownerPassword = NULL, GString *userPassword = NULL); // Load an already-created PDFDoc object. virtual void loadDoc(PDFDoc *docA); // Resize the window to fit page of the current document. void resizeToPage(int pg); // Update the display, given the specified parameters. virtual void update(int topPageA, int scrollXA, int scrollYA, double zoomA, int rotateA, GBool force, GBool addToHist, GBool adjustScrollX); //----- page/position changes virtual GBool gotoNextPage(int inc, GBool top); virtual GBool gotoPrevPage(int dec, GBool top, GBool bottom); virtual GBool goForward(); virtual GBool goBackward(); void startPan(int wx, int wy); void endPan(int wx, int wy); //----- selection void startSelection(int wx, int wy); void endSelection(int wx, int wy); void copySelection(); //----- hyperlinks void doAction(LinkAction *action); LinkAction *getLinkAction() { return linkAction; } GString *mungeURL(GString *url); //----- find virtual GBool find(char *s, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly); virtual GBool findU(Unicode *u, int len, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly); //----- simple modal dialogs GBool doQuestionDialog(const char *title, GString *msg); void doInfoDialog(const char *title, GString *msg); void doErrorDialog(const char *title, GString *msg); //----- password dialog virtual GString *getPassword(); //----- misc access Widget getWidget() { return scrolledWin; } Widget getDrawAreaWidget() { return drawArea; } virtual void setBusyCursor(GBool busy); Cursor getBusyCursor() { return busyCursor; } void takeFocus(); void enableHyperlinks(GBool on) { hyperlinksEnabled = on; } GBool getHyperlinksEnabled() { return hyperlinksEnabled; } void enableSelect(GBool on) { selectEnabled = on; } void setUpdateCbk(XPDFUpdateCbk cbk, void *data) { updateCbk = cbk; updateCbkData = data; } void setActionCbk(XPDFActionCbk cbk, void *data) { actionCbk = cbk; actionCbkData = data; } void setKeyPressCbk(XPDFKeyPressCbk cbk, void *data) { keyPressCbk = cbk; keyPressCbkData = data; } void setMouseCbk(XPDFMouseCbk cbk, void *data) { mouseCbk = cbk; mouseCbkData = data; } GBool getFullScreen() { return fullScreen; } private: virtual GBool checkForNewFile(); //----- hyperlinks void runCommand(GString *cmdFmt, GString *arg); //----- selection static Boolean convertSelectionCbk(Widget widget, Atom *selection, Atom *target, Atom *type, XtPointer *value, unsigned long *length, int *format); //----- GUI code void setupX(GBool installCmap, int rgbCubeSizeA); void initWindow(); static void hScrollChangeCbk(Widget widget, XtPointer ptr, XtPointer callData); static void hScrollDragCbk(Widget widget, XtPointer ptr, XtPointer callData); static void vScrollChangeCbk(Widget widget, XtPointer ptr, XtPointer callData); static void vScrollDragCbk(Widget widget, XtPointer ptr, XtPointer callData); static void resizeCbk(Widget widget, XtPointer ptr, XtPointer callData); static void redrawCbk(Widget widget, XtPointer ptr, XtPointer callData); static void inputCbk(Widget widget, XtPointer ptr, XtPointer callData); virtual PDFCoreTile *newTile(int xDestA, int yDestA); virtual void updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc, int width, int height, GBool composited); virtual void redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc, int xDest, int yDest, int width, int height, GBool composited); virtual void updateScrollbars(); void setCursor(Cursor cursor); GBool doDialog(int type, GBool hasCancel, const char *title, GString *msg); static void dialogOkCbk(Widget widget, XtPointer ptr, XtPointer callData); static void dialogCancelCbk(Widget widget, XtPointer ptr, XtPointer callData); void initPasswordDialog(); static void passwordTextVerifyCbk(Widget widget, XtPointer ptr, XtPointer callData); static void passwordOkCbk(Widget widget, XtPointer ptr, XtPointer callData); static void passwordCancelCbk(Widget widget, XtPointer ptr, XtPointer callData); Gulong paperPixel; Gulong mattePixel; //~unimp: move fullScreen into PDFCore? GBool fullScreen; Display *display; int screenNum; Visual *visual; Colormap colormap; Guint depth; // visual depth GBool trueColor; // set if using a TrueColor visual int rDiv, gDiv, bDiv; // RGB right shifts (for TrueColor) int rShift, gShift, bShift; // RGB left shifts (for TrueColor) int rgbCubeSize; // size of color cube (for non-TrueColor) Gulong // color cube (for non-TrueColor) colors[xMaxRGBCube * xMaxRGBCube * xMaxRGBCube]; Widget shell; // top-level shell containing the widget Widget parentWidget; // parent widget (not created by XPDFCore) Widget scrolledWin; Widget hScrollBar; Widget vScrollBar; Widget drawAreaFrame; Widget drawArea; Cursor busyCursor, linkCursor, selectCursor; Cursor currentCursor; GC drawAreaGC; // GC for blitting into drawArea static GString *currentSelection; // selected text static XPDFCore *currentSelectionOwner; static Atom targetsAtom; GBool panning; int panMX, panMY; time_t modTime; // last modification time of PDF file LinkAction *linkAction; // mouse cursor is over this link XPDFUpdateCbk updateCbk; void *updateCbkData; XPDFActionCbk actionCbk; void *actionCbkData; XPDFKeyPressCbk keyPressCbk; void *keyPressCbkData; XPDFMouseCbk mouseCbk; void *mouseCbkData; GBool hyperlinksEnabled; GBool selectEnabled; int dialogDone; Widget passwordDialog; Widget passwordText; GString *password; }; #endif xpdf-3.04/xpdf/leftArrow.xbm0000644000076400007640000000030112341430012015311 0ustar dereknderekn#define leftArrow_width 8 #define leftArrow_height 15 static unsigned char leftArrow_bits[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80}; xpdf-3.04/xpdf/Gfx.cc0000644000076400007640000037425012341430012013710 0ustar dereknderekn//======================================================================== // // Gfx.cc // // Copyright 1996-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include "gmem.h" #include "GString.h" #include "GList.h" #include "GlobalParams.h" #include "CharTypes.h" #include "Object.h" #include "PDFDoc.h" #include "Array.h" #include "Dict.h" #include "Stream.h" #include "Lexer.h" #include "Parser.h" #include "GfxFont.h" #include "GfxState.h" #include "OutputDev.h" #include "Page.h" #include "Annot.h" #include "OptionalContent.h" #include "Error.h" #include "TextString.h" #include "Gfx.h" // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // constants //------------------------------------------------------------------------ // Max recursive depth for a function shading fill. #define functionMaxDepth 6 // Max delta allowed in any color component for a function shading fill. #define functionColorDelta (dblToCol(1 / 256.0)) // Max number of splits along the t axis for an axial shading fill. #define axialMaxSplits 256 // Max delta allowed in any color component for an axial shading fill. #define axialColorDelta (dblToCol(1 / 256.0)) // Max number of splits along the t axis for a radial shading fill. #define radialMaxSplits 256 // Max delta allowed in any color component for a radial shading fill. #define radialColorDelta (dblToCol(1 / 256.0)) // Max recursive depth for a Gouraud triangle shading fill. #define gouraudMaxDepth 6 // Max delta allowed in any color component for a Gouraud triangle // shading fill. #define gouraudColorDelta (dblToCol(1 / 256.0)) // Max recursive depth for a patch mesh shading fill. #define patchMaxDepth 6 // Max delta allowed in any color component for a patch mesh shading // fill. #define patchColorDelta (dblToCol(1 / 256.0)) // Max errors (undefined operator, wrong number of args) allowed before // giving up on a content stream. #define contentStreamErrorLimit 500 //------------------------------------------------------------------------ // Operator table //------------------------------------------------------------------------ #ifdef _WIN32 // this works around a bug in the VC7 compiler # pragma optimize("",off) #endif Operator Gfx::opTab[] = { {"\"", 3, {tchkNum, tchkNum, tchkString}, &Gfx::opMoveSetShowText}, {"'", 1, {tchkString}, &Gfx::opMoveShowText}, {"B", 0, {tchkNone}, &Gfx::opFillStroke}, {"B*", 0, {tchkNone}, &Gfx::opEOFillStroke}, {"BDC", 2, {tchkName, tchkProps}, &Gfx::opBeginMarkedContent}, {"BI", 0, {tchkNone}, &Gfx::opBeginImage}, {"BMC", 1, {tchkName}, &Gfx::opBeginMarkedContent}, {"BT", 0, {tchkNone}, &Gfx::opBeginText}, {"BX", 0, {tchkNone}, &Gfx::opBeginIgnoreUndef}, {"CS", 1, {tchkName}, &Gfx::opSetStrokeColorSpace}, {"DP", 2, {tchkName, tchkProps}, &Gfx::opMarkPoint}, {"Do", 1, {tchkName}, &Gfx::opXObject}, {"EI", 0, {tchkNone}, &Gfx::opEndImage}, {"EMC", 0, {tchkNone}, &Gfx::opEndMarkedContent}, {"ET", 0, {tchkNone}, &Gfx::opEndText}, {"EX", 0, {tchkNone}, &Gfx::opEndIgnoreUndef}, {"F", 0, {tchkNone}, &Gfx::opFill}, {"G", 1, {tchkNum}, &Gfx::opSetStrokeGray}, {"ID", 0, {tchkNone}, &Gfx::opImageData}, {"J", 1, {tchkInt}, &Gfx::opSetLineCap}, {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opSetStrokeCMYKColor}, {"M", 1, {tchkNum}, &Gfx::opSetMiterLimit}, {"MP", 1, {tchkName}, &Gfx::opMarkPoint}, {"Q", 0, {tchkNone}, &Gfx::opRestore}, {"RG", 3, {tchkNum, tchkNum, tchkNum}, &Gfx::opSetStrokeRGBColor}, {"S", 0, {tchkNone}, &Gfx::opStroke}, {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opSetStrokeColor}, {"SCN", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN}, &Gfx::opSetStrokeColorN}, {"T*", 0, {tchkNone}, &Gfx::opTextNextLine}, {"TD", 2, {tchkNum, tchkNum}, &Gfx::opTextMoveSet}, {"TJ", 1, {tchkArray}, &Gfx::opShowSpaceText}, {"TL", 1, {tchkNum}, &Gfx::opSetTextLeading}, {"Tc", 1, {tchkNum}, &Gfx::opSetCharSpacing}, {"Td", 2, {tchkNum, tchkNum}, &Gfx::opTextMove}, {"Tf", 2, {tchkName, tchkNum}, &Gfx::opSetFont}, {"Tj", 1, {tchkString}, &Gfx::opShowText}, {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opSetTextMatrix}, {"Tr", 1, {tchkInt}, &Gfx::opSetTextRender}, {"Ts", 1, {tchkNum}, &Gfx::opSetTextRise}, {"Tw", 1, {tchkNum}, &Gfx::opSetWordSpacing}, {"Tz", 1, {tchkNum}, &Gfx::opSetHorizScaling}, {"W", 0, {tchkNone}, &Gfx::opClip}, {"W*", 0, {tchkNone}, &Gfx::opEOClip}, {"b", 0, {tchkNone}, &Gfx::opCloseFillStroke}, {"b*", 0, {tchkNone}, &Gfx::opCloseEOFillStroke}, {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opCurveTo}, {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opConcat}, {"cs", 1, {tchkName}, &Gfx::opSetFillColorSpace}, {"d", 2, {tchkArray, tchkNum}, &Gfx::opSetDash}, {"d0", 2, {tchkNum, tchkNum}, &Gfx::opSetCharWidth}, {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opSetCacheDevice}, {"f", 0, {tchkNone}, &Gfx::opFill}, {"f*", 0, {tchkNone}, &Gfx::opEOFill}, {"g", 1, {tchkNum}, &Gfx::opSetFillGray}, {"gs", 1, {tchkName}, &Gfx::opSetExtGState}, {"h", 0, {tchkNone}, &Gfx::opClosePath}, {"i", 1, {tchkNum}, &Gfx::opSetFlat}, {"j", 1, {tchkInt}, &Gfx::opSetLineJoin}, {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opSetFillCMYKColor}, {"l", 2, {tchkNum, tchkNum}, &Gfx::opLineTo}, {"m", 2, {tchkNum, tchkNum}, &Gfx::opMoveTo}, {"n", 0, {tchkNone}, &Gfx::opEndPath}, {"q", 0, {tchkNone}, &Gfx::opSave}, {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opRectangle}, {"rg", 3, {tchkNum, tchkNum, tchkNum}, &Gfx::opSetFillRGBColor}, {"ri", 1, {tchkName}, &Gfx::opSetRenderingIntent}, {"s", 0, {tchkNone}, &Gfx::opCloseStroke}, {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opSetFillColor}, {"scn", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN}, &Gfx::opSetFillColorN}, {"sh", 1, {tchkName}, &Gfx::opShFill}, {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opCurveTo1}, {"w", 1, {tchkNum}, &Gfx::opSetLineWidth}, {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, &Gfx::opCurveTo2}, }; #ifdef _WIN32 // this works around a bug in the VC7 compiler # pragma optimize("",on) #endif #define numOps (sizeof(opTab) / sizeof(Operator)) //------------------------------------------------------------------------ // GfxResources //------------------------------------------------------------------------ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) { Object obj1, obj2; Ref r; if (resDict) { // build font dictionary fonts = NULL; resDict->lookupNF("Font", &obj1); if (obj1.isRef()) { obj1.fetch(xref, &obj2); if (obj2.isDict()) { r = obj1.getRef(); fonts = new GfxFontDict(xref, &r, obj2.getDict()); } obj2.free(); } else if (obj1.isDict()) { fonts = new GfxFontDict(xref, NULL, obj1.getDict()); } obj1.free(); // get XObject dictionary resDict->lookup("XObject", &xObjDict); // get color space dictionary resDict->lookup("ColorSpace", &colorSpaceDict); // get pattern dictionary resDict->lookup("Pattern", &patternDict); // get shading dictionary resDict->lookup("Shading", &shadingDict); // get graphics state parameter dictionary resDict->lookup("ExtGState", &gStateDict); // get properties dictionary resDict->lookup("Properties", &propsDict); } else { fonts = NULL; xObjDict.initNull(); colorSpaceDict.initNull(); patternDict.initNull(); shadingDict.initNull(); gStateDict.initNull(); propsDict.initNull(); } next = nextA; } GfxResources::~GfxResources() { if (fonts) { delete fonts; } xObjDict.free(); colorSpaceDict.free(); patternDict.free(); shadingDict.free(); gStateDict.free(); propsDict.free(); } GfxFont *GfxResources::lookupFont(char *name) { GfxFont *font; GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->fonts) { if ((font = resPtr->fonts->lookup(name))) { return font; } } } error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name); return NULL; } GfxFont *GfxResources::lookupFontByRef(Ref ref) { GfxFont *font; GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->fonts) { if ((font = resPtr->fonts->lookupByRef(ref))) { return font; } } } error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen); return NULL; } GBool GfxResources::lookupXObject(const char *name, Object *obj) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->xObjDict.isDict()) { if (!resPtr->xObjDict.dictLookup(name, obj)->isNull()) return gTrue; obj->free(); } } error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name); return gFalse; } GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->xObjDict.isDict()) { if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull()) return gTrue; obj->free(); } } error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name); return gFalse; } void GfxResources::lookupColorSpace(const char *name, Object *obj) { GfxResources *resPtr; //~ should also test for G, RGB, and CMYK - but only in inline images (?) if (!strcmp(name, "DeviceGray") || !strcmp(name, "DeviceRGB") || !strcmp(name, "DeviceCMYK")) { obj->initNull(); return; } for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->colorSpaceDict.isDict()) { if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) { return; } obj->free(); } } obj->initNull(); } GfxPattern *GfxResources::lookupPattern(const char *name ) { GfxResources *resPtr; GfxPattern *pattern; Object objRef, obj; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->patternDict.isDict()) { if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) { resPtr->patternDict.dictLookupNF(name, &objRef); pattern = GfxPattern::parse(&objRef, &obj ); objRef.free(); obj.free(); return pattern; } obj.free(); } } error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name); return NULL; } GfxShading *GfxResources::lookupShading(const char *name ) { GfxResources *resPtr; GfxShading *shading; Object obj; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->shadingDict.isDict()) { if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) { shading = GfxShading::parse(&obj ); obj.free(); return shading; } obj.free(); } } error(errSyntaxError, -1, "Unknown shading '{0:s}'", name); return NULL; } GBool GfxResources::lookupGState(const char *name, Object *obj) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->gStateDict.isDict()) { if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) { return gTrue; } obj->free(); } } error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name); return gFalse; } GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->propsDict.isDict()) { if (!resPtr->propsDict.dictLookupNF(name, obj)->isNull()) { return gTrue; } obj->free(); } } error(errSyntaxError, -1, "Properties '{0:s}' is unknown", name); return gFalse; } //------------------------------------------------------------------------ // Gfx //------------------------------------------------------------------------ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict, double hDPI, double vDPI, PDFRectangle *box, PDFRectangle *cropBox, int rotate, GBool (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA) { int i; doc = docA; xref = doc->getXRef(); subPage = gFalse; printCommands = globalParams->getPrintCommands(); // start the resource stack res = new GfxResources(xref, resDict, NULL); // initialize out = outA; state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown()); fontChanged = gFalse; clip = clipNone; ignoreUndef = 0; out->startPage(pageNum, state); out->setDefaultCTM(state->getCTM()); out->updateAll(state); for (i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } formDepth = 0; textClipBBoxEmpty = gTrue; markedContentStack = new GList(); ocState = gTrue; parser = NULL; contentStreamStack = new GList(); abortCheckCbk = abortCheckCbkA; abortCheckCbkData = abortCheckCbkDataA; // set crop box if (cropBox) { state->moveTo(cropBox->x1, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y2); state->lineTo(cropBox->x1, cropBox->y2); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } } Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict, PDFRectangle *box, PDFRectangle *cropBox, GBool (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA) { int i; doc = docA; xref = doc->getXRef(); subPage = gTrue; printCommands = globalParams->getPrintCommands(); // start the resource stack res = new GfxResources(xref, resDict, NULL); // initialize out = outA; state = new GfxState(72, 72, box, 0, gFalse); fontChanged = gFalse; clip = clipNone; ignoreUndef = 0; for (i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } formDepth = 0; textClipBBoxEmpty = gTrue; markedContentStack = new GList(); ocState = gTrue; parser = NULL; contentStreamStack = new GList(); abortCheckCbk = abortCheckCbkA; abortCheckCbkData = abortCheckCbkDataA; // set crop box if (cropBox) { state->moveTo(cropBox->x1, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y2); state->lineTo(cropBox->x1, cropBox->y2); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } } Gfx::~Gfx() { if (!subPage) { out->endPage(); } while (state->hasSaves()) { restoreState(); } delete state; while (res) { popResources(); } deleteGList(markedContentStack, GfxMarkedContent); delete contentStreamStack; } void Gfx::display(Object *objRef, GBool topLevel) { Object obj1, obj2; int i; objRef->fetch(xref, &obj1); if (obj1.isArray()) { for (i = 0; i < obj1.arrayGetLength(); ++i) { obj1.arrayGetNF(i, &obj2); if (checkForContentStreamLoop(&obj2)) { obj2.free(); obj1.free(); return; } obj2.free(); } for (i = 0; i < obj1.arrayGetLength(); ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isStream()) { error(errSyntaxError, -1, "Invalid object type for content stream"); obj2.free(); obj1.free(); return; } obj2.free(); } contentStreamStack->append(&obj1); } else if (obj1.isStream()) { if (checkForContentStreamLoop(objRef)) { obj1.free(); return; } contentStreamStack->append(objRef); } else { error(errSyntaxError, -1, "Invalid object type for content stream"); obj1.free(); return; } parser = new Parser(xref, new Lexer(xref, &obj1), gFalse); go(topLevel); delete parser; parser = NULL; contentStreamStack->del(contentStreamStack->getLength() - 1); obj1.free(); } // If is already on contentStreamStack, i.e., if there is a loop // in the content streams, report an error, and return true. GBool Gfx::checkForContentStreamLoop(Object *ref) { Object *objPtr; Object obj1; int i, j; if (ref->isRef()) { for (i = 0; i < contentStreamStack->getLength(); ++i) { objPtr = (Object *)contentStreamStack->get(i); if (objPtr->isRef()) { if (ref->getRefNum() == objPtr->getRefNum() && ref->getRefGen() == objPtr->getRefGen()) { error(errSyntaxError, -1, "Loop in content streams"); return gTrue; } } else if (objPtr->isArray()) { for (j = 0; j < objPtr->arrayGetLength(); ++j) { objPtr->arrayGetNF(j, &obj1); if (obj1.isRef()) { if (ref->getRefNum() == obj1.getRefNum() && ref->getRefGen() == obj1.getRefGen()) { error(errSyntaxError, -1, "Loop in content streams"); obj1.free(); return gTrue; } } obj1.free(); } } } } return gFalse; } void Gfx::go(GBool topLevel) { Object obj; Object args[maxArgs]; int numArgs, i; int lastAbortCheck, errCount; // scan a sequence of objects updateLevel = 1; // make sure even empty pages trigger a call to dump() lastAbortCheck = 0; errCount = 0; numArgs = 0; parser->getObj(&obj); while (!obj.isEOF()) { // got a command - execute it if (obj.isCmd()) { if (printCommands) { obj.print(stdout); for (i = 0; i < numArgs; ++i) { printf(" "); args[i].print(stdout); } printf("\n"); fflush(stdout); } if (!execOp(&obj, args, numArgs)) { ++errCount; } obj.free(); for (i = 0; i < numArgs; ++i) args[i].free(); numArgs = 0; // periodically update display if (++updateLevel >= 20000) { out->dump(); updateLevel = 0; } // check for an abort if (abortCheckCbk) { if (updateLevel - lastAbortCheck > 10) { if ((*abortCheckCbk)(abortCheckCbkData)) { break; } lastAbortCheck = updateLevel; } } // check for too many errors if (errCount > contentStreamErrorLimit) { error(errSyntaxError, -1, "Too many errors - giving up on this content stream"); break; } // got an argument - save it } else if (numArgs < maxArgs) { args[numArgs++] = obj; // too many arguments - something is wrong } else { error(errSyntaxError, getPos(), "Too many args in content stream"); if (printCommands) { printf("throwing away arg: "); obj.print(stdout); printf("\n"); fflush(stdout); } obj.free(); } // grab the next object parser->getObj(&obj); } obj.free(); // args at end with no command if (numArgs > 0) { error(errSyntaxError, getPos(), "Leftover args in content stream"); if (printCommands) { printf("%d leftovers:", numArgs); for (i = 0; i < numArgs; ++i) { printf(" "); args[i].print(stdout); } printf("\n"); fflush(stdout); } for (i = 0; i < numArgs; ++i) args[i].free(); } // update display if (topLevel && updateLevel > 0) { out->dump(); } } // Returns true if successful, false on error. GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) { Operator *op; char *name; Object *argPtr; int i; // find operator name = cmd->getCmd(); if (!(op = findOp(name))) { if (ignoreUndef > 0) { return gTrue; } error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name); return gFalse; } // type check args argPtr = args; if (op->numArgs >= 0) { if (numArgs < op->numArgs) { error(errSyntaxError, getPos(), "Too few ({0:d}) args to '{1:s}' operator", numArgs, name); return gFalse; } if (numArgs > op->numArgs) { #if 0 error(errSyntaxWarning, getPos(), "Too many ({0:d}) args to '{1:s}' operator", numArgs, name); #endif argPtr += numArgs - op->numArgs; numArgs = op->numArgs; } } else { if (numArgs > -op->numArgs) { error(errSyntaxError, getPos(), "Too many ({0:d}) args to '{1:s}' operator", numArgs, name); return gFalse; } } for (i = 0; i < numArgs; ++i) { if (!checkArg(&argPtr[i], op->tchk[i])) { error(errSyntaxError, getPos(), "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})", i, name, argPtr[i].getTypeName()); return gFalse; } } // do it (this->*op->func)(argPtr, numArgs); return gTrue; } Operator *Gfx::findOp(char *name) { int a, b, m, cmp; a = -1; b = numOps; cmp = 0; // make gcc happy // invariant: opTab[a] < name < opTab[b] while (b - a > 1) { m = (a + b) / 2; cmp = strcmp(opTab[m].name, name); if (cmp < 0) a = m; else if (cmp > 0) b = m; else a = b = m; } if (cmp != 0) return NULL; return &opTab[a]; } GBool Gfx::checkArg(Object *arg, TchkType type) { switch (type) { case tchkBool: return arg->isBool(); case tchkInt: return arg->isInt(); case tchkNum: return arg->isNum(); case tchkString: return arg->isString(); case tchkName: return arg->isName(); case tchkArray: return arg->isArray(); case tchkProps: return arg->isDict() || arg->isName(); case tchkSCN: return arg->isNum() || arg->isName(); case tchkNone: return gFalse; } return gFalse; } GFileOffset Gfx::getPos() { return parser ? parser->getPos() : -1; } //------------------------------------------------------------------------ // graphics state operators //------------------------------------------------------------------------ void Gfx::opSave(Object args[], int numArgs) { saveState(); } void Gfx::opRestore(Object args[], int numArgs) { restoreState(); } void Gfx::opConcat(Object args[], int numArgs) { state->concatCTM(args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); out->updateCTM(state, args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); fontChanged = gTrue; } void Gfx::opSetDash(Object args[], int numArgs) { Array *a; int length; Object obj; double *dash; int i; a = args[0].getArray(); length = a->getLength(); if (length == 0) { dash = NULL; } else { dash = (double *)gmallocn(length, sizeof(double)); for (i = 0; i < length; ++i) { dash[i] = a->get(i, &obj)->getNum(); obj.free(); } } state->setLineDash(dash, length, args[1].getNum()); out->updateLineDash(state); } void Gfx::opSetFlat(Object args[], int numArgs) { state->setFlatness((int)args[0].getNum()); out->updateFlatness(state); } void Gfx::opSetLineJoin(Object args[], int numArgs) { state->setLineJoin(args[0].getInt()); out->updateLineJoin(state); } void Gfx::opSetLineCap(Object args[], int numArgs) { state->setLineCap(args[0].getInt()); out->updateLineCap(state); } void Gfx::opSetMiterLimit(Object args[], int numArgs) { state->setMiterLimit(args[0].getNum()); out->updateMiterLimit(state); } void Gfx::opSetLineWidth(Object args[], int numArgs) { state->setLineWidth(args[0].getNum()); out->updateLineWidth(state); } void Gfx::opSetExtGState(Object args[], int numArgs) { Object obj1, obj2, obj3, objRef3, obj4, obj5; Object args2[2]; GfxBlendMode mode; GBool haveFillOP; Function *funcs[4]; GfxColor backdropColor; GBool haveBackdropColor; GfxColorSpace *blendingColorSpace; GBool alpha, isolated, knockout; double opac; int i; if (!res->lookupGState(args[0].getName(), &obj1)) { return; } if (!obj1.isDict()) { error(errSyntaxError, getPos(), "ExtGState '{0:s}' is wrong type", args[0].getName()); obj1.free(); return; } if (printCommands) { printf(" gfx state dict: "); obj1.print(); printf("\n"); } // parameters that are also set by individual PDF operators if (obj1.dictLookup("LW", &obj2)->isNum()) { opSetLineWidth(&obj2, 1); } obj2.free(); if (obj1.dictLookup("LC", &obj2)->isInt()) { opSetLineCap(&obj2, 1); } obj2.free(); if (obj1.dictLookup("LJ", &obj2)->isInt()) { opSetLineJoin(&obj2, 1); } obj2.free(); if (obj1.dictLookup("ML", &obj2)->isNum()) { opSetMiterLimit(&obj2, 1); } obj2.free(); if (obj1.dictLookup("D", &obj2)->isArray() && obj2.arrayGetLength() == 2) { obj2.arrayGet(0, &args2[0]); obj2.arrayGet(1, &args2[1]); if (args2[0].isArray() && args2[1].isNum()) { opSetDash(args2, 2); } args2[0].free(); args2[1].free(); } obj2.free(); if (obj1.dictLookup("FL", &obj2)->isNum()) { opSetFlat(&obj2, 1); } obj2.free(); // font if (obj1.dictLookup("Font", &obj2)->isArray() && obj2.arrayGetLength() == 2) { obj2.arrayGetNF(0, &obj3); obj2.arrayGetNF(1, &obj4); if (obj3.isRef() && obj4.isNum()) { doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum()); } obj3.free(); obj4.free(); } obj2.free(); // transparency support: blend mode, fill/stroke opacity if (!obj1.dictLookup("BM", &obj2)->isNull()) { if (state->parseBlendMode(&obj2, &mode)) { state->setBlendMode(mode); out->updateBlendMode(state); } else { error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState"); } } obj2.free(); if (obj1.dictLookup("ca", &obj2)->isNum()) { opac = obj2.getNum(); state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); out->updateFillOpacity(state); } obj2.free(); if (obj1.dictLookup("CA", &obj2)->isNum()) { opac = obj2.getNum(); state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); out->updateStrokeOpacity(state); } obj2.free(); // fill/stroke overprint, overprint mode if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) { state->setFillOverprint(obj2.getBool()); out->updateFillOverprint(state); } obj2.free(); if (obj1.dictLookup("OP", &obj2)->isBool()) { state->setStrokeOverprint(obj2.getBool()); out->updateStrokeOverprint(state); if (!haveFillOP) { state->setFillOverprint(obj2.getBool()); out->updateFillOverprint(state); } } obj2.free(); if (obj1.dictLookup("OPM", &obj2)->isInt()) { state->setOverprintMode(obj2.getInt()); out->updateOverprintMode(state); } obj2.free(); // stroke adjust if (obj1.dictLookup("SA", &obj2)->isBool()) { state->setStrokeAdjust(obj2.getBool()); out->updateStrokeAdjust(state); } obj2.free(); // transfer function if (obj1.dictLookup("TR2", &obj2)->isNull()) { obj2.free(); obj1.dictLookup("TR", &obj2); } if (obj2.isName("Default") || obj2.isName("Identity")) { funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL; state->setTransfer(funcs); out->updateTransfer(state); } else if (obj2.isArray() && obj2.arrayGetLength() == 4) { for (i = 0; i < 4; ++i) { obj2.arrayGet(i, &obj3); funcs[i] = Function::parse(&obj3); obj3.free(); if (!funcs[i]) { break; } } if (i == 4) { state->setTransfer(funcs); out->updateTransfer(state); } } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) { if ((funcs[0] = Function::parse(&obj2))) { funcs[1] = funcs[2] = funcs[3] = NULL; state->setTransfer(funcs); out->updateTransfer(state); } } else if (!obj2.isNull()) { error(errSyntaxError, getPos(), "Invalid transfer function in ExtGState"); } obj2.free(); // soft mask if (!obj1.dictLookup("SMask", &obj2)->isNull()) { if (obj2.isName("None")) { out->clearSoftMask(state); } else if (obj2.isDict()) { if (obj2.dictLookup("S", &obj3)->isName("Alpha")) { alpha = gTrue; } else { // "Luminosity" alpha = gFalse; } obj3.free(); funcs[0] = NULL; if (!obj2.dictLookup("TR", &obj3)->isNull()) { if (obj3.isName("Default") || obj3.isName("Identity")) { funcs[0] = NULL; } else { funcs[0] = Function::parse(&obj3); if (funcs[0]->getInputSize() != 1 || funcs[0]->getOutputSize() != 1) { error(errSyntaxError, getPos(), "Invalid transfer function in soft mask in ExtGState"); delete funcs[0]; funcs[0] = NULL; } } } obj3.free(); if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) { for (i = 0; i < gfxColorMaxComps; ++i) { backdropColor.c[i] = 0; } for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) { obj3.arrayGet(i, &obj4); if (obj4.isNum()) { backdropColor.c[i] = dblToCol(obj4.getNum()); } obj4.free(); } } obj3.free(); if (obj2.dictLookup("G", &obj3)->isStream()) { if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) { blendingColorSpace = NULL; isolated = knockout = gFalse; if (!obj4.dictLookup("CS", &obj5)->isNull()) { blendingColorSpace = GfxColorSpace::parse(&obj5 ); } obj5.free(); if (obj4.dictLookup("I", &obj5)->isBool()) { isolated = obj5.getBool(); } obj5.free(); if (obj4.dictLookup("K", &obj5)->isBool()) { knockout = obj5.getBool(); } obj5.free(); if (!haveBackdropColor) { if (blendingColorSpace) { blendingColorSpace->getDefaultColor(&backdropColor); } else { //~ need to get the parent or default color space (?) for (i = 0; i < gfxColorMaxComps; ++i) { backdropColor.c[i] = 0; } } } obj2.dictLookupNF("G", &objRef3); doSoftMask(&obj3, &objRef3, alpha, blendingColorSpace, isolated, knockout, funcs[0], &backdropColor); objRef3.free(); if (funcs[0]) { delete funcs[0]; } } else { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group"); } obj4.free(); } else { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group"); } obj3.free(); } else if (!obj2.isNull()) { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState"); } } obj2.free(); obj1.free(); } void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, Function *transferFunc, GfxColor *backdropColor) { Dict *dict, *resDict; double m[6], bbox[4]; Object obj1, obj2; int i; // check for excessive recursion if (formDepth > 20) { return; } // get stream dict dict = str->streamGetDict(); // check form type dict->lookup("FormType", &obj1); if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { error(errSyntaxError, getPos(), "Unknown form type"); } obj1.free(); // get bounding box dict->lookup("BBox", &obj1); if (!obj1.isArray()) { obj1.free(); error(errSyntaxError, getPos(), "Bad form bounding box"); return; } for (i = 0; i < 4; ++i) { obj1.arrayGet(i, &obj2); bbox[i] = obj2.getNum(); obj2.free(); } obj1.free(); // get matrix dict->lookup("Matrix", &obj1); if (obj1.isArray()) { for (i = 0; i < 6; ++i) { obj1.arrayGet(i, &obj2); m[i] = obj2.getNum(); obj2.free(); } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } obj1.free(); // get resources dict->lookup("Resources", &obj1); resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL; // draw it ++formDepth; drawForm(strRef, resDict, m, bbox, gTrue, gTrue, blendingColorSpace, isolated, knockout, alpha, transferFunc, backdropColor); --formDepth; if (blendingColorSpace) { delete blendingColorSpace; } obj1.free(); } void Gfx::opSetRenderingIntent(Object args[], int numArgs) { } //------------------------------------------------------------------------ // color operators //------------------------------------------------------------------------ void Gfx::opSetFillGray(Object args[], int numArgs) { GfxColor color; state->setFillPattern(NULL); state->setFillColorSpace(GfxColorSpace::create(csDeviceGray)); out->updateFillColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeGray(Object args[], int numArgs) { GfxColor color; state->setStrokePattern(NULL); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray)); out->updateStrokeColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { GfxColor color; int i; state->setFillPattern(NULL); state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK)); out->updateFillColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { GfxColor color; int i; state->setStrokePattern(NULL); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK)); out->updateStrokeColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillRGBColor(Object args[], int numArgs) { GfxColor color; int i; state->setFillPattern(NULL); state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB)); out->updateFillColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { GfxColor color; int i; state->setStrokePattern(NULL); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB)); out->updateStrokeColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillColorSpace(Object args[], int numArgs) { Object obj; GfxColorSpace *colorSpace; GfxColor color; state->setFillPattern(NULL); res->lookupColorSpace(args[0].getName(), &obj); if (obj.isNull()) { colorSpace = GfxColorSpace::parse(&args[0] ); } else { colorSpace = GfxColorSpace::parse(&obj ); } obj.free(); if (colorSpace) { state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); colorSpace->getDefaultColor(&color); state->setFillColor(&color); out->updateFillColor(state); } else { error(errSyntaxError, getPos(), "Bad color space (fill)"); } } void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { Object obj; GfxColorSpace *colorSpace; GfxColor color; state->setStrokePattern(NULL); res->lookupColorSpace(args[0].getName(), &obj); if (obj.isNull()) { colorSpace = GfxColorSpace::parse(&args[0] ); } else { colorSpace = GfxColorSpace::parse(&obj ); } obj.free(); if (colorSpace) { state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); colorSpace->getDefaultColor(&color); state->setStrokeColor(&color); out->updateStrokeColor(state); } else { error(errSyntaxError, getPos(), "Bad color space (stroke)"); } } void Gfx::opSetFillColor(Object args[], int numArgs) { GfxColor color; int i; if (numArgs != state->getFillColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'sc' command"); return; } state->setFillPattern(NULL); for (i = 0; i < numArgs; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeColor(Object args[], int numArgs) { GfxColor color; int i; if (numArgs != state->getStrokeColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SC' command"); return; } state->setStrokePattern(NULL); for (i = 0; i < numArgs; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillColorN(Object args[], int numArgs) { GfxColor color; GfxPattern *pattern; int i; if (state->getFillColorSpace()->getMode() == csPattern) { if (numArgs > 1) { if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() || numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace()) ->getUnder()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'scn' command"); return; } for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } } state->setFillColor(&color); out->updateFillColor(state); } if (args[numArgs-1].isName() && (pattern = res->lookupPattern(args[numArgs-1].getName() ))) { state->setFillPattern(pattern); } } else { if (numArgs != state->getFillColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'scn' command"); return; } state->setFillPattern(NULL); for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } } state->setFillColor(&color); out->updateFillColor(state); } } void Gfx::opSetStrokeColorN(Object args[], int numArgs) { GfxColor color; GfxPattern *pattern; int i; if (state->getStrokeColorSpace()->getMode() == csPattern) { if (numArgs > 1) { if (!((GfxPatternColorSpace *)state->getStrokeColorSpace()) ->getUnder() || numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace()) ->getUnder()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command"); return; } for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } } state->setStrokeColor(&color); out->updateStrokeColor(state); } if (args[numArgs-1].isName() && (pattern = res->lookupPattern(args[numArgs-1].getName() ))) { state->setStrokePattern(pattern); } } else { if (numArgs != state->getStrokeColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command"); return; } state->setStrokePattern(NULL); for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } } state->setStrokeColor(&color); out->updateStrokeColor(state); } } //------------------------------------------------------------------------ // path segment operators //------------------------------------------------------------------------ void Gfx::opMoveTo(Object args[], int numArgs) { state->moveTo(args[0].getNum(), args[1].getNum()); } void Gfx::opLineTo(Object args[], int numArgs) { if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in lineto"); return; } state->lineTo(args[0].getNum(), args[1].getNum()); } void Gfx::opCurveTo(Object args[], int numArgs) { double x1, y1, x2, y2, x3, y3; if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in curveto"); return; } x1 = args[0].getNum(); y1 = args[1].getNum(); x2 = args[2].getNum(); y2 = args[3].getNum(); x3 = args[4].getNum(); y3 = args[5].getNum(); state->curveTo(x1, y1, x2, y2, x3, y3); } void Gfx::opCurveTo1(Object args[], int numArgs) { double x1, y1, x2, y2, x3, y3; if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in curveto1"); return; } x1 = state->getCurX(); y1 = state->getCurY(); x2 = args[0].getNum(); y2 = args[1].getNum(); x3 = args[2].getNum(); y3 = args[3].getNum(); state->curveTo(x1, y1, x2, y2, x3, y3); } void Gfx::opCurveTo2(Object args[], int numArgs) { double x1, y1, x2, y2, x3, y3; if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in curveto2"); return; } x1 = args[0].getNum(); y1 = args[1].getNum(); x2 = args[2].getNum(); y2 = args[3].getNum(); x3 = x2; y3 = y2; state->curveTo(x1, y1, x2, y2, x3, y3); } void Gfx::opRectangle(Object args[], int numArgs) { double x, y, w, h; x = args[0].getNum(); y = args[1].getNum(); w = args[2].getNum(); h = args[3].getNum(); state->moveTo(x, y); state->lineTo(x + w, y); state->lineTo(x + w, y + h); state->lineTo(x, y + h); state->closePath(); } void Gfx::opClosePath(Object args[], int numArgs) { if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in closepath"); return; } state->closePath(); } //------------------------------------------------------------------------ // path painting operators //------------------------------------------------------------------------ void Gfx::opEndPath(Object args[], int numArgs) { doEndPath(); } void Gfx::opStroke(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in stroke"); return; } if (state->isPath()) { if (ocState) { if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opCloseStroke(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in closepath/stroke"); return; } if (state->isPath()) { state->closePath(); if (ocState) { if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opFill(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in fill"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(gFalse); } else { out->fill(state); } } } doEndPath(); } void Gfx::opEOFill(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in eofill"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(gTrue); } else { out->eoFill(state); } } } doEndPath(); } void Gfx::opFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in fill/stroke"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(gFalse); } else { out->fill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opCloseFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke"); return; } if (state->isPath()) { state->closePath(); if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(gFalse); } else { out->fill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opEOFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in eofill/stroke"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(gTrue); } else { out->eoFill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opCloseEOFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke"); return; } if (state->isPath()) { state->closePath(); if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(gTrue); } else { out->eoFill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::doPatternFill(GBool eoFill) { GfxPattern *pattern; // this is a bit of a kludge -- patterns can be really slow, so we // skip them if we're only doing text extraction, since they almost // certainly don't contain any text if (!out->needNonText()) { return; } if (!(pattern = state->getFillPattern())) { return; } switch (pattern->getType()) { case 1: doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse); break; case 2: doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse); break; default: error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", pattern->getType()); break; } } void Gfx::doPatternStroke() { GfxPattern *pattern; // this is a bit of a kludge -- patterns can be really slow, so we // skip them if we're only doing text extraction, since they almost // certainly don't contain any text if (!out->needNonText()) { return; } if (!(pattern = state->getStrokePattern())) { return; } switch (pattern->getType()) { case 1: doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse); break; case 2: doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse); break; default: error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke", pattern->getType()); break; } } void Gfx::doPatternText() { GfxPattern *pattern; // this is a bit of a kludge -- patterns can be really slow, so we // skip them if we're only doing text extraction, since they almost // certainly don't contain any text if (!out->needNonText()) { return; } if (!(pattern = state->getFillPattern())) { return; } switch (pattern->getType()) { case 1: doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue); break; case 2: doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue); break; default: error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", pattern->getType()); break; } } void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { saveState(); out->setSoftMaskFromImageMask(state, ref, str, width, height, invert, inlineImg, interpolate); state->clearPath(); state->moveTo(0, 0); state->lineTo(1, 0); state->lineTo(1, 1); state->lineTo(0, 1); state->closePath(); doPatternFill(gTrue); restoreState(); } void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool stroke, GBool eoFill, GBool text) { GfxPatternColorSpace *patCS; GfxColorSpace *cs; GfxState *savedState; double xMin, yMin, xMax, yMax, x, y, x1, y1, t; double cxMin, cyMin, cxMax, cyMax; int xi0, yi0, xi1, yi1, xi, yi; double *ctm, *btm, *ptm; double bbox[4], m[6], ictm[6], m1[6], imb[6]; double det; double xstep, ystep; int i; // get color space patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace() : state->getFillColorSpace()); // construct a (pattern space) -> (current space) transform matrix ctm = state->getCTM(); btm = baseMatrix; ptm = tPat->getMatrix(); // iCTM = invert CTM det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; if (fabs(det) < 0.000001) { error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill"); return; } det = 1 / det; ictm[0] = ctm[3] * det; ictm[1] = -ctm[1] * det; ictm[2] = -ctm[2] * det; ictm[3] = ctm[0] * det; ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; // m1 = PTM * BTM = PTM * base transform matrix m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2]; m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3]; m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2]; m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3]; m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4]; m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5]; // m = m1 * iCTM = (PTM * BTM) * (iCTM) m[0] = m1[0] * ictm[0] + m1[1] * ictm[2]; m[1] = m1[0] * ictm[1] + m1[1] * ictm[3]; m[2] = m1[2] * ictm[0] + m1[3] * ictm[2]; m[3] = m1[2] * ictm[1] + m1[3] * ictm[3]; m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; // construct a (device space) -> (pattern space) transform matrix det = m1[0] * m1[3] - m1[1] * m1[2]; if (fabs(det) < 0.000001) { error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill"); return; } det = 1 / det; imb[0] = m1[3] * det; imb[1] = -m1[1] * det; imb[2] = -m1[2] * det; imb[3] = m1[0] * det; imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det; imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det; // save current graphics state savedState = saveStateStack(); // set underlying color space (for uncolored tiling patterns); set // various other parameters (stroke color, line width) to match // Adobe's behavior state->setFillPattern(NULL); state->setStrokePattern(NULL); if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) { state->setFillColorSpace(cs->copy()); out->updateFillColorSpace(state); state->setStrokeColorSpace(cs->copy()); out->updateStrokeColorSpace(state); state->setStrokeColor(state->getFillColor()); out->updateFillColor(state); out->updateStrokeColor(state); } else { state->setFillColorSpace(GfxColorSpace::create(csDeviceGray)); out->updateFillColorSpace(state); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray)); out->updateStrokeColorSpace(state); } if (!stroke) { state->setLineWidth(0); out->updateLineWidth(state); } // clip to current path if (stroke) { state->clipToStrokePath(); out->clipToStrokePath(state); } else if (!text) { state->clip(); if (eoFill) { out->eoClip(state); } else { out->clip(state); } } state->clearPath(); // get the clip region, check for empty state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax); if (cxMin > cxMax || cyMin > cyMax) { goto err; } // transform clip region bbox to pattern space xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4]; yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5]; x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4]; y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5]; if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4]; y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5]; if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4]; y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5]; if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } // draw the pattern //~ this should treat negative steps differently -- start at right/top //~ edge instead of left/bottom (?) bbox[0] = tPat->getBBox()[0]; bbox[1] = tPat->getBBox()[1]; bbox[2] = tPat->getBBox()[2]; bbox[3] = tPat->getBBox()[3]; if (bbox[0] > bbox[2]) { t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t; } if (bbox[1] > bbox[3]) { t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t; } xstep = fabs(tPat->getXStep()); ystep = fabs(tPat->getYStep()); xi0 = (int)ceil((xMin - bbox[2]) / xstep); xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1; yi0 = (int)ceil((yMin - bbox[3]) / ystep); yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1; for (i = 0; i < 4; ++i) { m1[i] = m[i]; } if (out->useTilingPatternFill()) { m1[4] = m[4]; m1[5] = m[5]; out->tilingPatternFill(state, this, tPat->getContentStreamRef(), tPat->getPaintType(), tPat->getResDict(), m1, bbox, xi0, yi0, xi1, yi1, xstep, ystep); } else { for (yi = yi0; yi < yi1; ++yi) { for (xi = xi0; xi < xi1; ++xi) { x = xi * xstep; y = yi * ystep; m1[4] = x * m[0] + y * m[2] + m[4]; m1[5] = x * m[1] + y * m[3] + m[5]; drawForm(tPat->getContentStreamRef(), tPat->getResDict(), m1, bbox); } } } // restore graphics state err: restoreStateStack(savedState); } void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool stroke, GBool eoFill, GBool text) { GfxShading *shading; GfxState *savedState; double *ctm, *btm, *ptm; double m[6], ictm[6], m1[6]; double xMin, yMin, xMax, yMax; double det; shading = sPat->getShading(); // save current graphics state savedState = saveStateStack(); // clip to current path if (stroke) { state->clipToStrokePath(); out->clipToStrokePath(state); } else if (!text) { state->clip(); if (eoFill) { out->eoClip(state); } else { out->clip(state); } } state->clearPath(); // construct a (pattern space) -> (current space) transform matrix ctm = state->getCTM(); btm = baseMatrix; ptm = sPat->getMatrix(); // iCTM = invert CTM det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; if (fabs(det) < 0.000001) { error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill"); return; } det = 1 / det; ictm[0] = ctm[3] * det; ictm[1] = -ctm[1] * det; ictm[2] = -ctm[2] * det; ictm[3] = ctm[0] * det; ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; // m1 = PTM * BTM = PTM * base transform matrix m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2]; m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3]; m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2]; m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3]; m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4]; m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5]; // m = m1 * iCTM = (PTM * BTM) * (iCTM) m[0] = m1[0] * ictm[0] + m1[1] * ictm[2]; m[1] = m1[0] * ictm[1] + m1[1] * ictm[3]; m[2] = m1[2] * ictm[0] + m1[3] * ictm[2]; m[3] = m1[2] * ictm[1] + m1[3] * ictm[3]; m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; // set the new matrix state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]); out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]); // clip to bbox if (shading->getHasBBox()) { shading->getBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } // set the color space state->setFillColorSpace(shading->getColorSpace()->copy()); out->updateFillColorSpace(state); // background color fill if (shading->getHasBackground()) { state->setFillColor(shading->getBackground()); out->updateFillColor(state); state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); out->fill(state); state->clearPath(); } #if 1 //~tmp: turn off anti-aliasing temporarily out->setInShading(gTrue); #endif // do shading type-specific operations switch (shading->getType()) { case 1: doFunctionShFill((GfxFunctionShading *)shading); break; case 2: doAxialShFill((GfxAxialShading *)shading); break; case 3: doRadialShFill((GfxRadialShading *)shading); break; case 4: case 5: doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); break; case 6: case 7: doPatchMeshShFill((GfxPatchMeshShading *)shading); break; } #if 1 //~tmp: turn off anti-aliasing temporarily out->setInShading(gFalse); #endif // restore graphics state restoreStateStack(savedState); } void Gfx::opShFill(Object args[], int numArgs) { GfxShading *shading; GfxState *savedState; double xMin, yMin, xMax, yMax; if (!out->needNonText()) { return; } if (!ocState) { return; } if (!(shading = res->lookupShading(args[0].getName() ))) { return; } // save current graphics state savedState = saveStateStack(); // clip to bbox if (shading->getHasBBox()) { shading->getBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } // set the color space state->setFillColorSpace(shading->getColorSpace()->copy()); out->updateFillColorSpace(state); #if 1 //~tmp: turn off anti-aliasing temporarily out->setInShading(gTrue); #endif // do shading type-specific operations switch (shading->getType()) { case 1: doFunctionShFill((GfxFunctionShading *)shading); break; case 2: doAxialShFill((GfxAxialShading *)shading); break; case 3: doRadialShFill((GfxRadialShading *)shading); break; case 4: case 5: doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); break; case 6: case 7: doPatchMeshShFill((GfxPatchMeshShading *)shading); break; } #if 1 //~tmp: turn off anti-aliasing temporarily out->setInShading(gFalse); #endif // restore graphics state restoreStateStack(savedState); delete shading; } void Gfx::doFunctionShFill(GfxFunctionShading *shading) { double x0, y0, x1, y1; GfxColor colors[4]; if (out->useShadedFills() && out->functionShadedFill(state, shading)) { return; } shading->getDomain(&x0, &y0, &x1, &y1); shading->getColor(x0, y0, &colors[0]); shading->getColor(x0, y1, &colors[1]); shading->getColor(x1, y0, &colors[2]); shading->getColor(x1, y1, &colors[3]); doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0); } void Gfx::doFunctionShFill1(GfxFunctionShading *shading, double x0, double y0, double x1, double y1, GfxColor *colors, int depth) { GfxColor fillColor; GfxColor color0M, color1M, colorM0, colorM1, colorMM; GfxColor colors2[4]; double *matrix; double xM, yM; int nComps, i, j; nComps = shading->getColorSpace()->getNComps(); matrix = shading->getMatrix(); // compare the four corner colors for (i = 0; i < 4; ++i) { for (j = 0; j < nComps; ++j) { if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) { break; } } if (j < nComps) { break; } } // center of the rectangle xM = 0.5 * (x0 + x1); yM = 0.5 * (y0 + y1); // the four corner colors are close (or we hit the recursive limit) // -- fill the rectangle; but require at least one subdivision // (depth==0) to avoid problems when the four outer corners of the // shaded region are the same color if ((i == 4 && depth > 0) || depth == functionMaxDepth) { // use the center color shading->getColor(xM, yM, &fillColor); state->setFillColor(&fillColor); out->updateFillColor(state); // fill the rectangle state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4], x0 * matrix[1] + y0 * matrix[3] + matrix[5]); state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4], x1 * matrix[1] + y0 * matrix[3] + matrix[5]); state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4], x1 * matrix[1] + y1 * matrix[3] + matrix[5]); state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4], x0 * matrix[1] + y1 * matrix[3] + matrix[5]); state->closePath(); out->fill(state); state->clearPath(); // the four corner colors are not close enough -- subdivide the // rectangle } else { // colors[0] colorM0 colors[2] // (x0,y0) (xM,y0) (x1,y0) // +----------+----------+ // | | | // | UL | UR | // color0M | colorMM | color1M // (x0,yM) +----------+----------+ (x1,yM) // | (xM,yM) | // | LL | LR | // | | | // +----------+----------+ // colors[1] colorM1 colors[3] // (x0,y1) (xM,y1) (x1,y1) shading->getColor(x0, yM, &color0M); shading->getColor(x1, yM, &color1M); shading->getColor(xM, y0, &colorM0); shading->getColor(xM, y1, &colorM1); shading->getColor(xM, yM, &colorMM); // upper-left sub-rectangle colors2[0] = colors[0]; colors2[1] = color0M; colors2[2] = colorM0; colors2[3] = colorMM; doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1); // lower-left sub-rectangle colors2[0] = color0M; colors2[1] = colors[1]; colors2[2] = colorMM; colors2[3] = colorM1; doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1); // upper-right sub-rectangle colors2[0] = colorM0; colors2[1] = colorMM; colors2[2] = colors[2]; colors2[3] = color1M; doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1); // lower-right sub-rectangle colors2[0] = colorMM; colors2[1] = colorM1; colors2[2] = color1M; colors2[3] = colors[3]; doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1); } } void Gfx::doAxialShFill(GfxAxialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, x1, y1; double dx, dy, mul; GBool dxdyZero, horiz; double tMin, tMax, t, tx, ty; double sMin, sMax, tmp; double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1; double t0, t1, tt; double ta[axialMaxSplits + 1]; int next[axialMaxSplits + 1]; GfxColor color0, color1; int nComps; int i, j, k; if (out->useShadedFills() && out->axialShadedFill(state, shading)) { return; } // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // compute min and max t values, based on the four corners of the // clip region bbox shading->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; dxdyZero = fabs(dx) < 0.01 && fabs(dy) < 0.01; horiz = fabs(dy) < fabs(dx); if (dxdyZero) { tMin = tMax = 0; } else { mul = 1 / (dx * dx + dy * dy); tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } if (tMin < 0 && !shading->getExtend0()) { tMin = 0; } if (tMax > 1 && !shading->getExtend1()) { tMax = 1; } } // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); // Traverse the t axis and do the shading. // // For each point (tx, ty) on the t axis, consider a line through // that point perpendicular to the t axis: // // x(s) = tx + s * -dy --> s = (x - tx) / -dy // y(s) = ty + s * dx --> s = (y - ty) / dx // // Then look at the intersection of this line with the bounding box // (xMin, yMin, xMax, yMax). For -1 < |dy/dx| < 1, look at the // intersection with yMin, yMax: // // s0 = (yMin - ty) / dx // s1 = (yMax - ty) / dx // // else look at the intersection with xMin, xMax: // // s0 = (xMin - tx) / -dy // s1 = (xMax - tx) / -dy // // Each filled polygon is bounded by two of these line segments // perpdendicular to the t axis. // // The t axis is bisected into smaller regions until the color // difference across a region is small enough, and then the region // is painted with a single color. // set up nComps = shading->getColorSpace()->getNComps(); ta[0] = tMin; next[0] = axialMaxSplits; ta[axialMaxSplits] = tMax; // compute the color at t = tMin if (tMin < 0) { tt = t0; } else if (tMin > 1) { tt = t1; } else { tt = t0 + (t1 - t0) * tMin; } shading->getColor(tt, &color0); // compute the coordinates of the point on the t axis at t = tMin; // then compute the intersection of the perpendicular line with the // bounding box tx = x0 + tMin * dx; ty = y0 + tMin * dy; if (dxdyZero) { sMin = sMax = 0; } else { if (horiz) { sMin = (yMin - ty) / dx; sMax = (yMax - ty) / dx; } else { sMin = (xMin - tx) / -dy; sMax = (xMax - tx) / -dy; } if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } } ux0 = tx - sMin * dy; uy0 = ty + sMin * dx; vx0 = tx - sMax * dy; vy0 = ty + sMax * dx; i = 0; while (i < axialMaxSplits) { // bisect until color difference is small enough or we hit the // bisection limit j = next[i]; while (j > i + 1) { if (ta[j] < 0) { tt = t0; } else if (ta[j] > 1) { tt = t1; } else { tt = t0 + (t1 - t0) * ta[j]; } // require at least two splits (to avoid problems where the // color doesn't change smoothly along the t axis) if (j - i <= axialMaxSplits / 4) { shading->getColor(tt, &color1); for (k = 0; k < nComps; ++k) { if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) { break; } } if (k == nComps) { break; } } k = (i + j) / 2; ta[k] = 0.5 * (ta[i] + ta[j]); next[i] = k; next[k] = j; j = k; } // use the average of the colors of the two sides of the region for (k = 0; k < nComps; ++k) { color0.c[k] = (color0.c[k] + color1.c[k]) / 2; } // compute the coordinates of the point on the t axis; then // compute the intersection of the perpendicular line with the // bounding box tx = x0 + ta[j] * dx; ty = y0 + ta[j] * dy; if (dxdyZero) { sMin = sMax = 0; } else { if (horiz) { sMin = (yMin - ty) / dx; sMax = (yMax - ty) / dx; } else { sMin = (xMin - tx) / -dy; sMax = (xMax - tx) / -dy; } if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } } ux1 = tx - sMin * dy; uy1 = ty + sMin * dx; vx1 = tx - sMax * dy; vy1 = ty + sMax * dx; // set the color state->setFillColor(&color0); out->updateFillColor(state); // fill the region state->moveTo(ux0, uy0); state->lineTo(vx0, vy0); state->lineTo(vx1, vy1); state->lineTo(ux1, uy1); state->closePath(); out->fill(state); state->clearPath(); // set up for next region ux0 = ux1; uy0 = uy1; vx0 = vx1; vy0 = vy1; color0 = color1; i = next[i]; } } void Gfx::doRadialShFill(GfxRadialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, r0, x1, y1, r1, t0, t1; int nComps; GfxColor colorA, colorB; double xa, ya, xb, yb, ra, rb; double ta, tb, sa, sb; double sMin, sMax, h; double sLeft, sRight, sTop, sBottom, sZero, sDiag; GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; GBool haveSMin, haveSMax; GBool enclosed; int ia, ib, k, n; double *ctm; double theta, alpha, angle, t; if (out->useShadedFills() && out->radialShadedFill(state, shading)) { return; } // get the shading info shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); t0 = shading->getDomain0(); t1 = shading->getDomain1(); nComps = shading->getColorSpace()->getNComps(); // Compute the point at which r(s) = 0; check for the enclosed // circles case; and compute the angles for the tangent lines. h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); if (h == 0) { enclosed = gTrue; theta = 0; // make gcc happy } else if (r1 - r0 == 0) { enclosed = gFalse; theta = 0; } else if (fabs(r1 - r0) >= h - 0.0001) { enclosed = gTrue; theta = 0; // make gcc happy } else { enclosed = gFalse; theta = asin((r1 - r0) / h); } if (enclosed) { alpha = 0; } else { alpha = atan2(y1 - y0, x1 - x0); } // compute the (possibly extended) s range state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); if (enclosed) { sMin = 0; sMax = 1; } else { // solve x(sLeft) + r(sLeft) = xMin if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); } else { sLeft = 0; // make gcc happy } // solve x(sRight) - r(sRight) = xMax if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); } else { sRight = 0; // make gcc happy } // solve y(sBottom) + r(sBottom) = yMin if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); } else { sBottom = 0; // make gcc happy } // solve y(sTop) - r(sTop) = yMax if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); } else { sTop = 0; // make gcc happy } // solve r(sZero) = 0 if ((haveSZero = fabs(r1 - r0) > 0.000001)) { sZero = -r0 / (r1 - r0); } else { sZero = 0; // make gcc happy } // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) if (haveSZero) { sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); } else { sDiag = 0; // make gcc happy } // compute sMin if (shading->getExtend0()) { sMin = 0; haveSMin = gFalse; if (x0 < x1 && haveSLeft && sLeft < 0) { sMin = sLeft; haveSMin = gTrue; } else if (x0 > x1 && haveSRight && sRight < 0) { sMin = sRight; haveSMin = gTrue; } if (y0 < y1 && haveSBottom && sBottom < 0) { if (!haveSMin || sBottom > sMin) { sMin = sBottom; haveSMin = gTrue; } } else if (y0 > y1 && haveSTop && sTop < 0) { if (!haveSMin || sTop > sMin) { sMin = sTop; haveSMin = gTrue; } } if (haveSZero && sZero <= 0) { if (!haveSMin || sZero > sMin) { sMin = sZero; } } } else { sMin = 0; } // compute sMax if (shading->getExtend1()) { sMax = 1; haveSMax = gFalse; if (x1 < x0 && haveSLeft && sLeft > 1) { sMax = sLeft; haveSMax = gTrue; } else if (x1 > x0 && haveSRight && sRight > 1) { sMax = sRight; haveSMax = gTrue; } if (y1 < y0 && haveSBottom && sBottom > 1) { if (!haveSMax || sBottom < sMax) { sMax = sBottom; haveSMax = gTrue; } } else if (y1 > y0 && haveSTop && sTop > 1) { if (!haveSMax || sTop < sMax) { sMax = sTop; haveSMax = gTrue; } } if (haveSZero && sDiag > 1) { if (!haveSMax || sDiag < sMax) { sMax = sDiag; } } } else { sMax = 1; } } // compute the number of steps into which circles must be divided to // achieve a curve flatness of 0.1 pixel in device space for the // largest circle (note that "device space" is 72 dpi when generating // PostScript, hence the relatively small 0.1 pixel accuracy) ctm = state->getCTM(); t = fabs(ctm[0]); if (fabs(ctm[1]) > t) { t = fabs(ctm[1]); } if (fabs(ctm[2]) > t) { t = fabs(ctm[2]); } if (fabs(ctm[3]) > t) { t = fabs(ctm[3]); } if (r0 > r1) { t *= r0; } else { t *= r1; } if (t < 1) { n = 3; } else { n = (int)(M_PI / acos(1 - 0.1 / t)); if (n < 3) { n = 3; } else if (n > 200) { n = 200; } } // setup for the start circle ia = 0; sa = sMin; ta = t0 + sa * (t1 - t0); xa = x0 + sa * (x1 - x0); ya = y0 + sa * (y1 - y0); ra = r0 + sa * (r1 - r0); if (ta < t0) { shading->getColor(t0, &colorA); } else if (ta > t1) { shading->getColor(t1, &colorA); } else { shading->getColor(ta, &colorA); } // fill the circles while (ia < radialMaxSplits) { // go as far along the t axis (toward t1) as we can, such that the // color difference is within the tolerance (radialColorDelta) -- // this uses bisection (between the current value, t, and t1), // limited to radialMaxSplits points along the t axis; require at // least one split to avoid problems when the innermost and // outermost colors are the same ib = radialMaxSplits; sb = sMax; tb = t0 + sb * (t1 - t0); if (tb < t0) { shading->getColor(t0, &colorB); } else if (tb > t1) { shading->getColor(t1, &colorB); } else { shading->getColor(tb, &colorB); } while (ib - ia > 1) { for (k = 0; k < nComps; ++k) { if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) { break; } } if (k == nComps && ib < radialMaxSplits) { break; } ib = (ia + ib) / 2; sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); tb = t0 + sb * (t1 - t0); if (tb < t0) { shading->getColor(t0, &colorB); } else if (tb > t1) { shading->getColor(t1, &colorB); } else { shading->getColor(tb, &colorB); } } // compute center and radius of the circle xb = x0 + sb * (x1 - x0); yb = y0 + sb * (y1 - y0); rb = r0 + sb * (r1 - r0); // use the average of the colors at the two circles for (k = 0; k < nComps; ++k) { colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2; } state->setFillColor(&colorA); out->updateFillColor(state); if (enclosed) { // construct path for first circle (counterclockwise) state->moveTo(xa + ra, ya); for (k = 1; k < n; ++k) { angle = ((double)k / (double)n) * 2 * M_PI; state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); // construct and append path for second circle (clockwise) state->moveTo(xb + rb, yb); for (k = 1; k < n; ++k) { angle = -((double)k / (double)n) * 2 * M_PI; state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); } state->closePath(); } else { // construct the first subpath (clockwise) state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), ya + ra * sin(alpha + theta + 0.5 * M_PI)); for (k = 0; k < n; ++k) { angle = alpha + theta + 0.5 * M_PI - ((double)k / (double)n) * (2 * theta + M_PI); state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); } for (k = 0; k < n; ++k) { angle = alpha - theta - 0.5 * M_PI + ((double)k / (double)n) * (2 * theta - M_PI); state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); // construct the second subpath (counterclockwise) state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), ya + ra * sin(alpha + theta + 0.5 * M_PI)); for (k = 0; k < n; ++k) { angle = alpha + theta + 0.5 * M_PI + ((double)k / (double)n) * (-2 * theta + M_PI); state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); } for (k = 0; k < n; ++k) { angle = alpha - theta - 0.5 * M_PI + ((double)k / (double)n) * (2 * theta + M_PI); state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); } // fill the path out->fill(state); state->clearPath(); // step to the next value of t ia = ib; sa = sb; ta = tb; xa = xb; ya = yb; ra = rb; colorA = colorB; } if (enclosed) { // extend the smaller circle if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) { if (r0 <= r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } shading->getColor(ta, &colorA); state->setFillColor(&colorA); out->updateFillColor(state); state->moveTo(xa + ra, ya); for (k = 1; k < n; ++k) { angle = ((double)k / (double)n) * 2 * M_PI; state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); out->fill(state); state->clearPath(); } // extend the larger circle if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) { if (r0 > r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } shading->getColor(ta, &colorA); state->setFillColor(&colorA); out->updateFillColor(state); state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); state->moveTo(xa + ra, ya); for (k = 1; k < n; ++k) { angle = ((double)k / (double)n) * 2 * M_PI; state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); out->fill(state); state->clearPath(); } } } void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) { double x0, y0, x1, y1, x2, y2; double color0[gfxColorMaxComps]; double color1[gfxColorMaxComps]; double color2[gfxColorMaxComps]; int i; for (i = 0; i < shading->getNTriangles(); ++i) { shading->getTriangle(i, &x0, &y0, color0, &x1, &y1, color1, &x2, &y2, color2); gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, shading, 0); } } void Gfx::gouraudFillTriangle(double x0, double y0, double *color0, double x1, double y1, double *color1, double x2, double y2, double *color2, GfxGouraudTriangleShading *shading, int depth) { double dx0, dy0, dx1, dy1, dx2, dy2; double x01, y01, x12, y12, x20, y20; double color01[gfxColorMaxComps]; double color12[gfxColorMaxComps]; double color20[gfxColorMaxComps]; GfxColor c0, c1, c2; int nComps, i; // recursion ends when: // (1) color difference is smaller than gouraudColorDelta; or // (2) triangles are smaller than 0.5 pixel (note that "device // space" is 72dpi when generating PostScript); or // (3) max recursion depth (gouraudMaxDepth) is hit. nComps = shading->getColorSpace()->getNComps(); shading->getColor(color0, &c0); shading->getColor(color1, &c1); shading->getColor(color2, &c2); for (i = 0; i < nComps; ++i) { if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta || abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) { break; } } state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0); state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1); state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2); if (i == nComps || depth == gouraudMaxDepth || (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 && fabs(dx1) < 0.5 && fabs(dy1) < 0.5 && fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) { state->setFillColor(&c0); out->updateFillColor(state); state->moveTo(x0, y0); state->lineTo(x1, y1); state->lineTo(x2, y2); state->closePath(); out->fill(state); state->clearPath(); } else { x01 = 0.5 * (x0 + x1); y01 = 0.5 * (y0 + y1); x12 = 0.5 * (x1 + x2); y12 = 0.5 * (y1 + y2); x20 = 0.5 * (x2 + x0); y20 = 0.5 * (y2 + y0); for (i = 0; i < shading->getNComps(); ++i) { color01[i] = 0.5 * (color0[i] + color1[i]); color12[i] = 0.5 * (color1[i] + color2[i]); color20[i] = 0.5 * (color2[i] + color0[i]); } gouraudFillTriangle(x0, y0, color0, x01, y01, color01, x20, y20, color20, shading, depth + 1); gouraudFillTriangle(x01, y01, color01, x1, y1, color1, x12, y12, color12, shading, depth + 1); gouraudFillTriangle(x01, y01, color01, x12, y12, color12, x20, y20, color20, shading, depth + 1); gouraudFillTriangle(x20, y20, color20, x12, y12, color12, x2, y2, color2, shading, depth + 1); } } void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) { int start, i; if (shading->getNPatches() > 128) { start = 3; } else if (shading->getNPatches() > 64) { start = 2; } else if (shading->getNPatches() > 16) { start = 1; } else { start = 0; } for (i = 0; i < shading->getNPatches(); ++i) { fillPatch(shading->getPatch(i), shading, start); } } void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) { GfxPatch patch00, patch01, patch10, patch11; GfxColor c00, c01, c10, c11; double xx[4][8], yy[4][8]; double xxm, yym; int nComps, i; nComps = shading->getColorSpace()->getNComps(); shading->getColor(patch->color[0][0], &c00); shading->getColor(patch->color[0][1], &c01); shading->getColor(patch->color[1][0], &c10); shading->getColor(patch->color[1][1], &c11); for (i = 0; i < nComps; ++i) { if (abs(c00.c[i] - c01.c[i]) > patchColorDelta || abs(c01.c[i] - c11.c[i]) > patchColorDelta || abs(c11.c[i] - c10.c[i]) > patchColorDelta || abs(c10.c[i] - c00.c[i]) > patchColorDelta) { break; } } if (i == nComps || depth == patchMaxDepth) { state->setFillColor(&c00); out->updateFillColor(state); state->moveTo(patch->x[0][0], patch->y[0][0]); state->curveTo(patch->x[0][1], patch->y[0][1], patch->x[0][2], patch->y[0][2], patch->x[0][3], patch->y[0][3]); state->curveTo(patch->x[1][3], patch->y[1][3], patch->x[2][3], patch->y[2][3], patch->x[3][3], patch->y[3][3]); state->curveTo(patch->x[3][2], patch->y[3][2], patch->x[3][1], patch->y[3][1], patch->x[3][0], patch->y[3][0]); state->curveTo(patch->x[2][0], patch->y[2][0], patch->x[1][0], patch->y[1][0], patch->x[0][0], patch->y[0][0]); state->closePath(); out->fill(state); state->clearPath(); } else { for (i = 0; i < 4; ++i) { xx[i][0] = patch->x[i][0]; yy[i][0] = patch->y[i][0]; xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]); yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]); xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]); yym = 0.5 * (patch->y[i][1] + patch->y[i][2]); xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]); yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]); xx[i][2] = 0.5 * (xx[i][1] + xxm); yy[i][2] = 0.5 * (yy[i][1] + yym); xx[i][5] = 0.5 * (xxm + xx[i][6]); yy[i][5] = 0.5 * (yym + yy[i][6]); xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]); yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]); xx[i][7] = patch->x[i][3]; yy[i][7] = patch->y[i][3]; } for (i = 0; i < 4; ++i) { patch00.x[0][i] = xx[0][i]; patch00.y[0][i] = yy[0][i]; patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]); patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]); xxm = 0.5 * (xx[1][i] + xx[2][i]); yym = 0.5 * (yy[1][i] + yy[2][i]); patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]); patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]); patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm); patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym); patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]); patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]); patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]); patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]); patch10.x[0][i] = patch00.x[3][i]; patch10.y[0][i] = patch00.y[3][i]; patch10.x[3][i] = xx[3][i]; patch10.y[3][i] = yy[3][i]; } for (i = 4; i < 8; ++i) { patch01.x[0][i-4] = xx[0][i]; patch01.y[0][i-4] = yy[0][i]; patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]); patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]); xxm = 0.5 * (xx[1][i] + xx[2][i]); yym = 0.5 * (yy[1][i] + yy[2][i]); patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]); patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]); patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm); patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym); patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]); patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]); patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]); patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]); patch11.x[0][i-4] = patch01.x[3][i-4]; patch11.y[0][i-4] = patch01.y[3][i-4]; patch11.x[3][i-4] = xx[3][i]; patch11.y[3][i-4] = yy[3][i]; } for (i = 0; i < shading->getNComps(); ++i) { patch00.color[0][0][i] = patch->color[0][0][i]; patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] + patch->color[0][1][i]); patch01.color[0][0][i] = patch00.color[0][1][i]; patch01.color[0][1][i] = patch->color[0][1][i]; patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] + patch->color[1][1][i]); patch11.color[0][1][i] = patch01.color[1][1][i]; patch11.color[1][1][i] = patch->color[1][1][i]; patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] + patch->color[1][0][i]); patch10.color[1][1][i] = patch11.color[1][0][i]; patch10.color[1][0][i] = patch->color[1][0][i]; patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] + patch->color[0][0][i]); patch00.color[1][0][i] = patch10.color[0][0][i]; patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] + patch01.color[1][1][i]); patch01.color[1][0][i] = patch00.color[1][1][i]; patch11.color[0][0][i] = patch00.color[1][1][i]; patch10.color[0][1][i] = patch00.color[1][1][i]; } fillPatch(&patch00, shading, depth + 1); fillPatch(&patch10, shading, depth + 1); fillPatch(&patch01, shading, depth + 1); fillPatch(&patch11, shading, depth + 1); } } void Gfx::doEndPath() { if (state->isCurPt() && clip != clipNone) { state->clip(); if (clip == clipNormal) { out->clip(state); } else { out->eoClip(state); } } clip = clipNone; state->clearPath(); } //------------------------------------------------------------------------ // path clipping operators //------------------------------------------------------------------------ void Gfx::opClip(Object args[], int numArgs) { clip = clipNormal; } void Gfx::opEOClip(Object args[], int numArgs) { clip = clipEO; } //------------------------------------------------------------------------ // text object operators //------------------------------------------------------------------------ void Gfx::opBeginText(Object args[], int numArgs) { state->setTextMat(1, 0, 0, 1, 0, 0); state->textMoveTo(0, 0); out->updateTextMat(state); out->updateTextPos(state); fontChanged = gTrue; textClipBBoxEmpty = gTrue; } void Gfx::opEndText(Object args[], int numArgs) { out->endTextObject(state); } //------------------------------------------------------------------------ // text state operators //------------------------------------------------------------------------ void Gfx::opSetCharSpacing(Object args[], int numArgs) { state->setCharSpace(args[0].getNum()); out->updateCharSpace(state); } void Gfx::opSetFont(Object args[], int numArgs) { doSetFont(res->lookupFont(args[0].getName()), args[1].getNum()); } void Gfx::doSetFont(GfxFont *font, double size) { if (!font) { state->setFont(NULL, 0); return; } if (printCommands) { printf(" font: tag=%s name='%s' %g\n", font->getTag()->getCString(), font->getName() ? font->getName()->getCString() : "???", size); fflush(stdout); } state->setFont(font, size); fontChanged = gTrue; } void Gfx::opSetTextLeading(Object args[], int numArgs) { state->setLeading(args[0].getNum()); } void Gfx::opSetTextRender(Object args[], int numArgs) { state->setRender(args[0].getInt()); out->updateRender(state); } void Gfx::opSetTextRise(Object args[], int numArgs) { state->setRise(args[0].getNum()); out->updateRise(state); } void Gfx::opSetWordSpacing(Object args[], int numArgs) { state->setWordSpace(args[0].getNum()); out->updateWordSpace(state); } void Gfx::opSetHorizScaling(Object args[], int numArgs) { state->setHorizScaling(args[0].getNum()); out->updateHorizScaling(state); fontChanged = gTrue; } //------------------------------------------------------------------------ // text positioning operators //------------------------------------------------------------------------ void Gfx::opTextMove(Object args[], int numArgs) { double tx, ty; tx = state->getLineX() + args[0].getNum(); ty = state->getLineY() + args[1].getNum(); state->textMoveTo(tx, ty); out->updateTextPos(state); } void Gfx::opTextMoveSet(Object args[], int numArgs) { double tx, ty; tx = state->getLineX() + args[0].getNum(); ty = args[1].getNum(); state->setLeading(-ty); ty += state->getLineY(); state->textMoveTo(tx, ty); out->updateTextPos(state); } void Gfx::opSetTextMatrix(Object args[], int numArgs) { state->setTextMat(args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); state->textMoveTo(0, 0); out->updateTextMat(state); out->updateTextPos(state); fontChanged = gTrue; } void Gfx::opTextNextLine(Object args[], int numArgs) { double tx, ty; tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateTextPos(state); } //------------------------------------------------------------------------ // text string operators //------------------------------------------------------------------------ void Gfx::opShowText(Object args[], int numArgs) { if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in show"); return; } if (fontChanged) { out->updateFont(state); fontChanged = gFalse; } if (ocState) { out->beginStringOp(state); doShowText(args[0].getString()); out->endStringOp(state); } else { doIncCharCount(args[0].getString()); } } void Gfx::opMoveShowText(Object args[], int numArgs) { double tx, ty; if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in move/show"); return; } if (fontChanged) { out->updateFont(state); fontChanged = gFalse; } tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateTextPos(state); if (ocState) { out->beginStringOp(state); doShowText(args[0].getString()); out->endStringOp(state); } else { doIncCharCount(args[0].getString()); } } void Gfx::opMoveSetShowText(Object args[], int numArgs) { double tx, ty; if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in move/set/show"); return; } if (fontChanged) { out->updateFont(state); fontChanged = gFalse; } state->setWordSpace(args[0].getNum()); state->setCharSpace(args[1].getNum()); tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateWordSpace(state); out->updateCharSpace(state); out->updateTextPos(state); if (ocState) { out->beginStringOp(state); doShowText(args[2].getString()); out->endStringOp(state); } else { doIncCharCount(args[2].getString()); } } void Gfx::opShowSpaceText(Object args[], int numArgs) { Array *a; Object obj; int wMode; int i; if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in show/space"); return; } if (fontChanged) { out->updateFont(state); fontChanged = gFalse; } if (ocState) { out->beginStringOp(state); wMode = state->getFont()->getWMode(); a = args[0].getArray(); for (i = 0; i < a->getLength(); ++i) { a->get(i, &obj); if (obj.isNum()) { if (wMode) { state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize()); } else { state->textShift(-obj.getNum() * 0.001 * state->getFontSize() * state->getHorizScaling(), 0); } out->updateTextShift(state, obj.getNum()); } else if (obj.isString()) { doShowText(obj.getString()); } else { error(errSyntaxError, getPos(), "Element of show/space array must be number or string"); } obj.free(); } out->endStringOp(state); } else { a = args[0].getArray(); for (i = 0; i < a->getLength(); ++i) { a->get(i, &obj); if (obj.isString()) { doIncCharCount(obj.getString()); } obj.free(); } } } void Gfx::doShowText(GString *s) { GfxFont *font; int wMode; double riseX, riseY; CharCode code; Unicode u[8]; double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy; double originX, originY, tOriginX, tOriginY; double x0, y0, x1, y1; double oldCTM[6], newCTM[6]; double *mat; Object charProcRef, charProc; Dict *resDict; Parser *oldParser; GfxState *savedState; char *p; int render; GBool patternFill; int len, n, uLen, nChars, nSpaces, i; font = state->getFont(); wMode = font->getWMode(); if (out->useDrawChar()) { out->beginString(state, s); } // if we're doing a pattern fill, set up clipping render = state->getRender(); if (!(render & 1) && state->getFillColorSpace()->getMode() == csPattern) { patternFill = gTrue; saveState(); // disable fill, enable clipping, leave stroke unchanged if ((render ^ (render >> 1)) & 1) { render = 5; } else { render = 7; } state->setRender(render); out->updateRender(state); } else { patternFill = gFalse; } state->textTransformDelta(0, state->getRise(), &riseX, &riseY); x0 = state->getCurX() + riseX; y0 = state->getCurY() + riseY; // handle a Type 3 char if (font->getType() == fontType3 && out->interpretType3Chars()) { mat = state->getCTM(); for (i = 0; i < 6; ++i) { oldCTM[i] = mat[i]; } mat = state->getTextMat(); newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2]; newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3]; newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2]; newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3]; mat = font->getFontMatrix(); newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2]; newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3]; newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2]; newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3]; newCTM[0] *= state->getFontSize(); newCTM[1] *= state->getFontSize(); newCTM[2] *= state->getFontSize(); newCTM[3] *= state->getFontSize(); newCTM[0] *= state->getHorizScaling(); newCTM[2] *= state->getHorizScaling(); curX = state->getCurX(); curY = state->getCurY(); oldParser = parser; p = s->getCString(); len = s->getLength(); while (len > 0) { n = font->getNextChar(p, len, &code, u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, &dx, &dy, &originX, &originY); dx = dx * state->getFontSize() + state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } dx *= state->getHorizScaling(); dy *= state->getFontSize(); state->textTransformDelta(dx, dy, &tdx, &tdy); state->transform(curX + riseX, curY + riseY, &x, &y); savedState = saveStateStack(); state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); //~ the CTM concat values here are wrong (but never used) out->updateCTM(state, 1, 0, 0, 1, 0, 0); state->transformDelta(dx, dy, &ddx, &ddy); if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy, code, u, uLen)) { ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef); charProcRef.fetch(xref, &charProc); if ((resDict = ((Gfx8BitFont *)font)->getResources())) { pushResources(resDict); } if (charProc.isStream()) { display(&charProcRef, gFalse); } else { error(errSyntaxError, getPos(), "Missing or bad Type3 CharProc entry"); } out->endType3Char(state); if (resDict) { popResources(); } charProc.free(); charProcRef.free(); } restoreStateStack(savedState); curX += tdx; curY += tdy; state->moveTo(curX, curY); p += n; len -= n; } parser = oldParser; } else if (out->useDrawChar()) { p = s->getCString(); len = s->getLength(); while (len > 0) { n = font->getNextChar(p, len, &code, u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, &dx, &dy, &originX, &originY); if (wMode) { dx *= state->getFontSize(); dy = dy * state->getFontSize() + state->getCharSpace(); if (n == 1 && *p == ' ') { dy += state->getWordSpace(); } } else { dx = dx * state->getFontSize() + state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } dx *= state->getHorizScaling(); dy *= state->getFontSize(); } state->textTransformDelta(dx, dy, &tdx, &tdy); originX *= state->getFontSize(); originY *= state->getFontSize(); state->textTransformDelta(originX, originY, &tOriginX, &tOriginY); out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY, tdx, tdy, tOriginX, tOriginY, code, n, u, uLen); state->shift(tdx, tdy); p += n; len -= n; } } else { dx = dy = 0; p = s->getCString(); len = s->getLength(); nChars = nSpaces = 0; while (len > 0) { n = font->getNextChar(p, len, &code, u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, &dx2, &dy2, &originX, &originY); dx += dx2; dy += dy2; if (n == 1 && *p == ' ') { ++nSpaces; } ++nChars; p += n; len -= n; } if (wMode) { dx *= state->getFontSize(); dy = dy * state->getFontSize() + nChars * state->getCharSpace() + nSpaces * state->getWordSpace(); } else { dx = dx * state->getFontSize() + nChars * state->getCharSpace() + nSpaces * state->getWordSpace(); dx *= state->getHorizScaling(); dy *= state->getFontSize(); } state->textTransformDelta(dx, dy, &tdx, &tdy); out->drawString(state, s); state->shift(tdx, tdy); } if (out->useDrawChar()) { out->endString(state); } if (patternFill) { out->saveTextPos(state); // tell the OutputDev to do the clipping out->endTextObject(state); // set up a clipping bbox so doPatternText will work -- assume // that the text bounding box does not extend past the baseline in // any direction by more than twice the font size x1 = state->getCurX() + riseX; y1 = state->getCurY() + riseY; if (x0 > x1) { x = x0; x0 = x1; x1 = x; } if (y0 > y1) { y = y0; y0 = y1; y1 = y; } state->textTransformDelta(0, state->getFontSize(), &dx, &dy); state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2); dx = fabs(dx); dx2 = fabs(dx2); if (dx2 > dx) { dx = dx2; } dy = fabs(dy); dy2 = fabs(dy2); if (dy2 > dy) { dy = dy2; } state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy); // set render mode to fill-only state->setRender(0); out->updateRender(state); doPatternText(); restoreState(); out->restoreTextPos(state); } updateLevel += 10 * s->getLength(); } // NB: this is only called when ocState is false. void Gfx::doIncCharCount(GString *s) { if (out->needCharCount()) { out->incCharCount(s->getLength()); } } //------------------------------------------------------------------------ // XObject operators //------------------------------------------------------------------------ void Gfx::opXObject(Object args[], int numArgs) { char *name; Object obj1, obj2, obj3, refObj; #if OPI_SUPPORT Object opiDict; #endif if (!ocState && !out->needCharCount()) { return; } name = args[0].getName(); if (!res->lookupXObject(name, &obj1)) { return; } if (!obj1.isStream()) { error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name); obj1.free(); return; } #if USE_EXCEPTIONS try { #endif #if OPI_SUPPORT obj1.streamGetDict()->lookup("OPI", &opiDict); if (opiDict.isDict()) { out->opiBegin(state, opiDict.getDict()); } #endif obj1.streamGetDict()->lookup("Subtype", &obj2); if (obj2.isName("Image")) { if (out->needNonText()) { res->lookupXObjectNF(name, &refObj); doImage(&refObj, obj1.getStream(), gFalse); refObj.free(); } } else if (obj2.isName("Form")) { res->lookupXObjectNF(name, &refObj); if (out->useDrawForm() && refObj.isRef()) { out->drawForm(refObj.getRef()); } else { doForm(&refObj, &obj1); } refObj.free(); } else if (obj2.isName("PS")) { obj1.streamGetDict()->lookup("Level1", &obj3); out->psXObject(obj1.getStream(), obj3.isStream() ? obj3.getStream() : (Stream *)NULL); } else if (obj2.isName()) { error(errSyntaxError, getPos(), "Unknown XObject subtype '{0:s}'", obj2.getName()); } else { error(errSyntaxError, getPos(), "XObject subtype is missing or wrong type"); } obj2.free(); #if OPI_SUPPORT if (opiDict.isDict()) { out->opiEnd(state, opiDict.getDict()); } opiDict.free(); #endif #if USE_EXCEPTIONS } catch (GMemException e) { obj1.free(); throw; } #endif obj1.free(); } void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { Dict *dict, *maskDict; int width, height; int bits, maskBits; StreamColorSpaceMode csMode; GBool mask; GBool invert; GfxColorSpace *colorSpace, *maskColorSpace; GfxImageColorMap *colorMap, *maskColorMap; Object maskObj, smaskObj; GBool haveColorKeyMask, haveExplicitMask, haveSoftMask; int maskColors[2*gfxColorMaxComps]; int maskWidth, maskHeight; GBool maskInvert; Stream *maskStr; GBool interpolate; Object obj1, obj2; int i, n; // get info from the stream bits = 0; csMode = streamCSNone; str->getImageParams(&bits, &csMode); // get stream dict dict = str->getDict(); // get size dict->lookup("Width", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("W", &obj1); } if (!obj1.isInt()) { goto err2; } width = obj1.getInt(); obj1.free(); if (width <= 0) { goto err1; } dict->lookup("Height", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("H", &obj1); } if (!obj1.isInt()) { goto err2; } height = obj1.getInt(); obj1.free(); if (height <= 0) { goto err1; } // image or mask? dict->lookup("ImageMask", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("IM", &obj1); } mask = gFalse; if (obj1.isBool()) mask = obj1.getBool(); else if (!obj1.isNull()) goto err2; obj1.free(); // bit depth if (bits == 0) { dict->lookup("BitsPerComponent", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("BPC", &obj1); } if (obj1.isInt()) { bits = obj1.getInt(); if (bits < 1 || bits > 16) { goto err2; } } else if (mask) { bits = 1; } else { goto err2; } obj1.free(); } // interpolate flag dict->lookup("Interpolate", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("I", &obj1); } interpolate = obj1.isBool() && obj1.getBool(); obj1.free(); // display a mask if (mask) { // check for inverted mask if (bits != 1) goto err1; invert = gFalse; dict->lookup("Decode", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("D", &obj1); } if (obj1.isArray()) { obj1.arrayGet(0, &obj2); invert = obj2.isNum() && obj2.getNum() == 1; obj2.free(); } else if (!obj1.isNull()) { goto err2; } obj1.free(); // if drawing is disabled, skip over inline image data if (!ocState) { str->reset(); n = height * ((width + 7) / 8); for (i = 0; i < n; ++i) { str->getChar(); } str->close(); // draw it } else { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternImageMask(ref, str, width, height, invert, inlineImg, interpolate); } else { out->drawImageMask(state, ref, str, width, height, invert, inlineImg, interpolate); } } } else { // get color space and color map dict->lookup("ColorSpace", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("CS", &obj1); } if (obj1.isName()) { res->lookupColorSpace(obj1.getName(), &obj2); if (!obj2.isNull()) { obj1.free(); obj1 = obj2; } else { obj2.free(); } } if (!obj1.isNull()) { colorSpace = GfxColorSpace::parse(&obj1 ); } else if (csMode == streamCSDeviceGray) { colorSpace = GfxColorSpace::create(csDeviceGray); } else if (csMode == streamCSDeviceRGB) { colorSpace = GfxColorSpace::create(csDeviceRGB); } else if (csMode == streamCSDeviceCMYK) { colorSpace = GfxColorSpace::create(csDeviceCMYK); } else { colorSpace = NULL; } obj1.free(); if (!colorSpace) { goto err1; } dict->lookup("Decode", &obj1); if (obj1.isNull()) { obj1.free(); dict->lookup("D", &obj1); } colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); obj1.free(); if (!colorMap->isOk()) { delete colorMap; goto err1; } // get the mask haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse; maskStr = NULL; // make gcc happy maskWidth = maskHeight = 0; // make gcc happy maskInvert = gFalse; // make gcc happy maskColorMap = NULL; // make gcc happy dict->lookup("Mask", &maskObj); dict->lookup("SMask", &smaskObj); if (smaskObj.isStream()) { // soft mask if (inlineImg) { goto err1; } maskStr = smaskObj.getStream(); maskDict = smaskObj.streamGetDict(); maskDict->lookup("Width", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("W", &obj1); } if (!obj1.isInt()) { goto err2; } maskWidth = obj1.getInt(); obj1.free(); maskDict->lookup("Height", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("H", &obj1); } if (!obj1.isInt()) { goto err2; } maskHeight = obj1.getInt(); obj1.free(); maskDict->lookup("BitsPerComponent", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("BPC", &obj1); } if (!obj1.isInt()) { goto err2; } maskBits = obj1.getInt(); obj1.free(); maskDict->lookup("ColorSpace", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("CS", &obj1); } if (obj1.isName()) { res->lookupColorSpace(obj1.getName(), &obj2); if (!obj2.isNull()) { obj1.free(); obj1 = obj2; } else { obj2.free(); } } maskColorSpace = GfxColorSpace::parse(&obj1 ); obj1.free(); if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) { goto err1; } maskDict->lookup("Decode", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("D", &obj1); } maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace); obj1.free(); if (!maskColorMap->isOk()) { delete maskColorMap; goto err1; } //~ handle the Matte entry haveSoftMask = gTrue; } else if (maskObj.isArray()) { // color key mask haveColorKeyMask = gTrue; for (i = 0; i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps; i += 2) { maskObj.arrayGet(i, &obj1); if (!obj1.isInt()) { obj1.free(); haveColorKeyMask = gFalse; break; } maskColors[i] = obj1.getInt(); obj1.free(); if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) { haveColorKeyMask = gFalse; break; } maskObj.arrayGet(i+1, &obj1); if (!obj1.isInt()) { obj1.free(); haveColorKeyMask = gFalse; break; } maskColors[i+1] = obj1.getInt(); obj1.free(); if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) || maskColors[i] > maskColors[i+1]) { haveColorKeyMask = gFalse; break; } } } else if (maskObj.isStream()) { // explicit mask if (inlineImg) { goto err1; } maskStr = maskObj.getStream(); maskDict = maskObj.streamGetDict(); maskDict->lookup("Width", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("W", &obj1); } if (!obj1.isInt()) { goto err2; } maskWidth = obj1.getInt(); obj1.free(); maskDict->lookup("Height", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("H", &obj1); } if (!obj1.isInt()) { goto err2; } maskHeight = obj1.getInt(); obj1.free(); maskDict->lookup("ImageMask", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("IM", &obj1); } if (!obj1.isBool() || !obj1.getBool()) { goto err2; } obj1.free(); maskInvert = gFalse; maskDict->lookup("Decode", &obj1); if (obj1.isNull()) { obj1.free(); maskDict->lookup("D", &obj1); } if (obj1.isArray()) { obj1.arrayGet(0, &obj2); maskInvert = obj2.isNum() && obj2.getNum() == 1; obj2.free(); } else if (!obj1.isNull()) { goto err2; } obj1.free(); haveExplicitMask = gTrue; } // if drawing is disabled, skip over inline image data if (!ocState) { str->reset(); n = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); for (i = 0; i < n; ++i) { str->getChar(); } str->close(); // draw it } else { if (haveSoftMask) { out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap, interpolate); delete maskColorMap; } else if (haveExplicitMask) { out->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert, interpolate); } else { out->drawImage(state, ref, str, width, height, colorMap, haveColorKeyMask ? maskColors : (int *)NULL, inlineImg, interpolate); } } delete colorMap; maskObj.free(); smaskObj.free(); } if ((i = width * height) > 1000) { i = 1000; } updateLevel += i; return; err2: obj1.free(); err1: error(errSyntaxError, getPos(), "Bad image parameters"); } void Gfx::doForm(Object *strRef, Object *str) { Dict *dict; GBool transpGroup, isolated, knockout; GfxColorSpace *blendingColorSpace; Object matrixObj, bboxObj; double m[6], bbox[4]; Object resObj; Dict *resDict; GBool oc, ocSaved; Object obj1, obj2, obj3; int i; // check for excessive recursion if (formDepth > 100) { return; } // get stream dict dict = str->streamGetDict(); // check form type dict->lookup("FormType", &obj1); if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { error(errSyntaxError, getPos(), "Unknown form type"); } obj1.free(); // check for optional content key ocSaved = ocState; dict->lookupNF("OC", &obj1); if (doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) { obj1.free(); if (out->needCharCount()) { ocState = gFalse; } else { return; } } obj1.free(); // get bounding box dict->lookup("BBox", &bboxObj); if (!bboxObj.isArray()) { bboxObj.free(); error(errSyntaxError, getPos(), "Bad form bounding box"); ocState = ocSaved; return; } for (i = 0; i < 4; ++i) { bboxObj.arrayGet(i, &obj1); bbox[i] = obj1.getNum(); obj1.free(); } bboxObj.free(); // get matrix dict->lookup("Matrix", &matrixObj); if (matrixObj.isArray()) { for (i = 0; i < 6; ++i) { matrixObj.arrayGet(i, &obj1); m[i] = obj1.getNum(); obj1.free(); } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } matrixObj.free(); // get resources dict->lookup("Resources", &resObj); resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; // check for a transparency group transpGroup = isolated = knockout = gFalse; blendingColorSpace = NULL; if (dict->lookup("Group", &obj1)->isDict()) { if (obj1.dictLookup("S", &obj2)->isName("Transparency")) { transpGroup = gTrue; if (!obj1.dictLookup("CS", &obj3)->isNull()) { blendingColorSpace = GfxColorSpace::parse(&obj3 ); } obj3.free(); if (obj1.dictLookup("I", &obj3)->isBool()) { isolated = obj3.getBool(); } obj3.free(); if (obj1.dictLookup("K", &obj3)->isBool()) { knockout = obj3.getBool(); } obj3.free(); } obj2.free(); } obj1.free(); // draw it ++formDepth; drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, blendingColorSpace, isolated, knockout); --formDepth; if (blendingColorSpace) { delete blendingColorSpace; } resObj.free(); ocState = ocSaved; } void Gfx::drawForm(Object *strRef, Dict *resDict, double *matrix, double *bbox, GBool transpGroup, GBool softMask, GfxColorSpace *blendingColorSpace, GBool isolated, GBool knockout, GBool alpha, Function *transferFunc, GfxColor *backdropColor) { Parser *oldParser; GfxState *savedState; double oldBaseMatrix[6]; int i; // push new resources on stack pushResources(resDict); // save current graphics state savedState = saveStateStack(); // kill any pre-existing path state->clearPath(); // save current parser oldParser = parser; // set form transformation matrix state->concatCTM(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); out->updateCTM(state, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); // set form bounding box state->moveTo(bbox[0], bbox[1]); state->lineTo(bbox[2], bbox[1]); state->lineTo(bbox[2], bbox[3]); state->lineTo(bbox[0], bbox[3]); state->closePath(); state->clip(); out->clip(state); state->clearPath(); if (softMask || transpGroup) { if (state->getBlendMode() != gfxBlendNormal) { state->setBlendMode(gfxBlendNormal); out->updateBlendMode(state); } if (state->getFillOpacity() != 1) { state->setFillOpacity(1); out->updateFillOpacity(state); } if (state->getStrokeOpacity() != 1) { state->setStrokeOpacity(1); out->updateStrokeOpacity(state); } out->clearSoftMask(state); out->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, softMask); } // set new base matrix for (i = 0; i < 6; ++i) { oldBaseMatrix[i] = baseMatrix[i]; baseMatrix[i] = state->getCTM()[i]; } // draw the form display(strRef, gFalse); if (softMask || transpGroup) { out->endTransparencyGroup(state); } // restore base matrix for (i = 0; i < 6; ++i) { baseMatrix[i] = oldBaseMatrix[i]; } // restore parser parser = oldParser; // restore graphics state restoreStateStack(savedState); // pop resource stack popResources(); if (softMask) { out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor); } else if (transpGroup) { out->paintTransparencyGroup(state, bbox); } return; } void Gfx::takeContentStreamStack(Gfx *oldGfx) { contentStreamStack->append(oldGfx->contentStreamStack); } //------------------------------------------------------------------------ // in-line image operators //------------------------------------------------------------------------ void Gfx::opBeginImage(Object args[], int numArgs) { Stream *str; int c1, c2, c3; // NB: this function is run even if ocState is false -- doImage() is // responsible for skipping over the inline image data // build dict/stream str = buildImageStream(); // display the image if (str) { doImage(NULL, str, gTrue); // skip 'EI' tag c1 = str->getUndecodedStream()->getChar(); c2 = str->getUndecodedStream()->getChar(); c3 = str->getUndecodedStream()->lookChar(); while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) { c1 = c2; c2 = str->getUndecodedStream()->getChar(); c3 = str->getUndecodedStream()->lookChar(); } delete str; } } Stream *Gfx::buildImageStream() { Object dict; Object obj; char *key; Stream *str; // build dictionary dict.initDict(xref); parser->getObj(&obj); while (!obj.isCmd("ID") && !obj.isEOF()) { if (!obj.isName()) { error(errSyntaxError, getPos(), "Inline image dictionary key must be a name object"); obj.free(); } else { key = copyString(obj.getName()); obj.free(); parser->getObj(&obj); if (obj.isEOF() || obj.isError()) { gfree(key); break; } dict.dictAdd(key, &obj); } parser->getObj(&obj); } if (obj.isEOF()) { error(errSyntaxError, getPos(), "End of file in inline image"); obj.free(); dict.free(); return NULL; } obj.free(); // make stream if (!(str = parser->getStream())) { error(errSyntaxError, getPos(), "Invalid inline image data"); dict.free(); return NULL; } str = new EmbedStream(str, &dict, gFalse, 0); str = str->addFilters(&dict); return str; } void Gfx::opImageData(Object args[], int numArgs) { error(errInternal, getPos(), "Got 'ID' operator"); } void Gfx::opEndImage(Object args[], int numArgs) { error(errInternal, getPos(), "Got 'EI' operator"); } //------------------------------------------------------------------------ // type 3 font operators //------------------------------------------------------------------------ void Gfx::opSetCharWidth(Object args[], int numArgs) { out->type3D0(state, args[0].getNum(), args[1].getNum()); } void Gfx::opSetCacheDevice(Object args[], int numArgs) { out->type3D1(state, args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); } //------------------------------------------------------------------------ // compatibility operators //------------------------------------------------------------------------ void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) { ++ignoreUndef; } void Gfx::opEndIgnoreUndef(Object args[], int numArgs) { if (ignoreUndef > 0) --ignoreUndef; } //------------------------------------------------------------------------ // marked content operators //------------------------------------------------------------------------ void Gfx::opBeginMarkedContent(Object args[], int numArgs) { GfxMarkedContent *mc; Object obj; GBool ocStateNew; TextString *s; GfxMarkedContentKind mcKind; if (printCommands) { printf(" marked content: %s ", args[0].getName()); if (numArgs == 2) { args[1].print(stdout); } printf("\n"); fflush(stdout); } mcKind = gfxMCOther; if (args[0].isName("OC") && numArgs == 2 && args[1].isName() && res->lookupPropertiesNF(args[1].getName(), &obj)) { if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) { ocState = ocStateNew; } obj.free(); mcKind = gfxMCOptionalContent; } else if (args[0].isName("Span") && numArgs == 2 && args[1].isDict()) { if (args[1].dictLookup("ActualText", &obj)->isString()) { s = new TextString(obj.getString()); out->beginActualText(state, s->getUnicode(), s->getLength()); delete s; mcKind = gfxMCActualText; } obj.free(); } mc = new GfxMarkedContent(mcKind, ocState); markedContentStack->append(mc); } void Gfx::opEndMarkedContent(Object args[], int numArgs) { GfxMarkedContent *mc; GfxMarkedContentKind mcKind; if (markedContentStack->getLength() > 0) { mc = (GfxMarkedContent *) markedContentStack->del(markedContentStack->getLength() - 1); mcKind = mc->kind; delete mc; if (mcKind == gfxMCOptionalContent) { if (markedContentStack->getLength() > 0) { mc = (GfxMarkedContent *) markedContentStack->get(markedContentStack->getLength() - 1); ocState = mc->ocState; } else { ocState = gTrue; } } else if (mcKind == gfxMCActualText) { out->endActualText(state); } } else { error(errSyntaxWarning, getPos(), "Mismatched EMC operator"); } } void Gfx::opMarkPoint(Object args[], int numArgs) { if (printCommands) { printf(" mark point: %s ", args[0].getName()); if (numArgs == 2) args[1].print(stdout); printf("\n"); fflush(stdout); } } //------------------------------------------------------------------------ // misc //------------------------------------------------------------------------ void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle, double xMin, double yMin, double xMax, double yMax) { Dict *dict, *resDict; Object str, matrixObj, bboxObj, resObj, obj1; double formXMin, formYMin, formXMax, formYMax; double x, y, sx, sy, tx, ty; double m[6], bbox[4]; double *borderColor; GfxColor color; double *dash, *dash2; int dashLength; int i; // this function assumes that we are in the default user space, // i.e., baseMatrix = ctm // if the bounding box has zero width or height, don't draw anything // at all if (xMin == xMax || yMin == yMax) { return; } // draw the appearance stream (if there is one) strRef->fetch(xref, &str); if (str.isStream()) { // get stream dict dict = str.streamGetDict(); // get the form bounding box dict->lookup("BBox", &bboxObj); if (!bboxObj.isArray()) { error(errSyntaxError, getPos(), "Bad form bounding box"); bboxObj.free(); str.free(); return; } for (i = 0; i < 4; ++i) { bboxObj.arrayGet(i, &obj1); bbox[i] = obj1.getNum(); obj1.free(); } bboxObj.free(); // get the form matrix dict->lookup("Matrix", &matrixObj); if (matrixObj.isArray()) { for (i = 0; i < 6; ++i) { matrixObj.arrayGet(i, &obj1); m[i] = obj1.getNum(); obj1.free(); } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } matrixObj.free(); // transform the four corners of the form bbox to default user // space, and construct the transformed bbox x = bbox[0] * m[0] + bbox[1] * m[2] + m[4]; y = bbox[0] * m[1] + bbox[1] * m[3] + m[5]; formXMin = formXMax = x; formYMin = formYMax = y; x = bbox[0] * m[0] + bbox[3] * m[2] + m[4]; y = bbox[0] * m[1] + bbox[3] * m[3] + m[5]; if (x < formXMin) { formXMin = x; } else if (x > formXMax) { formXMax = x; } if (y < formYMin) { formYMin = y; } else if (y > formYMax) { formYMax = y; } x = bbox[2] * m[0] + bbox[1] * m[2] + m[4]; y = bbox[2] * m[1] + bbox[1] * m[3] + m[5]; if (x < formXMin) { formXMin = x; } else if (x > formXMax) { formXMax = x; } if (y < formYMin) { formYMin = y; } else if (y > formYMax) { formYMax = y; } x = bbox[2] * m[0] + bbox[3] * m[2] + m[4]; y = bbox[2] * m[1] + bbox[3] * m[3] + m[5]; if (x < formXMin) { formXMin = x; } else if (x > formXMax) { formXMax = x; } if (y < formYMin) { formYMin = y; } else if (y > formYMax) { formYMax = y; } // construct a mapping matrix, [sx 0 0], which maps the transformed // [0 sy 0] // [tx ty 1] // bbox to the annotation rectangle if (formXMin == formXMax) { // this shouldn't happen sx = 1; } else { sx = (xMax - xMin) / (formXMax - formXMin); } if (formYMin == formYMax) { // this shouldn't happen sy = 1; } else { sy = (yMax - yMin) / (formYMax - formYMin); } tx = -formXMin * sx + xMin; ty = -formYMin * sy + yMin; // the final transform matrix is (form matrix) * (mapping matrix) m[0] *= sx; m[1] *= sy; m[2] *= sx; m[3] *= sy; m[4] = m[4] * sx + tx; m[5] = m[5] * sy + ty; // get the resources dict->lookup("Resources", &resObj); resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; // draw it drawForm(strRef, resDict, m, bbox); resObj.free(); } str.free(); // draw the border if (borderStyle && borderStyle->getWidth() > 0 && borderStyle->getNumColorComps() > 0) { borderColor = borderStyle->getColor(); switch (borderStyle->getNumColorComps()) { case 1: if (state->getStrokeColorSpace()->getMode() != csDeviceGray) { state->setStrokePattern(NULL); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray)); out->updateStrokeColorSpace(state); } break; case 3: if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) { state->setStrokePattern(NULL); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB)); out->updateStrokeColorSpace(state); } break; case 4: if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) { state->setStrokePattern(NULL); state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK)); out->updateStrokeColorSpace(state); } break; } color.c[0] = dblToCol(borderColor[0]); color.c[1] = dblToCol(borderColor[1]); color.c[2] = dblToCol(borderColor[2]); color.c[3] = dblToCol(borderColor[3]); state->setStrokeColor(&color); out->updateStrokeColor(state); state->setLineWidth(borderStyle->getWidth()); out->updateLineWidth(state); borderStyle->getDash(&dash, &dashLength); if (borderStyle->getType() == annotBorderDashed && dashLength > 0) { dash2 = (double *)gmallocn(dashLength, sizeof(double)); memcpy(dash2, dash, dashLength * sizeof(double)); state->setLineDash(dash2, dashLength, 0); out->updateLineDash(state); } //~ this doesn't currently handle the beveled and engraved styles state->clearPath(); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); if (borderStyle->getType() != annotBorderUnderlined) { state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); } out->stroke(state); } } void Gfx::saveState() { out->saveState(state); state = state->save(); } void Gfx::restoreState() { state = state->restore(); out->restoreState(state); } // Create a new state stack, and initialize it with a copy of the // current state. GfxState *Gfx::saveStateStack() { GfxState *oldState; out->saveState(state); oldState = state; state = state->copy(gTrue); return oldState; } // Switch back to the previous state stack. void Gfx::restoreStateStack(GfxState *oldState) { while (state->hasSaves()) { restoreState(); } delete state; state = oldState; out->restoreState(state); } void Gfx::pushResources(Dict *resDict) { res = new GfxResources(xref, resDict, res); } void Gfx::popResources() { GfxResources *resPtr; resPtr = res->getNext(); delete res; res = resPtr; } xpdf-3.04/xpdf/Outline.h0000644000076400007640000000313012341430012014427 0ustar dereknderekn//======================================================================== // // Outline.h // // Copyright 2002-2013 Glyph & Cog, LLC // //======================================================================== #ifndef OUTLINE_H #define OUTLINE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Object.h" #include "CharTypes.h" class GString; class GList; class XRef; class LinkAction; class TextString; //------------------------------------------------------------------------ class Outline { public: Outline(Object *outlineObj, XRef *xref); ~Outline(); GList *getItems() { return items; } private: GList *items; // NULL if document has no outline // [OutlineItem] }; //------------------------------------------------------------------------ class OutlineItem { public: OutlineItem(Object *itemRefA, Dict *dict, OutlineItem *parentA, XRef *xrefA); ~OutlineItem(); static GList *readItemList(Object *firstItemRef, Object *lastItemRef, OutlineItem *parentA, XRef *xrefA); void open(); void close(); Unicode *getTitle(); int getTitleLength(); TextString *getTitleTextString() { return title; } LinkAction *getAction() { return action; } GBool isOpen() { return startsOpen; } GBool hasKids() { return firstRef.isRef(); } GList *getKids() { return kids; } private: XRef *xref; 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 xpdf-3.04/xpdf/PDFCore.h0000644000076400007640000002555712341430012014253 0ustar dereknderekn//======================================================================== // // PDFCore.h // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #ifndef PDFCORE_H #define PDFCORE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "SplashTypes.h" #include "CharTypes.h" class GString; class GList; class SplashBitmap; class SplashPattern; class BaseStream; class PDFDoc; class Links; class LinkDest; class LinkAction; class TextPage; class HighlightFile; class CoreOutputDev; class PDFCore; //------------------------------------------------------------------------ // zoom factor //------------------------------------------------------------------------ #define zoomPage -1 #define zoomWidth -2 #define defZoom 125 //------------------------------------------------------------------------ //------------------------------------------------------------------------ // Number of pixels of matte color between pages in continuous mode. #define continuousModePageSpacing 3 //------------------------------------------------------------------------ // PDFCorePage //------------------------------------------------------------------------ class PDFCorePage { public: PDFCorePage(int pageA, int wA, int hA, int tileWA, int tileHA); ~PDFCorePage(); int page; GList *tiles; // cached tiles [PDFCoreTile] int xDest, yDest; // position of upper-left corner // in the drawing area int w, h; // size of whole page bitmap int tileW, tileH; // size of tiles Links *links; // hyperlinks for this page TextPage *text; // extracted text }; //------------------------------------------------------------------------ // PDFCoreTile //------------------------------------------------------------------------ class PDFCoreTile { public: PDFCoreTile(int xDestA, int yDestA); virtual ~PDFCoreTile(); int xMin, yMin, xMax, yMax; int xDest, yDest; Guint edges; SplashBitmap *bitmap; double ctm[6]; // coordinate transform matrix: // default user space -> device space double ictm[6]; // inverse CTM }; #define pdfCoreTileTopEdge 0x01 #define pdfCoreTileBottomEdge 0x02 #define pdfCoreTileLeftEdge 0x04 #define pdfCoreTileRightEdge 0x08 #define pdfCoreTileTopSpace 0x10 #define pdfCoreTileBottomSpace 0x20 //------------------------------------------------------------------------ // PDFHistory //------------------------------------------------------------------------ struct PDFHistory { #ifdef _WIN32 wchar_t *fileName; #else GString *fileName; #endif int page; }; #define pdfHistorySize 50 //------------------------------------------------------------------------ // PDFCore //------------------------------------------------------------------------ class PDFCore { public: PDFCore(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool incrementalUpdate); virtual ~PDFCore(); //----- loadFile / displayPage / displayDest // Load a new file. Returns pdfOk or error code. virtual int loadFile(GString *fileName, GString *ownerPassword = NULL, GString *userPassword = NULL); #ifdef _WIN32 // Load a new file. Returns pdfOk or error code. virtual int loadFile(wchar_t *fileName, int fileNameLen, GString *ownerPassword = NULL, GString *userPassword = NULL); #endif // Load a new file, via a Stream instead of a file name. Returns // pdfOk or error code. virtual int loadFile(BaseStream *stream, GString *ownerPassword = NULL, GString *userPassword = NULL); // Load an already-created PDFDoc object. virtual void loadDoc(PDFDoc *docA); // Clear out the current document, if any. virtual void clear(); // Same as clear(), but returns the PDFDoc object instead of // deleting it. virtual PDFDoc *takeDoc(GBool redraw); // Display (or redisplay) the specified page. If is // set, the window is vertically scrolled to the top; otherwise, no // scrolling is done. If is set, this page change is // added to the history list. virtual void displayPage(int topPageA, double zoomA, int rotateA, GBool scrollToTop, GBool addToHist); // Display a link destination. virtual void displayDest(LinkDest *dest, double zoomA, int rotateA, GBool addToHist); // Update the display, given the specified parameters. virtual void update(int topPageA, int scrollXA, int scrollYA, double zoomA, int rotateA, GBool force, GBool addToHist, GBool adjustScrollX); //----- page/position changes virtual GBool gotoNextPage(int inc, GBool top); virtual GBool gotoPrevPage(int dec, GBool top, GBool bottom); virtual GBool gotoNamedDestination(GString *dest); virtual GBool goForward(); virtual GBool goBackward(); virtual void scrollLeft(int nCols = 16); virtual void scrollRight(int nCols = 16); virtual void scrollUp(int nLines = 16); virtual void scrollUpPrevPage(int nLines = 16); virtual void scrollDown(int nLines = 16); virtual void scrollDownNextPage(int nLines = 16); virtual void scrollPageUp(); virtual void scrollPageDown(); virtual void scrollTo(int x, int y); virtual void scrollToLeftEdge(); virtual void scrollToRightEdge(); virtual void scrollToTopEdge(); virtual void scrollToBottomEdge(); virtual void scrollToTopLeft(); virtual void scrollToBottomRight(); virtual void zoomToRect(int pg, double ulx, double uly, double lrx, double lry); virtual void zoomCentered(double zoomA); virtual void zoomToCurrentWidth(); virtual void setContinuousMode(GBool cm); //----- selection // Selection color. void setSelectionColor(SplashColor color); // Current selected region. void setSelection(int newSelectPage, int newSelectULX, int newSelectULY, int newSelectLRX, int newSelectLRY); void moveSelection(int pg, int x, int y); GBool getSelection(int *pg, double *ulx, double *uly, double *lrx, double *lry); // Text extraction. GString *extractText(int pg, double xMin, double yMin, double xMax, double yMax); //----- find virtual GBool find(char *s, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly); virtual GBool findU(Unicode *u, int len, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly); //----- coordinate conversion // user space: per-pace, as defined by PDF file; unit = point // device space: (0,0) is upper-left corner of a page; unit = pixel // window space: (0,0) is upper-left corner of drawing area; unit = pixel GBool cvtWindowToUser(int xw, int yw, int *pg, double *xu, double *yu); GBool cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd); void cvtUserToWindow(int pg, double xy, double yu, int *xw, int *yw); void cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd); void cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw); void cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu); //----- password dialog virtual GString *getPassword() { return NULL; } //----- misc access PDFDoc *getDoc() { return doc; } int getPageNum() { return topPage; } double getZoom() { return zoom; } double getZoomDPI() { return dpi; } int getRotate() { return rotate; } GBool getContinuousMode() { return continuousMode; } virtual void setReverseVideo(GBool reverseVideoA); GBool canGoBack() { return historyBLen > 1; } GBool canGoForward() { return historyFLen > 0; } int getScrollX() { return scrollX; } int getScrollY() { return scrollY; } int getDrawAreaWidth() { return drawAreaWidth; } int getDrawAreaHeight() { return drawAreaHeight; } virtual void setBusyCursor(GBool busy) = 0; LinkAction *findLink(int pg, double x, double y); protected: int loadFile2(PDFDoc *newDoc); void addPage(int pg, int rot); void needTile(PDFCorePage *page, int x, int y); void xorRectangle(int pg, int x0, int y0, int x1, int y1, SplashPattern *pattern, PDFCoreTile *oneTile = NULL); int loadHighlightFile(HighlightFile *hf, SplashColorPtr color, SplashColorPtr selectColor, GBool selectable); PDFCorePage *findPage(int pg); static void redrawCbk(void *data, int x0, int y0, int x1, int y1, GBool composited); void redrawWindow(int x, int y, int width, int height, GBool needUpdate); virtual PDFCoreTile *newTile(int xDestA, int yDestA); virtual void updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc, int width, int height, GBool composited); virtual void redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc, int xDest, int yDest, int width, int height, GBool composited) = 0; void clippedRedrawRect(PDFCoreTile *tile, int xSrc, int ySrc, int xDest, int yDest, int width, int height, int xClip, int yClip, int wClip, int hClip, GBool needUpdate, GBool composited = gTrue); virtual void updateScrollbars() = 0; virtual GBool checkForNewFile() { return gFalse; } PDFDoc *doc; // current PDF file GBool continuousMode; // false for single-page mode, true for // continuous mode int drawAreaWidth, // size of the PDF display area drawAreaHeight; double maxUnscaledPageW, // maximum unscaled page size maxUnscaledPageH; int maxPageW; // maximum page width (only used in // continuous mode) int totalDocH; // total document height (only used in // continuous mode) int *pageY; // top coordinates for each page (only used // in continuous mode) int topPage; // page at top of window int midPage; // page at middle of window int scrollX, scrollY; // offset from top left corner of topPage // to top left corner of window double zoom; // current zoom level, in percent of 72 dpi double dpi; // current zoom level, in DPI int rotate; // current page rotation int selectPage; // page number of current selection int selectULX, // coordinates of current selection, selectULY, // in device space -- (ULX==LRX || ULY==LRY) selectLRX, // means there is no selection selectLRY; GBool dragging; // set while selection is being dragged GBool lastDragLeft; // last dragged selection edge was left/right GBool lastDragTop; // last dragged selection edge was top/bottom SplashColor selectXorColor; // selection xor color PDFHistory // page history queue history[pdfHistorySize]; int historyCur; // currently displayed page int historyBLen; // number of valid entries backward from // current entry int historyFLen; // number of valid entries forward from // current entry GList *pages; // cached pages [PDFCorePage] PDFCoreTile *curTile; // tile currently being rasterized PDFCorePage *curPage; // page to which curTile belongs SplashColor paperColor; CoreOutputDev *out; friend class PDFCoreTile; }; #endif xpdf-3.04/xpdf/forwardArrow.xbm0000644000076400007640000000045012341430012016030 0ustar dereknderekn#define forwardArrow_width 16 #define forwardArrow_height 15 static unsigned char forwardArrow_bits[] = { 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x33, 0x3f, 0x33, 0x7f, 0x33, 0xff, 0x33, 0x7f, 0x33, 0x3f, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x03, 0x00, 0x01}; xpdf-3.04/xpdf/XPDFTree.cc0000644000076400007640000006576112341430012014551 0ustar dereknderekn//======================================================================== // // XPDFTree.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #include #include "gmem.h" #include "XPDFTreeP.h" //------------------------------------------------------------------------ #define xpdfTreeIndent 16 //------------------------------------------------------------------------ struct _XPDFTreeEntry { Widget widget; XPDFTreeEntry *children; XPDFTreeEntry *next; }; //------------------------------------------------------------------------ static void classPartInitialize(WidgetClass widgetClass); static void initialize(Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs); static void destroy(Widget widget); static void destroySubtree(XPDFTreeEntry *e); static void resize(Widget widget); static void redisplay(Widget widget, XEvent *event, Region region); static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e, XEvent *event, Region region); static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y); static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y); static Boolean setValues(Widget oldWidget, Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs); static void setValuesAlmost(Widget oldWidget, Widget newWidget, XtWidgetGeometry *request, XtWidgetGeometry *reply); static XtGeometryResult queryGeometry(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply); static XtGeometryResult geometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply); static void changeManaged(Widget widget); static void initConstraint(Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs); static void destroyConstraint(Widget widget); static void deleteSubtree(Widget widget); static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs); static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead); static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead); static void createGC(Widget widget); static void destroyGC(Widget widget); static void layout(Widget widget, Widget instigator); static int layoutSubtree(XPDFTreeWidget w, Widget instigator, XPDFTreeEntry *e, Position x, Position y, Boolean visible); static void calcSize(Widget widget, Widget instigator, Dimension *totalWidth, Dimension *totalHeight); static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator, XPDFTreeEntry *e, Dimension *width, Dimension *height); static Boolean needRelayout(Widget oldWidget, Widget newWidget); static void click(Widget widget, XEvent *event, String *params, Cardinal *numParams); static Boolean findPosition(XPDFTreeWidget w, int x, int y, XPDFTreeEntry **e, Boolean *onExpandIcon); static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y, XPDFTreeEntry **e, Boolean *onExpandIcon); //------------------------------------------------------------------------ static XtResource resources[] = { { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth), XmRImmediate, (XtPointer)0 }, { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight), XmRImmediate, (XtPointer)0 }, { XPDFNselectionCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffsetOf(XPDFTreeRec, tree.selectCallback), XmRImmediate, (XtPointer)NULL } }; static XmSyntheticResource synResources[] = { { XmNmarginWidth, sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth), #if XmVERSION > 1 XmeFromHorizontalPixels, XmeToHorizontalPixels #else _XmFromHorizontalPixels, _XmToHorizontalPixels #endif }, { XmNmarginHeight, sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight), #if XmVERSION > 1 XmeFromVerticalPixels, XmeToVerticalPixels #else _XmFromVerticalPixels, _XmToVerticalPixels #endif } }; static XtResource constraints[] = { { XPDFNentryParent, XPDFCentryParent, XmRWidget, sizeof(Widget), XtOffsetOf(XPDFTreeConstraintRec, tree.entryParent), XmRImmediate, (XtPointer)NULL }, { XPDFNentryExpanded, XPDFCentryExpanded, XmRBoolean, sizeof(Boolean), XtOffsetOf(XPDFTreeConstraintRec, tree.entryExpanded), XmRImmediate, (XtPointer)False }, { XPDFNentryPosition, XPDFCentryPosition, XmRInt, sizeof(int), XtOffsetOf(XPDFTreeConstraintRec, tree.entryPosition), XmRImmediate, (XtPointer)0 } }; static char defaultTranslations[] = ": XPDFTreeClick()"; static XtActionsRec actions[] = { { "XPDFTreeClick", click } }; externaldef(xpdftreeclassrec) XPDFTreeClassRec xpdfTreeClassRec = { { // Core (WidgetClass)&xmManagerClassRec, // superclass "XPDFTree", // class_name sizeof(XPDFTreeRec), // widget_size NULL, // class_initialize &classPartInitialize, // class_part_initialize FALSE, // class_inited &initialize, // initialize NULL, // initialize_hook XtInheritRealize, // realize actions, // actions XtNumber(actions), // num_actions resources, // resources XtNumber(resources), // num_resources NULLQUARK, // xrm_class TRUE, // compress_motion XtExposeCompressMaximal, // compress_exposure TRUE, // compress_enterleave FALSE, // visible_interest &destroy, // destroy &resize, // resize &redisplay, // expose &setValues, // set_values NULL, // set_values_hook &setValuesAlmost, // set_values_almost NULL, // get_values_hook NULL, // accept_focus XtVersion, // version NULL, // callback_private defaultTranslations, // tm_table &queryGeometry, // query_geometry NULL, // display_accelerator NULL // extension }, { // Composite &geometryManager, // geometry_manager &changeManaged, // change_managed XtInheritInsertChild, // insert_child XtInheritDeleteChild, // delete_child NULL // extension }, { // Constraint constraints, // constraint_resources XtNumber(constraints), // constraint_num_resources sizeof(XPDFTreeConstraintRec), // constraint_size &initConstraint, // constraint_initialize &destroyConstraint, // constraint_destroy &constraintSetValues, // constraint_set_values NULL // extension }, { // XmManager XtInheritTranslations, // translations #if XmVERSION > 1 synResources, // syn_resources XtNumber(synResources), // num_syn_resources #else NULL, // syn_resources 0, // num_syn_resources #endif NULL, // syn_constraint_resources 0, // num_syn_constraint_res's XmInheritParentProcess, // parent_process NULL // extension }, { // XPDFTree &createGC, // createGC &destroyGC, // destroyGC &layout, // layout &calcSize, // calcSize &needRelayout, // needRelayout NULL // extension } }; externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass = (WidgetClass)&xpdfTreeClassRec; //------------------------------------------------------------------------ static void classPartInitialize(WidgetClass widgetCls) { XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls; XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass; // method inheritance if (wc->treeClass.createGC == XPDFInheritCreateGC) { wc->treeClass.createGC = sc->treeClass.createGC; } if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) { wc->treeClass.destroyGC = sc->treeClass.destroyGC; } if (wc->treeClass.layout == XPDFInheritLayout) { wc->treeClass.layout = sc->treeClass.layout; } if (wc->treeClass.calcSize == XPDFInheritCalcSize) { wc->treeClass.calcSize = sc->treeClass.calcSize; } if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) { wc->treeClass.needRelayout = sc->treeClass.needRelayout; } } static void initialize(Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs) { XPDFTreeWidget nw = (XPDFTreeWidget)newWidget; XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget); nw->tree.root = NULL; nw->tree.redrawY = -1; if (cls->treeClass.createGC) { (*cls->treeClass.createGC)(newWidget); } else { createGC(newWidget); } } static void destroy(Widget widget) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget); if (w->tree.root) { destroySubtree(w->tree.root); w->tree.root = NULL; } if (cls->treeClass.destroyGC) { (*cls->treeClass.destroyGC)(widget); } else { destroyGC(widget); } } static void destroySubtree(XPDFTreeEntry *e) { if (e->children) { destroySubtree(e->children); } if (e->next) { destroySubtree(e->next); } } static void resize(Widget widget) { XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget); if (cls->treeClass.layout) { (*cls->treeClass.layout)(widget, NULL); } else { layout(widget, NULL); } } static void redisplay(Widget widget, XEvent *event, Region region) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XPDFTreeEntry *e; if (w->tree.redrawY >= 0) { XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w), 0, w->tree.redrawY, w->core.width, w->core.height, False); w->tree.redrawY = -1; } for (e = w->tree.root; e; e = e->next) { redisplaySubtree(w, e, event, region); } } static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e, XEvent *event, Region region) { XPDFTreeConstraint c; Position x, y, y2; XPDFTreeEntry *child; (*XtClass(e->widget)->core_class.expose)(e->widget, event, region); c = XPDFTreeCPart(e->widget); x = e->widget->core.x; y = e->widget->core.y + e->widget->core.height / 2; if (e->children) { if (c->entryExpanded) { drawExpandedIcon(w, x - 8, y); y2 = y; // make gcc happy for (child = e->children; child; child = child->next) { y2 = child->widget->core.y + child->widget->core.height / 2; XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC, x - 8, y2, x + 6, y2); redisplaySubtree(w, child, event, region); } XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC, x - 8, y + 2, x - 8, y2); } else { drawCollapsedIcon(w, x - 8, y); } } } static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) { XPoint pts[4]; pts[0].x = x - 4; pts[0].y = y - 2; pts[1].x = x + 4; pts[1].y = y - 2; pts[2].x = x; pts[2].y = y + 2; pts[3].x = x - 4; pts[3].y = y - 2; XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC, pts, 4, CoordModeOrigin); } static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) { XPoint pts[4]; pts[0].x = x - 2; pts[0].y = y - 4; pts[1].x = x - 2; pts[1].y = y + 4; pts[2].x = x + 2; pts[2].y = y; pts[3].x = x - 2; pts[3].y = y - 4; XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC, pts, 4, CoordModeOrigin); } static Boolean setValues(Widget oldWidget, Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs) { XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget; XPDFTreeWidget nw = (XPDFTreeWidget)newWidget; XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(nw); Boolean relayout, redisp; // check to see if layout-affecting resources have changed if (cls->treeClass.needRelayout) { relayout = (*cls->treeClass.needRelayout)((Widget)ow, (Widget)nw); } else { relayout = needRelayout((Widget)ow, (Widget)nw); } redisp = False; if (relayout) { // calculate a new ideal size (reset the widget size first so // calcSize will compute a new one) if (nw->core.width == ow->core.width) { nw->core.width = 0; } if (nw->core.height == ow->core.height) { nw->core.height = 0; } if (cls->treeClass.calcSize) { (*cls->treeClass.calcSize)((Widget)nw, NULL, &nw->core.width, &nw->core.height); } else { calcSize((Widget)nw, NULL, &nw->core.width, &nw->core.height); } // if resources have changed but size hasn't, layout manually // (because Xt just looks at the size) if (nw->core.width == ow->core.width && nw->core.height == ow->core.height) { if (cls->treeClass.layout) { (*cls->treeClass.layout)((Widget)nw, NULL); } else { layout((Widget)nw, NULL); } redisp = True; } } return redisp; } static void setValuesAlmost(Widget oldWidget, Widget newWidget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget); // our parent rejected a geometry request, so accept the compromise // and relayout if (!reply->request_mode) { if (cls->treeClass.layout) { (*cls->treeClass.layout)(newWidget, NULL); } else { layout(newWidget, NULL); } } *request = *reply; } static XtGeometryResult queryGeometry(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget); if (!XtIsRealized(widget)) { reply->width = XtWidth(widget); reply->height = XtHeight(widget); } else { reply->width = 0; reply->height = 0; } if (cls->treeClass.calcSize) { (*cls->treeClass.calcSize)(widget, NULL, &reply->width, &reply->height); } else { calcSize(widget, NULL, &reply->width, &reply->height); } #if XmVERSION > 1 return XmeReplyToQueryGeometry(widget, request, reply); #else if ((request->request_mode & CWWidth) && (request->request_mode & CWHeight) && request->width == reply->width && request->height == reply->height) { return XtGeometryYes; } if (reply->width == XtWidth(widget) && reply->height == XtHeight(widget)) { return XtGeometryNo; } reply->request_mode = CWWidth | CWHeight; return XtGeometryAlmost; #endif } static XtGeometryResult geometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget); XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(w); Dimension curWidth, curHeight, curBW; XtWidgetGeometry parentReq; XtGeometryResult result; // deny any requests for a new position if ((request->request_mode & CWX) || (request->request_mode & CWY)) { return XtGeometryNo; } // save the current geometry curWidth = w->core.width; curHeight = w->core.height; curBW = w->core.border_width; // make the requested changes if (request->request_mode & CWWidth) { w->core.width = request->width; } if (request->request_mode & CWHeight) { w->core.height = request->height; } if (request->request_mode & CWBorderWidth) { w->core.border_width = request->border_width; } // calculate the new ideal size parentReq.width = 0; parentReq.height = 0; if (cls->treeClass.calcSize) { (*cls->treeClass.calcSize)((Widget)w, widget, &parentReq.width, &reply->height); } else { calcSize((Widget)w, widget, &parentReq.width, &reply->height); } // send geometry request to our parent parentReq.request_mode = CWWidth | CWHeight; if (request->request_mode & XtCWQueryOnly) { parentReq.request_mode |= XtCWQueryOnly; } result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL); if (result == XtGeometryAlmost) { result = XtGeometryNo; } if (result == XtGeometryNo || (request->request_mode & XtCWQueryOnly)) { // restore the original geometry w->core.width = curWidth; w->core.height = curHeight; w->core.border_width = curBW; } else { if (cls->treeClass.layout) { (*cls->treeClass.layout)((Widget)w, widget); } else { layout((Widget)w, widget); } } return result; } static void changeManaged(Widget widget) { Dimension width, height; XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget); // compute the ideal size if (!XtIsRealized(widget)) { width = XtWidth(widget); height = XtHeight(widget); } else { width = 0; height = 0; } if (cls->treeClass.calcSize) { (*cls->treeClass.calcSize)(widget, NULL, &width, &height); } else { calcSize(widget, NULL, &width, &height); } // make resize request to parent -- keep asking until we get a yes // or no while (XtMakeResizeRequest(widget, width, height, &width, &height) == XtGeometryAlmost) ; // relayout if (cls->treeClass.layout) { (*cls->treeClass.layout)(widget, NULL); } else { layout(widget, NULL); } #if XmVERSION > 1 // update keyboard traversal XmeNavigChangeManaged(widget); #else _XmNavigChangeManaged(widget); #endif } static void initConstraint(Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs) { XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget); XPDFTreeConstraint c; c = XPDFTreeCPart(newWidget); c->e = (XPDFTreeEntry *)gmalloc(sizeof(XPDFTreeEntry)); c->e->widget = newWidget; c->e->children = NULL; c->e->next = NULL; if (c->entryParent) { insertChildOnList(c->e, &XPDFTreeCPart(c->entryParent)->e->children); } else { insertChildOnList(c->e, &w->tree.root); } } static void destroyConstraint(Widget widget) { deleteSubtree(widget); } static void deleteSubtree(Widget widget) { XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget); XPDFTreeConstraint c; c = XPDFTreeCPart(widget); if (!c->e) { return; } while (c->e->children) { deleteSubtree(c->e->children->widget); } if (c->entryParent) { deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children); } else { deleteChildFromList(c->e, &w->tree.root); } gfree(c->e); c->e = NULL; } static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget, Widget newWidget, ArgList args, Cardinal *numArgs) { XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget); XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass((Widget)w); XPDFTreeConstraint oc, nc; Boolean relayout; Dimension width, height; if (!XtIsManaged(newWidget)) { return False; } oc = XPDFTreeCPart(oldWidget); nc = XPDFTreeCPart(newWidget); relayout = False; if (nc->entryParent != oc->entryParent || nc->entryPosition != oc->entryPosition) { if (oc->entryParent) { deleteChildFromList(oc->e, &XPDFTreeCPart(oc->entryParent)->e->children); } else { deleteChildFromList(oc->e, &w->tree.root); } if (nc->entryParent) { insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children); } else { insertChildOnList(nc->e, &w->tree.root); } relayout = True; } else if (nc->entryExpanded != oc->entryExpanded) { relayout = True; } if (relayout) { // calculate a new ideal size (reset the widget size first so // calcSize will compute a new one) width = 0; height = 0; if (cls->treeClass.calcSize) { (*cls->treeClass.calcSize)((Widget)w, NULL, &width, &height); } else { calcSize((Widget)w, NULL, &width, &height); } // make resize request to parent -- keep asking until we get a yes // or no while (XtMakeResizeRequest((Widget)w, width, height, &width, &height) == XtGeometryAlmost) ; // relayout the widget if (cls->treeClass.layout) { (*cls->treeClass.layout)((Widget)w, NULL); } else { layout((Widget)w, NULL); } } return relayout; } static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) { int pos; XPDFTreeEntry *e2; pos = XPDFTreeCPart(e->widget)->entryPosition; if (!*listHead || pos < XPDFTreeCPart((*listHead)->widget)->entryPosition) { e->next = *listHead; *listHead = e; } else { for (e2 = *listHead; e2->next && pos >= XPDFTreeCPart(e2->next->widget)->entryPosition; e2 = e2->next) ; e->next = e2->next; e2->next = e; } } static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) { XPDFTreeEntry **p; for (p = listHead; *p; p = &(*p)->next) { if (*p == e) { *p = e->next; e->next = NULL; return; } } } static void createGC(Widget widget) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XGCValues gcValues; gcValues.foreground = w->manager.foreground; gcValues.line_width = 0; gcValues.line_style = LineSolid; w->tree.plainGC = XtGetGC(widget, GCForeground | GCLineWidth | GCLineStyle, &gcValues); gcValues.line_style = LineOnOffDash; gcValues.dashes = 1; gcValues.dash_offset = 0; w->tree.dottedGC = XtGetGC(widget, GCForeground | GCLineWidth | GCLineStyle | GCDashList | GCDashOffset, &gcValues); } static void destroyGC(Widget widget) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XtReleaseGC(widget, w->tree.plainGC); XtReleaseGC(widget, w->tree.dottedGC); } static void layout(Widget widget, Widget instigator) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XPDFTreeEntry *e; Position x, y; x = w->tree.marginWidth + xpdfTreeIndent; y = w->tree.marginHeight; for (e = w->tree.root; e; e = e->next) { y = layoutSubtree(w, instigator, e, x, y, True); } } static int layoutSubtree(XPDFTreeWidget w, Widget instigator, XPDFTreeEntry *e, Position x, Position y, Boolean visible) { Widget ew; XPDFTreeEntry *child; XPDFTreeConstraint c; int dy; ew = e->widget; if (!XtIsManaged(ew)) { return y; } c = XPDFTreeCPart(ew); // place this entry if (ew) { if (visible) { if (ew == instigator) { ew->core.x = x; ew->core.y = y; } else { #if XmVERSION > 1 XmeConfigureObject(ew, x, y, ew->core.width, ew->core.height, ew->core.border_width); #else _XmConfigureObject(ew, x, y, ew->core.width, ew->core.height, ew->core.border_width); #endif } dy = ew->core.height + 2 * ew->core.border_width; // this is a kludge to avoid crashes if the widget becomes too // tall if ((int)y + dy > 32767) { y = 32767; } else { y += dy; } } } // place this entry's children x += xpdfTreeIndent; for (child = e->children; child; child = child->next) { y = layoutSubtree(w, instigator, child, x, y, visible && (!c || c->entryExpanded)); } return y; } static void calcSize(Widget widget, Widget instigator, Dimension *totalWidth, Dimension *totalHeight) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XPDFTreeEntry *e; int h1; Dimension w1, w2, h2; w1 = h1 = 0; for (e = w->tree.root; e; e = e->next) { calcSubtreeSize(w, instigator, e, &w2, &h2); if (w2 > w1) { w1 = w2; } h1 += (int)h2; } w1 += xpdfTreeIndent + 2 * w->tree.marginWidth; h1 += 2 * (int)w->tree.marginHeight; if (h1 == 0) { h1 = 1; } else if (h1 > 32767) { // this is a kludge to avoid crashes if the widget becomes too // tall h1 = 32767; } if (!*totalWidth) { *totalWidth = w1; } if (!*totalHeight) { *totalHeight = (Dimension)h1; } } static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator, XPDFTreeEntry *e, Dimension *width, Dimension *height) { Widget ew; XPDFTreeEntry *child; XPDFTreeConstraint c; XtWidgetGeometry geom; int h1; Dimension w1, w2, h2; ew = e->widget; if (!XtIsManaged(ew)) { *width = *height = 0; return; } c = XPDFTreeCPart(ew); // get size of this entry if (ew) { if (!XtIsManaged(ew)) { *width = *height = 0; return; } if (ew == instigator) { w1 = ew->core.width; h1 = (int)ew->core.height; } else { XtQueryGeometry(ew, NULL, &geom); w1 = (geom.request_mode & CWWidth) ? geom.width : ew->core.width; h1 = (int)((geom.request_mode & CWHeight) ? geom.height : ew->core.height); } h1 += 2 * (int)ew->core.border_width; } else { // root of tree w1 = 0; h1 = 0; } // if this entry is expanded, get size of all of its children if (c->entryExpanded) { for (child = e->children; child; child = child->next) { calcSubtreeSize(w, instigator, child, &w2, &h2); w2 += xpdfTreeIndent; if (w2 > w1) { w1 = w2; } h1 += (int)h2; } } // this is a kludge to avoid crashes if the widget becomes too tall if (h1 > 32767) { h1 = 32767; } *width = w1; *height = h1; } static Boolean needRelayout(Widget oldWidget, Widget newWidget) { XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget; XPDFTreeWidget nw = (XPDFTreeWidget)newWidget; if (nw->tree.marginWidth != ow->tree.marginWidth || nw->tree.marginHeight != ow->tree.marginHeight) { return True; } return False; } static void click(Widget widget, XEvent *event, String *params, Cardinal *numParams) { XPDFTreeWidget w = (XPDFTreeWidget)widget; XButtonPressedEvent *bpe; XPDFTreeEntry *e; Boolean onExpandIcon; XPDFTreeConstraint c; XPDFTreeSelectCallbackStruct cbs; if (event->type != ButtonPress) { return; } bpe = (XButtonPressedEvent *)event; if (findPosition(w, bpe->x, bpe->y, &e, &onExpandIcon)) { if (onExpandIcon) { c = XPDFTreeCPart(e->widget); w->tree.redrawY = e->widget->core.y; XtVaSetValues(e->widget, XPDFNentryExpanded, !c->entryExpanded, NULL); } else { XmProcessTraversal(e->widget, XmTRAVERSE_CURRENT); XtCallActionProc(widget, "ManagerGadgetActivate", event, NULL, 0); cbs.reason = XmCR_ACTIVATE; cbs.event = event; cbs.selectedItem = e->widget; XtCallCallbackList(widget, w->tree.selectCallback, &cbs); } } } static Boolean findPosition(XPDFTreeWidget w, int x, int y, XPDFTreeEntry **e, Boolean *onExpandIcon) { XPDFTreeEntry *e2; for (e2 = w->tree.root; e2; e2 = e2->next) { *e = e2; if (findPositionInSubtree(w, x, y, e, onExpandIcon)) { return True; } } return False; } // If (x,y) falls on either an expand/collapse icon or a label gadget, // set * and * and return true. static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y, XPDFTreeEntry **e, Boolean *onExpandIcon) { Widget child; XPDFTreeConstraint c; XPDFTreeEntry *e2; int y1; child = (*e)->widget; y1 = child->core.y + child->core.height / 2; if (x >= child->core.x && x < child->core.x + child->core.width && y >= child->core.y && y < child->core.y + child->core.height) { *onExpandIcon = False; return True; } else if (x >= child->core.x - 16 && x < child->core.x - 4 && y >= y1 - 6 && y < y1 + 6 && (*e)->children) { *onExpandIcon = True; return True; } c = XPDFTreeCPart(child); if (!c || c->entryExpanded) { for (e2 = (*e)->children; e2; e2 = e2->next) { *e = e2; if (findPositionInSubtree(w, x, y, e, onExpandIcon)) { return True; } } } return False; } Widget XPDFCreateTree(Widget parent, char *name, ArgList argList, Cardinal numArgs) { return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs); } xpdf-3.04/xpdf/PSTokenizer.h0000644000076400007640000000140312341430012015226 0ustar dereknderekn//======================================================================== // // PSTokenizer.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PSTOKENIZER_H #define PSTOKENIZER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" //------------------------------------------------------------------------ class PSTokenizer { public: PSTokenizer(int (*getCharFuncA)(void *), void *dataA); ~PSTokenizer(); // Get the next PostScript token. Returns false at end-of-stream. GBool getToken(char *buf, int size, int *length); private: int lookChar(); int getChar(); int (*getCharFunc)(void *); void *data; int charBuf; }; #endif xpdf-3.04/xpdf/backArrowDis.xbm0000644000076400007640000000045012341430012015724 0ustar dereknderekn#define backArrowDis_width 16 #define backArrowDis_height 15 static unsigned char backArrowDis_bits[] = { 0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x50, 0x00, 0xa8, 0x00, 0x54, 0x44, 0xaa, 0x88, 0x55, 0x44, 0xaa, 0x88, 0x54, 0x44, 0xa8, 0x88, 0x50, 0x00, 0xa0, 0x00, 0x40, 0x00, 0x80, 0x00}; xpdf-3.04/xpdf/CoreOutputDev.cc0000644000076400007640000000271212341430012015723 0ustar dereknderekn//======================================================================== // // CoreOutputDev.cc // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "Object.h" #include "TextOutputDev.h" #include "CoreOutputDev.h" //------------------------------------------------------------------------ // CoreOutputDev //------------------------------------------------------------------------ CoreOutputDev::CoreOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool incrementalUpdateA, CoreOutRedrawCbk redrawCbkA, void *redrawCbkDataA): SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA) { incrementalUpdate = incrementalUpdateA; redrawCbk = redrawCbkA; redrawCbkData = redrawCbkDataA; } CoreOutputDev::~CoreOutputDev() { } void CoreOutputDev::endPage() { SplashOutputDev::endPage(); if (!incrementalUpdate) { (*redrawCbk)(redrawCbkData, 0, 0, getBitmapWidth(), getBitmapHeight(), gTrue); } } void CoreOutputDev::dump() { int x0, y0, x1, y1; if (incrementalUpdate) { getModRegion(&x0, &y0, &x1, &y1); clearModRegion(); if (x1 >= x0 && y1 >= y0) { (*redrawCbk)(redrawCbkData, x0, y0, x1, y1, gFalse); } } } void CoreOutputDev::clear() { startDoc(NULL); startPage(0, NULL); } xpdf-3.04/xpdf/backArrow.xbm0000644000076400007640000000043712341430012015271 0ustar dereknderekn#define backArrow_width 16 #define backArrow_height 15 static unsigned char backArrow_bits[] = { 0x80, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf8, 0x00, 0xfc, 0xcc, 0xfe, 0xcc, 0xff, 0xcc, 0xfe, 0xcc, 0xfc, 0xcc, 0xf8, 0xcc, 0xf0, 0x00, 0xe0, 0x00, 0xc0, 0x00, 0x80, 0x00}; xpdf-3.04/xpdf/XPDFApp.h0000644000076400007640000000561112341430012014220 0ustar dereknderekn//======================================================================== // // XPDFApp.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef XPDFAPP_H #define XPDFAPP_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #define Object XtObject #include #undef Object #include "gtypes.h" #include "SplashTypes.h" class GString; class GList; class PDFDoc; class XPDFViewer; //------------------------------------------------------------------------ #define xpdfAppName "Xpdf" //------------------------------------------------------------------------ // XPDFApp //------------------------------------------------------------------------ class XPDFApp { public: XPDFApp(int *argc, char *argv[]); ~XPDFApp(); XPDFViewer *open(GString *fileName, int page = 1, GString *ownerPassword = NULL, GString *userPassword = NULL); XPDFViewer *openAtDest(GString *fileName, GString *dest, GString *ownerPassword = NULL, GString *userPassword = NULL); XPDFViewer *reopen(XPDFViewer *viewer, PDFDoc *doc, int page, GBool fullScreenA); void close(XPDFViewer *viewer, GBool closeLast); void quit(); void run(); //----- remote server void setRemoteName(char *remoteName); GBool remoteServerRunning(); void remoteExec(char *cmd); void remoteOpen(GString *fileName, int page, GBool raise); void remoteOpenAtDest(GString *fileName, GString *dest, GBool raise); void remoteReload(GBool raise); void remoteRaise(); void remoteQuit(); //----- resource/option values GString *getGeometry() { return geometry; } GString *getTitle() { return title; } GBool getInstallCmap() { return installCmap; } int getRGBCubeSize() { return rgbCubeSize; } GBool getReverseVideo() { return reverseVideo; } SplashColorPtr getPaperRGB() { return paperRGB; } Gulong getPaperPixel() { return paperPixel; } Gulong getMattePixel(GBool fullScreenA) { return fullScreenA ? fullScreenMattePixel : mattePixel; } GString *getInitialZoom() { return initialZoom; } void setFullScreen(GBool fullScreenA) { fullScreen = fullScreenA; } GBool getFullScreen() { return fullScreen; } XtAppContext getAppContext() { return appContext; } Widget getAppShell() { return appShell; } private: void getResources(); static void remoteMsgCbk(Widget widget, XtPointer ptr, XEvent *event, Boolean *cont); Display *display; int screenNum; XtAppContext appContext; Widget appShell; GList *viewers; // [XPDFViewer] Atom remoteAtom; Window remoteXWin; XPDFViewer *remoteViewer; Widget remoteWin; //----- resource/option values GString *geometry; GString *title; GBool installCmap; int rgbCubeSize; GBool reverseVideo; SplashColor paperRGB; Gulong paperPixel; Gulong mattePixel; Gulong fullScreenMattePixel; GString *initialZoom; GBool fullScreen; }; #endif xpdf-3.04/xpdf/Array.cc0000644000076400007640000000247312341430012014235 0ustar dereknderekn//======================================================================== // // Array.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "Object.h" #include "Array.h" //------------------------------------------------------------------------ // Array //------------------------------------------------------------------------ Array::Array(XRef *xrefA) { xref = xrefA; elems = NULL; size = length = 0; ref = 1; } Array::~Array() { int i; for (i = 0; i < length; ++i) elems[i].free(); gfree(elems); } void Array::add(Object *elem) { if (length == size) { if (length == 0) { size = 8; } else { size *= 2; } elems = (Object *)greallocn(elems, size, sizeof(Object)); } elems[length] = *elem; ++length; } Object *Array::get(int i, Object *obj) { if (i < 0 || i >= length) { #ifdef DEBUG_MEM abort(); #else return obj->initNull(); #endif } return elems[i].fetch(xref, obj); } Object *Array::getNF(int i, Object *obj) { if (i < 0 || i >= length) { #ifdef DEBUG_MEM abort(); #else return obj->initNull(); #endif } return elems[i].copy(obj); } xpdf-3.04/xpdf/JPXStream.cc0000644000076400007640000031107512341430012014775 0ustar dereknderekn//======================================================================== // // JPXStream.cc // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmem.h" #include "Error.h" #include "JArithmeticDecoder.h" #include "JPXStream.h" //~ to do: // - precincts // - ROI // - progression order changes // - packed packet headers // - support for palettes, channel maps, etc. // - make sure all needed JP2/JPX subboxes are parsed (readBoxes) // - can we assume that QCC segments must come after the QCD segment? // - handle tilePartToEOC in readTilePartData // - progression orders 2, 3, and 4 // - in coefficient decoding (readCodeBlockData): // - selective arithmetic coding bypass // (this also affects reading the cb->dataLen array) // - coeffs longer than 31 bits (should just ignore the extra bits?) // - handle boxes larger than 2^32 bytes // - the fixed-point arithmetic won't handle 16-bit pixels //------------------------------------------------------------------------ // number of contexts for the arithmetic decoder #define jpxNContexts 19 #define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup #define jpxContextSign 9 // 9 - 13: sign #define jpxContextMagRef 14 // 14 - 16: magnitude refinement #define jpxContextRunLength 17 // cleanup: run length #define jpxContextUniform 18 // cleanup: first signif coeff //------------------------------------------------------------------------ #define jpxPassSigProp 0 #define jpxPassMagRef 1 #define jpxPassCleanup 2 //------------------------------------------------------------------------ // arithmetic decoder context for the significance propagation and // cleanup passes: // [horiz][vert][diag][subband] // where subband = 0 for HL // = 1 for LH and LL // = 2 for HH static Guint sigPropContext[3][3][5][3] = { {{{ 0, 0, 0 }, // horiz=0, vert=0, diag=0 { 1, 1, 3 }, // horiz=0, vert=0, diag=1 { 2, 2, 6 }, // horiz=0, vert=0, diag=2 { 2, 2, 8 }, // horiz=0, vert=0, diag=3 { 2, 2, 8 }}, // horiz=0, vert=0, diag=4 {{ 5, 3, 1 }, // horiz=0, vert=1, diag=0 { 6, 3, 4 }, // horiz=0, vert=1, diag=1 { 6, 3, 7 }, // horiz=0, vert=1, diag=2 { 6, 3, 8 }, // horiz=0, vert=1, diag=3 { 6, 3, 8 }}, // horiz=0, vert=1, diag=4 {{ 8, 4, 2 }, // horiz=0, vert=2, diag=0 { 8, 4, 5 }, // horiz=0, vert=2, diag=1 { 8, 4, 7 }, // horiz=0, vert=2, diag=2 { 8, 4, 8 }, // horiz=0, vert=2, diag=3 { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4 {{{ 3, 5, 1 }, // horiz=1, vert=0, diag=0 { 3, 6, 4 }, // horiz=1, vert=0, diag=1 { 3, 6, 7 }, // horiz=1, vert=0, diag=2 { 3, 6, 8 }, // horiz=1, vert=0, diag=3 { 3, 6, 8 }}, // horiz=1, vert=0, diag=4 {{ 7, 7, 2 }, // horiz=1, vert=1, diag=0 { 7, 7, 5 }, // horiz=1, vert=1, diag=1 { 7, 7, 7 }, // horiz=1, vert=1, diag=2 { 7, 7, 8 }, // horiz=1, vert=1, diag=3 { 7, 7, 8 }}, // horiz=1, vert=1, diag=4 {{ 8, 7, 2 }, // horiz=1, vert=2, diag=0 { 8, 7, 5 }, // horiz=1, vert=2, diag=1 { 8, 7, 7 }, // horiz=1, vert=2, diag=2 { 8, 7, 8 }, // horiz=1, vert=2, diag=3 { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4 {{{ 4, 8, 2 }, // horiz=2, vert=0, diag=0 { 4, 8, 5 }, // horiz=2, vert=0, diag=1 { 4, 8, 7 }, // horiz=2, vert=0, diag=2 { 4, 8, 8 }, // horiz=2, vert=0, diag=3 { 4, 8, 8 }}, // horiz=2, vert=0, diag=4 {{ 7, 8, 2 }, // horiz=2, vert=1, diag=0 { 7, 8, 5 }, // horiz=2, vert=1, diag=1 { 7, 8, 7 }, // horiz=2, vert=1, diag=2 { 7, 8, 8 }, // horiz=2, vert=1, diag=3 { 7, 8, 8 }}, // horiz=2, vert=1, diag=4 {{ 8, 8, 2 }, // horiz=2, vert=2, diag=0 { 8, 8, 5 }, // horiz=2, vert=2, diag=1 { 8, 8, 7 }, // horiz=2, vert=2, diag=2 { 8, 8, 8 }, // horiz=2, vert=2, diag=3 { 8, 8, 8 }}} // horiz=2, vert=2, diag=4 }; // arithmetic decoder context and xor bit for the sign bit in the // significance propagation pass: // [horiz][vert][k] // where horiz/vert are offset by 2 (i.e., range is -2 .. 2) // and k = 0 for the context // = 1 for the xor bit static Guint signContext[5][5][2] = { {{ 13, 1 }, // horiz=-2, vert=-2 { 13, 1 }, // horiz=-2, vert=-1 { 12, 1 }, // horiz=-2, vert= 0 { 11, 1 }, // horiz=-2, vert=+1 { 11, 1 }}, // horiz=-2, vert=+2 {{ 13, 1 }, // horiz=-1, vert=-2 { 13, 1 }, // horiz=-1, vert=-1 { 12, 1 }, // horiz=-1, vert= 0 { 11, 1 }, // horiz=-1, vert=+1 { 11, 1 }}, // horiz=-1, vert=+2 {{ 10, 1 }, // horiz= 0, vert=-2 { 10, 1 }, // horiz= 0, vert=-1 { 9, 0 }, // horiz= 0, vert= 0 { 10, 0 }, // horiz= 0, vert=+1 { 10, 0 }}, // horiz= 0, vert=+2 {{ 11, 0 }, // horiz=+1, vert=-2 { 11, 0 }, // horiz=+1, vert=-1 { 12, 0 }, // horiz=+1, vert= 0 { 13, 0 }, // horiz=+1, vert=+1 { 13, 0 }}, // horiz=+1, vert=+2 {{ 11, 0 }, // horiz=+2, vert=-2 { 11, 0 }, // horiz=+2, vert=-1 { 12, 0 }, // horiz=+2, vert= 0 { 13, 0 }, // horiz=+2, vert=+1 { 13, 0 }}, // horiz=+2, vert=+2 }; //------------------------------------------------------------------------ // constants used in the IDWT #define idwtAlpha -1.586134342059924 #define idwtBeta -0.052980118572961 #define idwtGamma 0.882911075530934 #define idwtDelta 0.443506852043971 #define idwtKappa 1.230174104914001 #define idwtIKappa (1.0 / idwtKappa) // 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 //------------------------------------------------------------------------ // floor(x / y) #define jpxFloorDiv(x, y) ((x) / (y)) // floor(x / 2^y) #define jpxFloorDivPow2(x, y) ((x) >> (y)) // ceil(x / y) #define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y)) // ceil(x / 2^y) #define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y)) //------------------------------------------------------------------------ #if 1 //----- disable coverage tracking #define cover(idx) #else //----- enable coverage tracking class JPXCover { public: JPXCover(int sizeA); ~JPXCover(); void incr(int idx); private: int size, used; int *data; }; JPXCover::JPXCover(int sizeA) { size = sizeA; used = -1; data = (int *)gmallocn(size, sizeof(int)); memset(data, 0, size * sizeof(int)); } JPXCover::~JPXCover() { int i; printf("JPX coverage:\n"); for (i = 0; i <= used; ++i) { printf(" %4d: %8d\n", i, data[i]); } gfree(data); } void JPXCover::incr(int idx) { if (idx < size) { ++data[idx]; if (idx > used) { used = idx; } } } JPXCover jpxCover(150); #define cover(idx) jpxCover.incr(idx) #endif //----- coverage tracking //------------------------------------------------------------------------ JPXStream::JPXStream(Stream *strA): FilterStream(strA) { bufStr = new BufStream(str, 2); 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; byteCount = 0; } JPXStream::~JPXStream() { close(); delete bufStr; } void JPXStream::reset() { bufStr->reset(); if (readBoxes() == jpxDecodeFatalError) { // readBoxes reported an error, so we go immediately to EOF curY = img.ySizeR; } else { curY = img.yOffsetR; } curX = img.xOffsetR; curComp = 0; readBufLen = 0; } void JPXStream::close() { JPXTile *tile; JPXTileComp *tileComp; JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; Guint comp, i, k, r, pre, sb; gfree(bpc); bpc = NULL; if (havePalette) { gfree(palette.bpc); gfree(palette.c); havePalette = gFalse; } if (haveCompMap) { gfree(compMap.comp); gfree(compMap.type); gfree(compMap.pComp); haveCompMap = gFalse; } if (haveChannelDefn) { gfree(channelDefn.idx); gfree(channelDefn.type); gfree(channelDefn.assoc); haveChannelDefn = gFalse; } if (img.tiles) { for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { tile = &img.tiles[i]; if (tile->tileComps) { for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; gfree(tileComp->quantSteps); gfree(tileComp->data); gfree(tileComp->buf); if (tileComp->resLevels) { for (r = 0; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; if (resLevel->precincts) { for (pre = 0; pre < 1; ++pre) { precinct = &resLevel->precincts[pre]; if (precinct->subbands) { for (sb = 0; sb < (Guint)(r == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; gfree(subband->inclusion); gfree(subband->zeroBitPlane); if (subband->cbs) { for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) { cb = &subband->cbs[k]; gfree(cb->dataLen); gfree(cb->touched); if (cb->arithDecoder) { delete cb->arithDecoder; } if (cb->stats) { delete cb->stats; } } gfree(subband->cbs); } } gfree(precinct->subbands); } } gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts); } } gfree(img.tiles[i].tileComps[comp].resLevels); } } gfree(img.tiles[i].tileComps); } } gfree(img.tiles); img.tiles = NULL; } bufStr->close(); } int JPXStream::getChar() { int c; if (readBufLen < 8) { fillReadBuf(); } if (readBufLen == 8) { c = readBuf & 0xff; readBufLen = 0; } else if (readBufLen > 8) { c = (readBuf >> (readBufLen - 8)) & 0xff; readBufLen -= 8; } else if (readBufLen == 0) { c = EOF; } else { c = (readBuf << (8 - readBufLen)) & 0xff; readBufLen = 0; } return c; } int JPXStream::lookChar() { int c; if (readBufLen < 8) { fillReadBuf(); } if (readBufLen == 8) { c = readBuf & 0xff; } else if (readBufLen > 8) { c = (readBuf >> (readBufLen - 8)) & 0xff; } else if (readBufLen == 0) { c = EOF; } else { c = (readBuf << (8 - readBufLen)) & 0xff; } return c; } void JPXStream::fillReadBuf() { JPXTileComp *tileComp; Guint tileIdx, tx, ty; int pix, pixBits, k; GBool eol; do { if (curY >= img.ySizeR) { return; } 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.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 if (havePalette) { if (pix >= 0 && pix < palette.nEntries) { pix = palette.c[pix * palette.nComps + curComp]; } else { pix = 0; } pixBits = palette.bpc[curComp]; } if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) { #endif curComp = 0; if (++curX == img.xSizeR) { curX = img.xOffsetR; ++curY; eol = gTrue; } } if (pixBits == 8) { readBuf = (readBuf << 8) | (pix & 0xff); } else { readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1)); } readBufLen += pixBits; if (eol && (k = readBufLen & 7)) { readBuf <<= 8 - k; readBufLen += 8 - k; } } while (readBufLen < 8); } GString *JPXStream::getPSFilter(int psLevel, const char *indent) { return NULL; } GBool JPXStream::isBinary(GBool last) { return str->isBinary(gTrue); } void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) { Guint boxType, boxLen, dataLen, csEnum; Guint bpc1, dummy; int csMeth, csPrec, csPrec1, dummy2; StreamColorSpaceMode csMode1; GBool haveBPC, haveCSMode; csPrec = 0; // make gcc happy haveBPC = haveCSMode = gFalse; bufStr->reset(); if (bufStr->lookChar() == 0xff) { getImageParams2(bitsPerComponent, csMode); } else { while (readBoxHdr(&boxType, &boxLen, &dataLen)) { if (boxType == 0x6a703268) { // JP2 header cover(0); // skip the superbox } else if (boxType == 0x69686472) { // image header cover(1); if (readULong(&dummy) && readULong(&dummy) && readUWord(&dummy) && readUByte(&bpc1) && readUByte(&dummy) && readUByte(&dummy) && readUByte(&dummy)) { *bitsPerComponent = bpc1 + 1; haveBPC = gTrue; } } else if (boxType == 0x636F6C72) { // color specification cover(2); if (readByte(&csMeth) && readByte(&csPrec1) && readByte(&dummy2)) { if (csMeth == 1) { if (readULong(&csEnum)) { csMode1 = streamCSNone; if (csEnum == jpxCSBiLevel || csEnum == jpxCSGrayscale) { csMode1 = streamCSDeviceGray; } else if (csEnum == jpxCSCMYK) { csMode1 = streamCSDeviceCMYK; } else if (csEnum == jpxCSsRGB || csEnum == jpxCSCISesRGB || csEnum == jpxCSROMMRGB) { csMode1 = streamCSDeviceRGB; } if (csMode1 != streamCSNone && (!haveCSMode || csPrec1 > csPrec)) { *csMode = csMode1; csPrec = csPrec1; haveCSMode = gTrue; } if (dataLen > 7) { bufStr->discardChars(dataLen - 7); } } } else { if (dataLen > 3) { bufStr->discardChars(dataLen - 3); } } } } else if (boxType == 0x6A703263) { // codestream cover(3); if (!(haveBPC && haveCSMode)) { getImageParams2(bitsPerComponent, csMode); } break; } else { cover(4); bufStr->discardChars(dataLen); } } } bufStr->close(); } // Get image parameters from the codestream. void JPXStream::getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode) { int segType; Guint segLen, nComps1, bpc1, dummy; while (readMarkerHdr(&segType, &segLen)) { if (segType == 0x51) { // SIZ - image and tile size cover(5); if (readUWord(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readUWord(&nComps1) && readUByte(&bpc1)) { *bitsPerComponent = (bpc1 & 0x7f) + 1; // if there's no color space info, take a guess if (nComps1 == 1) { *csMode = streamCSDeviceGray; } else if (nComps1 == 3) { *csMode = streamCSDeviceRGB; } else if (nComps1 == 4) { *csMode = streamCSDeviceCMYK; } } break; } else { cover(6); if (segLen > 2) { bufStr->discardChars(segLen - 2); } } } } JPXDecodeResult JPXStream::readBoxes() { JPXDecodeResult result; Guint boxType, boxLen, dataLen; Guint bpc1, compression, unknownColorspace, ipr; Guint i, j; haveImgHdr = gFalse; // check for a naked JPEG 2000 codestream (without the JP2/JPX // wrapper) -- this appears to be a violation of the PDF spec, but // Acrobat allows it if (bufStr->lookChar() == 0xff) { cover(7); error(errSyntaxWarning, getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper"); if ((result = readCodestream(0)) == jpxDecodeFatalError) { return result; } nComps = img.nComps; bpc = (Guint *)gmallocn(nComps, sizeof(Guint)); for (i = 0; i < nComps; ++i) { bpc[i] = img.tiles[0].tileComps[i].prec; } width = img.xSize - img.xOffset; height = img.ySize - img.yOffset; return result; } while (readBoxHdr(&boxType, &boxLen, &dataLen)) { switch (boxType) { case 0x6a703268: // JP2 header // this is a grouping box ('superbox') which has no real // contents and doesn't appear to be used consistently, i.e., // some things which should be subboxes of the JP2 header box // show up outside of it - so we simply ignore the JP2 header // box cover(8); break; case 0x69686472: // image header cover(9); if (!readULong(&height) || !readULong(&width) || !readUWord(&nComps) || !readUByte(&bpc1) || !readUByte(&compression) || !readUByte(&unknownColorspace) || !readUByte(&ipr)) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } if (compression != 7) { error(errSyntaxError, getPos(), "Unknown compression type in JPX stream"); return jpxDecodeFatalError; } bpc = (Guint *)gmallocn(nComps, sizeof(Guint)); for (i = 0; i < nComps; ++i) { bpc[i] = bpc1; } haveImgHdr = gTrue; break; case 0x62706363: // bits per component cover(10); if (!haveImgHdr) { error(errSyntaxError, getPos(), "Found bits per component box before image header box in JPX stream"); return jpxDecodeFatalError; } if (dataLen != nComps) { error(errSyntaxError, getPos(), "Invalid bits per component box in JPX stream"); return jpxDecodeFatalError; } for (i = 0; i < nComps; ++i) { if (!readUByte(&bpc[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } } break; case 0x636F6C72: // color specification cover(11); if (!readColorSpecBox(dataLen)) { return jpxDecodeFatalError; } break; case 0x70636c72: // palette cover(12); if (!readUWord(&palette.nEntries) || !readUByte(&palette.nComps)) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); 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 jpxDecodeFatalError; } ++palette.bpc[i]; } for (i = 0; i < palette.nEntries; ++i) { for (j = 0; j < palette.nComps; ++j) { if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3, (palette.bpc[j] & 0x80) ? gTrue : gFalse, &palette.c[i * palette.nComps + j])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } } } 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)); compMap.pComp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint)); for (i = 0; i < compMap.nChannels; ++i) { if (!readUWord(&compMap.comp[i]) || !readUByte(&compMap.type[i]) || !readUByte(&compMap.pComp[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } } break; case 0x63646566: // channel definition cover(14); if (!readUWord(&channelDefn.nChannels)) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } haveChannelDefn = gTrue; channelDefn.idx = (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint)); channelDefn.type = (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint)); channelDefn.assoc = (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint)); for (i = 0; i < channelDefn.nChannels; ++i) { if (!readUWord(&channelDefn.idx[i]) || !readUWord(&channelDefn.type[i]) || !readUWord(&channelDefn.assoc[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } } break; case 0x6A703263: // contiguous codestream cover(15); if (!bpc) { error(errSyntaxError, getPos(), "JPX stream is missing the image header box"); } if (!haveCS) { error(errSyntaxError, getPos(), "JPX stream has no supported color spec"); } if ((result = readCodestream(dataLen)) != jpxDecodeOk) { return result; } break; default: cover(16); if (bufStr->discardChars(dataLen) != dataLen) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return jpxDecodeFatalError; } break; } } return jpxDecodeOk; } GBool JPXStream::readColorSpecBox(Guint dataLen) { JPXColorSpec newCS; Guint csApprox, csEnum; GBool ok; ok = gFalse; if (!readUByte(&newCS.meth) || !readByte(&newCS.prec) || !readUByte(&csApprox)) { goto err; } switch (newCS.meth) { case 1: // enumerated colorspace cover(17); if (!readULong(&csEnum)) { goto err; } newCS.enumerated.type = (JPXColorSpaceType)csEnum; switch (newCS.enumerated.type) { case jpxCSBiLevel: ok = gTrue; break; case jpxCSYCbCr1: ok = gTrue; break; case jpxCSYCbCr2: ok = gTrue; break; case jpxCSYCBCr3: ok = gTrue; break; case jpxCSPhotoYCC: ok = gTrue; break; case jpxCSCMY: ok = gTrue; break; case jpxCSCMYK: ok = gTrue; break; case jpxCSYCCK: ok = gTrue; break; case jpxCSCIELab: if (dataLen == 7 + 7*4) { if (!readULong(&newCS.enumerated.cieLab.rl) || !readULong(&newCS.enumerated.cieLab.ol) || !readULong(&newCS.enumerated.cieLab.ra) || !readULong(&newCS.enumerated.cieLab.oa) || !readULong(&newCS.enumerated.cieLab.rb) || !readULong(&newCS.enumerated.cieLab.ob) || !readULong(&newCS.enumerated.cieLab.il)) { goto err; } } else if (dataLen == 7) { //~ this assumes the 8-bit case cover(92); newCS.enumerated.cieLab.rl = 100; newCS.enumerated.cieLab.ol = 0; newCS.enumerated.cieLab.ra = 255; newCS.enumerated.cieLab.oa = 128; newCS.enumerated.cieLab.rb = 255; newCS.enumerated.cieLab.ob = 96; newCS.enumerated.cieLab.il = 0x00443530; } else { goto err; } ok = gTrue; break; case jpxCSsRGB: ok = gTrue; break; case jpxCSGrayscale: ok = gTrue; break; case jpxCSBiLevel2: ok = gTrue; break; case jpxCSCIEJab: // not allowed in PDF goto err; case jpxCSCISesRGB: ok = gTrue; break; case jpxCSROMMRGB: ok = gTrue; break; case jpxCSsRGBYCbCr: ok = gTrue; break; case jpxCSYPbPr1125: ok = gTrue; break; case jpxCSYPbPr1250: ok = gTrue; break; default: goto err; } break; case 2: // restricted ICC profile case 3: // any ICC profile (JPX) case 4: // vendor color (JPX) cover(18); if (dataLen > 3 && bufStr->discardChars(dataLen - 3) != dataLen - 3) { goto err; } break; } if (ok && (!haveCS || newCS.prec > cs.prec)) { cs = newCS; haveCS = gTrue; } return gTrue; err: error(errSyntaxError, getPos(), "Error in JPX color spec"); return gFalse; } JPXDecodeResult JPXStream::readCodestream(Guint len) { JPXTile *tile; JPXTileComp *tileComp; int segType; GBool haveSIZ, haveCOD, haveQCD, haveSOT, ok; Guint precinctSize, style; Guint segLen, capabilities, comp, i, j, r; //----- main header haveSIZ = haveCOD = haveQCD = haveSOT = gFalse; do { if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX codestream"); return jpxDecodeFatalError; } switch (segType) { case 0x4f: // SOC - start of codestream // marker only cover(19); break; case 0x51: // SIZ - image and tile size cover(20); if (haveSIZ) { error(errSyntaxError, getPos(), "Duplicate SIZ marker segment in JPX stream"); return jpxDecodeFatalError; } if (!readUWord(&capabilities) || !readULong(&img.xSize) || !readULong(&img.ySize) || !readULong(&img.xOffset) || !readULong(&img.yOffset) || !readULong(&img.xTileSize) || !readULong(&img.yTileSize) || !readULong(&img.xTileOffset) || !readULong(&img.yTileOffset) || !readUWord(&img.nComps)) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); return jpxDecodeFatalError; } if (haveImgHdr && img.nComps != nComps) { error(errSyntaxError, getPos(), "Different number of components in JPX SIZ marker segment"); return jpxDecodeFatalError; } if (img.xSize == 0 || img.ySize == 0 || img.xOffset >= img.xSize || img.yOffset >= img.ySize || img.xTileSize == 0 || img.yTileSize == 0 || img.xTileOffset > img.xOffset || img.yTileOffset > img.yOffset || img.xTileSize + img.xTileOffset <= img.xOffset || img.yTileSize + img.yTileOffset <= img.yOffset) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); 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) / img.yTileSize; // check for overflow before allocating memory if (img.nXTiles <= 0 || img.nYTiles <= 0 || img.nXTiles >= INT_MAX / img.nYTiles) { error(errSyntaxError, getPos(), "Bad tile count in JPX SIZ marker segment"); 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) { img.tiles[i].tileComps[comp].quantSteps = NULL; img.tiles[i].tileComps[comp].data = NULL; img.tiles[i].tileComps[comp].buf = NULL; img.tiles[i].tileComps[comp].resLevels = NULL; } } for (comp = 0; comp < img.nComps; ++comp) { if (!readUByte(&img.tiles[0].tileComps[comp].prec) || !readUByte(&img.tiles[0].tileComps[comp].hSep) || !readUByte(&img.tiles[0].tileComps[comp].vSep)) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); 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 jpxDecodeFatalError; } img.tiles[0].tileComps[comp].sgned = (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse; img.tiles[0].tileComps[comp].prec = (img.tiles[0].tileComps[comp].prec & 0x7f) + 1; for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp]; } } haveSIZ = gTrue; break; case 0x52: // COD - coding style default cover(21); if (!haveSIZ) { error(errSyntaxError, getPos(), "JPX COD marker segment before SIZ segment"); return jpxDecodeFatalError; } if (!readUByte(&img.tiles[0].tileComps[0].style) || !readUByte(&img.tiles[0].progOrder) || !readUWord(&img.tiles[0].nLayers) || !readUByte(&img.tiles[0].multiComp) || !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) || !readUByte(&img.tiles[0].tileComps[0].codeBlockW) || !readUByte(&img.tiles[0].tileComps[0].codeBlockH) || !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) || !readUByte(&img.tiles[0].tileComps[0].transform)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); 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 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) { if (i != 0) { img.tiles[i].progOrder = img.tiles[0].progOrder; img.tiles[i].nLayers = img.tiles[0].nLayers; img.tiles[i].multiComp = img.tiles[0].multiComp; } for (comp = 0; comp < img.nComps; ++comp) { if (!(i == 0 && comp == 0)) { img.tiles[i].tileComps[comp].style = img.tiles[0].tileComps[0].style; img.tiles[i].tileComps[comp].nDecompLevels = img.tiles[0].tileComps[0].nDecompLevels; img.tiles[i].tileComps[comp].codeBlockW = img.tiles[0].tileComps[0].codeBlockW; img.tiles[i].tileComps[comp].codeBlockH = img.tiles[0].tileComps[0].codeBlockH; img.tiles[i].tileComps[comp].codeBlockStyle = img.tiles[0].tileComps[0].codeBlockStyle; img.tiles[i].tileComps[comp].transform = img.tiles[0].tileComps[0].transform; } img.tiles[i].tileComps[comp].resLevels = (JPXResLevel *)gmallocn( (img.tiles[i].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL; } } } for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) { if (img.tiles[0].tileComps[0].style & 0x01) { cover(91); if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return jpxDecodeFatalError; } img.tiles[0].tileComps[0].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[0].tileComps[0].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15; img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15; } } for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { for (comp = 0; comp < img.nComps; ++comp) { if (!(i == 0 && comp == 0)) { for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = img.tiles[0].tileComps[0].resLevels[r].precinctWidth; img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = img.tiles[0].tileComps[0].resLevels[r].precinctHeight; } } } } haveCOD = gTrue; break; case 0x53: // COC - coding style component cover(22); if (!haveCOD) { error(errSyntaxError, getPos(), "JPX COC marker segment before COD segment"); return jpxDecodeFatalError; } if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&style) || !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) || !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) || !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) || !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) || !readUByte(&img.tiles[0].tileComps[comp].transform)) { error(errSyntaxError, getPos(), "Error in JPX COC marker segment"); 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 jpxDecodeFatalError; } img.tiles[0].tileComps[comp].style = (img.tiles[0].tileComps[comp].style & ~1) | (style & 1); img.tiles[0].tileComps[comp].codeBlockW += 2; img.tiles[0].tileComps[comp].codeBlockH += 2; for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { if (i != 0) { img.tiles[i].tileComps[comp].style = img.tiles[0].tileComps[comp].style; img.tiles[i].tileComps[comp].nDecompLevels = img.tiles[0].tileComps[comp].nDecompLevels; img.tiles[i].tileComps[comp].codeBlockW = img.tiles[0].tileComps[comp].codeBlockW; img.tiles[i].tileComps[comp].codeBlockH = img.tiles[0].tileComps[comp].codeBlockH; img.tiles[i].tileComps[comp].codeBlockStyle = img.tiles[0].tileComps[comp].codeBlockStyle; img.tiles[i].tileComps[comp].transform = img.tiles[0].tileComps[comp].transform; } img.tiles[i].tileComps[comp].resLevels = (JPXResLevel *)greallocn( img.tiles[i].tileComps[comp].resLevels, (img.tiles[i].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL; } } for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) { if (img.tiles[0].tileComps[comp].style & 0x01) { if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return jpxDecodeFatalError; } img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15; img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15; } } for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = img.tiles[0].tileComps[comp].resLevels[r].precinctWidth; img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = img.tiles[0].tileComps[comp].resLevels[r].precinctHeight; } } break; case 0x5c: // QCD - quantization default cover(23); if (!haveSIZ) { error(errSyntaxError, getPos(), "JPX QCD marker segment before SIZ segment"); return jpxDecodeFatalError; } if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return jpxDecodeFatalError; } if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) { if (segLen <= 3) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return jpxDecodeFatalError; } img.tiles[0].tileComps[0].nQuantSteps = segLen - 3; img.tiles[0].tileComps[0].quantSteps = (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return jpxDecodeFatalError; } } } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) { img.tiles[0].tileComps[0].nQuantSteps = 1; img.tiles[0].tileComps[0].quantSteps = (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(Guint)); if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); 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 jpxDecodeFatalError; } img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2; img.tiles[0].tileComps[0].quantSteps = (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return jpxDecodeFatalError; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return jpxDecodeFatalError; } for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { for (comp = 0; comp < img.nComps; ++comp) { if (!(i == 0 && comp == 0)) { img.tiles[i].tileComps[comp].quantStyle = img.tiles[0].tileComps[0].quantStyle; img.tiles[i].tileComps[comp].nQuantSteps = img.tiles[0].tileComps[0].nQuantSteps; img.tiles[i].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(Guint)); for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) { img.tiles[i].tileComps[comp].quantSteps[j] = img.tiles[0].tileComps[0].quantSteps[j]; } } } } haveQCD = gTrue; break; case 0x5d: // QCC - quantization component cover(24); if (!haveQCD) { error(errSyntaxError, getPos(), "JPX QCC marker segment before QCD segment"); 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 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 jpxDecodeFatalError; } img.tiles[0].tileComps[comp].nQuantSteps = segLen - (img.nComps > 256 ? 5 : 4); img.tiles[0].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return jpxDecodeFatalError; } } } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) { img.tiles[0].tileComps[comp].nQuantSteps = 1; img.tiles[0].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(Guint)); if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); 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 jpxDecodeFatalError; } img.tiles[0].tileComps[comp].nQuantSteps = (segLen - (img.nComps > 256 ? 5 : 4)) / 2; img.tiles[0].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return jpxDecodeFatalError; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return jpxDecodeFatalError; } for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { img.tiles[i].tileComps[comp].quantStyle = img.tiles[0].tileComps[comp].quantStyle; img.tiles[i].tileComps[comp].nQuantSteps = img.tiles[0].tileComps[comp].nQuantSteps; img.tiles[i].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(Guint)); for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) { img.tiles[i].tileComps[comp].quantSteps[j] = img.tiles[0].tileComps[comp].quantSteps[j]; } } break; case 0x5e: // RGN - region of interest cover(25); #if 1 //~ ROI is unimplemented error(errUnimplemented, -1, "got a JPX RGN segment"); 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)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&compInfo[comp].defROI.style) || !readUByte(&compInfo[comp].defROI.shift)) { error(errSyntaxError, getPos(), "Error in JPX RGN marker segment"); return jpxDecodeFatalError; } #endif break; case 0x5f: // POC - progression order change cover(26); #if 1 //~ progression order changes are unimplemented error(errUnimplemented, -1, "got a JPX POC segment"); 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); progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder)); for (i = 0; i < nProgs; ++i) { if (!readUByte(&progs[i].startRes) || !(img.nComps > 256 && readUWord(&progs[i].startComp)) || !(img.nComps <= 256 && readUByte(&progs[i].startComp)) || !readUWord(&progs[i].endLayer) || !readUByte(&progs[i].endRes) || !(img.nComps > 256 && readUWord(&progs[i].endComp)) || !(img.nComps <= 256 && readUByte(&progs[i].endComp)) || !readUByte(&progs[i].progOrder)) { error(errSyntaxError, getPos(), "Error in JPX POC marker segment"); return jpxDecodeFatalError; } } #endif break; case 0x60: // PPM - packed packet headers, main header cover(27); #if 1 //~ packed packet headers are unimplemented error(errUnimplemented, -1, "Got a JPX PPM segment"); 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); 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); 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); 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); 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 cover(32); haveSOT = gTrue; break; default: cover(33); error(errSyntaxError, getPos(), "Unknown marker segment {0:02x} in JPX stream", segType); if (segLen > 2) { bufStr->discardChars(segLen - 2); } break; } } while (!haveSOT); if (!haveSIZ) { error(errSyntaxError, getPos(), "Missing SIZ marker segment in JPX stream"); return jpxDecodeFatalError; } if (!haveCOD) { error(errSyntaxError, getPos(), "Missing COD marker segment in JPX stream"); return jpxDecodeFatalError; } if (!haveQCD) { error(errSyntaxError, getPos(), "Missing QCD marker segment in JPX stream"); return jpxDecodeFatalError; } //----- read the tile-parts ok = gTrue; while (1) { if (!readTilePart()) { ok = gFalse; break; } if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX codestream"); ok = gFalse; break; } if (segType != 0x90) { // SOT - start of tile break; } } if (segType != 0xd9) { // EOC - end of codestream error(errSyntaxError, getPos(), "Missing EOC marker in JPX codestream"); ok = gFalse; } //----- finish decoding the image for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { tile = &img.tiles[i]; if (!tile->init) { error(errSyntaxError, getPos(), "Uninitialized tile in JPX codestream"); return jpxDecodeFatalError; } for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; inverseTransform(tileComp); } if (!inverseMultiCompAndDC(tile)) { return jpxDecodeFatalError; } } //~ can free memory below tileComps here, and also tileComp.buf return ok ? jpxDecodeOk : jpxDecodeNonFatalError; } GBool JPXStream::readTilePart() { JPXTile *tile; JPXTileComp *tileComp; JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; int *sbCoeffs; GBool haveSOD; Guint tileIdx, tilePartLen, tilePartIdx, nTileParts; GBool tilePartToEOC; Guint precinctSize, style; Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen; Guint i, j, k, cbX, cbY, r, pre, sb, cbi, cbj; int segType, level; // process the SOT marker segment if (!readUWord(&tileIdx) || !readULong(&tilePartLen) || !readUByte(&tilePartIdx) || !readUByte(&nTileParts)) { error(errSyntaxError, getPos(), "Error in JPX SOT marker segment"); return gFalse; } // 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 haveSOD = gFalse; do { if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX tile-part codestream"); return gFalse; } tilePartLen -= 2 + segLen; switch (segType) { case 0x52: // COD - coding style default cover(34); if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) || !readUByte(&img.tiles[tileIdx].progOrder) || !readUWord(&img.tiles[tileIdx].nLayers) || !readUByte(&img.tiles[tileIdx].multiComp) || !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) || !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) || !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) || !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) || !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return gFalse; } if (img.tiles[tileIdx].tileComps[0].nDecompLevels > 32 || img.tiles[tileIdx].tileComps[0].codeBlockW > 8 || img.tiles[tileIdx].tileComps[0].codeBlockH > 8) { 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) { if (comp != 0) { img.tiles[tileIdx].tileComps[comp].style = img.tiles[tileIdx].tileComps[0].style; img.tiles[tileIdx].tileComps[comp].nDecompLevels = img.tiles[tileIdx].tileComps[0].nDecompLevels; img.tiles[tileIdx].tileComps[comp].codeBlockW = img.tiles[tileIdx].tileComps[0].codeBlockW; img.tiles[tileIdx].tileComps[comp].codeBlockH = img.tiles[tileIdx].tileComps[0].codeBlockH; img.tiles[tileIdx].tileComps[comp].codeBlockStyle = img.tiles[tileIdx].tileComps[0].codeBlockStyle; img.tiles[tileIdx].tileComps[comp].transform = img.tiles[tileIdx].tileComps[0].transform; } img.tiles[tileIdx].tileComps[comp].resLevels = (JPXResLevel *)greallocn( img.tiles[tileIdx].tileComps[comp].resLevels, (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL; } } for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) { if (img.tiles[tileIdx].tileComps[0].style & 0x01) { if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15; img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15; } } for (comp = 1; comp < img.nComps; ++comp) { for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth; img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight; } } break; case 0x53: // COC - coding style component cover(35); if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&style) || !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) || !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) || !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) || !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) || !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) { error(errSyntaxError, getPos(), "Error in JPX COC marker segment"); return gFalse; } if (img.tiles[tileIdx].tileComps[comp].nDecompLevels > 32 || img.tiles[tileIdx].tileComps[comp].codeBlockW > 8 || img.tiles[tileIdx].tileComps[comp].codeBlockH > 8) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[comp].style = (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1); img.tiles[tileIdx].tileComps[comp].codeBlockW += 2; img.tiles[tileIdx].tileComps[comp].codeBlockH += 2; img.tiles[tileIdx].tileComps[comp].resLevels = (JPXResLevel *)greallocn( img.tiles[tileIdx].tileComps[comp].resLevels, (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL; } for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { if (img.tiles[tileIdx].tileComps[comp].style & 0x01) { if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15; img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15; } } break; case 0x5c: // QCD - quantization default cover(36); if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) { if (segLen <= 3) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[0].nQuantSteps = segLen - 3; img.tiles[tileIdx].tileComps[0].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } } } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) { img.tiles[tileIdx].tileComps[0].nQuantSteps = 1; img.tiles[tileIdx].tileComps[0].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(Guint)); if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) { if (segLen < 5) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2; img.tiles[tileIdx].tileComps[0].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } for (comp = 1; comp < img.nComps; ++comp) { img.tiles[tileIdx].tileComps[comp].quantStyle = img.tiles[tileIdx].tileComps[0].quantStyle; img.tiles[tileIdx].tileComps[comp].nQuantSteps = img.tiles[tileIdx].tileComps[0].nQuantSteps; img.tiles[tileIdx].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(Guint)); for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) { img.tiles[tileIdx].tileComps[comp].quantSteps[j] = img.tiles[tileIdx].tileComps[0].quantSteps[j]; } } break; case 0x5d: // QCC - quantization component cover(37); if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return gFalse; } if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) { if (segLen <= (img.nComps > 256 ? 5U : 4U)) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[comp].nQuantSteps = segLen - (img.nComps > 256 ? 5 : 4); img.tiles[tileIdx].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[comp].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return gFalse; } } } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x01) { img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1; img.tiles[tileIdx].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[comp].nQuantSteps, sizeof(Guint)); if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return gFalse; } } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x02) { if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return gFalse; } img.tiles[tileIdx].tileComps[comp].nQuantSteps = (segLen - (img.nComps > 256 ? 5 : 4)) / 2; img.tiles[tileIdx].tileComps[comp].quantSteps = (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[comp].nQuantSteps, sizeof(Guint)); for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return gFalse; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return gFalse; } break; case 0x5e: // RGN - region of interest cover(38); #if 1 //~ ROI is unimplemented error(errUnimplemented, -1, "Got a JPX RGN segment"); 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)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&compInfo[comp].roi.style) || !readUByte(&compInfo[comp].roi.shift)) { error(errSyntaxError, getPos(), "Error in JPX RGN marker segment"); return gFalse; } #endif break; case 0x5f: // POC - progression order change cover(39); #if 1 //~ progression order changes are unimplemented error(errUnimplemented, -1, "Got a JPX POC segment"); 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); tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder)); for (i = 0; i < nTileProgs; ++i) { if (!readUByte(&tileProgs[i].startRes) || !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) || !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) || !readUWord(&tileProgs[i].endLayer) || !readUByte(&tileProgs[i].endRes) || !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) || !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) || !readUByte(&tileProgs[i].progOrder)) { error(errSyntaxError, getPos(), "Error in JPX POC marker segment"); return gFalse; } } #endif break; case 0x61: // PPT - packed packet headers, tile-part hdr cover(40); #if 1 //~ packed packet headers are unimplemented error(errUnimplemented, -1, "Got a JPX PPT segment"); 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); 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); 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 cover(43); haveSOD = gTrue; break; default: cover(44); error(errSyntaxError, getPos(), "Unknown marker segment {0:02x} in JPX tile-part stream", segType); if (segLen > 2) { bufStr->discardChars(segLen - 2); } break; } } while (!haveSOD); //----- initialize the tile, precincts, and code-blocks if (tilePartIdx == 0) { tile = &img.tiles[tileIdx]; tile->init = gTrue; i = tileIdx / img.nXTiles; j = tileIdx % img.nXTiles; if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) { tile->x0 = img.xOffset; } if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) { tile->y0 = img.yOffset; } if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) { tile->x1 = img.xSize; } if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) { tile->y1 = img.ySize; } tile->comp = 0; tile->res = 0; tile->precinct = 0; tile->layer = 0; tile->maxNDecompLevels = 0; for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; if (tileComp->nDecompLevels > tile->maxNDecompLevels) { tile->maxNDecompLevels = tileComp->nDecompLevels; } tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep); tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep); tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep); tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep); tileComp->cbW = 1 << tileComp->codeBlockW; tileComp->cbH = 1 << tileComp->codeBlockH; 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 { n = tileComp->y1 - tileComp->y0; } tileComp->buf = (int *)gmallocn(n + 8, sizeof(int)); for (r = 0; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; k = r == 0 ? tileComp->nDecompLevels : tileComp->nDecompLevels - r + 1; resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k); resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k); resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k); resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k); if (r == 0) { resLevel->bx0[0] = resLevel->x0; resLevel->by0[0] = resLevel->y0; resLevel->bx1[0] = resLevel->x1; resLevel->by1[0] = resLevel->y1; } else { resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); resLevel->by0[0] = resLevel->y0; resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); resLevel->by1[0] = resLevel->y1; resLevel->bx0[1] = resLevel->x0; resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); resLevel->bx1[1] = resLevel->x1; resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); } resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct)); for (pre = 0; pre < 1; ++pre) { resLevel->precincts[pre].subbands = NULL; } for (pre = 0; pre < 1; ++pre) { precinct = &resLevel->precincts[pre]; precinct->x0 = resLevel->x0; precinct->y0 = resLevel->y0; precinct->x1 = resLevel->x1; precinct->y1 = resLevel->y1; nSBs = r == 0 ? 1 : 3; precinct->subbands = (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband)); for (sb = 0; sb < nSBs; ++sb) { 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]; subband->x1 = resLevel->bx1[sb]; subband->y1 = resLevel->by1[sb]; subband->nXCBs = jpxCeilDivPow2(subband->x1, tileComp->codeBlockW) - jpxFloorDivPow2(subband->x0, tileComp->codeBlockW); subband->nYCBs = jpxCeilDivPow2(subband->y1, tileComp->codeBlockH) - jpxFloorDivPow2(subband->y0, tileComp->codeBlockH); n = subband->nXCBs > subband->nYCBs ? subband->nXCBs : subband->nYCBs; for (subband->maxTTLevel = 0, --n; n; ++subband->maxTTLevel, n >>= 1) ; n = 0; for (level = subband->maxTTLevel; level >= 0; --level) { nx = jpxCeilDivPow2(subband->nXCBs, level); ny = jpxCeilDivPow2(subband->nYCBs, level); n += nx * ny; } subband->inclusion = (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); subband->zeroBitPlane = (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); for (k = 0; k < n; ++k) { subband->inclusion[k].finished = gFalse; subband->inclusion[k].val = 0; subband->zeroBitPlane[k].finished = gFalse; subband->zeroBitPlane[k].val = 0; } subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs * subband->nYCBs, sizeof(JPXCodeBlock)); 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 sbCoeffs = tileComp->data; } else if (sb == 0) { // (NL-r+1)HL sbCoeffs = tileComp->data + resLevel->bx1[1] - resLevel->bx0[1]; } else if (sb == 1) { // (NL-r+1)LH sbCoeffs = tileComp->data + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w; } else { // (NL-r+1)HH sbCoeffs = tileComp->data + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w + resLevel->bx1[1] - resLevel->bx0[1]; } cb = subband->cbs; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW; cb->x1 = cb->x0 + tileComp->cbW; if (subband->x0 > cb->x0) { cb->x0 = subband->x0; } if (subband->x1 < cb->x1) { cb->x1 = subband->x1; } cb->y0 = (sby0 + cbY) << tileComp->codeBlockH; cb->y1 = cb->y0 + tileComp->cbH; if (subband->y0 > cb->y0) { cb->y0 = subband->y0; } if (subband->y1 < cb->y1) { cb->y1 = subband->y1; } cb->seen = gFalse; cb->lBlock = 3; cb->nextPass = jpxPassCleanup; cb->nZeroBitPlanes = 0; cb->dataLenSize = 1; cb->dataLen = (Guint *)gmalloc(sizeof(Guint)); 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; } ++cb; } } } } } } } return readTilePartData(tileIdx, tilePartLen, tilePartToEOC); } GBool JPXStream::readTilePartData(Guint tileIdx, Guint tilePartLen, GBool tilePartToEOC) { JPXTile *tile; JPXTileComp *tileComp; JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; Guint ttVal; Guint bits, cbX, cbY, nx, ny, i, j, n, sb; int level; tile = &img.tiles[tileIdx]; // read all packets from this tile-part while (1) { if (tilePartToEOC) { //~ peek for an EOC marker cover(93); } else if (tilePartLen == 0) { break; } tileComp = &tile->tileComps[tile->comp]; resLevel = &tileComp->resLevels[tile->res]; precinct = &resLevel->precincts[tile->precinct]; //----- packet header // setup startBitBuf(tilePartLen); if (tileComp->style & 0x02) { skipSOP(); } // zero-length flag if (!readBits(1, &bits)) { goto err; } if (!bits) { // packet is empty -- clear all code-block inclusion flags cover(45); for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb = &subband->cbs[cbY * subband->nXCBs + cbX]; cb->included = gFalse; } } } } else { for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb = &subband->cbs[cbY * subband->nXCBs + cbX]; // skip code-blocks with no coefficients if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) { cover(46); cb->included = gFalse; continue; } // code-block inclusion if (cb->seen) { cover(47); if (!readBits(1, &cb->included)) { goto err; } } else { cover(48); ttVal = 0; i = 0; for (level = subband->maxTTLevel; level >= 0; --level) { nx = jpxCeilDivPow2(subband->nXCBs, level); ny = jpxCeilDivPow2(subband->nYCBs, level); j = i + (cbY >> level) * nx + (cbX >> level); if (!subband->inclusion[j].finished && !subband->inclusion[j].val) { subband->inclusion[j].val = ttVal; } else { ttVal = subband->inclusion[j].val; } while (!subband->inclusion[j].finished && ttVal <= tile->layer) { if (!readBits(1, &bits)) { goto err; } if (bits == 1) { subband->inclusion[j].finished = gTrue; } else { ++ttVal; } } subband->inclusion[j].val = ttVal; if (ttVal > tile->layer) { break; } i += nx * ny; } cb->included = level < 0; } if (cb->included) { cover(49); // zero bit-plane count if (!cb->seen) { cover(50); ttVal = 0; i = 0; for (level = subband->maxTTLevel; level >= 0; --level) { nx = jpxCeilDivPow2(subband->nXCBs, level); ny = jpxCeilDivPow2(subband->nYCBs, level); j = i + (cbY >> level) * nx + (cbX >> level); if (!subband->zeroBitPlane[j].finished && !subband->zeroBitPlane[j].val) { subband->zeroBitPlane[j].val = ttVal; } else { ttVal = subband->zeroBitPlane[j].val; } while (!subband->zeroBitPlane[j].finished) { if (!readBits(1, &bits)) { goto err; } if (bits == 1) { subband->zeroBitPlane[j].finished = gTrue; } else { ++ttVal; } } subband->zeroBitPlane[j].val = ttVal; i += nx * ny; } cb->nZeroBitPlanes = ttVal; } // number of coding passes if (!readBits(1, &bits)) { goto err; } if (bits == 0) { cover(51); cb->nCodingPasses = 1; } else { if (!readBits(1, &bits)) { goto err; } if (bits == 0) { cover(52); cb->nCodingPasses = 2; } else { cover(53); if (!readBits(2, &bits)) { goto err; } if (bits < 3) { cover(54); cb->nCodingPasses = 3 + bits; } else { cover(55); if (!readBits(5, &bits)) { goto err; } if (bits < 31) { cover(56); cb->nCodingPasses = 6 + bits; } else { cover(57); if (!readBits(7, &bits)) { goto err; } cb->nCodingPasses = 37 + bits; } } } } // update Lblock while (1) { if (!readBits(1, &bits)) { goto err; } if (!bits) { break; } ++cb->lBlock; } // one codeword segment for each of the coding passes if (tileComp->codeBlockStyle & 0x04) { if (cb->nCodingPasses > cb->dataLenSize) { cb->dataLenSize = cb->nCodingPasses; cb->dataLen = (Guint *)greallocn(cb->dataLen, cb->dataLenSize, sizeof(Guint)); } // read the lengths for (i = 0; i < cb->nCodingPasses; ++i) { if (!readBits(cb->lBlock, &cb->dataLen[i])) { goto err; } } // one codeword segment for all of the coding passes } else { // read the length for (n = cb->lBlock, i = cb->nCodingPasses >> 1; i; ++n, i >>= 1) ; if (!readBits(n, &cb->dataLen[0])) { goto err; } } } } } } } if (tileComp->style & 0x04) { skipEPH(); } tilePartLen = finishBitBuf(); //----- packet data for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb = &subband->cbs[cbY * subband->nXCBs + cbX]; if (cb->included) { if (!readCodeBlockData(tileComp, resLevel, precinct, subband, tile->res, sb, cb)) { return gFalse; } if (tileComp->codeBlockStyle & 0x04) { for (i = 0; i < cb->nCodingPasses; ++i) { tilePartLen -= cb->dataLen[i]; } } else { tilePartLen -= cb->dataLen[0]; } cb->seen = gTrue; } } } } //----- next packet switch (tile->progOrder) { case 0: // layer, resolution level, component, precinct cover(58); if (++tile->comp == img.nComps) { tile->comp = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->layer == tile->nLayers) { tile->layer = 0; } } } break; case 1: // resolution level, layer, component, precinct cover(59); if (++tile->comp == img.nComps) { tile->comp = 0; if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; } } } break; case 2: // resolution level, precinct, component, layer //~ this isn't correct -- see B.12.1.3 cover(60); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->comp == img.nComps) { tile->comp = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; } } } break; case 3: // precinct, component, resolution level, layer //~ this isn't correct -- see B.12.1.4 cover(61); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; } } } break; case 4: // component, precinct, resolution level, layer //~ this isn't correct -- see B.12.1.5 cover(62); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; } } } break; } } return gTrue; err: error(errSyntaxError, getPos(), "Error in JPX stream"); return gFalse; } GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, JPXResLevel *resLevel, JPXPrecinct *precinct, JPXSubband *subband, Guint res, Guint sb, JPXCodeBlock *cb) { int *coeff0, *coeff1, *coeff; char *touched0, *touched1, *touched; Guint horiz, vert, diag, all, cx, xorBit; int horizSign, vertSign, bit; int segSym; 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); cb->arithDecoder->restart(cb->dataLen[0]); } else { cover(64); cb->arithDecoder = new JArithmeticDecoder(); cb->arithDecoder->setStream(bufStr, cb->dataLen[0]); cb->arithDecoder->start(); cb->stats = new JArithmeticDecoderStats(jpxNContexts); cb->stats->setEntry(jpxContextSigProp, 4, 0); cb->stats->setEntry(jpxContextRunLength, 3, 0); cb->stats->setEntry(jpxContextUniform, 46, 0); } for (i = 0; i < cb->nCodingPasses; ++i) { if ((tileComp->codeBlockStyle & 0x04) && i > 0) { cb->arithDecoder->setStream(bufStr, cb->dataLen[i]); cb->arithDecoder->start(); } switch (cb->nextPass) { //----- significance propagation pass case jpxPassSigProp: cover(65); for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, touched0 += 4 << tileComp->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { for (y1 = 0, coeff = coeff1, touched = touched1; y1 < 4 && y0+y1 < cb->y1; ++y1, coeff += tileComp->w, touched += tileComp->cbW) { if (!*coeff) { horiz = vert = diag = 0; horizSign = vertSign = 2; if (x > cb->x0) { if (coeff[-1]) { ++horiz; horizSign += coeff[-1] < 0 ? -1 : 1; } if (y0+y1 > cb->y0) { diag += coeff[-(int)tileComp->w - 1] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w - 1] ? 1 : 0; } } if (x < cb->x1 - 1) { if (coeff[1]) { ++horiz; horizSign += coeff[1] < 0 ? -1 : 1; } if (y0+y1 > cb->y0) { diag += coeff[-(int)tileComp->w + 1] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w + 1] ? 1 : 0; } } if (y0+y1 > cb->y0) { if (coeff[-(int)tileComp->w]) { ++vert; vertSign += coeff[-(int)tileComp->w] < 0 ? -1 : 1; } } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { if (coeff[tileComp->w]) { ++vert; vertSign += coeff[tileComp->w] < 0 ? -1 : 1; } } cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; if (cx != 0) { if (cb->arithDecoder->decodeBit(cx, cb->stats)) { cx = signContext[horizSign][vertSign][0]; xorBit = signContext[horizSign][vertSign][1]; if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { *coeff = -1; } else { *coeff = 1; } } *touched = 1; } } } } } ++cb->nextPass; break; //----- magnitude refinement pass case jpxPassMagRef: cover(66); for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, touched0 += 4 << tileComp->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { for (y1 = 0, coeff = coeff1, touched = touched1; y1 < 4 && y0+y1 < cb->y1; ++y1, coeff += tileComp->w, touched += tileComp->cbW) { if (*coeff && !*touched) { if (*coeff == 1 || *coeff == -1) { all = 0; if (x > cb->x0) { all += coeff[-1] ? 1 : 0; if (y0+y1 > cb->y0) { all += coeff[-(int)tileComp->w - 1] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { all += coeff[tileComp->w - 1] ? 1 : 0; } } if (x < cb->x1 - 1) { all += coeff[1] ? 1 : 0; if (y0+y1 > cb->y0) { all += coeff[-(int)tileComp->w + 1] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { all += coeff[tileComp->w + 1] ? 1 : 0; } } if (y0+y1 > cb->y0) { all += coeff[-(int)tileComp->w] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { all += coeff[tileComp->w] ? 1 : 0; } cx = all ? 15 : 14; } else { cx = 16; } bit = cb->arithDecoder->decodeBit(cx, cb->stats); if (*coeff < 0) { *coeff = (*coeff << 1) - bit; } else { *coeff = (*coeff << 1) + bit; } *touched = 1; } } } } ++cb->nextPass; break; //----- cleanup pass case jpxPassCleanup: cover(67); for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, touched0 += 4 << tileComp->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { y1 = 0; if (y0 + 3 < cb->y1 && !(*touched1) && !(touched1[tileComp->cbW]) && !(touched1[2 * tileComp->cbW]) && !(touched1[3 * tileComp->cbW]) && (x == cb->x0 || y0 == cb->y0 || !coeff1[-(int)tileComp->w - 1]) && (y0 == cb->y0 || !coeff1[-(int)tileComp->w]) && (x == cb->x1 - 1 || y0 == cb->y0 || !coeff1[-(int)tileComp->w + 1]) && (x == cb->x0 || (!coeff1[-1] && !coeff1[tileComp->w - 1] && !coeff1[2 * tileComp->w - 1] && !coeff1[3 * tileComp->w - 1])) && (x == cb->x1 - 1 || (!coeff1[1] && !coeff1[tileComp->w + 1] && !coeff1[2 * tileComp->w + 1] && !coeff1[3 * tileComp->w + 1])) && ((tileComp->codeBlockStyle & 0x08) || ((x == cb->x0 || y0+4 == cb->y1 || !coeff1[4 * tileComp->w - 1]) && (y0+4 == cb->y1 || !coeff1[4 * tileComp->w]) && (x == cb->x1 - 1 || y0+4 == cb->y1 || !coeff1[4 * tileComp->w + 1])))) { if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) { y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats); y1 = (y1 << 1) | cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats); coeff = &coeff1[y1 * tileComp->w]; cx = signContext[2][2][0]; xorBit = signContext[2][2][1]; if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { *coeff = -1; } else { *coeff = 1; } ++y1; } else { y1 = 4; } } for (coeff = &coeff1[y1 * tileComp->w], touched = &touched1[y1 << tileComp->codeBlockW]; y1 < 4 && y0 + y1 < cb->y1; ++y1, coeff += tileComp->w, touched += tileComp->cbW) { if (!*touched) { horiz = vert = diag = 0; horizSign = vertSign = 2; if (x > cb->x0) { if (coeff[-1]) { ++horiz; horizSign += coeff[-1] < 0 ? -1 : 1; } if (y0+y1 > cb->y0) { diag += coeff[-(int)tileComp->w - 1] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w - 1] ? 1 : 0; } } if (x < cb->x1 - 1) { if (coeff[1]) { ++horiz; horizSign += coeff[1] < 0 ? -1 : 1; } if (y0+y1 > cb->y0) { diag += coeff[-(int)tileComp->w + 1] ? 1 : 0; } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w + 1] ? 1 : 0; } } if (y0+y1 > cb->y0) { if (coeff[-(int)tileComp->w]) { ++vert; vertSign += coeff[-(int)tileComp->w] < 0 ? -1 : 1; } } if (y0+y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { if (coeff[tileComp->w]) { ++vert; vertSign += coeff[tileComp->w] < 0 ? -1 : 1; } } cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; if (cb->arithDecoder->decodeBit(cx, cb->stats)) { cx = signContext[horizSign][vertSign][0]; xorBit = signContext[horizSign][vertSign][1]; if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { *coeff = -1; } else { *coeff = 1; } } } else { *touched = 0; } } } } ++cb->len; // look for a segmentation symbol if (tileComp->codeBlockStyle & 0x20) { segSym = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats) << 3; segSym |= cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats) << 2; segSym |= cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats) << 1; segSym |= cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats); if (segSym != 0x0a) { // in theory this should be a fatal error, but it seems to // be problematic error(errSyntaxWarning, getPos(), "Missing or invalid segmentation symbol in JPX stream"); } } cb->nextPass = jpxPassSigProp; break; } if (tileComp->codeBlockStyle & 0x02) { cb->stats->reset(); cb->stats->setEntry(jpxContextSigProp, 4, 0); cb->stats->setEntry(jpxContextRunLength, 3, 0); cb->stats->setEntry(jpxContextUniform, 46, 0); } if (tileComp->codeBlockStyle & 0x04) { cb->arithDecoder->cleanup(); } } cb->arithDecoder->cleanup(); return gTrue; } // Inverse quantization, and wavelet transform (IDWT). This also does // the initial shift to convert to fixed point format. void JPXStream::inverseTransform(JPXTileComp *tileComp) { JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; int *coeff0, *coeff; char *touched0, *touched; Guint qStyle, guard, eps, shift; int shift2; double mu; int val; Guint r, cbX, cbY, x, y; cover(68); //----- (NL)LL subband (resolution level 0) resLevel = &tileComp->resLevels[0]; precinct = &resLevel->precincts[0]; subband = &precinct->subbands[0]; // i-quant parameters qStyle = tileComp->quantStyle & 0x1f; guard = (tileComp->quantStyle >> 5) & 7; if (qStyle == 0) { cover(69); eps = (tileComp->quantSteps[0] >> 3) & 0x1f; shift = guard + eps - 1; mu = 0; // make gcc happy } else { cover(70); shift = guard - 1 + tileComp->prec; mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0; } if (tileComp->transform == 0) { cover(71); shift += fracBits - tileComp->prec; } // do fixed point adjustment and dequantization on (NL)LL cb = subband->cbs; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y < cb->y1; ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) { for (x = cb->x0, coeff = coeff0, touched = touched0; x < cb->x1; ++x, ++coeff, ++touched) { val = *coeff; if (val != 0) { shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); if (shift2 > 0) { cover(94); if (val < 0) { val = (val << shift2) - (1 << (shift2 - 1)); } else { val = (val << shift2) + (1 << (shift2 - 1)); } } else { cover(95); val >>= -shift2; } if (qStyle == 0) { cover(96); if (tileComp->transform == 0) { cover(97); val &= -1 << (fracBits - tileComp->prec); } } else { cover(98); val = (int)((double)val * mu); } } *coeff = val; } } ++cb; } } //----- IDWT for each level for (r = 1; r <= tileComp->nDecompLevels - reduction; ++r) { resLevel = &tileComp->resLevels[r]; // (n)LL is already in the upper-left corner of the // tile-component data array -- interleave with (n)HL/LH/HH // and inverse transform to get (n-1)LL, which will be stored // in the upper-left corner of the tile-component data array inverseTransformLevel(tileComp, r, resLevel); } } // Do one level of the inverse transform: // - take (n)LL, (n)HL, (n)LH, and (n)HH from the upper-left corner // of the tile-component data array // - leave the resulting (n-1)LL in the same place void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, Guint r, JPXResLevel *resLevel) { JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; int *coeff0, *coeff; char *touched0, *touched; Guint qStyle, guard, eps, shift, t; int shift2; double mu; int val; int *dataPtr, *bufPtr; Guint nx1, nx2, ny1, ny2, offset; Guint x, y, sb, cbX, cbY; //----- fixed-point adjustment and dequantization qStyle = tileComp->quantStyle & 0x1f; guard = (tileComp->quantStyle >> 5) & 7; precinct = &resLevel->precincts[0]; for (sb = 0; sb < 3; ++sb) { // i-quant parameters if (qStyle == 0) { cover(100); eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f; shift = guard + eps - 1; mu = 0; // make gcc happy } else { cover(101); shift = guard + tileComp->prec; if (sb == 2) { cover(102); ++shift; } t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)]; mu = (double)(0x800 + (t & 0x7ff)) / 2048.0; } if (tileComp->transform == 0) { cover(103); shift += fracBits - tileComp->prec; } // fixed point adjustment and dequantization subband = &precinct->subbands[sb]; cb = subband->cbs; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y < cb->y1; ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) { for (x = cb->x0, coeff = coeff0, touched = touched0; x < cb->x1; ++x, ++coeff, ++touched) { val = *coeff; if (val != 0) { shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); if (shift2 > 0) { cover(74); if (val < 0) { val = (val << shift2) - (1 << (shift2 - 1)); } else { val = (val << shift2) + (1 << (shift2 - 1)); } } else { cover(75); val >>= -shift2; } if (qStyle == 0) { cover(76); if (tileComp->transform == 0) { val &= -1 << (fracBits - tileComp->prec); } } else { cover(77); val = (int)((double)val * mu); } } *coeff = val; } } ++cb; } } } //----- inverse transform // compute the subband bounds: // 0 nx1 nx2 // | | | // v v v // +----+----+ // | LL | HL | <- 0 // +----+----+ // | LH | HH | <- ny1 // +----+----+ // <- ny2 nx1 = precinct->subbands[1].x1 - precinct->subbands[1].x0; nx2 = nx1 + precinct->subbands[0].x1 - precinct->subbands[0].x0; ny1 = precinct->subbands[0].y1 - precinct->subbands[0].y0; ny2 = ny1 + precinct->subbands[1].y1 - precinct->subbands[1].y0; // horizontal (row) transforms if (r == tileComp->nDecompLevels) { offset = 3 + (tileComp->x0 & 1); } else { offset = 3 + (tileComp->resLevels[r+1].x0 & 1); } for (y = 0, dataPtr = tileComp->data; y < ny2; ++y, dataPtr += tileComp->w) { if (precinct->subbands[0].x0 == precinct->subbands[1].x0) { // fetch LL/LH for (x = 0, bufPtr = tileComp->buf + offset; x < nx1; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } // fetch HL/HH for (x = nx1, bufPtr = tileComp->buf + offset + 1; x < nx2; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } } else { // fetch LL/LH for (x = 0, bufPtr = tileComp->buf + offset + 1; x < nx1; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } // fetch HL/HH for (x = nx1, bufPtr = tileComp->buf + offset; x < nx2; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } } inverseTransform1D(tileComp, tileComp->buf, offset, nx2); for (x = 0, bufPtr = tileComp->buf + offset; x < nx2; ++x, ++bufPtr) { dataPtr[x] = *bufPtr; } } // vertical (column) transforms if (r == tileComp->nDecompLevels) { offset = 3 + (tileComp->y0 & 1); } else { offset = 3 + (tileComp->resLevels[r+1].y0 & 1); } for (x = 0, dataPtr = tileComp->data; x < nx2; ++x, ++dataPtr) { if (precinct->subbands[1].y0 == precinct->subbands[0].y0) { // fetch LL/HL for (y = 0, bufPtr = tileComp->buf + offset; y < ny1; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } // fetch LH/HH for (y = ny1, bufPtr = tileComp->buf + offset + 1; y < ny2; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } } else { // fetch LL/HL for (y = 0, bufPtr = tileComp->buf + offset + 1; y < ny1; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } // fetch LH/HH for (y = ny1, bufPtr = tileComp->buf + offset; y < ny2; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } } inverseTransform1D(tileComp, tileComp->buf, offset, ny2); for (y = 0, bufPtr = tileComp->buf + offset; y < ny2; ++y, ++bufPtr) { dataPtr[y * tileComp->w] = *bufPtr; } } } void JPXStream::inverseTransform1D(JPXTileComp *tileComp, int *data, Guint offset, Guint n) { Guint end, i; //----- special case for length = 1 if (n == 1) { cover(79); if (offset == 4) { cover(104); *data >>= 1; } } else { cover(80); end = offset + n; //----- extend right data[end] = data[end - 2]; if (n == 2) { cover(81); data[end+1] = data[offset + 1]; data[end+2] = data[offset]; data[end+3] = data[offset + 1]; } else { cover(82); data[end+1] = data[end - 3]; if (n == 3) { cover(105); data[end+2] = data[offset + 1]; data[end+3] = data[offset + 2]; } else { cover(106); data[end+2] = data[end - 4]; if (n == 4) { cover(107); data[end+3] = data[offset + 1]; } else { cover(108); data[end+3] = data[end - 5]; } } } //----- extend left data[offset - 1] = data[offset + 1]; data[offset - 2] = data[offset + 2]; data[offset - 3] = data[offset + 3]; if (offset == 4) { cover(83); data[0] = data[offset + 4]; } //----- 9-7 irreversible filter if (tileComp->transform == 0) { cover(84); // step 1 (even) for (i = 1; i <= end + 2; i += 2) { data[i] = (int)(idwtKappa * data[i]); } // step 2 (odd) for (i = 0; i <= end + 3; i += 2) { data[i] = (int)(idwtIKappa * data[i]); } // step 3 (even) for (i = 1; i <= end + 2; i += 2) { data[i] = (int)(data[i] - idwtDelta * (data[i-1] + data[i+1])); } // step 4 (odd) for (i = 2; i <= end + 1; i += 2) { data[i] = (int)(data[i] - idwtGamma * (data[i-1] + data[i+1])); } // step 5 (even) for (i = 3; i <= end; i += 2) { data[i] = (int)(data[i] - idwtBeta * (data[i-1] + data[i+1])); } // step 6 (odd) for (i = 4; i <= end - 1; i += 2) { data[i] = (int)(data[i] - idwtAlpha * (data[i-1] + data[i+1])); } //----- 5-3 reversible filter } else { cover(85); // step 1 (even) for (i = 3; i <= end; i += 2) { data[i] -= (data[i-1] + data[i+1] + 2) >> 2; } // step 2 (odd) for (i = 4; i < end; i += 2) { data[i] += (data[i-1] + data[i+1]) >> 1; } } } } // Inverse multi-component transform and DC level shift. This also // converts fixed point samples back to integers. GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) { JPXTileComp *tileComp; int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal; int *dataPtr; Guint j, comp, x, y; //----- inverse multi-component transform if (tile->multiComp == 1) { cover(86); if (img.nComps < 3 || tile->tileComps[0].hSep != tile->tileComps[1].hSep || tile->tileComps[0].vSep != tile->tileComps[1].vSep || tile->tileComps[1].hSep != tile->tileComps[2].hSep || tile->tileComps[1].vSep != tile->tileComps[2].vSep) { return gFalse; } // inverse irreversible multiple component transform if (tile->tileComps[0].transform == 0) { cover(87); j = 0; for (y = 0; y < tile->tileComps[0].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]; tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5); tile->tileComps[1].data[j] = (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5); tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5); ++j; } } // inverse reversible multiple component transform } else { cover(88); j = 0; for (y = 0; y < tile->tileComps[0].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]; tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2); tile->tileComps[0].data[j] = d2 + t; tile->tileComps[2].data[j] = d1 + t; ++j; } } } } //----- DC level shift for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; // signed: clip if (tileComp->sgned) { cover(89); minVal = -(1 << (tileComp->prec - 1)); maxVal = (1 << (tileComp->prec - 1)) - 1; dataPtr = tileComp->data; for (y = 0; y < tileComp->h; ++y) { for (x = 0; x < tileComp->w; ++x) { coeff = *dataPtr; if (tileComp->transform == 0) { cover(109); coeff >>= fracBits - tileComp->prec; } if (coeff < minVal) { cover(110); coeff = minVal; } else if (coeff > maxVal) { cover(111); coeff = maxVal; } *dataPtr++ = coeff; } } // unsigned: inverse DC level shift and clip } else { cover(90); maxVal = (1 << tileComp->prec) - 1; zeroVal = 1 << (tileComp->prec - 1); dataPtr = tileComp->data; for (y = 0; y < tileComp->h; ++y) { for (x = 0; x < tileComp->w; ++x) { coeff = *dataPtr; if (tileComp->transform == 0) { cover(112); coeff >>= fracBits - tileComp->prec; } coeff += zeroVal; if (coeff < 0) { cover(113); coeff = 0; } else if (coeff > maxVal) { cover(114); coeff = maxVal; } *dataPtr++ = coeff; } } } } return gTrue; } GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) { Guint len, lenH; if (!readULong(&len) || !readULong(boxType)) { return gFalse; } if (len == 1) { if (!readULong(&lenH) || !readULong(&len)) { return gFalse; } if (lenH) { error(errSyntaxError, getPos(), "JPX stream contains a box larger than 2^32 bytes"); return gFalse; } *boxLen = len; *dataLen = len - 16; } else if (len == 0) { *boxLen = 0; *dataLen = 0; } else { *boxLen = len; *dataLen = len - 8; } return gTrue; } int JPXStream::readMarkerHdr(int *segType, Guint *segLen) { int c; do { do { if ((c = bufStr->getChar()) == EOF) { return gFalse; } } while (c != 0xff); do { if ((c = bufStr->getChar()) == EOF) { return gFalse; } } while (c == 0xff); } while (c == 0x00); *segType = c; if ((c >= 0x30 && c <= 0x3f) || c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) { *segLen = 0; return gTrue; } return readUWord(segLen); } GBool JPXStream::readUByte(Guint *x) { int c0; if ((c0 = bufStr->getChar()) == EOF) { return gFalse; } *x = (Guint)c0; return gTrue; } GBool JPXStream::readByte(int *x) { int c0; if ((c0 = bufStr->getChar()) == EOF) { return gFalse; } *x = c0; if (c0 & 0x80) { *x |= -1 - 0xff; } return gTrue; } GBool JPXStream::readUWord(Guint *x) { int c0, c1; if ((c0 = bufStr->getChar()) == EOF || (c1 = bufStr->getChar()) == EOF) { return gFalse; } *x = (Guint)((c0 << 8) | c1); return gTrue; } GBool JPXStream::readULong(Guint *x) { int c0, c1, c2, c3; if ((c0 = bufStr->getChar()) == EOF || (c1 = bufStr->getChar()) == EOF || (c2 = bufStr->getChar()) == EOF || (c3 = bufStr->getChar()) == EOF) { return gFalse; } *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); return gTrue; } GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) { int y, c, i; y = 0; for (i = 0; i < nBytes; ++i) { if ((c = bufStr->getChar()) == EOF) { return gFalse; } y = (y << 8) + c; } if (signd) { if (y & (1 << (8 * nBytes - 1))) { y |= -1 << (8 * nBytes); } } *x = y; return gTrue; } void JPXStream::startBitBuf(Guint byteCountA) { bitBufLen = 0; bitBufSkip = gFalse; byteCount = byteCountA; } GBool JPXStream::readBits(int nBits, Guint *x) { int c; while (bitBufLen < nBits) { if (byteCount == 0 || (c = bufStr->getChar()) == EOF) { return gFalse; } --byteCount; if (bitBufSkip) { bitBuf = (bitBuf << 7) | (c & 0x7f); bitBufLen += 7; } else { bitBuf = (bitBuf << 8) | (c & 0xff); bitBufLen += 8; } bitBufSkip = c == 0xff; } *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1); bitBufLen -= nBits; return gTrue; } void JPXStream::skipSOP() { // 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) { bufStr->discardChars(6); byteCount -= 6; bitBufLen = 0; bitBufSkip = gFalse; } } void JPXStream::skipEPH() { int k; k = bitBufSkip ? 1 : 0; if (byteCount >= (Guint)(k + 2) && bufStr->lookChar(k) == 0xff && bufStr->lookChar(k + 1) == 0x92) { bufStr->discardChars(k + 2); byteCount -= k + 2; bitBufLen = 0; bitBufSkip = gFalse; } } Guint JPXStream::finishBitBuf() { if (bitBufSkip) { bufStr->getChar(); --byteCount; } return byteCount; } xpdf-3.04/xpdf/Catalog.h0000644000076400007640000000655712341430012014402 0ustar dereknderekn//======================================================================== // // Catalog.h // // Copyright 1996-2007 Glyph & Cog, LLC // //======================================================================== #ifndef CATALOG_H #define CATALOG_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "CharTypes.h" class GList; class PDFDoc; class XRef; class Object; class Page; class PageAttrs; struct Ref; class LinkDest; class PageTreeNode; class Form; //------------------------------------------------------------------------ // Catalog //------------------------------------------------------------------------ class Catalog { public: // Constructor. Catalog(PDFDoc *docA); // Destructor. ~Catalog(); // Is catalog valid? GBool isOk() { return ok; } // Get number of pages. int getNumPages() { return numPages; } // Get a page. Page *getPage(int i); // Get the reference for a page object. Ref *getPageRef(int i); // Remove a page from the catalog. (It can be reloaded later by // calling getPage). void doneWithPage(int i); // Return base URI, or NULL if none. GString *getBaseURI() { return baseURI; } // Return the contents of the metadata stream, or NULL if there is // no metadata. GString *readMetadata(); // Return the structure tree root object. Object *getStructTreeRoot() { return &structTreeRoot; } // Find a page, given its object ID. Returns page number, or 0 if // not found. int findPage(int num, int gen); // Find a named destination. Returns the link destination, or // NULL if is not a destination. LinkDest *findDest(GString *name); Object *getDests() { return &dests; } Object *getNameTree() { return &nameTree; } Object *getOutline() { return &outline; } Object *getAcroForm() { return &acroForm; } Form *getForm() { return form; } Object *getOCProperties() { return &ocProperties; } // Get the list of embedded files. int getNumEmbeddedFiles(); Unicode *getEmbeddedFileName(int idx); int getEmbeddedFileNameLength(int idx); Object *getEmbeddedFileStreamRef(int idx); Object *getEmbeddedFileStreamObj(int idx, Object *strObj); private: PDFDoc *doc; XRef *xref; // the xref table for this PDF file PageTreeNode *pageTree; // the page tree Page **pages; // array of pages Ref *pageRefs; // object ID for each page int numPages; // number of pages int pagesSize; // size of pages array Object dests; // named destination dictionary Object nameTree; // name tree GString *baseURI; // base URI for URI-type links Object metadata; // metadata stream Object structTreeRoot; // structure tree root dictionary Object outline; // outline dictionary Object acroForm; // AcroForm dictionary Form *form; // parsed form Object ocProperties; // OCProperties dictionary GList *embeddedFiles; // embedded file list [EmbeddedFile] GBool ok; // true if catalog is valid Object *findDestInTree(Object *tree, GString *name, Object *obj); GBool readPageTree(Object *catDict); int countPageTree(Object *pagesObj); void loadPage(int pg); void loadPage2(int pg, int relPg, PageTreeNode *node); void readEmbeddedFileList(Dict *catDict); void readEmbeddedFileTree(Object *node); void readFileAttachmentAnnots(Object *pageNodeRef, char *touchedObjs); void readEmbeddedFile(Object *fileSpec, Object *name1); }; #endif xpdf-3.04/xpdf/NameToCharCode.cc0000644000076400007640000000417312341430012015732 0ustar dereknderekn//======================================================================== // // NameToCharCode.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmem.h" #include "NameToCharCode.h" //------------------------------------------------------------------------ struct NameToCharCodeEntry { char *name; CharCode c; }; //------------------------------------------------------------------------ NameToCharCode::NameToCharCode() { int i; size = 31; len = 0; tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry)); for (i = 0; i < size; ++i) { tab[i].name = NULL; } } NameToCharCode::~NameToCharCode() { int i; for (i = 0; i < size; ++i) { if (tab[i].name) { gfree(tab[i].name); } } gfree(tab); } void NameToCharCode::add(const char *name, CharCode c) { NameToCharCodeEntry *oldTab; int h, i, oldSize; // expand the table if necessary if (len >= size / 2) { oldSize = size; oldTab = tab; size = 2*size + 1; tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry)); for (h = 0; h < size; ++h) { tab[h].name = NULL; } for (i = 0; i < oldSize; ++i) { if (oldTab[i].name) { h = hash(oldTab[i].name); while (tab[h].name) { if (++h == size) { h = 0; } } tab[h] = oldTab[i]; } } gfree(oldTab); } // add the new name h = hash(name); while (tab[h].name && strcmp(tab[h].name, name)) { if (++h == size) { h = 0; } } if (!tab[h].name) { tab[h].name = copyString(name); } tab[h].c = c; ++len; } CharCode NameToCharCode::lookup(const char *name) { int h; h = hash(name); while (tab[h].name) { if (!strcmp(tab[h].name, name)) { return tab[h].c; } if (++h == size) { h = 0; } } return 0; } int NameToCharCode::hash(const char *name) { const char *p; unsigned int h; h = 0; for (p = name; *p; ++p) { h = 17 * h + (int)(*p & 0xff); } return (int)(h % size); } xpdf-3.04/xpdf/printDis.xbm0000644000076400007640000000043412341430012015147 0ustar dereknderekn#define printDis_width 15 #define printDis_height 15 static unsigned char printDis_bits[] = { 0xa0, 0x2a, 0x10, 0x40, 0x00, 0x00, 0x40, 0x01, 0x08, 0x20, 0x40, 0x01, 0x00, 0x00, 0x14, 0x10, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x40, 0xaa, 0x2a}; xpdf-3.04/xpdf/PDFDoc.h0000644000076400007640000001374712341430012014066 0ustar dereknderekn//======================================================================== // // PDFDoc.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PDFDOC_H #define PDFDOC_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "XRef.h" #include "Catalog.h" #include "Page.h" class GString; class BaseStream; class OutputDev; class Links; class LinkAction; class LinkDest; class Outline; class OptionalContent; class PDFCore; //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ class PDFDoc { public: PDFDoc(GString *fileNameA, GString *ownerPassword = NULL, GString *userPassword = NULL, PDFCore *coreA = NULL); #ifdef _WIN32 PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL, GString *userPassword = NULL, PDFCore *coreA = NULL); #endif PDFDoc(BaseStream *strA, GString *ownerPassword = NULL, GString *userPassword = NULL, PDFCore *coreA = NULL); ~PDFDoc(); // Was PDF document successfully opened? GBool isOk() { return ok; } // Get the error code (if isOk() returns false). int getErrorCode() { return errCode; } // Get file name. GString *getFileName() { return fileName; } #ifdef _WIN32 wchar_t *getFileNameU() { return fileNameU; } #endif // Get the xref table. XRef *getXRef() { return xref; } // Get catalog. Catalog *getCatalog() { return catalog; } // Get base stream. BaseStream *getBaseStream() { return str; } // Get page parameters. double getPageMediaWidth(int page) { return catalog->getPage(page)->getMediaWidth(); } double getPageMediaHeight(int page) { return catalog->getPage(page)->getMediaHeight(); } double getPageCropWidth(int page) { return catalog->getPage(page)->getCropWidth(); } double getPageCropHeight(int page) { return catalog->getPage(page)->getCropHeight(); } int getPageRotate(int page) { return catalog->getPage(page)->getRotate(); } // Get number of pages. int getNumPages() { return catalog->getNumPages(); } // Return the contents of the metadata stream, or NULL if there is // no metadata. GString *readMetadata() { return catalog->readMetadata(); } // Return the structure tree root object. Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); } // Display a page. void displayPage(OutputDev *out, int page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); // Display a range of pages. void displayPages(OutputDev *out, int firstPage, int lastPage, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); // Display part of a page. void displayPageSlice(OutputDev *out, int page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, int sliceX, int sliceY, int sliceW, int sliceH, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); // Find a page, given its object ID. Returns page number, or 0 if // not found. int findPage(int num, int gen) { return catalog->findPage(num, gen); } // Returns the links for the current page, transferring ownership to // the caller. Links *getLinks(int page); // Find a named destination. Returns the link destination, or // NULL if is not a destination. LinkDest *findDest(GString *name) { return catalog->findDest(name); } // Process the links for a page. void processLinks(OutputDev *out, int page); #ifndef DISABLE_OUTLINE // Return the outline object. Outline *getOutline() { return outline; } #endif // Return the OptionalContent object. OptionalContent *getOptionalContent() { return optContent; } // Is the file encrypted? GBool isEncrypted() { return xref->isEncrypted(); } // Check various permissions. GBool okToPrint(GBool ignoreOwnerPW = gFalse) { return xref->okToPrint(ignoreOwnerPW); } GBool okToChange(GBool ignoreOwnerPW = gFalse) { return xref->okToChange(ignoreOwnerPW); } GBool okToCopy(GBool ignoreOwnerPW = gFalse) { return xref->okToCopy(ignoreOwnerPW); } GBool okToAddNotes(GBool ignoreOwnerPW = gFalse) { return xref->okToAddNotes(ignoreOwnerPW); } // Is this document linearized? GBool isLinearized(); // Return the document's Info dictionary (if any). Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); } Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); } // Return the PDF version specified by the file. double getPDFVersion() { return pdfVersion; } // Save this file with another name. GBool saveAs(GString *name); // Return a pointer to the PDFCore object. PDFCore *getCore() { return core; } // Get the list of embedded files. int getNumEmbeddedFiles() { return catalog->getNumEmbeddedFiles(); } Unicode *getEmbeddedFileName(int idx) { return catalog->getEmbeddedFileName(idx); } int getEmbeddedFileNameLength(int idx) { return catalog->getEmbeddedFileNameLength(idx); } GBool saveEmbeddedFile(int idx, char *path); #ifdef _WIN32 GBool saveEmbeddedFile(int idx, wchar_t *path, int pathLen); #endif char *getEmbeddedFileMem(int idx, int *size); private: GBool setup(GString *ownerPassword, GString *userPassword); GBool setup2(GString *ownerPassword, GString *userPassword, GBool repairXRef); void checkHeader(); GBool checkEncryption(GString *ownerPassword, GString *userPassword); GBool saveEmbeddedFile2(int idx, FILE *f); GString *fileName; #ifdef _WIN32 wchar_t *fileNameU; #endif FILE *file; BaseStream *str; PDFCore *core; double pdfVersion; XRef *xref; Catalog *catalog; #ifndef DISABLE_OUTLINE Outline *outline; #endif OptionalContent *optContent; GBool ok; int errCode; }; #endif xpdf-3.04/xpdf/Array.h0000644000076400007640000000214712341430012014075 0ustar dereknderekn//======================================================================== // // Array.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef ARRAY_H #define ARRAY_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Object.h" class XRef; //------------------------------------------------------------------------ // Array //------------------------------------------------------------------------ class Array { public: // Constructor. Array(XRef *xrefA); // Destructor. ~Array(); // Reference counting. int incRef() { return ++ref; } int decRef() { return --ref; } // Get number of elements. int getLength() { return length; } // Add an element. void add(Object *elem); // Accessors. Object *get(int i, Object *obj); Object *getNF(int i, Object *obj); private: XRef *xref; // the xref table for this PDF file Object *elems; // array of elements int size; // size of array int length; // number of elements in array int ref; // reference count }; #endif xpdf-3.04/xpdf/UnicodeTypeTable.h0000644000076400007640000000105312341430012016212 0ustar dereknderekn//======================================================================== // // UnicodeTypeTable.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef UNICODETYPETABLE_H #define UNICODETYPETABLE_H #include "gtypes.h" extern GBool unicodeTypeL(Unicode c); extern GBool unicodeTypeR(Unicode c); extern GBool unicodeTypeNum(Unicode c); extern GBool unicodeTypeAlphaNum(Unicode c); extern GBool unicodeTypeWord(Unicode c); extern Unicode unicodeToUpper(Unicode c); #endif xpdf-3.04/xpdf/CoreOutputDev.h0000644000076400007640000000260112341430012015562 0ustar dereknderekn//======================================================================== // // CoreOutputDev.h // // Copyright 2004 Glyph & Cog, LLC // //======================================================================== #ifndef COREOUTPUTDEV_H #define COREOUTPUTDEV_H #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" #include "SplashOutputDev.h" class TextPage; //------------------------------------------------------------------------ typedef void (*CoreOutRedrawCbk)(void *data, int x0, int y0, int x1, int y1, GBool composited); //------------------------------------------------------------------------ // CoreOutputDev //------------------------------------------------------------------------ class CoreOutputDev: public SplashOutputDev { public: CoreOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool incrementalUpdateA, CoreOutRedrawCbk redrawCbkA, void *redrawCbkDataA); virtual ~CoreOutputDev(); //----- initialization and control // End a page. virtual void endPage(); // Dump page contents to display. virtual void dump(); //----- special access // Clear out the document (used when displaying an empty window). void clear(); private: GBool incrementalUpdate; // incrementally update the display? CoreOutRedrawCbk redrawCbk; void *redrawCbkData; }; #endif xpdf-3.04/xpdf/XFAForm.cc0000644000076400007640000011725012341430012014421 0ustar dereknderekn//======================================================================== // // 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; } xpdf-3.04/xpdf/dblLeftArrowDis.xbm0000644000076400007640000000046112341430012016402 0ustar dereknderekn#define dblLeftArrowDis_width 16 #define dblLeftArrowDis_height 15 static unsigned char dblLeftArrowDis_bits[] = { 0x80, 0x80, 0x40, 0x40, 0xa0, 0xa0, 0x50, 0x50, 0xa8, 0xa8, 0x54, 0x54, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x54, 0x54, 0xa8, 0xa8, 0x50, 0x50, 0xa0, 0xa0, 0x40, 0x40, 0x80, 0x80}; xpdf-3.04/xpdf/Catalog.cc0000644000076400007640000004210212341430012014522 0ustar dereknderekn//======================================================================== // // Catalog.cc // // Copyright 1996-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include "gmem.h" #include "gfile.h" #include "GList.h" #include "Object.h" #include "CharTypes.h" #include "PDFDoc.h" #include "XRef.h" #include "Array.h" #include "Dict.h" #include "Page.h" #include "Error.h" #include "Link.h" #include "Form.h" #include "TextString.h" #include "Catalog.h" //------------------------------------------------------------------------ // PageTreeNode //------------------------------------------------------------------------ class PageTreeNode { public: PageTreeNode(Ref refA, int countA, PageTreeNode *parentA); ~PageTreeNode(); Ref ref; int count; PageTreeNode *parent; GList *kids; // [PageTreeNode] PageAttrs *attrs; }; PageTreeNode::PageTreeNode(Ref refA, int countA, PageTreeNode *parentA) { ref = refA; count = countA; parent = parentA; kids = NULL; attrs = NULL; } PageTreeNode::~PageTreeNode() { delete attrs; if (kids) { deleteGList(kids, PageTreeNode); } } //------------------------------------------------------------------------ // EmbeddedFile //------------------------------------------------------------------------ class EmbeddedFile { public: EmbeddedFile(TextString *nameA, Object *streamRefA); ~EmbeddedFile(); TextString *name; Object streamRef; }; EmbeddedFile::EmbeddedFile(TextString *nameA, Object *streamRefA) { name = nameA; streamRefA->copy(&streamRef); } EmbeddedFile::~EmbeddedFile() { delete name; streamRef.free(); } //------------------------------------------------------------------------ // Catalog //------------------------------------------------------------------------ Catalog::Catalog(PDFDoc *docA) { Object catDict; Object obj, obj2; ok = gTrue; doc = docA; xref = doc->getXRef(); pageTree = NULL; pages = NULL; pageRefs = NULL; numPages = 0; baseURI = NULL; form = NULL; embeddedFiles = NULL; xref->getCatalog(&catDict); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); goto err1; } // read page tree if (!readPageTree(&catDict)) { goto err1; } // read named destination dictionary catDict.dictLookup("Dests", &dests); // read root of named destination tree if (catDict.dictLookup("Names", &obj)->isDict()) obj.dictLookup("Dests", &nameTree); else nameTree.initNull(); obj.free(); // read base URI if (catDict.dictLookup("URI", &obj)->isDict()) { if (obj.dictLookup("Base", &obj2)->isString()) { baseURI = obj2.getString()->copy(); } obj2.free(); } obj.free(); if (!baseURI || baseURI->getLength() == 0) { if (baseURI) { delete baseURI; } if (doc->getFileName()) { baseURI = makePathAbsolute(grabPath(doc->getFileName()->getCString())); if (baseURI->getChar(0) == '/') { baseURI->insert(0, "file://localhost"); } else { baseURI->insert(0, "file://localhost/"); } } else { baseURI = new GString("file://localhost/"); } } // get the metadata stream catDict.dictLookup("Metadata", &metadata); // get the structure tree root catDict.dictLookup("StructTreeRoot", &structTreeRoot); // get the outline dictionary catDict.dictLookup("Outlines", &outline); // get the AcroForm dictionary catDict.dictLookup("AcroForm", &acroForm); if (!acroForm.isNull()) { form = Form::load(doc, this, &acroForm); } // get the OCProperties dictionary catDict.dictLookup("OCProperties", &ocProperties); // get the list of embedded files readEmbeddedFileList(catDict.getDict()); catDict.free(); return; err1: catDict.free(); dests.initNull(); nameTree.initNull(); ok = gFalse; } Catalog::~Catalog() { int i; if (pageTree) { delete pageTree; } if (pages) { for (i = 0; i < numPages; ++i) { if (pages[i]) { delete pages[i]; } } gfree(pages); gfree(pageRefs); } dests.free(); nameTree.free(); if (baseURI) { delete baseURI; } metadata.free(); structTreeRoot.free(); outline.free(); acroForm.free(); if (form) { delete form; } ocProperties.free(); if (embeddedFiles) { deleteGList(embeddedFiles, EmbeddedFile); } } Page *Catalog::getPage(int i) { if (!pages[i-1]) { loadPage(i); } return pages[i-1]; } Ref *Catalog::getPageRef(int i) { if (!pages[i-1]) { loadPage(i); } return &pageRefs[i-1]; } void Catalog::doneWithPage(int i) { if (pages[i-1]) { delete pages[i-1]; pages[i-1] = NULL; } } GString *Catalog::readMetadata() { GString *s; Dict *dict; Object obj; char buf[4096]; int n; if (!metadata.isStream()) { return NULL; } dict = metadata.streamGetDict(); if (!dict->lookup("Subtype", &obj)->isName("XML")) { error(errSyntaxWarning, -1, "Unknown Metadata type: '{0:s}'", obj.isName() ? obj.getName() : "???"); } obj.free(); s = new GString(); metadata.streamReset(); while ((n = metadata.streamGetBlock(buf, sizeof(buf))) > 0) { s->append(buf, n); } metadata.streamClose(); return s; } int Catalog::findPage(int num, int gen) { int i; for (i = 0; i < numPages; ++i) { if (!pages[i]) { loadPage(i+1); } if (pageRefs[i].num == num && pageRefs[i].gen == gen) return i + 1; } return 0; } LinkDest *Catalog::findDest(GString *name) { LinkDest *dest; Object obj1, obj2; GBool found; // try named destination dictionary then name tree found = gFalse; if (dests.isDict()) { if (!dests.dictLookup(name->getCString(), &obj1)->isNull()) found = gTrue; else obj1.free(); } if (!found && nameTree.isDict()) { if (!findDestInTree(&nameTree, name, &obj1)->isNull()) found = gTrue; else obj1.free(); } if (!found) return NULL; // construct LinkDest dest = NULL; if (obj1.isArray()) { dest = new LinkDest(obj1.getArray()); } else if (obj1.isDict()) { if (obj1.dictLookup("D", &obj2)->isArray()) dest = new LinkDest(obj2.getArray()); else error(errSyntaxWarning, -1, "Bad named destination value"); obj2.free(); } else { error(errSyntaxWarning, -1, "Bad named destination value"); } obj1.free(); if (dest && !dest->isOk()) { delete dest; dest = NULL; } return dest; } Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) { Object names, name1; Object kids, kid, limits, low, high; GBool done, found; int cmp, i; // leaf node if (tree->dictLookup("Names", &names)->isArray()) { done = found = gFalse; for (i = 0; !done && i < names.arrayGetLength(); i += 2) { if (names.arrayGet(i, &name1)->isString()) { cmp = name->cmp(name1.getString()); if (cmp == 0) { names.arrayGet(i+1, obj); found = gTrue; done = gTrue; } else if (cmp < 0) { done = gTrue; } } name1.free(); } names.free(); if (!found) obj->initNull(); return obj; } names.free(); // root or intermediate node done = gFalse; if (tree->dictLookup("Kids", &kids)->isArray()) { for (i = 0; !done && i < kids.arrayGetLength(); ++i) { if (kids.arrayGet(i, &kid)->isDict()) { if (kid.dictLookup("Limits", &limits)->isArray()) { if (limits.arrayGet(0, &low)->isString() && name->cmp(low.getString()) >= 0) { if (limits.arrayGet(1, &high)->isString() && name->cmp(high.getString()) <= 0) { findDestInTree(&kid, name, obj); done = gTrue; } high.free(); } low.free(); } limits.free(); } kid.free(); } } kids.free(); // name was outside of ranges of all kids if (!done) obj->initNull(); return obj; } GBool Catalog::readPageTree(Object *catDict) { Object topPagesRef, topPagesObj, countObj; int i; if (!catDict->dictLookupNF("Pages", &topPagesRef)->isRef()) { error(errSyntaxError, -1, "Top-level pages reference is wrong type ({0:s})", topPagesRef.getTypeName()); topPagesRef.free(); return gFalse; } if (!topPagesRef.fetch(xref, &topPagesObj)->isDict()) { error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})", topPagesObj.getTypeName()); topPagesObj.free(); topPagesRef.free(); return gFalse; } if (topPagesObj.dictLookup("Count", &countObj)->isInt()) { numPages = countObj.getInt(); if (numPages == 0) { // Acrobat apparently scans the page tree if it sees a zero count numPages = countPageTree(&topPagesObj); } } else { // assume we got a Page node instead of a Pages node numPages = 1; } countObj.free(); if (numPages < 0) { error(errSyntaxError, -1, "Invalid page count"); topPagesObj.free(); topPagesRef.free(); numPages = 0; return gFalse; } pageTree = new PageTreeNode(topPagesRef.getRef(), numPages, NULL); topPagesObj.free(); topPagesRef.free(); pages = (Page **)greallocn(pages, numPages, sizeof(Page *)); pageRefs = (Ref *)greallocn(pageRefs, numPages, sizeof(Ref)); for (i = 0; i < numPages; ++i) { pages[i] = NULL; pageRefs[i].num = -1; pageRefs[i].gen = -1; } return gTrue; } int Catalog::countPageTree(Object *pagesObj) { Object kids, kid; int n, n2, i; if (!pagesObj->isDict()) { return 0; } if (pagesObj->dictLookup("Kids", &kids)->isArray()) { n = 0; for (i = 0; i < kids.arrayGetLength(); ++i) { kids.arrayGet(i, &kid); n2 = countPageTree(&kid); if (n2 < INT_MAX - n) { n += n2; } else { error(errSyntaxError, -1, "Page tree contains too many pages"); n = INT_MAX; } kid.free(); } } else { n = 1; } kids.free(); return n; } void Catalog::loadPage(int pg) { loadPage2(pg, pg - 1, pageTree); } void Catalog::loadPage2(int pg, int relPg, PageTreeNode *node) { Object pageRefObj, pageObj, kidsObj, kidRefObj, kidObj, countObj; PageTreeNode *kidNode, *p; PageAttrs *attrs; int count, i; if (relPg >= node->count) { error(errSyntaxError, -1, "Internal error in page tree"); pages[pg-1] = new Page(doc, pg); return; } // if this node has not been filled in yet, it's either a leaf node // or an unread internal node if (!node->kids) { // check for a loop in the page tree for (p = node->parent; p; p = p->parent) { if (node->ref.num == p->ref.num && node->ref.gen == p->ref.gen) { error(errSyntaxError, -1, "Loop in Pages tree"); pages[pg-1] = new Page(doc, pg); return; } } // fetch the Page/Pages object pageRefObj.initRef(node->ref.num, node->ref.gen); if (!pageRefObj.fetch(xref, &pageObj)->isDict()) { error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})", pageObj.getTypeName()); pageObj.free(); pageRefObj.free(); pages[pg-1] = new Page(doc, pg); return; } // merge the PageAttrs attrs = new PageAttrs(node->parent ? node->parent->attrs : (PageAttrs *)NULL, pageObj.getDict()); // if "Kids" exists, it's an internal node if (pageObj.dictLookup("Kids", &kidsObj)->isArray()) { // save the PageAttrs node->attrs = attrs; // read the kids node->kids = new GList(); for (i = 0; i < kidsObj.arrayGetLength(); ++i) { if (kidsObj.arrayGetNF(i, &kidRefObj)->isRef()) { if (kidRefObj.fetch(xref, &kidObj)->isDict()) { if (kidObj.dictLookup("Count", &countObj)->isInt()) { count = countObj.getInt(); } else { count = 1; } countObj.free(); node->kids->append(new PageTreeNode(kidRefObj.getRef(), count, node)); } else { error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})", kidObj.getTypeName()); } kidObj.free(); } else { error(errSyntaxError, -1, "Page tree reference is wrong type ({0:s})", kidRefObj.getTypeName()); } kidRefObj.free(); } } else { // create the Page object pageRefs[pg-1] = node->ref; pages[pg-1] = new Page(doc, pg, pageObj.getDict(), attrs); if (!pages[pg-1]->isOk()) { delete pages[pg-1]; pages[pg-1] = new Page(doc, pg); } } kidsObj.free(); pageObj.free(); pageRefObj.free(); } // recursively descend the tree if (node->kids) { for (i = 0; i < node->kids->getLength(); ++i) { kidNode = (PageTreeNode *)node->kids->get(i); if (relPg < kidNode->count) { loadPage2(pg, relPg, kidNode); break; } relPg -= kidNode->count; } // this will only happen if the page tree is invalid // (i.e., parent count > sum of children counts) if (i == node->kids->getLength()) { error(errSyntaxError, -1, "Invalid page count in page tree"); pages[pg-1] = new Page(doc, pg); } } } void Catalog::readEmbeddedFileList(Dict *catDict) { Object obj1, obj2; char *touchedObjs; // read the embedded file name tree if (catDict->lookup("Names", &obj1)->isDict()) { if (obj1.dictLookup("EmbeddedFiles", &obj2)->isDict()) { readEmbeddedFileTree(&obj2); } obj2.free(); } obj1.free(); // look for file attachment annotations touchedObjs = (char *)gmalloc(xref->getNumObjects()); memset(touchedObjs, 0, xref->getNumObjects()); readFileAttachmentAnnots(catDict->lookupNF("Pages", &obj1), touchedObjs); obj1.free(); gfree(touchedObjs); } void Catalog::readEmbeddedFileTree(Object *node) { Object kidsObj, kidObj; Object namesObj, nameObj, fileSpecObj; int i; if (node->dictLookup("Kids", &kidsObj)->isArray()) { for (i = 0; i < kidsObj.arrayGetLength(); ++i) { if (kidsObj.arrayGet(i, &kidObj)->isDict()) { readEmbeddedFileTree(&kidObj); } kidObj.free(); } } else { if (node->dictLookup("Names", &namesObj)->isArray()) { for (i = 0; i+1 < namesObj.arrayGetLength(); ++i) { namesObj.arrayGet(i, &nameObj); namesObj.arrayGet(i+1, &fileSpecObj); readEmbeddedFile(&fileSpecObj, &nameObj); nameObj.free(); fileSpecObj.free(); } } namesObj.free(); } kidsObj.free(); } void Catalog::readFileAttachmentAnnots(Object *pageNodeRef, char *touchedObjs) { Object pageNode, kids, kid, annots, annot, subtype, fileSpec, contents; int i; // check for an invalid object reference (e.g., in a damaged PDF file) if (pageNodeRef->getRefNum() < 0 || pageNodeRef->getRefNum() >= xref->getNumObjects()) { return; } // check for a page tree loop if (pageNodeRef->isRef()) { if (touchedObjs[pageNodeRef->getRefNum()]) { return; } touchedObjs[pageNodeRef->getRefNum()] = 1; xref->fetch(pageNodeRef->getRefNum(), pageNodeRef->getRefGen(), &pageNode); } else { pageNodeRef->copy(&pageNode); } if (pageNode.isDict()) { if (pageNode.dictLookup("Kids", &kids)->isArray()) { for (i = 0; i < kids.arrayGetLength(); ++i) { readFileAttachmentAnnots(kids.arrayGetNF(i, &kid), touchedObjs); kid.free(); } } else { if (pageNode.dictLookup("Annots", &annots)->isArray()) { for (i = 0; i < annots.arrayGetLength(); ++i) { if (annots.arrayGet(i, &annot)->isDict()) { if (annot.dictLookup("Subtype", &subtype) ->isName("FileAttachment")) { if (annot.dictLookup("FS", &fileSpec)) { readEmbeddedFile(&fileSpec, annot.dictLookup("Contents", &contents)); contents.free(); } fileSpec.free(); } subtype.free(); } annot.free(); } } annots.free(); } kids.free(); } pageNode.free(); } void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) { Object name2, efObj, streamObj; GString *s; TextString *name; if (fileSpec->isDict()) { if (fileSpec->dictLookup("UF", &name2)->isString()) { name = new TextString(name2.getString()); } else { name2.free(); if (fileSpec->dictLookup("F", &name2)->isString()) { name = new TextString(name2.getString()); } else if (name1 && name1->isString()) { name = new TextString(name1->getString()); } else { s = new GString("?"); name = new TextString(s); delete s; } } name2.free(); if (fileSpec->dictLookup("EF", &efObj)->isDict()) { if (efObj.dictLookupNF("F", &streamObj)->isRef()) { if (!embeddedFiles) { embeddedFiles = new GList(); } embeddedFiles->append(new EmbeddedFile(name, &streamObj)); } else { delete name; } streamObj.free(); } else { delete name; } efObj.free(); } } int Catalog::getNumEmbeddedFiles() { return embeddedFiles ? embeddedFiles->getLength() : 0; } Unicode *Catalog::getEmbeddedFileName(int idx) { return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getUnicode(); } int Catalog::getEmbeddedFileNameLength(int idx) { return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getLength(); } Object *Catalog::getEmbeddedFileStreamRef(int idx) { return &((EmbeddedFile *)embeddedFiles->get(idx))->streamRef; } Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) { ((EmbeddedFile *)embeddedFiles->get(idx))->streamRef.fetch(xref, strObj); if (!strObj->isStream()) { strObj->free(); return NULL; } return strObj; } xpdf-3.04/xpdf/Page.cc0000644000076400007640000002672412341430012014040 0ustar dereknderekn//======================================================================== // // Page.cc // // Copyright 1996-2007 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "GlobalParams.h" #include "Object.h" #include "Array.h" #include "Dict.h" #include "PDFDoc.h" #include "XRef.h" #include "Link.h" #include "OutputDev.h" #ifndef PDF_PARSER_ONLY #include "Gfx.h" #include "GfxState.h" #include "Annot.h" #include "Form.h" #endif #include "Error.h" #include "Catalog.h" #include "Page.h" //------------------------------------------------------------------------ // PDFRectangle //------------------------------------------------------------------------ void PDFRectangle::clipTo(PDFRectangle *rect) { if (x1 < rect->x1) { x1 = rect->x1; } else if (x1 > rect->x2) { x1 = rect->x2; } if (x2 < rect->x1) { x2 = rect->x1; } else if (x2 > rect->x2) { x2 = rect->x2; } if (y1 < rect->y1) { y1 = rect->y1; } else if (y1 > rect->y2) { y1 = rect->y2; } if (y2 < rect->y1) { y2 = rect->y1; } else if (y2 > rect->y2) { y2 = rect->y2; } } //------------------------------------------------------------------------ // PageAttrs //------------------------------------------------------------------------ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) { Object obj1; // get old/default values if (attrs) { mediaBox = attrs->mediaBox; cropBox = attrs->cropBox; haveCropBox = attrs->haveCropBox; rotate = attrs->rotate; attrs->resources.copy(&resources); } else { // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary // but some (non-compliant) PDF files don't specify a MediaBox mediaBox.x1 = 0; mediaBox.y1 = 0; mediaBox.x2 = 612; mediaBox.y2 = 792; cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0; haveCropBox = gFalse; rotate = 0; resources.initNull(); } // media box readBox(dict, "MediaBox", &mediaBox); // crop box if (readBox(dict, "CropBox", &cropBox)) { haveCropBox = gTrue; } if (!haveCropBox) { cropBox = mediaBox; } // other boxes bleedBox = cropBox; readBox(dict, "BleedBox", &bleedBox); trimBox = cropBox; readBox(dict, "TrimBox", &trimBox); artBox = cropBox; readBox(dict, "ArtBox", &artBox); // rotate dict->lookup("Rotate", &obj1); if (obj1.isInt()) { rotate = obj1.getInt(); } obj1.free(); while (rotate < 0) { rotate += 360; } while (rotate >= 360) { rotate -= 360; } // misc attributes dict->lookup("LastModified", &lastModified); dict->lookup("BoxColorInfo", &boxColorInfo); dict->lookup("Group", &group); dict->lookup("Metadata", &metadata); dict->lookup("PieceInfo", &pieceInfo); dict->lookup("SeparationInfo", &separationInfo); 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); if (obj1.isDict()) { resources.free(); obj1.copy(&resources); } obj1.free(); } PageAttrs::PageAttrs() { mediaBox.x1 = mediaBox.y1 = 0; mediaBox.x2 = mediaBox.y2 = 50; cropBox = mediaBox; haveCropBox = gFalse; bleedBox = cropBox; trimBox = cropBox; artBox = cropBox; rotate = 0; lastModified.initNull(); boxColorInfo.initNull(); group.initNull(); metadata.initNull(); pieceInfo.initNull(); separationInfo.initNull(); resources.initNull(); } PageAttrs::~PageAttrs() { lastModified.free(); boxColorInfo.free(); group.free(); metadata.free(); pieceInfo.free(); separationInfo.free(); resources.free(); } void PageAttrs::clipBoxes() { cropBox.clipTo(&mediaBox); bleedBox.clipTo(&mediaBox); trimBox.clipTo(&mediaBox); artBox.clipTo(&mediaBox); } GBool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) { PDFRectangle tmp; double t; Object obj1, obj2; GBool ok; dict->lookup(key, &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 4) { ok = gTrue; obj1.arrayGet(0, &obj2); if (obj2.isNum()) { tmp.x1 = obj2.getNum(); } else { ok = gFalse; } obj2.free(); obj1.arrayGet(1, &obj2); if (obj2.isNum()) { tmp.y1 = obj2.getNum(); } else { ok = gFalse; } obj2.free(); obj1.arrayGet(2, &obj2); if (obj2.isNum()) { tmp.x2 = obj2.getNum(); } else { ok = gFalse; } obj2.free(); obj1.arrayGet(3, &obj2); if (obj2.isNum()) { tmp.y2 = obj2.getNum(); } else { ok = gFalse; } obj2.free(); if (ok) { if (tmp.x1 > tmp.x2) { t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t; } if (tmp.y1 > tmp.y2) { t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t; } *box = tmp; } } else { ok = gFalse; } obj1.free(); return ok; } //------------------------------------------------------------------------ // Page //------------------------------------------------------------------------ Page::Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA) { ok = gTrue; doc = docA; xref = doc->getXRef(); num = numA; // get attributes attrs = attrsA; attrs->clipBoxes(); // annotations pageDict->lookupNF("Annots", &annots); if (!(annots.isRef() || annots.isArray() || annots.isNull())) { error(errSyntaxError, -1, "Page annotations object (page {0:d}) is wrong type ({1:s})", num, annots.getTypeName()); annots.free(); goto err2; } // contents pageDict->lookupNF("Contents", &contents); if (!(contents.isRef() || contents.isArray() || contents.isNull())) { error(errSyntaxError, -1, "Page contents object (page {0:d}) is wrong type ({1:s})", num, contents.getTypeName()); contents.free(); goto err1; } return; err2: annots.initNull(); err1: contents.initNull(); ok = gFalse; } Page::Page(PDFDoc *docA, int numA) { doc = docA; xref = doc->getXRef(); num = numA; attrs = new PageAttrs(); annots.initNull(); contents.initNull(); ok = gTrue; } Page::~Page() { delete attrs; annots.free(); contents.free(); } Links *Page::getLinks() { Links *links; Object obj; links = new Links(getAnnots(&obj), doc->getCatalog()->getBaseURI()); obj.free(); return links; } void Page::display(OutputDev *out, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, GBool printing, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, -1, -1, -1, -1, printing, abortCheckCbk, abortCheckCbkData); } void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { #ifndef PDF_PARSER_ONLY PDFRectangle *mediaBox, *cropBox; PDFRectangle box; Gfx *gfx; Object obj; Annots *annotList; Form *form; int i; if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData)) { return; } rotate += getRotate(); if (rotate >= 360) { rotate -= 360; } else if (rotate < 0) { rotate += 360; } makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(), sliceX, sliceY, sliceW, sliceH, &box, &crop); cropBox = getCropBox(); if (globalParams->getPrintCommands()) { mediaBox = getMediaBox(); printf("***** MediaBox = ll:%g,%g ur:%g,%g\n", mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2); printf("***** CropBox = ll:%g,%g ur:%g,%g\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2); printf("***** Rotate = %d\n", attrs->getRotate()); } gfx = new Gfx(doc, out, num, attrs->getResourceDict(), hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL, rotate, abortCheckCbk, abortCheckCbkData); contents.fetch(xref, &obj); if (!obj.isNull()) { gfx->saveState(); gfx->display(&contents); while (gfx->getState()->hasSaves()) { gfx->restoreState(); } } else { // empty pages need to call dump to do any setup required by the // OutputDev out->dump(); } obj.free(); // draw (non-form) annotations if (globalParams->getDrawAnnotations()) { annotList = new Annots(doc, getAnnots(&obj)); obj.free(); annotList->generateAnnotAppearances(); if (annotList->getNumAnnots() > 0) { if (globalParams->getPrintCommands()) { printf("***** Annotations\n"); } for (i = 0; i < annotList->getNumAnnots(); ++i) { annotList->getAnnot(i)->draw(gfx, printing); } out->dump(); } delete annotList; } // draw form fields if ((form = doc->getCatalog()->getForm())) { form->draw(num, gfx, printing); out->dump(); } delete gfx; #endif } void Page::makeBox(double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool upsideDown, double sliceX, double sliceY, double sliceW, double sliceH, PDFRectangle *box, GBool *crop) { PDFRectangle *mediaBox, *cropBox, *baseBox; double kx, ky; mediaBox = getMediaBox(); cropBox = getCropBox(); if (sliceW >= 0 && sliceH >= 0) { baseBox = useMediaBox ? mediaBox : cropBox; kx = 72.0 / hDPI; ky = 72.0 / vDPI; if (rotate == 90) { if (upsideDown) { box->x1 = baseBox->x1 + ky * sliceY; box->x2 = baseBox->x1 + ky * (sliceY + sliceH); } else { box->x1 = baseBox->x2 - ky * (sliceY + sliceH); box->x2 = baseBox->x2 - ky * sliceY; } box->y1 = baseBox->y1 + kx * sliceX; box->y2 = baseBox->y1 + kx * (sliceX + sliceW); } else if (rotate == 180) { box->x1 = baseBox->x2 - kx * (sliceX + sliceW); box->x2 = baseBox->x2 - kx * sliceX; if (upsideDown) { box->y1 = baseBox->y1 + ky * sliceY; box->y2 = baseBox->y1 + ky * (sliceY + sliceH); } else { box->y1 = baseBox->y2 - ky * (sliceY + sliceH); box->y2 = baseBox->y2 - ky * sliceY; } } else if (rotate == 270) { if (upsideDown) { box->x1 = baseBox->x2 - ky * (sliceY + sliceH); box->x2 = baseBox->x2 - ky * sliceY; } else { box->x1 = baseBox->x1 + ky * sliceY; box->x2 = baseBox->x1 + ky * (sliceY + sliceH); } box->y1 = baseBox->y2 - kx * (sliceX + sliceW); box->y2 = baseBox->y2 - kx * sliceX; } else { box->x1 = baseBox->x1 + kx * sliceX; box->x2 = baseBox->x1 + kx * (sliceX + sliceW); if (upsideDown) { box->y1 = baseBox->y2 - ky * (sliceY + sliceH); box->y2 = baseBox->y2 - ky * sliceY; } else { box->y1 = baseBox->y1 + ky * sliceY; box->y2 = baseBox->y1 + ky * (sliceY + sliceH); } } } else if (useMediaBox) { *box = *mediaBox; } else { *box = *cropBox; *crop = gFalse; } } void Page::processLinks(OutputDev *out) { Links *links; int i; links = getLinks(); for (i = 0; i < links->getNumLinks(); ++i) { out->processLink(links->getLink(i)); } delete links; } #ifndef PDF_PARSER_ONLY void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool upsideDown) { GfxState *state; int i; rotate += getRotate(); if (rotate >= 360) { rotate -= 360; } else if (rotate < 0) { rotate += 360; } state = new GfxState(hDPI, vDPI, useMediaBox ? getMediaBox() : getCropBox(), rotate, upsideDown); for (i = 0; i < 6; ++i) { ctm[i] = state->getCTM()[i]; } delete state; } #endif xpdf-3.04/xpdf/pdftops.cc0000644000076400007640000002431412341430012014634 0ustar dereknderekn//======================================================================== // // pdftops.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #include #include #include #include #ifdef DEBUG_FP_LINUX # include # include #endif #include "parseargs.h" #include "GString.h" #include "gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "PSOutputDev.h" #include "Error.h" #include "config.h" static int firstPage = 1; static int lastPage = 0; static GBool level1 = gFalse; static GBool level1Sep = gFalse; static GBool level2 = gFalse; static GBool level2Sep = gFalse; static GBool level3 = gFalse; static GBool level3Sep = gFalse; static GBool doEPS = gFalse; static GBool doForm = gFalse; #if OPI_SUPPORT static GBool doOPI = gFalse; #endif static GBool noEmbedT1Fonts = gFalse; static GBool noEmbedTTFonts = gFalse; static GBool noEmbedCIDPSFonts = gFalse; static GBool noEmbedCIDTTFonts = gFalse; static GBool preload = gFalse; static char paperSize[15] = ""; static int paperWidth = 0; static int paperHeight = 0; static GBool noCrop = gFalse; static GBool expand = gFalse; static GBool noShrink = gFalse; static GBool noCenter = gFalse; static GBool pageCrop = gFalse; static GBool duplex = gFalse; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static GBool quiet = gFalse; static char cfgFileName[256] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-f", argInt, &firstPage, 0, "first page to print"}, {"-l", argInt, &lastPage, 0, "last page to print"}, {"-level1", argFlag, &level1, 0, "generate Level 1 PostScript"}, {"-level1sep", argFlag, &level1Sep, 0, "generate Level 1 separable PostScript"}, {"-level2", argFlag, &level2, 0, "generate Level 2 PostScript"}, {"-level2sep", argFlag, &level2Sep, 0, "generate Level 2 separable PostScript"}, {"-level3", argFlag, &level3, 0, "generate Level 3 PostScript"}, {"-level3sep", argFlag, &level3Sep, 0, "generate Level 3 separable PostScript"}, {"-eps", argFlag, &doEPS, 0, "generate Encapsulated PostScript (EPS)"}, {"-form", argFlag, &doForm, 0, "generate a PostScript form"}, #if OPI_SUPPORT {"-opi", argFlag, &doOPI, 0, "generate OPI comments"}, #endif {"-noembt1", argFlag, &noEmbedT1Fonts, 0, "don't embed Type 1 fonts"}, {"-noembtt", argFlag, &noEmbedTTFonts, 0, "don't embed TrueType fonts"}, {"-noembcidps", argFlag, &noEmbedCIDPSFonts, 0, "don't embed CID PostScript fonts"}, {"-noembcidtt", argFlag, &noEmbedCIDTTFonts, 0, "don't embed CID TrueType fonts"}, {"-preload", argFlag, &preload, 0, "preload images and forms"}, {"-paper", argString, paperSize, sizeof(paperSize), "paper size (letter, legal, A4, A3, match)"}, {"-paperw", argInt, &paperWidth, 0, "paper width, in points"}, {"-paperh", argInt, &paperHeight, 0, "paper height, in points"}, {"-nocrop", argFlag, &noCrop, 0, "don't crop pages to CropBox"}, {"-expand", argFlag, &expand, 0, "expand pages smaller than the paper size"}, {"-noshrink", argFlag, &noShrink, 0, "don't shrink pages larger than the paper size"}, {"-nocenter", argFlag, &noCenter, 0, "don't center pages smaller than the paper size"}, {"-pagecrop", argFlag, &pageCrop, 0, "treat the CropBox as the page size"}, {"-duplex", argFlag, &duplex, 0, "enable duplex printing"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-q", argFlag, &quiet, 0, "don't print any messages or errors"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; int main(int argc, char *argv[]) { PDFDoc *doc; GString *fileName; GString *psFileName; PSLevel level; PSOutMode mode; GString *ownerPW, *userPW; PSOutputDev *psOut; GBool ok; char *p; int exitCode; #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 ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { fprintf(stderr, "pdftops version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftops", " []", argDesc); } exit(1); } if ((level1 ? 1 : 0) + (level1Sep ? 1 : 0) + (level2 ? 1 : 0) + (level2Sep ? 1 : 0) + (level3 ? 1 : 0) + (level3Sep ? 1 : 0) > 1) { fprintf(stderr, "Error: use only one of the 'level' options.\n"); exit(1); } if (doEPS && doForm) { fprintf(stderr, "Error: use only one of -eps and -form\n"); exit(1); } if (level1) { level = psLevel1; } else if (level1Sep) { level = psLevel1Sep; } else if (level2Sep) { level = psLevel2Sep; } else if (level3) { level = psLevel3; } else if (level3Sep) { level = psLevel3Sep; } else { level = psLevel2; } if (doForm && level < psLevel2) { fprintf(stderr, "Error: forms are only available with Level 2 output.\n"); exit(1); } mode = doEPS ? psModeEPS : doForm ? psModeForm : psModePS; fileName = new GString(argv[1]); // read config file globalParams = new GlobalParams(cfgFileName); #if HAVE_SPLASH globalParams->setupBaseFonts(NULL); #endif if (paperSize[0]) { if (!globalParams->setPSPaperSize(paperSize)) { fprintf(stderr, "Invalid paper size\n"); delete fileName; goto err0; } } else { if (paperWidth) { globalParams->setPSPaperWidth(paperWidth); } if (paperHeight) { globalParams->setPSPaperHeight(paperHeight); } } if (noCrop) { globalParams->setPSCrop(gFalse); } if (pageCrop) { globalParams->setPSUseCropBoxAsPage(gTrue); } if (expand) { globalParams->setPSExpandSmaller(gTrue); } if (noShrink) { globalParams->setPSShrinkLarger(gFalse); } if (noCenter) { globalParams->setPSCenter(gFalse); } if (duplex) { globalParams->setPSDuplex(duplex); } if (level1 || level1Sep || level2 || level2Sep || level3 || level3Sep) { globalParams->setPSLevel(level); } if (noEmbedT1Fonts) { globalParams->setPSEmbedType1(!noEmbedT1Fonts); } if (noEmbedTTFonts) { globalParams->setPSEmbedTrueType(!noEmbedTTFonts); } if (noEmbedCIDPSFonts) { globalParams->setPSEmbedCIDPostScript(!noEmbedCIDPSFonts); } if (noEmbedCIDTTFonts) { globalParams->setPSEmbedCIDTrueType(!noEmbedCIDTTFonts); } if (preload) { globalParams->setPSPreload(preload); } #if OPI_SUPPORT if (doOPI) { globalParams->setPSOPI(doOPI); } #endif if (quiet) { globalParams->setErrQuiet(quiet); } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = new GString(ownerPassword); } else { ownerPW = NULL; } if (userPassword[0] != '\001') { userPW = new GString(userPassword); } else { userPW = NULL; } doc = new PDFDoc(fileName, ownerPW, userPW); if (userPW) { delete userPW; } if (ownerPW) { delete ownerPW; } if (!doc->isOk()) { exitCode = 1; goto err1; } // check for print permission if (!doc->okToPrint()) { error(errNotAllowed, -1, "Printing this document is not allowed."); exitCode = 3; goto err1; } // construct PostScript file name if (argc == 3) { psFileName = new GString(argv[2]); } else { p = fileName->getCString() + fileName->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { psFileName = new GString(fileName->getCString(), fileName->getLength() - 4); } else { psFileName = fileName->copy(); } psFileName->append(doEPS ? ".eps" : ".ps"); } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } // check for multi-page EPS or form if ((doEPS || doForm) && firstPage != lastPage) { error(errCommandLine, -1, "EPS and form files can only contain one page."); goto err2; } // write PostScript file psOut = new PSOutputDev(psFileName->getCString(), doc, firstPage, lastPage, mode); if (psOut->isOk()) { doc->displayPages(psOut, firstPage, lastPage, 72, 72, 0, !globalParams->getPSUseCropBoxAsPage(), globalParams->getPSCrop(), gTrue); } else { delete psOut; exitCode = 2; goto err2; } exitCode = 0; if (!psOut->checkIO()) { exitCode = 2; } delete psOut; // clean up err2: delete psFileName; err1: delete doc; err0: delete globalParams; // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); return exitCode; } xpdf-3.04/xpdf/pdftoppm.cc0000644000076400007640000001435212341430012015007 0ustar dereknderekn//======================================================================== // // pdftoppm.cc // // Copyright 2003 Glyph & Cog, LLC // //======================================================================== #include #include #ifdef _WIN32 # include # include #endif #ifdef DEBUG_FP_LINUX # include # include #endif #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} }; int main(int argc, char *argv[]) { PDFDoc *doc; GString *fileName; char *ppmRoot; GString *ppmFile; GString *ownerPW, *userPW; SplashColor paperColor; SplashOutputDev *splashOut; GBool ok; 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 ok = parseArgs(argDesc, &argc, argv); if (mono && gray) { ok = gFalse; } if (!ok || argc != 3 || printVersion || printHelp) { fprintf(stderr, "pdftoppm version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftoppm", " ", argDesc); } goto err0; } fileName = new GString(argv[1]); ppmRoot = 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 PPM files if (mono) { paperColor[0] = 0xff; splashOut = new SplashOutputDev(splashModeMono1, 1, gFalse, paperColor); } else if (gray) { paperColor[0] = 0xff; splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor); } else { paperColor[0] = paperColor[1] = paperColor[2] = 0xff; splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor); } splashOut->startDoc(doc->getXRef()); for (pg = firstPage; pg <= lastPage; ++pg) { doc->displayPage(splashOut, pg, resolution, resolution, 0, gFalse, gTrue, gFalse); 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}", ppmRoot, pg, mono ? "pbm" : gray ? "pgm" : "ppm"); splashOut->getBitmap()->writePNMFile(ppmFile->getCString()); delete ppmFile; } } delete splashOut; exitCode = 0; // clean up err1: delete doc; delete globalParams; err0: // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); return exitCode; } xpdf-3.04/xpdf/rightArrowDis.xbm0000644000076400007640000000031512341430012016141 0ustar dereknderekn#define rightArrowDis_width 8 #define rightArrowDis_height 15 static unsigned char rightArrowDis_bits[] = { 0x01, 0x02, 0x05, 0x0a, 0x15, 0x2a, 0x55, 0xaa, 0x55, 0x2a, 0x15, 0x0a, 0x05, 0x02, 0x01}; xpdf-3.04/xpdf/PSOutputDev.h0000644000076400007640000003532212341430012015222 0ustar dereknderekn//======================================================================== // // PSOutputDev.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PSOUTPUTDEV_H #define PSOUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "config.h" #include "Object.h" #include "GlobalParams.h" #include "OutputDev.h" class GHash; class PDFDoc; class XRef; class Function; class GfxPath; class GfxFont; class GfxColorSpace; class GfxSeparationColorSpace; class PDFRectangle; class PSOutCustomColor; class PSOutputDev; class PSFontFileInfo; //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ enum PSOutMode { psModePS, psModeEPS, psModeForm }; enum PSFileType { psFile, // write to file psPipe, // write to pipe psStdout, // write to stdout psGeneric // write to a generic stream }; enum PSOutCustomCodeLocation { psOutCustomDocSetup, psOutCustomPageSetup }; typedef void (*PSOutputFunc)(void *stream, const char *data, int len); typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut, PSOutCustomCodeLocation loc, int n, void *data); class PSOutputDev: public OutputDev { public: // Open a PostScript output file, and write the prolog. PSOutputDev(char *fileName, PDFDoc *docA, int firstPage, int lastPage, PSOutMode modeA, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, GBool manualCtrlA = gFalse, PSOutCustomCodeCbk customCodeCbkA = NULL, void *customCodeCbkDataA = NULL); // Open a PSOutputDev that will write to a generic stream. PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, PDFDoc *docA, int firstPage, int lastPage, PSOutMode modeA, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, GBool manualCtrlA = gFalse, PSOutCustomCodeCbk customCodeCbkA = NULL, void *customCodeCbkDataA = NULL); // Destructor -- writes the trailer and closes the file. virtual ~PSOutputDev(); // 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? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return gFalse; } // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() { return gFalse; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual GBool useTilingPatternFill() { return gTrue; } // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. virtual GBool useShadedFills() { return level >= psLevel2; } // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). virtual GBool useDrawForm() { return preload; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gFalse; } //----- header/trailer (used only if manualCtrl is true) // Write the document-level header. void writeHeader(int firstPage, int lastPage, PDFRectangle *mediaBox, PDFRectangle *cropBox, int pageRotate); // Write the Xpdf procset. void writeXpdfProcset(); // Write the document-level setup. void writeDocSetup(Catalog *catalog, int firstPage, int lastPage); // Write the trailer for the current page. void writePageTrailer(); // Write the document trailer. void writeTrailer(); //----- initialization and control // Check to see if a page slice should be displayed. If this // returns false, the page display is aborted. Typically, an // OutputDev will use some alternate means to display the page // before returning false. virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); // Start a page. virtual void startPage(int pageNum, GfxState *state); // End a page. virtual void endPage(); //----- save/restore graphics state virtual void saveState(GfxState *state); virtual void restoreState(GfxState *state); //----- update graphics state virtual void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32); virtual void updateLineDash(GfxState *state); virtual void updateFlatness(GfxState *state); virtual void updateLineJoin(GfxState *state); virtual void updateLineCap(GfxState *state); virtual void updateMiterLimit(GfxState *state); virtual void updateLineWidth(GfxState *state); virtual void updateFillColorSpace(GfxState *state); virtual void updateStrokeColorSpace(GfxState *state); virtual void updateFillColor(GfxState *state); virtual void updateStrokeColor(GfxState *state); virtual void updateFillOverprint(GfxState *state); virtual void updateStrokeOverprint(GfxState *state); virtual void updateTransfer(GfxState *state); //----- update text state virtual void updateFont(GfxState *state); virtual void updateTextMat(GfxState *state); virtual void updateCharSpace(GfxState *state); virtual void updateRender(GfxState *state); virtual void updateRise(GfxState *state); virtual void updateWordSpace(GfxState *state); virtual void updateHorizScaling(GfxState *state); virtual void updateTextPos(GfxState *state); virtual void updateTextShift(GfxState *state, double shift); virtual void saveTextPos(GfxState *state); virtual void restoreTextPos(GfxState *state); //----- path painting virtual void stroke(GfxState *state); virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); 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, double xStep, double yStep); virtual GBool functionShadedFill(GfxState *state, GfxFunctionShading *shading); virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); //----- path clipping virtual void clip(GfxState *state); virtual void eoClip(GfxState *state); virtual void clipToStrokePath(GfxState *state); //----- text drawing virtual void drawString(GfxState *state, GString *s); virtual void endTextObject(GfxState *state); //----- image drawing virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, 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); #if OPI_SUPPORT //----- OPI functions virtual void opiBegin(GfxState *state, Dict *opiDict); virtual void opiEnd(GfxState *state, Dict *opiDict); #endif //----- Type 3 font operators virtual void type3D0(GfxState *state, double wx, double wy); virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury); //----- form XObjects virtual void drawForm(Ref ref); //----- PostScript XObjects virtual void psXObject(Stream *psStream, Stream *level1Stream); //----- miscellaneous void setOffset(double x, double y) { tx0 = x; ty0 = y; } void setScale(double x, double y) { xScale0 = x; yScale0 = y; } void setRotate(int rotateA) { rotate0 = rotateA; } void setClip(double llx, double lly, double urx, double ury) { clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; } void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), void *data) { underlayCbk = cbk; underlayCbkData = data; } void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), void *data) { overlayCbk = cbk; overlayCbkData = data; } void writePSChar(char c); void writePSBlock(char *s, int len); void writePS(const char *s); void writePSFmt(const char *fmt, ...); void writePSString(GString *s); void writePSName(const char *s); private: void init(PSOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, PDFDoc *docA, int firstPage, int lastPage, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA); void setupResources(Dict *resDict); void setupFonts(Dict *resDict); void setupFont(GfxFont *font, Dict *parentResDict); 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(Object *strRef, Object *strObj); void addProcessColor(double c, double m, double y, double k); void addCustomColor(GfxSeparationColorSpace *sepCS); void doPath(GfxPath *path); void doImageL1(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len); void doImageL1Sep(GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len); void doImageL2(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert); void doImageL3(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert); void dumpColorSpaceL2(GfxColorSpace *colorSpace, GBool genXform, GBool updateColors, GBool map01); #if OPI_SUPPORT void opiBegin20(GfxState *state, Dict *dict); void opiBegin13(GfxState *state, Dict *dict); void opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1); GBool getFileSpec(Object *fileSpec, Object *fileName); #endif void cvtFunction(Function *func); GString *filterPSName(GString *name); void writePSTextLine(GString *s); PSLevel level; // PostScript level (1, 2, separation) PSOutMode mode; // PostScript mode (PS, EPS, form) int paperWidth; // width of paper, in pts int paperHeight; // height of paper, in pts GBool paperMatch; // true if paper size is set to match each page int imgLLX, imgLLY, // imageable area, in pts imgURX, imgURY; GBool preload; // load all images into memory, and // predefine forms PSOutputFunc outputFunc; void *outputStream; PSFileType fileType; // file / pipe / stdout GBool manualCtrl; int seqPage; // current sequential page number void (*underlayCbk)(PSOutputDev *psOut, void *data); void *underlayCbkData; void (*overlayCbk)(PSOutputDev *psOut, void *data); void *overlayCbkData; GString *(*customCodeCbk)(PSOutputDev *psOut, PSOutCustomCodeLocation loc, int n, void *data); void *customCodeCbkData; PDFDoc *doc; XRef *xref; // the xref table for this PDF file 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 Ref *formIDs; // list of IDs for predefined forms int formIDLen; // number of entries in formIDs array int formIDSize; // size of formIDs array GList *xobjStack; // stack of XObject dicts currently being // processed int numSaves; // current number of gsaves int numTilingPatterns; // current number of nested tiling patterns int nextFunc; // next unique number to use for a function GList *paperSizes; // list of used paper sizes, if paperMatch // is true [PSOutPaperSize] double tx0, ty0; // global translation double xScale0, yScale0; // global scaling int rotate0; // rotation angle (0, 90, 180, 270) double clipLLX0, clipLLY0, clipURX0, clipURY0; double tx, ty; // global translation for current page double xScale, yScale; // global scaling for current page int rotate; // rotation angle for current page double epsX1, epsY1, // EPS bounding box (unrotated) epsX2, epsY2; GString *embFontList; // resource comments for embedded fonts int processColors; // used process colors PSOutCustomColor // used custom colors *customColors; GBool haveTextClip; // set if text has been drawn with a // clipping render mode GBool inType3Char; // inside a Type 3 CharProc GString *t3String; // Type 3 content string double t3WX, t3WY, // Type 3 character parameters t3LLX, t3LLY, t3URX, t3URY; GBool t3FillColorOnly; // operators should only use the fill color GBool t3Cacheable; // cleared if char is not cacheable GBool t3NeedsRestore; // set if a 'q' operator was issued #if OPI_SUPPORT int opi13Nest; // nesting level of OPI 1.3 objects int opi20Nest; // nesting level of OPI 2.0 objects #endif GBool ok; // set up ok? friend class WinPDFPrinter; }; #endif xpdf-3.04/xpdf/dblRightArrow.xbm0000644000076400007640000000045312341430012016126 0ustar dereknderekn#define dblRightArrow_width 16 #define dblRightArrow_height 15 static unsigned char dblRightArrow_bits[] = { 0x01, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x0f, 0x1f, 0x1f, 0x3f, 0x3f, 0x7f, 0x7f, 0xff, 0xff, 0x7f, 0x7f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f, 0x07, 0x07, 0x03, 0x03, 0x01, 0x01}; xpdf-3.04/xpdf/rightArrow.xbm0000644000076400007640000000030412341430012015477 0ustar dereknderekn#define rightArrow_width 8 #define rightArrow_height 15 static unsigned char rightArrow_bits[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; xpdf-3.04/xpdf/UTF8.h0000644000076400007640000000242212341430012013541 0ustar dereknderekn//======================================================================== // // UTF8.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== static int mapUTF8(Unicode u, char *buf, int bufSize) { if (u <= 0x0000007f) { if (bufSize < 1) { return 0; } buf[0] = (char)u; return 1; } else if (u <= 0x000007ff) { if (bufSize < 2) { return 0; } buf[0] = (char)(0xc0 + (u >> 6)); buf[1] = (char)(0x80 + (u & 0x3f)); return 2; } else if (u <= 0x0000ffff) { if (bufSize < 3) { return 0; } buf[0] = (char)(0xe0 + (u >> 12)); buf[1] = (char)(0x80 + ((u >> 6) & 0x3f)); buf[2] = (char)(0x80 + (u & 0x3f)); return 3; } else if (u <= 0x0010ffff) { if (bufSize < 4) { return 0; } buf[0] = (char)(0xf0 + (u >> 18)); buf[1] = (char)(0x80 + ((u >> 12) & 0x3f)); buf[2] = (char)(0x80 + ((u >> 6) & 0x3f)); buf[3] = (char)(0x80 + (u & 0x3f)); return 4; } else { return 0; } } static int mapUCS2(Unicode u, char *buf, int bufSize) { if (u <= 0xffff) { if (bufSize < 2) { return 0; } buf[0] = (char)((u >> 8) & 0xff); buf[1] = (char)(u & 0xff); return 2; } else { return 0; } } xpdf-3.04/xpdf/NameToCharCode.h0000644000076400007640000000132612341430012015571 0ustar dereknderekn//======================================================================== // // NameToCharCode.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef NAMETOCHARCODE_H #define NAMETOCHARCODE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "CharTypes.h" struct NameToCharCodeEntry; //------------------------------------------------------------------------ class NameToCharCode { public: NameToCharCode(); ~NameToCharCode(); void add(const char *name, CharCode c); CharCode lookup(const char *name); private: int hash(const char *name); NameToCharCodeEntry *tab; int size; int len; }; #endif xpdf-3.04/xpdf/pdffonts.cc0000644000076400007640000002553412341430012015005 0ustar dereknderekn//======================================================================== // // pdffonts.cc // // Copyright 2001-2007 Glyph & Cog, LLC // //======================================================================== #include #include #include #include #include #include #include #include "parseargs.h" #include "GString.h" #include "gmem.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Dict.h" #include "GfxFont.h" #include "Annot.h" #include "Form.h" #include "PDFDoc.h" #include "config.h" // NB: this must match the definition of GfxFontType in GfxFont.h. static const char *fontTypeNames[] = { "unknown", "Type 1", "Type 1C", "Type 1C (OT)", "Type 3", "TrueType", "TrueType (OT)", "CID Type 0", "CID Type 0C", "CID Type 0C (OT)", "CID TrueType", "CID TrueType (OT)" }; static void scanFonts(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] = ""; static GBool printVersion = gFalse; static GBool printHelp = gFalse; static ArgDesc argDesc[] = { {"-f", argInt, &firstPage, 0, "first page to examine"}, {"-l", argInt, &lastPage, 0, "last page to examine"}, {"-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), "user password (for encrypted files)"}, {"-cfg", argString, cfgFileName, sizeof(cfgFileName), "configuration file to use in place of .xpdfrc"}, {"-v", argFlag, &printVersion, 0, "print copyright and version info"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL} }; static Ref *fonts; static int fontsLen; static int fontsSize; static Ref *seenObjs; static int seenObjsLen; static int seenObjsSize; int main(int argc, char *argv[]) { PDFDoc *doc; GString *fileName; GString *ownerPW, *userPW; GBool ok; Page *page; Dict *resDict; Annots *annots; Form *form; Object obj1, obj2; int pg, i, j; int exitCode; exitCode = 99; // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 2 || printVersion || printHelp) { fprintf(stderr, "pdffonts version %s\n", xpdfVersion); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdffonts", "", argDesc); } goto err0; } fileName = new GString(argv[1]); // read config file globalParams = new GlobalParams(cfgFileName); globalParams->setupBaseFonts(NULL); // open PDF file if (ownerPassword[0] != '\001') { ownerPW = new GString(ownerPassword); } else { ownerPW = NULL; } if (userPassword[0] != '\001') { userPW = new GString(userPassword); } else { userPW = NULL; } doc = new PDFDoc(fileName, ownerPW, userPW); if (userPW) { delete userPW; } if (ownerPW) { delete ownerPW; } if (!doc->isOk()) { exitCode = 1; goto err1; } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } // scan the fonts 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())) { scanFonts(resDict, doc); } annots = new Annots(doc, page->getAnnots(&obj1)); obj1.free(); for (i = 0; i < annots->getNumAnnots(); ++i) { if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { 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; err0: // check for memory leaks Object::memCheck(stderr); gMemReport(stderr); 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; Object patternDict, pattern, gsDict, gs, smask, smaskGroup, resObj; Ref r; GfxFontDict *gfxFontDict; GfxFont *font; int i; // scan the fonts in this resource dictionary gfxFontDict = NULL; resDict->lookupNF("Font", &obj1); if (obj1.isRef()) { obj1.fetch(doc->getXRef(), &obj2); if (obj2.isDict()) { r = obj1.getRef(); gfxFontDict = new GfxFontDict(doc->getXRef(), &r, obj2.getDict()); } obj2.free(); } else if (obj1.isDict()) { gfxFontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict()); } if (gfxFontDict) { for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { if ((font = gfxFontDict->getFont(i))) { scanFont(font, doc); } } delete gfxFontDict; } obj1.free(); // recursively scan any resource dictionaries in 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()->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) { Ref fontRef, embRef; Object fontObj, toUnicodeObj; GString *name; GBool emb, subset, hasToUnicode; GfxFontLoc *loc; int i; fontRef = *font->getID(); // check for an already-seen font for (i = 0; i < fontsLen; ++i) { if (fontRef.num == fonts[i].num && fontRef.gen == fonts[i].gen) { return; } } // font name name = font->getName(); // check for an embedded font if (font->getType() == fontType3) { emb = gTrue; } else { emb = font->getEmbeddedFontID(&embRef); } // look for a ToUnicode map hasToUnicode = gFalse; if (doc->getXRef()->fetch(fontRef.num, fontRef.gen, &fontObj)->isDict()) { hasToUnicode = fontObj.dictLookup("ToUnicode", &toUnicodeObj)->isStream(); toUnicodeObj.free(); } fontObj.free(); // check for a font subset name: capital letters followed by a '+' // sign subset = gFalse; if (name) { for (i = 0; i < name->getLength(); ++i) { if (name->getChar(i) < 'A' || name->getChar(i) > 'Z') { break; } } subset = i > 0 && i < name->getLength() && name->getChar(i) == '+'; } // print the font info printf("%-36s %-17s %-3s %-3s %-3s", name ? name->getCString() : "[none]", fontTypeNames[font->getType()], emb ? "yes" : "no", subset ? "yes" : "no", hasToUnicode ? "yes" : "no"); if (fontRef.gen >= 100000) { printf(" [none]"); } else { 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) { if (fontsSize <= INT_MAX - 32) { fontsSize += 32; } else { // let greallocn throw an exception fontsSize = -1; } fonts = (Ref *)greallocn(fonts, fontsSize, sizeof(Ref)); } fonts[fontsLen++] = *font->getID(); } xpdf-3.04/xpdf/Object.h0000644000076400007640000002105412341430012014223 0ustar dereknderekn//======================================================================== // // Object.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef OBJECT_H #define OBJECT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include #include "gtypes.h" #include "gmem.h" #include "gfile.h" #include "GString.h" class XRef; class Array; class Dict; class Stream; //------------------------------------------------------------------------ // Ref //------------------------------------------------------------------------ struct Ref { int num; // object number int gen; // generation number }; //------------------------------------------------------------------------ // object types //------------------------------------------------------------------------ enum ObjType { // simple objects objBool, // boolean objInt, // integer objReal, // real objString, // string objName, // name objNull, // null // complex objects objArray, // array objDict, // dictionary objStream, // stream objRef, // indirect reference // special objects objCmd, // command name objError, // error return from Lexer objEOF, // end of file return from Lexer objNone // uninitialized object }; #define numObjTypes 14 // total number of object types //------------------------------------------------------------------------ // Object //------------------------------------------------------------------------ #ifdef DEBUG_MEM #define initObj(t) ++numAlloc[type = t] #else #define initObj(t) type = t #endif class Object { public: // Default constructor. Object(): type(objNone) {} // Initialize an object. Object *initBool(GBool boolnA) { initObj(objBool); booln = boolnA; return this; } Object *initInt(int intgA) { initObj(objInt); intg = intgA; return this; } Object *initReal(double realA) { initObj(objReal); real = realA; return this; } Object *initString(GString *stringA) { initObj(objString); string = stringA; return this; } Object *initName(const char *nameA) { initObj(objName); name = copyString(nameA); return this; } Object *initNull() { initObj(objNull); return this; } Object *initArray(XRef *xref); Object *initDict(XRef *xref); Object *initDict(Dict *dictA); Object *initStream(Stream *streamA); Object *initRef(int numA, int genA) { initObj(objRef); ref.num = numA; ref.gen = genA; return this; } Object *initCmd(char *cmdA) { initObj(objCmd); cmd = copyString(cmdA); return this; } Object *initError() { initObj(objError); return this; } Object *initEOF() { initObj(objEOF); return this; } // Copy an object. Object *copy(Object *obj); // If object is a Ref, fetch and return the referenced object. // Otherwise, return a copy of the object. Object *fetch(XRef *xref, Object *obj, int recursion = 0); // Free object contents. void free(); // Type checking. ObjType getType() { return type; } GBool isBool() { return type == objBool; } GBool isInt() { return type == objInt; } GBool isReal() { return type == objReal; } GBool isNum() { return type == objInt || type == objReal; } GBool isString() { return type == objString; } GBool isName() { return type == objName; } GBool isNull() { return type == objNull; } GBool isArray() { return type == objArray; } GBool isDict() { return type == objDict; } GBool isStream() { return type == objStream; } GBool isRef() { return type == objRef; } GBool isCmd() { return type == objCmd; } GBool isError() { return type == objError; } GBool isEOF() { return type == objEOF; } GBool isNone() { return type == objNone; } // Special type checking. GBool isName(const char *nameA) { return type == objName && !strcmp(name, nameA); } GBool isDict(const char *dictType); GBool isStream(char *dictType); GBool isCmd(const char *cmdA) { return type == objCmd && !strcmp(cmd, cmdA); } // Accessors. NB: these assume object is of correct type. GBool getBool() { return booln; } int getInt() { return intg; } double getReal() { return real; } double getNum() { return type == objInt ? (double)intg : real; } GString *getString() { return string; } char *getName() { return name; } Array *getArray() { return array; } Dict *getDict() { return dict; } Stream *getStream() { return stream; } Ref getRef() { return ref; } int getRefNum() { return ref.num; } int getRefGen() { return ref.gen; } char *getCmd() { return cmd; } // Array accessors. int arrayGetLength(); void arrayAdd(Object *elem); Object *arrayGet(int i, Object *obj); Object *arrayGetNF(int i, Object *obj); // Dict accessors. int dictGetLength(); void dictAdd(char *key, Object *val); GBool dictIs(const char *dictType); Object *dictLookup(const char *key, Object *obj, int recursion = 0); Object *dictLookupNF(const char *key, Object *obj); char *dictGetKey(int i); Object *dictGetVal(int i, Object *obj); Object *dictGetValNF(int i, Object *obj); // Stream accessors. GBool streamIs(char *dictType); void streamReset(); void streamClose(); int streamGetChar(); int streamLookChar(); int streamGetBlock(char *blk, int size); char *streamGetLine(char *buf, int size); GFileOffset streamGetPos(); void streamSetPos(GFileOffset pos, int dir = 0); Dict *streamGetDict(); // Output. const char *getTypeName(); void print(FILE *f = stdout); // Memory testing. static void memCheck(FILE *f); private: ObjType type; // object type union { // value for each type: GBool booln; // boolean int intg; // integer double real; // real GString *string; // string char *name; // name Array *array; // array Dict *dict; // dictionary Stream *stream; // stream Ref ref; // indirect reference char *cmd; // command }; #ifdef DEBUG_MEM static int // number of each type of object numAlloc[numObjTypes]; // currently allocated #endif }; //------------------------------------------------------------------------ // Array accessors. //------------------------------------------------------------------------ #include "Array.h" inline int Object::arrayGetLength() { return array->getLength(); } inline void Object::arrayAdd(Object *elem) { array->add(elem); } inline Object *Object::arrayGet(int i, Object *obj) { return array->get(i, obj); } inline Object *Object::arrayGetNF(int i, Object *obj) { return array->getNF(i, obj); } //------------------------------------------------------------------------ // Dict accessors. //------------------------------------------------------------------------ #include "Dict.h" inline int Object::dictGetLength() { return dict->getLength(); } inline void Object::dictAdd(char *key, Object *val) { dict->add(key, val); } inline GBool Object::dictIs(const char *dictType) { return dict->is(dictType); } inline GBool Object::isDict(const char *dictType) { return type == objDict && dictIs(dictType); } inline Object *Object::dictLookup(const char *key, Object *obj, int recursion) { return dict->lookup(key, obj, recursion); } inline Object *Object::dictLookupNF(const char *key, Object *obj) { return dict->lookupNF(key, obj); } inline char *Object::dictGetKey(int i) { return dict->getKey(i); } inline Object *Object::dictGetVal(int i, Object *obj) { return dict->getVal(i, obj); } inline Object *Object::dictGetValNF(int i, Object *obj) { return dict->getValNF(i, obj); } //------------------------------------------------------------------------ // Stream accessors. //------------------------------------------------------------------------ #include "Stream.h" inline GBool Object::streamIs(char *dictType) { return stream->getDict()->is(dictType); } inline GBool Object::isStream(char *dictType) { return type == objStream && streamIs(dictType); } inline void Object::streamReset() { stream->reset(); } inline void Object::streamClose() { stream->close(); } inline int Object::streamGetChar() { return stream->getChar(); } inline int Object::streamLookChar() { return stream->lookChar(); } inline 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 GFileOffset Object::streamGetPos() { return stream->getPos(); } inline void Object::streamSetPos(GFileOffset pos, int dir) { stream->setPos(pos, dir); } inline Dict *Object::streamGetDict() { return stream->getDict(); } #endif xpdf-3.04/xpdf/AcroForm.h0000644000076400007640000000665512341430012014537 0ustar dereknderekn//======================================================================== // // AcroForm.h // // Copyright 2012 Glyph & Cog, LLC // //======================================================================== #ifndef ACROFORM_H #define ACROFORM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "Form.h" class TextString; class GfxFont; class GfxFontDict; //------------------------------------------------------------------------ class AcroForm: public Form { public: static AcroForm *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA); virtual ~AcroForm(); virtual const char *getType() { return "AcroForm"; } virtual void draw(int pageNum, Gfx *gfx, GBool printing); virtual int getNumFields(); virtual FormField *getField(int idx); private: AcroForm(PDFDoc *docA, Object *acroFormObjA); void buildAnnotPageList(Catalog *catalog); int lookupAnnotPage(Object *annotRef); void scanField(Object *fieldRef); Object acroFormObj; GBool needAppearances; GList *annotPages; // [AcroFormAnnotPage] GList *fields; // [AcroFormField] friend class AcroFormField; }; //------------------------------------------------------------------------ enum AcroFormFieldType { acroFormFieldPushbutton, acroFormFieldRadioButton, acroFormFieldCheckbox, acroFormFieldFileSelect, acroFormFieldMultilineText, acroFormFieldText, acroFormFieldComboBox, acroFormFieldListBox, acroFormFieldSignature }; class AcroFormField: public FormField { public: static AcroFormField *load(AcroForm *acroFormA, Object *fieldRefA); virtual ~AcroFormField(); virtual const char *getType(); virtual Unicode *getName(int *length); virtual Unicode *getValue(int *length); virtual Object *getResources(Object *res); private: AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA, AcroFormFieldType typeA, TextString *nameA, Guint flagsA); void draw(int pageNum, Gfx *gfx, GBool printing); void drawAnnot(int pageNum, Gfx *gfx, GBool printing, Object *annotRef, Object *annotObj); void drawExistingAppearance(Gfx *gfx, Dict *annot, double xMin, double yMin, double xMax, double yMax); void drawNewAppearance(Gfx *gfx, Dict *annot, double xMin, double yMin, double xMax, double yMax); void setColor(Array *a, GBool fill, int adjust); void drawText(GString *text, GString *da, GfxFontDict *fontDict, GBool multiline, int comb, int quadding, GBool txField, GBool forceZapfDingbats, int rot, double xMin, double yMin, double xMax, double yMax, double border); void drawListBox(GString **text, GBool *selection, int nOptions, int topIdx, GString *da, GfxFontDict *fontDict, GBool quadding, double xMin, double yMin, double xMax, double yMax, double border); void getNextLine(GString *text, int start, GfxFont *font, double fontSize, double wMax, int *end, double *width, int *next); void drawCircle(double cx, double cy, double r, const char *cmd); void drawCircleTopLeft(double cx, double cy, double r); void drawCircleBottomRight(double cx, double cy, double r); Object *getAnnotResources(Dict *annot, Object *res); Object *fieldLookup(const char *key, Object *obj); Object *fieldLookup(Dict *dict, const char *key, Object *obj); AcroForm *acroForm; Object fieldRef; Object fieldObj; AcroFormFieldType type; TextString *name; Guint flags; GString *appearBuf; friend class AcroForm; }; #endif xpdf-3.04/xpdf/JPXStream.h0000644000076400007640000002507312341430012014637 0ustar dereknderekn//======================================================================== // // JPXStream.h // // Copyright 2002-2003 Glyph & Cog, LLC // //======================================================================== #ifndef JPXSTREAM_H #define JPXSTREAM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" #include "Stream.h" class JArithmeticDecoder; class JArithmeticDecoderStats; //------------------------------------------------------------------------ enum JPXColorSpaceType { jpxCSBiLevel = 0, jpxCSYCbCr1 = 1, jpxCSYCbCr2 = 3, jpxCSYCBCr3 = 4, jpxCSPhotoYCC = 9, jpxCSCMY = 11, jpxCSCMYK = 12, jpxCSYCCK = 13, jpxCSCIELab = 14, jpxCSsRGB = 16, jpxCSGrayscale = 17, jpxCSBiLevel2 = 18, jpxCSCIEJab = 19, jpxCSCISesRGB = 20, jpxCSROMMRGB = 21, jpxCSsRGBYCbCr = 22, jpxCSYPbPr1125 = 23, jpxCSYPbPr1250 = 24 }; struct JPXColorSpecCIELab { Guint rl, ol, ra, oa, rb, ob, il; }; struct JPXColorSpecEnumerated { JPXColorSpaceType type; // color space type union { JPXColorSpecCIELab cieLab; }; }; struct JPXColorSpec { Guint meth; // method int prec; // precedence union { JPXColorSpecEnumerated enumerated; }; }; //------------------------------------------------------------------------ struct JPXPalette { Guint nEntries; // number of entries in the palette Guint nComps; // number of components in each entry Guint *bpc; // bits per component, for each component int *c; // color data: // c[i*nComps+j] = entry i, component j }; //------------------------------------------------------------------------ struct JPXCompMap { Guint nChannels; // number of channels Guint *comp; // codestream components mapped to each channel Guint *type; // 0 for direct use, 1 for palette mapping Guint *pComp; // palette components to use }; //------------------------------------------------------------------------ struct JPXChannelDefn { Guint nChannels; // number of channels Guint *idx; // channel indexes Guint *type; // channel types Guint *assoc; // channel associations }; //------------------------------------------------------------------------ struct JPXTagTreeNode { GBool finished; // true if this node is finished Guint val; // current value }; //------------------------------------------------------------------------ struct JPXCodeBlock { //----- size Guint x0, y0, x1, y1; // bounds //----- persistent state GBool seen; // true if this code-block has already // been seen Guint lBlock; // base number of bits used for pkt data length Guint nextPass; // next coding pass //---- info from first packet Guint nZeroBitPlanes; // number of zero bit planes //----- info for the current packet Guint included; // code-block inclusion in this packet: // 0=not included, 1=included Guint nCodingPasses; // number of coding passes in this pkt Guint *dataLen; // data lengths (one per codeword segment) Guint dataLenSize; // size of the dataLen array //----- coefficient data int *coeffs; char *touched; // coefficient 'touched' flags Gushort len; // coefficient length JArithmeticDecoder // arithmetic decoder *arithDecoder; JArithmeticDecoderStats // arithmetic decoder stats *stats; }; //------------------------------------------------------------------------ struct JPXSubband { //----- computed Guint x0, y0, x1, y1; // bounds Guint nXCBs, nYCBs; // number of code-blocks in the x and y // directions //----- tag trees Guint maxTTLevel; // max tag tree level JPXTagTreeNode *inclusion; // inclusion tag tree for each subband JPXTagTreeNode *zeroBitPlane; // zero-bit plane tag tree for each // subband //----- children JPXCodeBlock *cbs; // the code-blocks (len = nXCBs * nYCBs) }; //------------------------------------------------------------------------ struct JPXPrecinct { //----- computed Guint x0, y0, x1, y1; // bounds of the precinct //----- children JPXSubband *subbands; // the subbands }; //------------------------------------------------------------------------ struct JPXResLevel { //----- from the COD and COC segments (main and tile) Guint precinctWidth; // log2(precinct width) Guint precinctHeight; // log2(precinct height) //----- computed Guint x0, y0, x1, y1; // bounds of the tile-comp (for this res level) Guint bx0[3], by0[3], // subband bounds bx1[3], by1[3]; //---- children JPXPrecinct *precincts; // the precincts }; //------------------------------------------------------------------------ struct JPXTileComp { //----- from the SIZ segment GBool sgned; // 1 for signed, 0 for unsigned Guint prec; // precision, in bits Guint hSep; // horizontal separation of samples Guint vSep; // vertical separation of samples //----- from the COD and COC segments (main and tile) Guint style; // coding style parameter (Scod / Scoc) Guint nDecompLevels; // number of decomposition levels Guint codeBlockW; // log2(code-block width) Guint codeBlockH; // log2(code-block height) Guint codeBlockStyle; // code-block style Guint transform; // wavelet transformation //----- from the QCD and QCC segments (main and tile) Guint quantStyle; // quantization style Guint *quantSteps; // quantization step size for each subband Guint nQuantSteps; // number of entries in quantSteps //----- computed Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction Guint cbW; // code-block width Guint cbH; // code-block height //----- image data int *data; // the decoded image data int *buf; // intermediate buffer for the inverse // transform //----- children JPXResLevel *resLevels; // the resolution levels // (len = nDecompLevels + 1) }; //------------------------------------------------------------------------ struct JPXTile { GBool init; //----- from the COD segments (main and tile) Guint progOrder; // progression order Guint nLayers; // number of layers Guint multiComp; // multiple component transformation //----- computed Guint x0, y0, x1, y1; // bounds of the tile, in ref coords Guint maxNDecompLevels; // max number of decomposition levels used // in any component in this tile //----- progression order loop counters Guint comp; // component Guint res; // resolution level Guint precinct; // precinct Guint layer; // layer //----- tile part info Guint nextTilePart; // next expected tile-part //----- children JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps) }; //------------------------------------------------------------------------ struct JPXImage { //----- from the SIZ segment Guint xSize, ySize; // size of reference grid Guint xOffset, yOffset; // image offset Guint xTileSize, yTileSize; // size of tiles Guint xTileOffset, // offset of first tile yTileOffset; Guint 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 Guint nXTiles; // number of tiles in x direction Guint nYTiles; // number of tiles in y direction //----- children JPXTile *tiles; // the tiles (len = nXTiles * nYTiles) }; //------------------------------------------------------------------------ enum JPXDecodeResult { jpxDecodeOk, jpxDecodeNonFatalError, jpxDecodeFatalError }; //------------------------------------------------------------------------ class JPXStream: public FilterStream { public: JPXStream(Stream *strA); virtual ~JPXStream(); virtual StreamKind getKind() { return strJPX; } virtual void reset(); virtual void close(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); 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); JPXDecodeResult readBoxes(); GBool readColorSpecBox(Guint dataLen); JPXDecodeResult readCodestream(Guint len); GBool readTilePart(); GBool readTilePartData(Guint tileIdx, Guint tilePartLen, GBool tilePartToEOC); GBool readCodeBlockData(JPXTileComp *tileComp, JPXResLevel *resLevel, JPXPrecinct *precinct, JPXSubband *subband, Guint res, Guint sb, JPXCodeBlock *cb); void inverseTransform(JPXTileComp *tileComp); void inverseTransformLevel(JPXTileComp *tileComp, Guint r, JPXResLevel *resLevel); void inverseTransform1D(JPXTileComp *tileComp, int *data, Guint offset, Guint n); GBool inverseMultiCompAndDC(JPXTile *tile); GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen); int readMarkerHdr(int *segType, Guint *segLen); GBool readUByte(Guint *x); GBool readByte(int *x); GBool readUWord(Guint *x); GBool readULong(Guint *x); GBool readNBytes(int nBytes, GBool signd, int *x); void startBitBuf(Guint byteCountA); GBool readBits(int nBits, Guint *x); void skipSOP(); void skipEPH(); Guint finishBitBuf(); BufStream *bufStr; // buffered stream (for lookahead) 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 GBool haveCS; // set if a color spec has been found JPXPalette palette; // the palette GBool havePalette; // set if a palette has been found JPXCompMap compMap; // the component mapping GBool haveCompMap; // set if a component mapping has been found JPXChannelDefn channelDefn; // channel definition GBool haveChannelDefn; // set if a channel defn has been found JPXImage img; // JPEG2000 decoder data Guint bitBuf; // buffer for bit reads int bitBufLen; // number of bits in bitBuf GBool bitBufSkip; // true if next bit should be skipped // (for bit stuffing) Guint byteCount; // number of available bytes left Guint curX, curY, curComp; // current position for lookChar/getChar Guint readBuf; // read buffer Guint readBufLen; // number of valid bits in readBuf }; #endif xpdf-3.04/xpdf/findDis.xbm0000644000076400007640000000043112341430012014730 0ustar dereknderekn#define findDis_width 15 #define findDis_height 15 static unsigned char findDis_bits[] = { 0x10, 0x04, 0x28, 0x0a, 0x04, 0x10, 0xaa, 0x2a, 0x55, 0x55, 0x20, 0x02, 0x41, 0x41, 0x20, 0x02, 0x41, 0x41, 0xa0, 0x02, 0x01, 0x40, 0x20, 0x02, 0x01, 0x40, 0x20, 0x02, 0x15, 0x54}; xpdf-3.04/xpdf/dblRightArrowDis.xbm0000644000076400007640000000046412341430012016570 0ustar dereknderekn#define dblRightArrowDis_width 16 #define dblRightArrowDis_height 15 static unsigned char dblRightArrowDis_bits[] = { 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x0a, 0x0a, 0x15, 0x15, 0x2a, 0x2a, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0x2a, 0x2a, 0x15, 0x15, 0x0a, 0x0a, 0x05, 0x05, 0x02, 0x02, 0x01, 0x01}; xpdf-3.04/INSTALL0000644000076400007640000001230612341430012012734 0ustar derekndereknXpdf ==== version 3.04 2014-may-28 The Xpdf software and documentation are copyright 1996-2014 Glyph & Cog, LLC. Email: derekn@foolabs.com WWW: http://www.foolabs.com/xpdf/ Compiling xpdf -------------- Xpdf is written in C++ (with a little bit of C). It should work with any ANSI-compliant C++ and C compilers. The systems and compilers it's been tested with are listed on the xpdf web page. Xpdf requires the Motif (or Lesstif) toolkit. The following notes give specific instructions for compiling on different systems. ************** *** UNIX *** ************** * Install FreeType 2 (this is required). WARNING: You must have version 2.0.5 or newer. Some older versions of XFree86 ship with an older version of FreeType, which means you'll need to explicitly set include and library paths to get the correct version: --with-freetype2-library=PATH --with-freetype2-includes=PATH * If you have Motif (or Lesstif) installed in a non-standard place, you can use the following options to tell configure where to find it: --with-Xm-library=PATH --with-Xm-includes=PATH * Run the configure script: ./configure This should produce a set of makefiles customized for your system. The configure script accepts the following options (in addition to the usual things accepted by autoconf configure scripts): --prefix=PREFIX Changes the directory where xpdf is installed. The default is /usr/local. --enable-a4-paper Switches the default paper size for PostScript output (xpdf and pdftops) to A4. The default is Letter size. --enable-no-text-select With this option, xpdf will not copy text. (This is only useful on closed systems where the user can't get at the PDF file directly.) --enable-opi Enables support for generation of OPI (Open Prepress Interface) comments with pdftops. --sysconfdir=DIR Look for the system-wide xpdfrc config file in this directory. The default is PREFIX/etc. --with-appdef-dir=DIR Use the specified app-defaults directory. The default is /usr/lib/X11/app-defaults. If you need to pass specific options to the C and/or C++ compiler, you can set the CFLAGS and/or CXXFLAGS environment variables before running the configure script. Any options given that way will be added to the CFLAGS/CXXFLAGS used by all of the Xpdf makefiles. * Type 'make'. This should build the executables: xpdf/xpdf xpdf/pdftops xpdf/pdftotext xpdf/pdfinfo xpdf/pdffonts xpdf/pdfdetach xpdf/pdftoppm xpdf/pdfimages * If desired, type 'make install' to install the binaries and man pages. The old Makefile.config and Makefiles are no longer provided or supported. If you really want to manually configure Xpdf (which is not recommended), the files that need to be created are aconf.h, Makefile, goo/Makefile, fofi/Makefile, splash/Makefile, and xpdf/Makefile, all of which are generated from the corresponding '.in' files. If you want to run a quick test, there is a tiny PDF file included with xpdf, as misc/hello.pdf . ************** *** OS/2 *** ************** Xpdf is known to run under OS/2 with the EMX runtime environment and XFree86. Using a proper autoconf port you can generate a valid configure script version. *************** *** Win32 *** *************** The non-X programs (pdftops, pdftotext, pdfinfo, pdffonts, pdfdetach, pdfimages, and pdftoppm) will compile with both gcc (from cygwin), djgpp (the Delorie port of gcc) and Microsoft Visual C++. With cygwin, the build procedure is the same as for Unix: * Open a shell. * ./configure * make * make install It is also possible to build the Xpdf viewer with the cygwin XFree86 port (thanks to Michael A. Richmond for these instructions): * make sure you have the lesstif, XFree86-base, and XFree86-prog cygwin packages installed (all from the "XFree86" category) * to build xpdf: - cd xpdf-x.yy - ./configure --with-freetype2-library=/usr/lib --with-freetype2-includes=/usr/include/freetype2 --with-Xm-library=/usr/lib --with-Xm-includes=/usr/include (all on one line) - make (this should build xpdf.exe, in addition to the command line utilities) With djgpp: * Open a DOS window. * ./dj_make * djgpp is a DOS/Windows port of gcc, available from http://www.delorie.com/ With djgpp, for DOS 6 (instead of Win32): * ./dj_make * cd xpdf * strip pdftops.exe * exe2coff pdftops.exe * copy /B c:\djgpp\bin\cwsdstub.exe+pdftops pdftops.exe * upx pdftops.exe - if you want compressed executables * and similarly for the other executables * cwsdstub.exe comes from djgpp/v2misc/csdpmi5b.zip on any of the djgpp ftp mirrors; exe2coff is part of the standard djgpp install * upx comes from http://upx.sourceforge.net/ With the Microsoft tools: * Open a DOS window. * Type "cl". If you get the message "Bad command or file name", you must run VCVARS32.BAT. (The location of this BAT file can be determined with Explorer.) * Type "ms_make" The dj_make and ms_make scripts don't build pdftoppm -- you'll need to install FreeType 2 before you can compile pdftoppm. xpdf-3.04/aconf2.h0000644000076400007640000000136312341430012013225 0ustar dereknderekn/* * aconf2.h * * This gets included by aconf.h, and contains miscellaneous global * settings not directly controlled by autoconf. This is a separate * file because otherwise the configure script will munge any * #define/#undef constructs. * * Copyright 2002-2003 Glyph & Cog, LLC */ #ifndef ACONF2_H #define ACONF2_H /* * This controls the use of the interface/implementation pragmas. */ #ifdef __GNUC__ #define USE_GCC_PRAGMAS #endif /* There is a bug in the version of gcc which ships with MacOS X 10.2 */ #if defined(__APPLE__) && defined(__MACH__) # include #endif #ifdef MAC_OS_X_VERSION_MAX_ALLOWED # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 # undef USE_GCC_PRAGMAS # endif #endif #endif xpdf-3.04/aconf.h.in0000644000076400007640000000313412341430012013546 0ustar dereknderekn/* * aconf.h * * Copyright 2002-2003 Glyph & Cog, LLC */ #ifndef ACONF_H #define ACONF_H #include /* * Use A4 paper size instead of Letter for PostScript output. */ #undef A4_PAPER /* * Do not allow text selection. */ #undef NO_TEXT_SELECT /* * Include support for OPI comments. */ #undef OPI_SUPPORT /* * Enable multithreading support. */ #undef MULTITHREADED /* * Enable C++ exceptions. */ #undef USE_EXCEPTIONS /* * Use fixed point (instead of floating point) arithmetic. */ #undef USE_FIXEDPOINT /* * Directory with the Xpdf app-defaults file. */ #undef APPDEFDIR /* * Full path for the system-wide xpdfrc file. */ #undef SYSTEM_XPDFRC /* * Various include files and functions. */ #undef HAVE_DIRENT_H #undef HAVE_SYS_NDIR_H #undef HAVE_SYS_DIR_H #undef HAVE_NDIR_H #undef HAVE_SYS_SELECT_H #undef HAVE_SYS_BSDTYPES_H #undef HAVE_STRINGS_H #undef HAVE_BSTRING_H #undef HAVE_POPEN #undef HAVE_MKSTEMP #undef HAVE_MKSTEMPS #undef SELECT_TAKES_INT #undef HAVE_STD_SORT #undef HAVE_FSEEKO #undef HAVE_FSEEK64 #undef HAVE_FSEEKI64 #undef _FILE_OFFSET_BITS #undef _LARGE_FILES #undef _LARGEFILE_SOURCE #undef HAVE_XTAPPSETEXITFLAG /* * This is defined if using libXpm. */ #undef HAVE_X11_XPM_H /* * One of these is defined if using FreeType 2. */ #undef HAVE_FREETYPE_H #undef HAVE_FREETYPE_FREETYPE_H /* * This is defined if using libpaper. */ #undef HAVE_PAPER_H /* * Enable support for loading plugins. */ #undef ENABLE_PLUGINS /* * Defined if the Splash library is avaiable. */ #undef HAVE_SPLASH /* * Enable support for CMYK output. */ #undef SPLASH_CMYK #endif xpdf-3.04/splash/0000755000076400007640000000000012341430012013173 5ustar derekndereknxpdf-3.04/splash/SplashFontFile.cc0000644000076400007640000000247712341430012016375 0ustar dereknderekn//======================================================================== // // SplashFontFile.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #ifndef _WIN32 # include #endif #include "GString.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" #ifdef VMS #if (__VMS_VER < 70000000) extern "C" int unlink(char *filename); #endif #endif //------------------------------------------------------------------------ // SplashFontFile //------------------------------------------------------------------------ SplashFontFile::SplashFontFile(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA #else char *fileNameA, GBool deleteFileA #endif ) { id = idA; #if LOAD_FONTS_FROM_MEM fontBuf = fontBufA; #else fileName = new GString(fileNameA); deleteFile = deleteFileA; #endif refCnt = 0; } SplashFontFile::~SplashFontFile() { #if LOAD_FONTS_FROM_MEM delete fontBuf; #else if (deleteFile) { unlink(fileName->getCString()); } delete fileName; #endif delete id; } void SplashFontFile::incRefCnt() { ++refCnt; } void SplashFontFile::decRefCnt() { if (!--refCnt) { delete this; } } xpdf-3.04/splash/SplashMath.h0000644000076400007640000001761312341430012015420 0ustar dereknderekn//======================================================================== // // SplashMath.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHMATH_H #define SPLASHMATH_H #include #if USE_FIXEDPONT #include "FixedPoint.h" #else #include #endif #include "SplashTypes.h" static inline SplashCoord splashAbs(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::abs(x); #else return fabs(x); #endif } static inline int splashFloor(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::floor(x); #else #if __GNUC__ && __i386__ // floor() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly Gushort oldCW, newCW, t; int result; __asm__ volatile("fnstcw %0\n" "movw %0, %3\n" "andw $0xf3ff, %3\n" "orw $0x0400, %3\n" "movw %3, %1\n" // round down "fldcw %1\n" "fistl %2\n" "fldcw %0\n" : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) : "t" (x)); return result; #elif defined(_WIN32) && defined(_M_IX86) // floor() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly Gushort oldCW, newCW; int result; __asm fld QWORD PTR x __asm fnstcw WORD PTR oldCW __asm mov ax, WORD PTR oldCW __asm and ax, 0xf3ff __asm or ax, 0x0400 __asm mov WORD PTR newCW, ax // round down __asm fldcw WORD PTR newCW __asm fistp DWORD PTR result __asm fldcw WORD PTR oldCW return result; #else return (int)floor(x); #endif #endif } static inline int splashCeil(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::ceil(x); #else #if __GNUC__ && __i386__ // ceil() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly Gushort oldCW, newCW, t; int result; __asm__ volatile("fnstcw %0\n" "movw %0, %3\n" "andw $0xf3ff, %3\n" "orw $0x0800, %3\n" "movw %3, %1\n" // round up "fldcw %1\n" "fistl %2\n" "fldcw %0\n" : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) : "t" (x)); return result; #elif defined(_WIN32) && defined(_M_IX86) // ceil() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly Gushort oldCW, newCW; int result; __asm fld QWORD PTR x __asm fnstcw WORD PTR oldCW __asm mov ax, WORD PTR oldCW __asm and ax, 0xf3ff __asm or ax, 0x0800 __asm mov WORD PTR newCW, ax // round up __asm fldcw WORD PTR newCW __asm fistp DWORD PTR result __asm fldcw WORD PTR oldCW return result; #else return (int)ceil(x); #endif #endif } static inline int splashRound(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::round(x); #else #if __GNUC__ && __i386__ // this could use round-to-nearest mode and avoid the "+0.5", // but that produces slightly different results (because i+0.5 // sometimes rounds up and sometimes down using the even rule) Gushort oldCW, newCW, t; int result; x += 0.5; __asm__ volatile("fnstcw %0\n" "movw %0, %3\n" "andw $0xf3ff, %3\n" "orw $0x0400, %3\n" "movw %3, %1\n" // round down "fldcw %1\n" "fistl %2\n" "fldcw %0\n" : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) : "t" (x)); return result; #elif defined(_WIN32) && defined(_M_IX86) // this could use round-to-nearest mode and avoid the "+0.5", // but that produces slightly different results (because i+0.5 // sometimes rounds up and sometimes down using the even rule) Gushort oldCW, newCW; int result; x += 0.5; __asm fld QWORD PTR x __asm fnstcw WORD PTR oldCW __asm mov ax, WORD PTR oldCW __asm and ax, 0xf3ff __asm or ax, 0x0400 __asm mov WORD PTR newCW, ax // round down __asm fldcw WORD PTR newCW __asm fistp DWORD PTR result __asm fldcw WORD PTR oldCW return result; #else return (int)floor(x + 0.5); #endif #endif } static inline SplashCoord splashAvg(SplashCoord x, SplashCoord y) { #if USE_FIXEDPOINT return FixedPoint::avg(x, y); #else return 0.5 * (x + y); #endif } static inline SplashCoord splashSqrt(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::sqrt(x); #else return sqrt(x); #endif } static inline SplashCoord splashPow(SplashCoord x, SplashCoord y) { #if USE_FIXEDPOINT return FixedPoint::pow(x, y); #else return pow(x, y); #endif } static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { SplashCoord dx, dy; dx = x1 - x0; dy = y1 - y0; #if USE_FIXEDPOINT // this handles the situation where dx*dx or dy*dy is too large to // fit in the 16.16 fixed point format SplashCoord dxa, dya, d; dxa = splashAbs(dx); dya = splashAbs(dy); if (dxa == 0 && dya == 0) { return 0; } else if (dxa > dya) { d = dya / dxa; return dxa * FixedPoint::sqrt(d*d + 1); } else { d = dxa / dya; return dya * FixedPoint::sqrt(d*d + 1); } #else return sqrt(dx * dx + dy * dy); #endif } static inline GBool splashCheckDet(SplashCoord m11, SplashCoord m12, SplashCoord m21, SplashCoord m22, SplashCoord epsilon) { #if USE_FIXEDPOINT return FixedPoint::checkDet(m11, m12, m21, m22, epsilon); #else return fabs(m11 * m22 - m12 * m21) >= epsilon; #endif } // Perform stroke adjustment on a SplashCoord range [xMin, xMax), // resulting in an int range [*xMinI, *xMaxI). // // There are several options: // // 1. Round both edge coordinates. // Pro: adjacent strokes/fills line up without any gaps or // overlaps // Con: lines with the same original floating point width can // end up with different integer widths, e.g.: // xMin = 10.1 xMax = 11.3 (width = 1.2) // --> xMinI = 10 xMaxI = 11 (width = 1) // but // xMin = 10.4 xMax = 11.6 (width = 1.2) // --> xMinI = 10 xMaxI = 12 (width = 2) // // 2. Round the min coordinate; add the ceiling of the width. // Pro: lines with the same original floating point width will // always end up with the same integer width // Con: adjacent strokes/fills can have overlaps (which is // problematic with transparency) // (This could use floor on the min coordinate, instead of // rounding, with similar results.) // (If the width is rounded instead of using ceiling, the results // Are similar, except that adjacent strokes/fills can have gaps // as well as overlaps.) // // 3. Use floor on the min coordinate and ceiling on the max // coordinate. // Pro: lines always end up at least as wide as the original // floating point width // Con: adjacent strokes/fills can have overlaps, and lines with // the same original floating point width can end up with // different integer widths; the integer width can be more // than one pixel wider than the original width, e.g.: // xMin = 10.9 xMax = 12.1 (width = 1.2) // --> xMinI = 10 xMaxI = 13 (width = 3) // but // xMin = 10.1 xMax = 11.3 (width = 1.2) // --> xMinI = 10 xMaxI = 12 (width = 2) static inline void splashStrokeAdjust(SplashCoord xMin, SplashCoord xMax, int *xMinI, int *xMaxI) { int x0, x1; // NB: enable exactly one of these. #if 1 // 1. Round both edge coordinates. x0 = splashRound(xMin); x1 = splashRound(xMax); #endif #if 0 // 2. Round the min coordinate; add the ceiling of the width. x0 = splashRound(xMin); x1 = x0 + splashCeil(xMax - xMin); #endif #if 0 // 3. Use floor on the min coord and ceiling on the max coord. x0 = splashFloor(xMin); x1 = splashCeil(xMax); #endif if (x1 == x0) { ++x1; } *xMinI = x0; *xMaxI = x1; } #endif xpdf-3.04/splash/SplashFontEngine.h0000644000076400007640000000607612341430012016564 0ustar dereknderekn//======================================================================== // // SplashFontEngine.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFONTENGINE_H #define SPLASHFONTENGINE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" class GString; class SplashFTFontEngine; class SplashDTFontEngine; class SplashDT4FontEngine; class SplashFontFile; class SplashFontFileID; class SplashFont; //------------------------------------------------------------------------ #define splashFontCacheSize 16 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #define splashFTNoHinting (1 << 0) #endif //------------------------------------------------------------------------ // SplashFontEngine //------------------------------------------------------------------------ class SplashFontEngine { public: // Create a font engine. SplashFontEngine( #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H GBool enableFreeType, Guint freeTypeFlags, #endif GBool aa); ~SplashFontEngine(); // Get a font file from the cache. Returns NULL if there is no // matching entry in the cache. SplashFontFile *getFontFile(SplashFontFileID *id); // Load fonts - these create new SplashFontFile objects. SplashFontFile *loadType1Font(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc); SplashFontFile *loadType1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc); SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc); SplashFontFile *loadCIDFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf #else char *fileName, GBool deleteFile #endif ); SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int *codeToGID, int codeToGIDLen); SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int fontNum, int *codeToGID, int codeToGIDLen, char *fontName); // Get a font - this does a cache lookup first, and if not found, // creates a new SplashFont object and adds it to the cache. The // matrix, mat = textMat * ctm: // [ mat[0] mat[1] ] // [ mat[2] mat[3] ] // specifies the font transform in PostScript style: // [x' y'] = [x y] * mat // Note that the Splash y axis points downward. SplashFont *getFont(SplashFontFile *fontFile, SplashCoord *textMat, SplashCoord *ctm); private: SplashFont *fontCache[splashFontCacheSize]; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H SplashFTFontEngine *ftEngine; #endif }; #endif xpdf-3.04/splash/SplashPattern.h0000644000076400007640000000264512341430012016143 0ustar dereknderekn//======================================================================== // // SplashPattern.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHPATTERN_H #define SPLASHPATTERN_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" class SplashScreen; //------------------------------------------------------------------------ // SplashPattern //------------------------------------------------------------------------ class SplashPattern { public: SplashPattern(); virtual SplashPattern *copy() = 0; virtual ~SplashPattern(); // Return the color value for a specific pixel. virtual void getColor(int x, int y, SplashColorPtr c) = 0; // Returns true if this pattern object will return the same color // value for all pixels. virtual GBool isStatic() = 0; private: }; //------------------------------------------------------------------------ // SplashSolidColor //------------------------------------------------------------------------ class SplashSolidColor: public SplashPattern { public: SplashSolidColor(SplashColorPtr colorA); virtual SplashPattern *copy() { return new SplashSolidColor(color); } virtual ~SplashSolidColor(); virtual void getColor(int x, int y, SplashColorPtr c); virtual GBool isStatic() { return gTrue; } private: SplashColor color; }; #endif xpdf-3.04/splash/SplashBitmap.h0000644000076400007640000000400112341430012015726 0ustar dereknderekn//======================================================================== // // SplashBitmap.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHBITMAP_H #define SPLASHBITMAP_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "SplashTypes.h" //------------------------------------------------------------------------ // SplashBitmap //------------------------------------------------------------------------ class SplashBitmap { public: // Create a new bitmap. It will have x pixels in // color mode . Rows will be padded out to a multiple of // bytes. If is false, the bitmap will be stored // upside-down, i.e., with the last row first in memory. SplashBitmap(int widthA, int heightA, int rowPad, SplashColorMode modeA, GBool alphaA, GBool topDown = gTrue); ~SplashBitmap(); int getWidth() { return width; } int getHeight() { return height; } int getRowSize() { return rowSize; } int getAlphaRowSize() { return width; } SplashColorMode getMode() { return mode; } SplashColorPtr getDataPtr() { return data; } Guchar *getAlphaPtr() { return alpha; } SplashError writePNMFile(char *fileName); SplashError writePNMFile(FILE *f); SplashError writeAlphaPGMFile(char *fileName); void getPixel(int x, int y, SplashColorPtr pixel); Guchar getAlpha(int x, int y); // Caller takes ownership of the bitmap data. The SplashBitmap // object is no longer valid -- the next call should be to the // destructor. SplashColorPtr takeData(); private: int width, height; // size of bitmap int rowSize; // size of one row of data, in bytes // - negative for bottom-up bitmaps SplashColorMode mode; // color mode SplashColorPtr data; // pointer to row zero of the color data Guchar *alpha; // pointer to row zero of the alpha data // (always top-down) friend class Splash; }; #endif xpdf-3.04/splash/SplashBitmap.cc0000644000076400007640000001102012341430012016063 0ustar dereknderekn//======================================================================== // // SplashBitmap.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "SplashErrorCodes.h" #include "SplashBitmap.h" //------------------------------------------------------------------------ // SplashBitmap //------------------------------------------------------------------------ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad, SplashColorMode modeA, GBool alphaA, GBool topDown) { width = widthA; height = heightA; mode = modeA; switch (mode) { case splashModeMono1: if (width > 0) { rowSize = (width + 7) >> 3; } else { rowSize = -1; } break; case splashModeMono8: if (width > 0) { rowSize = width; } else { rowSize = -1; } break; case splashModeRGB8: case splashModeBGR8: if (width > 0 && width <= INT_MAX / 3) { rowSize = width * 3; } else { rowSize = -1; } break; #if SPLASH_CMYK case splashModeCMYK8: if (width > 0 && width <= INT_MAX / 4) { rowSize = width * 4; } else { rowSize = -1; } break; #endif } if (rowSize > 0) { rowSize += rowPad - 1; rowSize -= rowSize % rowPad; } data = (SplashColorPtr)gmallocn(height, rowSize); if (!topDown) { data += (height - 1) * rowSize; rowSize = -rowSize; } if (alphaA) { alpha = (Guchar *)gmallocn(width, height); } else { alpha = NULL; } } SplashBitmap::~SplashBitmap() { if (data) { if (rowSize < 0) { gfree(data + (height - 1) * rowSize); } else { gfree(data); } } gfree(alpha); } SplashError SplashBitmap::writePNMFile(char *fileName) { FILE *f; SplashError err; if (!(f = fopen(fileName, "wb"))) { return splashErrOpenFile; } err = writePNMFile(f); fclose(f); return err; } SplashError SplashBitmap::writePNMFile(FILE *f) { SplashColorPtr row, p; int x, y; switch (mode) { case splashModeMono1: fprintf(f, "P4\n%d %d\n", width, height); row = data; for (y = 0; y < height; ++y) { p = row; for (x = 0; x < width; x += 8) { fputc(*p ^ 0xff, f); ++p; } row += rowSize; } break; case splashModeMono8: fprintf(f, "P5\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { fwrite(row, 1, width, f); row += rowSize; } break; case splashModeRGB8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { fwrite(row, 1, 3 * width, f); row += rowSize; } break; case splashModeBGR8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { p = row; for (x = 0; x < width; ++x) { fputc(splashBGR8R(p), f); fputc(splashBGR8G(p), f); fputc(splashBGR8B(p), f); p += 3; } row += rowSize; } break; #if SPLASH_CMYK case splashModeCMYK8: // PNM doesn't support CMYK break; #endif } return splashOk; } SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) { FILE *f; if (!alpha) { return splashErrModeMismatch; } if (!(f = fopen(fileName, "wb"))) { return splashErrOpenFile; } fprintf(f, "P5\n%d %d\n255\n", width, height); fwrite(alpha, 1, width * height, f); fclose(f); return splashOk; } void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) { SplashColorPtr p; if (y < 0 || y >= height || x < 0 || x >= width) { return; } switch (mode) { case splashModeMono1: p = &data[y * rowSize + (x >> 3)]; pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00; break; case splashModeMono8: p = &data[y * rowSize + x]; pixel[0] = p[0]; break; case splashModeRGB8: p = &data[y * rowSize + 3 * x]; pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; break; case splashModeBGR8: p = &data[y * rowSize + 3 * x]; pixel[0] = p[2]; pixel[1] = p[1]; pixel[2] = p[0]; break; #if SPLASH_CMYK case splashModeCMYK8: p = &data[y * rowSize + 4 * x]; pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; pixel[3] = p[3]; break; #endif } } Guchar SplashBitmap::getAlpha(int x, int y) { return alpha[y * width + x]; } SplashColorPtr SplashBitmap::takeData() { SplashColorPtr data2; data2 = data; data = NULL; return data2; } xpdf-3.04/splash/SplashFTFontFile.cc0000644000076400007640000001003312341430012016612 0ustar dereknderekn//======================================================================== // // SplashFTFontFile.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "gmem.h" #include "GString.h" #include "SplashFTFontEngine.h" #include "SplashFTFont.h" #include "SplashFTFontFile.h" //------------------------------------------------------------------------ // SplashFTFontFile //------------------------------------------------------------------------ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif const char **encA, GBool useLightHintingA) { FT_Face faceA; int *codeToGIDA; const char *name; int i; #if LOAD_FONTS_FROM_MEM if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(), fontBufA->getLength(), 0, &faceA)) { #else if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) { #endif return NULL; } codeToGIDA = (int *)gmallocn(256, sizeof(int)); for (i = 0; i < 256; ++i) { codeToGIDA[i] = 0; if ((name = encA[i])) { codeToGIDA[i] = (int)FT_Get_Name_Index(faceA, (char *)name); } } return new SplashFTFontFile(engineA, idA, #if LOAD_FONTS_FROM_MEM fontBufA, #else fileNameA, deleteFileA, #endif faceA, codeToGIDA, 256, gFalse, useLightHintingA); } SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif int *codeToGIDA, int codeToGIDLenA) { FT_Face faceA; #if LOAD_FONTS_FROM_MEM if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(), fontBufA->getLength(), 0, &faceA)) { #else if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) { #endif return NULL; } return new SplashFTFontFile(engineA, idA, #if LOAD_FONTS_FROM_MEM fontBufA, #else fileNameA, deleteFileA, #endif faceA, codeToGIDA, codeToGIDLenA, gFalse, gFalse); } SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif int fontNum, int *codeToGIDA, int codeToGIDLenA) { FT_Face faceA; #if LOAD_FONTS_FROM_MEM if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(), fontBufA->getLength(), fontNum, &faceA)) { #else if (FT_New_Face(engineA->lib, fileNameA, fontNum, &faceA)) { #endif return NULL; } return new SplashFTFontFile(engineA, idA, #if LOAD_FONTS_FROM_MEM fontBufA, #else fileNameA, deleteFileA, #endif faceA, codeToGIDA, codeToGIDLenA, gTrue, gFalse); } SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, GBool trueTypeA, GBool useLightHintingA): #if LOAD_FONTS_FROM_MEM SplashFontFile(idA, fontBufA) #else SplashFontFile(idA, fileNameA, deleteFileA) #endif { engine = engineA; face = faceA; codeToGID = codeToGIDA; codeToGIDLen = codeToGIDLenA; trueType = trueTypeA; useLightHinting = useLightHintingA; } SplashFTFontFile::~SplashFTFontFile() { if (face) { FT_Done_Face(face); } if (codeToGID) { gfree(codeToGID); } } SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat, SplashCoord *textMat) { SplashFont *font; font = new SplashFTFont(this, mat, textMat); font->initCache(); return font; } #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H xpdf-3.04/splash/SplashScreen.cc0000644000076400007640000002303212341430012016074 0ustar dereknderekn//======================================================================== // // SplashScreen.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #if HAVE_STD_SORT #include #endif #include "gmem.h" #include "SplashMath.h" #include "SplashScreen.h" //------------------------------------------------------------------------ static SplashScreenParams defaultParams = { splashScreenDispersed, // type 2, // size 2, // dotRadius 1.0, // gamma 0.0, // blackThreshold 1.0 // whiteThreshold }; //------------------------------------------------------------------------ struct SplashScreenPoint { int x, y; int dist; }; #if HAVE_STD_SORT struct cmpDistancesFunctor { bool operator()(const SplashScreenPoint &p0, const SplashScreenPoint &p1) { return p0.dist < p1.dist; } }; #else // HAVE_STD_SORT static int cmpDistances(const void *p0, const void *p1) { return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist; } #endif //------------------------------------------------------------------------ // SplashScreen //------------------------------------------------------------------------ // If is true, this generates a 45 degree screen using a // circular dot spot function. DPI = resolution / ((size / 2) * // sqrt(2)). If is false, this generates an optimal // threshold matrix using recursive tesselation. Gamma correction // (gamma = 1 / 1.33) is also computed here. SplashScreen::SplashScreen(SplashScreenParams *params) { Guchar u; int black, white, i; if (!params) { params = &defaultParams; } // size must be a power of 2, and at least 2 for (size = 2, log2Size = 1; size < params->size; size <<= 1, ++log2Size) ; switch (params->type) { case splashScreenDispersed: mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); buildDispersedMatrix(size/2, size/2, 1, size/2, 1); break; case splashScreenClustered: mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); buildClusteredMatrix(); break; case splashScreenStochasticClustered: // size must be at least 2*r while (size < (params->dotRadius << 1)) { size <<= 1; ++log2Size; } mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); buildSCDMatrix(params->dotRadius); break; } sizeM1 = size - 1; // do gamma correction and compute minVal/maxVal minVal = 255; maxVal = 0; black = splashRound((SplashCoord)255.0 * params->blackThreshold); if (black < 1) { black = 1; } white = splashRound((SplashCoord)255.0 * params->whiteThreshold); if (white > 255) { white = 255; } for (i = 0; i < size * size; ++i) { u = splashRound((SplashCoord)255.0 * splashPow((SplashCoord)mat[i] / 255.0, params->gamma)); if (u < black) { u = (Guchar)black; } else if (u >= white) { u = (Guchar)white; } mat[i] = u; if (u < minVal) { minVal = u; } else if (u > maxVal) { maxVal = u; } } } void SplashScreen::buildDispersedMatrix(int i, int j, int val, int delta, int offset) { if (delta == 0) { // map values in [1, size^2] --> [1, 255] mat[(i << log2Size) + j] = 1 + (254 * (val - 1)) / (size * size - 1); } else { buildDispersedMatrix(i, j, val, delta / 2, 4*offset); buildDispersedMatrix((i + delta) % size, (j + delta) % size, val + offset, delta / 2, 4*offset); buildDispersedMatrix((i + delta) % size, j, val + 2*offset, delta / 2, 4*offset); buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size, val + 3*offset, delta / 2, 4*offset); } } void SplashScreen::buildClusteredMatrix() { SplashCoord *dist; SplashCoord u, v, d; Guchar val; int size2, x, y, x1, y1, i; size2 = size >> 1; // initialize the threshold matrix for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { mat[(y << log2Size) + x] = 0; } } // build the distance matrix dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord)); for (y = 0; y < size2; ++y) { for (x = 0; x < size2; ++x) { if (x + y < size2 - 1) { u = (SplashCoord)x + 0.5 - 0; v = (SplashCoord)y + 0.5 - 0; } else { u = (SplashCoord)x + 0.5 - (SplashCoord)size2; v = (SplashCoord)y + 0.5 - (SplashCoord)size2; } dist[y * size2 + x] = u*u + v*v; } } for (y = 0; y < size2; ++y) { for (x = 0; x < size2; ++x) { if (x < y) { u = (SplashCoord)x + 0.5 - 0; v = (SplashCoord)y + 0.5 - (SplashCoord)size2; } else { u = (SplashCoord)x + 0.5 - (SplashCoord)size2; v = (SplashCoord)y + 0.5 - 0; } dist[(size2 + y) * size2 + x] = u*u + v*v; } } // build the threshold matrix x1 = y1 = 0; // make gcc happy for (i = 0; i < size * size2; ++i) { d = -1; for (y = 0; y < size; ++y) { for (x = 0; x < size2; ++x) { if (mat[(y << log2Size) + x] == 0 && dist[y * size2 + x] > d) { x1 = x; y1 = y; d = dist[y1 * size2 + x1]; } } } // map values in [0, 2*size*size2-1] --> [1, 255] val = 1 + (254 * (2*i)) / (2*size*size2 - 1); mat[(y1 << log2Size) + x1] = val; val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1); if (y1 < size2) { mat[((y1 + size2) << log2Size) + x1 + size2] = val; } else { mat[((y1 - size2) << log2Size) + x1 + size2] = val; } } gfree(dist); } // Compute the distance between two points on a toroid. int SplashScreen::distance(int x0, int y0, int x1, int y1) { int dx0, dx1, dx, dy0, dy1, dy; dx0 = abs(x0 - x1); dx1 = size - dx0; dx = dx0 < dx1 ? dx0 : dx1; dy0 = abs(y0 - y1); dy1 = size - dy0; dy = dy0 < dy1 ? dy0 : dy1; return dx * dx + dy * dy; } // Algorithm taken from: // Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot // Dithering" in Color Imaging: Device-Independent Color, Color // Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999. void SplashScreen::buildSCDMatrix(int r) { SplashScreenPoint *dots, *pts; int dotsLen, dotsSize; char *tmpl; char *grid; int *region, *dist; int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n; //~ this should probably happen somewhere else srand(123); // generate the random space-filling curve pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint)); i = 0; for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { pts[i].x = x; pts[i].y = y; ++i; } } for (i = 0; i < size * size; ++i) { j = i + (int)((double)(size * size - i) * (double)rand() / ((double)RAND_MAX + 1.0)); x = pts[i].x; y = pts[i].y; pts[i].x = pts[j].x; pts[i].y = pts[j].y; pts[j].x = x; pts[j].y = y; } // construct the circle template tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char)); for (y = 0; y <= r; ++y) { for (x = 0; x <= r; ++x) { tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0; } } // mark all grid cells as free grid = (char *)gmallocn(size * size, sizeof(char)); for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { grid[(y << log2Size) + x] = 0; } } // walk the space-filling curve, adding dots dotsLen = 0; dotsSize = 32; dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint)); for (i = 0; i < size * size; ++i) { x = pts[i].x; y = pts[i].y; if (!grid[(y << log2Size) + x]) { if (dotsLen == dotsSize) { dotsSize *= 2; dots = (SplashScreenPoint *)greallocn(dots, dotsSize, sizeof(SplashScreenPoint)); } dots[dotsLen++] = pts[i]; for (yy = 0; yy <= r; ++yy) { y0 = (y + yy) % size; y1 = (y - yy + size) % size; for (xx = 0; xx <= r; ++xx) { if (tmpl[yy*(r+1) + xx]) { x0 = (x + xx) % size; x1 = (x - xx + size) % size; grid[(y0 << log2Size) + x0] = 1; grid[(y0 << log2Size) + x1] = 1; grid[(y1 << log2Size) + x0] = 1; grid[(y1 << log2Size) + x1] = 1; } } } } } gfree(tmpl); gfree(grid); // assign each cell to a dot, compute distance to center of dot region = (int *)gmallocn(size * size, sizeof(int)); dist = (int *)gmallocn(size * size, sizeof(int)); for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { iMin = 0; dMin = distance(dots[0].x, dots[0].y, x, y); for (i = 1; i < dotsLen; ++i) { d = distance(dots[i].x, dots[i].y, x, y); if (d < dMin) { iMin = i; dMin = d; } } region[(y << log2Size) + x] = iMin; dist[(y << log2Size) + x] = dMin; } } // compute threshold values for (i = 0; i < dotsLen; ++i) { n = 0; for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { if (region[(y << log2Size) + x] == i) { pts[n].x = x; pts[n].y = y; pts[n].dist = distance(dots[i].x, dots[i].y, x, y); ++n; } } } #if HAVE_STD_SORT std::sort(pts, pts + n, cmpDistancesFunctor()); #else qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances); #endif for (j = 0; j < n; ++j) { // map values in [0 .. n-1] --> [255 .. 1] mat[(pts[j].y << log2Size) + pts[j].x] = 255 - (254 * j) / (n - 1); } } gfree(pts); gfree(region); gfree(dist); gfree(dots); } SplashScreen::SplashScreen(SplashScreen *screen) { size = screen->size; sizeM1 = screen->sizeM1; log2Size = screen->log2Size; mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); memcpy(mat, screen->mat, size * size * sizeof(Guchar)); minVal = screen->minVal; maxVal = screen->maxVal; } SplashScreen::~SplashScreen() { gfree(mat); } xpdf-3.04/splash/SplashErrorCodes.h0000644000076400007640000000164212341430012016571 0ustar dereknderekn//======================================================================== // // SplashErrorCodes.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHERRORCODES_H #define SPLASHERRORCODES_H #include //------------------------------------------------------------------------ #define splashOk 0 // no error #define splashErrNoCurPt 1 // no current point #define splashErrEmptyPath 2 // zero points in path #define splashErrBogusPath 3 // only one point in subpath #define splashErrNoSave 4 // state stack is empty #define splashErrOpenFile 5 // couldn't open file #define splashErrNoGlyph 6 // couldn't get the requested glyph #define splashErrModeMismatch 7 // invalid combination of color modes #define splashErrSingularMatrix 8 // matrix is singular #endif xpdf-3.04/splash/SplashFTFont.cc0000644000076400007640000003103712341430012016021 0ustar dereknderekn//======================================================================== // // SplashFTFont.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include FT_OUTLINE_H #include FT_SIZES_H #include FT_GLYPH_H #include "gmem.h" #include "SplashMath.h" #include "SplashGlyphBitmap.h" #include "SplashPath.h" #include "SplashFontEngine.h" #include "SplashFTFontEngine.h" #include "SplashFTFontFile.h" #include "SplashFTFont.h" //------------------------------------------------------------------------ static int glyphPathMoveTo(const FT_Vector *pt, void *path); static int glyphPathLineTo(const FT_Vector *pt, void *path); static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path); static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path); //------------------------------------------------------------------------ // SplashFTFont //------------------------------------------------------------------------ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, SplashCoord *textMatA): SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa) { FT_Face face; int size, div; int x, y; #if USE_FIXEDPOINT SplashCoord scale; #endif face = fontFileA->face; if (FT_New_Size(face, &sizeObj)) { return; } face->size = sizeObj; size = splashRound(splashDist(0, 0, mat[2], mat[3])); if (size < 1) { size = 1; } if (FT_Set_Pixel_Sizes(face, 0, size)) { return; } // if the textMat values are too small, FreeType's fixed point // arithmetic doesn't work so well textScale = splashDist(0, 0, textMat[2], textMat[3]) / size; div = face->bbox.xMax > 20000 ? 65536 : 1; #if USE_FIXEDPOINT scale = (SplashCoord)1 / (SplashCoord)face->units_per_EM; // transform the four corners of the font bounding box -- the min // and max values form the bounding box of the transformed font x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) + mat[2] * (scale * (face->bbox.yMin / div))); xMin = xMax = x; y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) + mat[3] * (scale * (face->bbox.yMin / div))); yMin = yMax = y; x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) + mat[2] * (scale * (face->bbox.yMax / div))); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) + mat[3] * (scale * (face->bbox.yMax / div))); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) + mat[2] * (scale * (face->bbox.yMin / div))); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) + mat[3] * (scale * (face->bbox.yMin / div))); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) + mat[2] * (scale * (face->bbox.yMax / div))); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) + mat[3] * (scale * (face->bbox.yMax / div))); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } #else // USE_FIXEDPOINT // transform the four corners of the font bounding box -- the min // and max values form the bounding box of the transformed font x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) / (div * face->units_per_EM)); xMin = xMax = x; y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) / (div * face->units_per_EM)); yMin = yMax = y; x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } #endif // USE_FIXEDPOINT // This is a kludge: some buggy PDF generators embed fonts with // zero bounding boxes. if (xMax == xMin) { xMin = 0; xMax = size; } if (yMax == yMin) { yMin = 0; yMax = (int)((SplashCoord)1.2 * size); } // compute the transform matrix #if USE_FIXEDPOINT matrix.xx = (FT_Fixed)((mat[0] / size).get16Dot16()); matrix.yx = (FT_Fixed)((mat[1] / size).get16Dot16()); matrix.xy = (FT_Fixed)((mat[2] / size).get16Dot16()); matrix.yy = (FT_Fixed)((mat[3] / size).get16Dot16()); textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)).get16Dot16()); textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)).get16Dot16()); textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)).get16Dot16()); textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)).get16Dot16()); #else matrix.xx = (FT_Fixed)((mat[0] / size) * 65536); matrix.yx = (FT_Fixed)((mat[1] / size) * 65536); matrix.xy = (FT_Fixed)((mat[2] / size) * 65536); matrix.yy = (FT_Fixed)((mat[3] / size) * 65536); textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536); textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536); textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536); textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536); #endif } SplashFTFont::~SplashFTFont() { } GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap) { return SplashFont::getGlyph(c, xFrac, 0, bitmap); } GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap) { SplashFTFontFile *ff; FT_Vector offset; FT_GlyphSlot slot; FT_UInt gid; FT_Int32 flags; int rowSize; Guchar *p, *q; int i; ff = (SplashFTFontFile *)fontFile; ff->face->size = sizeObj; offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64); offset.y = 0; FT_Set_Transform(ff->face, &matrix, &offset); slot = ff->face->glyph; if (ff->codeToGID && c < ff->codeToGIDLen) { gid = (FT_UInt)ff->codeToGID[c]; } else { gid = (FT_UInt)c; } if (ff->trueType && gid < 0) { // skip the TrueType notdef glyph return gFalse; } // Set up the load flags: // * disable bitmaps because they look ugly when scaled, rotated, // etc. // * disable autohinting because it can fail badly with font subsets // that use invalid glyph names (the FreeType autohinter depends // on the glyph name to figure out how to autohint the glyph) // * but enable light autohinting for Type 1 fonts because regular // hinting looks pretty bad, and the invalid glyph name issue // seems to be very rare (Type 1 fonts are mostly used for // substitution, in which case the full font is being used, which // means we have the glyph names) flags = FT_LOAD_NO_BITMAP; if (ff->engine->flags & splashFTNoHinting) { flags |= FT_LOAD_NO_HINTING; } else if (ff->useLightHinting) { flags |= FT_LOAD_TARGET_LIGHT; } else { flags |= FT_LOAD_NO_AUTOHINT; } if (FT_Load_Glyph(ff->face, gid, flags)) { return gFalse; } if (FT_Render_Glyph(slot, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO)) { return gFalse; } if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) { // this can happen if (a) the glyph is really tiny or (b) the // metrics in the TrueType file are broken return gFalse; } bitmap->x = -slot->bitmap_left; bitmap->y = slot->bitmap_top; bitmap->w = slot->bitmap.width; bitmap->h = slot->bitmap.rows; bitmap->aa = aa; if (aa) { rowSize = bitmap->w; } else { rowSize = (bitmap->w + 7) >> 3; } bitmap->data = (Guchar *)gmallocn(bitmap->h, rowSize); bitmap->freeData = gTrue; for (i = 0, p = bitmap->data, q = slot->bitmap.buffer; i < bitmap->h; ++i, p += rowSize, q += slot->bitmap.pitch) { memcpy(p, q, rowSize); } return gTrue; } struct SplashFTFontPath { SplashPath *path; SplashCoord textScale; GBool needClose; }; SplashPath *SplashFTFont::getGlyphPath(int c) { static FT_Outline_Funcs outlineFuncs = { #if FREETYPE_MINOR <= 1 (int (*)(FT_Vector *, void *))&glyphPathMoveTo, (int (*)(FT_Vector *, void *))&glyphPathLineTo, (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo, (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo, #else &glyphPathMoveTo, &glyphPathLineTo, &glyphPathConicTo, &glyphPathCubicTo, #endif 0, 0 }; SplashFTFontFile *ff; SplashFTFontPath path; FT_GlyphSlot slot; FT_UInt gid; FT_Glyph glyph; ff = (SplashFTFontFile *)fontFile; ff->face->size = sizeObj; FT_Set_Transform(ff->face, &textMatrix, NULL); slot = ff->face->glyph; if (ff->codeToGID && c < ff->codeToGIDLen) { gid = ff->codeToGID[c]; } else { gid = (FT_UInt)c; } if (ff->trueType && gid < 0) { // skip the TrueType notdef glyph return NULL; } if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) { return NULL; } if (FT_Get_Glyph(slot, &glyph)) { return NULL; } path.path = new SplashPath(); path.textScale = textScale; path.needClose = gFalse; FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline, &outlineFuncs, &path); if (path.needClose) { path.path->close(); } FT_Done_Glyph(glyph); return path.path; } static int glyphPathMoveTo(const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; if (p->needClose) { p->path->close(); p->needClose = gFalse; } p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); return 0; } static int glyphPathLineTo(const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); p->needClose = gTrue; return 0; } static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; if (!p->path->getCurPt(&x0, &y0)) { return 0; } xc = (SplashCoord)ctrl->x * p->textScale / 64.0; yc = (SplashCoord)ctrl->y * p->textScale / 64.0; x3 = (SplashCoord)pt->x * p->textScale / 64.0; y3 = (SplashCoord)pt->y * p->textScale / 64.0; // A second-order Bezier curve is defined by two endpoints, p0 and // p3, and one control point, pc: // // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3 // // A third-order Bezier curve is defined by the same two endpoints, // p0 and p3, and two control points, p1 and p2: // // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3 // // Applying some algebra, we can convert a second-order curve to a // third-order curve: // // p1 = (1/3) * (p0 + 2pc) // p2 = (1/3) * (2pc + p3) x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc); y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc); x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3); y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3); p->path->curveTo(x1, y1, x2, y2, x3, y3); p->needClose = gTrue; return 0; } static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0, (SplashCoord)ctrl1->y * p->textScale / 64.0, (SplashCoord)ctrl2->x * p->textScale / 64.0, (SplashCoord)ctrl2->y * p->textScale / 64.0, (SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); p->needClose = gTrue; return 0; } #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H xpdf-3.04/splash/SplashFTFontEngine.h0000644000076400007640000000433612341430012017013 0ustar dereknderekn//======================================================================== // // SplashFTFontEngine.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFTFONTENGINE_H #define SPLASHFTFONTENGINE_H #include #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include FT_FREETYPE_H #include "gtypes.h" class GString; class SplashFontFile; class SplashFontFileID; //------------------------------------------------------------------------ // SplashFTFontEngine //------------------------------------------------------------------------ class SplashFTFontEngine { public: static SplashFTFontEngine *init(GBool aaA, Guint flagsA); ~SplashFTFontEngine(); // Load fonts. SplashFontFile *loadType1Font(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc); SplashFontFile *loadType1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc); SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc); SplashFontFile *loadCIDFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf #else char *fileName, GBool deleteFile #endif ); SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int *codeToGID, int codeToGIDLen); SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int fontNum, int *codeToGID, int codeToGIDLen); private: SplashFTFontEngine(GBool aaA, Guint flagsA, FT_Library libA); GBool aa; Guint flags; FT_Library lib; GBool useCIDs; friend class SplashFTFontFile; friend class SplashFTFont; }; #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #endif xpdf-3.04/splash/SplashXPathScanner.cc0000644000076400007640000003162112341430012017216 0ustar dereknderekn//======================================================================== // // SplashXPathScanner.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #if HAVE_STD_SORT #include #endif #include "gmem.h" #include "GList.h" #include "SplashMath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" //------------------------------------------------------------------------ #define minVertStep 0.05 //------------------------------------------------------------------------ SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA, int yMinA, int yMaxA) { xPath = xPathA; eo = eoA; yMin = yMinA; yMax = yMaxA; activeSegs = new GList(); nextSeg = 0; yNext = xPath->yMin; } SplashXPathScanner::~SplashXPathScanner() { delete activeSegs; } void SplashXPathScanner::getSpan(Guchar *line, int y, int x0, int x1) { SplashXPathSeg *seg, *seg0; SplashCoord y0, y1, y1p; GBool intersect, last; int eoMask, state0, state1, count, i; //--- clear the scan line buffer memset(line + x0, 0, x1 - x0 + 1); //--- reset the path if (yNext != y) { delete activeSegs; activeSegs = new GList(); nextSeg = 0; while (nextSeg < xPath->length) { seg = &xPath->segs[nextSeg]; if (seg->y0 >= y) { break; } if (seg->y0 != seg->y1 && seg->y1 > y) { if (seg->y0 == y) { seg->xCur0 = seg->x0; } else { seg->xCur0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy; } activeSegs->append(seg); } ++nextSeg; } activeSegs->sort(&SplashXPathSeg::cmpXi); } //--- process the scan line y0 = y; while (y0 < y + 1) { //--- delete finished segs i = 0; while (i < activeSegs->getLength()) { seg = (SplashXPathSeg *)activeSegs->get(i); if (seg->y1 <= y0) { activeSegs->del(i); } else { ++i; } } //--- check for bottom of path if (!activeSegs->getLength() && nextSeg >= xPath->length) { break; } //--- sort activeSegs sortActiveSegs(); //--- add waiting segs while (nextSeg < xPath->length) { seg = &xPath->segs[nextSeg]; if (seg->y0 > y0) { break; } if (seg->y0 != seg->y1) { seg->xCur0 = seg->x0; insertActiveSeg(seg); } ++nextSeg; } //--- get the next "interesting" y value y1 = y + 1; if (nextSeg < xPath->length && xPath->segs[nextSeg].y0 < y1) { y1 = xPath->segs[nextSeg].y0; } for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); if (seg->y1 < y1) { y1 = seg->y1; } } //--- compute xCur1 values, check for intersections seg0 = NULL; intersect = gFalse; for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); if (seg->y1 == y1) { seg->xCur1 = seg->x1; } else { seg->xCur1 = seg->x0 + (y1 - seg->y0) * seg->dxdy; } if (seg0 && seg0->xCur1 > seg->xCur1) { intersect = gTrue; } seg0 = seg; } //--- draw rectangles if (intersect) { for (; y0 < y1; y0 += minVertStep) { if ((y1p = y0 + minVertStep) >= y1) { y1p = y1; last = gTrue; } else { last = gFalse; } state0 = state1 = count = 0; seg0 = NULL; eoMask = eo ? 1 : 0xffffffff; for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); if (last && seg->y1 == y1) { seg->xCur1 = seg->x1; } else { seg->xCur1 = seg->x0 + (y1p - seg->y0) * seg->dxdy; } count += seg->count; state1 = count & eoMask; if (!state0 && state1) { seg0 = seg; } else if (state0 && !state1) { drawRectangle(line, x0, x1, y0, y1p, seg0->xCur0, seg->xCur0); } state0 = state1; } for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); seg->xCur0 = seg->xCur1; } sortActiveSegs(); } //--- draw trapezoids } else { state0 = state1 = count = 0; seg0 = NULL; eoMask = eo ? 1 : 0xffffffff; for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); count += seg->count; state1 = count & eoMask; if (!state0 && state1) { seg0 = seg; } else if (state0 && !state1) { drawTrapezoid(line, x0, x1, y0, y1, seg0->xCur0, seg0->xCur1, seg0->dydx, seg->xCur0, seg->xCur1, seg->dydx); } state0 = state1; } for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); seg->xCur0 = seg->xCur1; } } //--- next slice y0 = y1; } yNext = y + 1; } void SplashXPathScanner::getSpanBinary(Guchar *line, int y, int x0, int x1) { SplashXPathSeg *seg; int xx0, xx1, xx; int eoMask, state0, state1, count, i; //--- clear the scan line buffer memset(line + x0, 0, x1 - x0 + 1); //--- reset the path if (yNext != y) { delete activeSegs; activeSegs = new GList(); nextSeg = 0; while (nextSeg < xPath->length) { seg = &xPath->segs[nextSeg]; if (seg->y0 >= y) { break; } if (seg->y1 > y) { if (seg->y0 == y) { seg->xCur0 = seg->x0; } else { seg->xCur0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy; } activeSegs->append(seg); } ++nextSeg; } activeSegs->sort(&SplashXPathSeg::cmpXi); } //--- delete finished segs i = 0; while (i < activeSegs->getLength()) { seg = (SplashXPathSeg *)activeSegs->get(i); if (seg->y1 <= y) { activeSegs->del(i); } else { ++i; } } //--- sort activeSegs sortActiveSegs(); //--- add waiting segs while (nextSeg < xPath->length) { seg = &xPath->segs[nextSeg]; if (seg->y0 >= y + 1) { break; } seg->xCur0 = seg->x0; insertActiveSeg(seg); ++nextSeg; } //--- compute xCur1 values for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); if (seg->y1 <= y + 1) { seg->xCur1 = seg->x1; } else { seg->xCur1 = seg->x0 + ((SplashCoord)(y + 1) - seg->y0) * seg->dxdy; } } //--- draw spans state0 = state1 = count = 0; eoMask = eo ? 1 : 0xffffffff; xx0 = xx1 = 0; // make gcc happy for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); if (seg->y0 <= y && seg->y0 < seg->y1) { count += seg->count; state1 = count & eoMask; } if (state0) { xx = splashCeil(seg->xCur0) - 1; if (xx > xx1) { xx1 = xx; } xx = splashFloor(seg->xCur1); if (xx < xx0) { xx0 = xx; } xx = splashCeil(seg->xCur1) - 1; if (xx > xx1) { xx1 = xx; } } else { if (seg->xCur0 < seg->xCur1) { xx0 = splashFloor(seg->xCur0); xx1 = splashCeil(seg->xCur1) - 1; } else { xx0 = splashFloor(seg->xCur1); xx1 = splashCeil(seg->xCur0) - 1; } } if (!state1) { if (xx0 < x0) { xx0 = x0; } if (xx1 > x1) { xx1 = x1; } for (xx = xx0; xx <= xx1; ++xx) { line[xx] = 0xff; } } state0 = state1; } //--- update xCur0 values for (i = 0; i < activeSegs->getLength(); ++i) { seg = (SplashXPathSeg *)activeSegs->get(i); seg->xCur0 = seg->xCur1; } yNext = y + 1; } inline void SplashXPathScanner::addArea(Guchar *line, int x, SplashCoord a) { int a2, t; a2 = splashRound(a * 255); if (a2 <= 0) { return; } t = line[x] + a2; if (t > 255) { t = 255; } line[x] = t; } // Draw a trapezoid with edges: // top: (xa0, y0) - (xb0, y0) // left: (xa0, y0) - (xa1, y1) // right: (xb0, y0) - (xb1, y1) // bottom: (xa1, y1) - (xb1, y1) void SplashXPathScanner::drawTrapezoid(Guchar *line, int xMin, int xMax, SplashCoord y0, SplashCoord y1, SplashCoord xa0, SplashCoord xa1, SplashCoord dydxa, SplashCoord xb0, SplashCoord xb1, SplashCoord dydxb) { SplashCoord a, dy; int x0, x1, x2, x3, x; // check for a rectangle if (dydxa == 0 && dydxb == 0 && xa0 >= xMin && xb0 <= xMax) { x0 = splashFloor(xa0); x3 = splashFloor(xb0); dy = y1 - y0; if (x0 == x3) { addArea(line, x0, (xb0 - xa0) * dy); } else { addArea(line, x0, ((SplashCoord)1 - (xa0 - x0)) * dy); for (x = x0 + 1; x <= x3 - 1; ++x) { addArea(line, x, y1 - y0); } addArea(line, x3, (xb0 - x3) * (y1 - y0)); } return; } if (dydxa > 0) { x0 = splashFloor(xa0); x1 = splashFloor(xa1); } else { x0 = splashFloor(xa1); x1 = splashFloor(xa0); } if (x0 < xMin) { x0 = xMin; } if (dydxb > 0) { x2 = splashFloor(xb0); x3 = splashFloor(xb1); } else { x2 = splashFloor(xb1); x3 = splashFloor(xb0); } if (x3 > xMax) { x3 = xMax; } for (x = x0; x <= x3; ++x) { a = y1 - y0; if (x <= x1) { a -= areaLeft(x, xa0, y0, xa1, y1, dydxa); } if (x >= x2) { a -= areaRight(x, xb0, y0, xb1, y1, dydxb); } addArea(line, x, a); } } // Compute area within a pixel slice ((xp,y0)-(xp+1,y1)) to the left // of a trapezoid edge ((x0,y0)-(x1,y1)). SplashCoord SplashXPathScanner::areaLeft(int xp, SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord dydx) { SplashCoord a, ya, yb; if (dydx >= 0) { if (x0 >= xp) { if (x1 <= xp + 1) { a = ((x0 + x1) * 0.5 - xp) * (y1 - y0); } else { yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; a = (y1 - y0) - ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5; } } else { if (x1 <= xp + 1) { ya = y0 + ((SplashCoord)xp - x0) * dydx; a = (x1 - xp) * (y1 - ya) * 0.5; } else { // ya = y1 - (x1 - xp - 0.5) * dydx; // a = y1 - ya; a = (x1 - xp - 0.5) * dydx; } } } else { if (x0 <= xp + 1) { if (x1 >= xp) { a = ((x0 + x1) * 0.5 - xp) * (y1 - y0); } else { ya = y0 + ((SplashCoord)xp - x0) * dydx; a = (x0 - xp) * (ya - y0) * 0.5; } } else { if (x1 >= xp) { yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; a = (y1 - y0) - ((SplashCoord)(xp + 1) - x1) * (y1 - yb) * 0.5; } else { // ya = y0 + (xp - x0 + 0.5) * dydx; // a = ya - y0; a = ((SplashCoord)xp - x0 + 0.5) * dydx; } } } return a; } // Compute area within a pixel slice ((xp,y0)-(xp+1,y1)) to the left // of a trapezoid edge ((x0,y0)-(x1,y1)). SplashCoord SplashXPathScanner::areaRight(int xp, SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord dydx) { SplashCoord a, ya, yb; if (dydx >= 0) { if (x0 >= xp) { if (x1 <= xp + 1) { a = ((SplashCoord)(xp + 1) - (x0 + x1) * 0.5) * (y1 - y0); } else { yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; a = ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5; } } else { if (x1 <= xp + 1) { ya = y0 + ((SplashCoord)xp - x0) * dydx; a = (y1 - y0) - (x1 - xp) * (y1 - ya) * 0.5; } else { // ya = y0 + (xp - x0 + 0.5) * dydx; // a = ya - y0; a = ((SplashCoord)xp + 0.5 - x0) * dydx; } } } else { if (x0 <= xp + 1) { if (x1 >= xp) { a = ((SplashCoord)(xp + 1) - (x0 + x1) * 0.5) * (y1 - y0); } else { ya = y0 + ((SplashCoord)xp - x0) * dydx; a = (y1 - y0) - (x0 - xp) * (ya - y0) * 0.5; } } else { if (x1 >= xp) { yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; a = ((SplashCoord)(xp + 1) - x1) * (y1 - yb) * 0.5; } else { // ya = y1 - (x1 - xp - 0.5) * dydx; // a = y1 - ya; a = (x1 - xp - 0.5) * dydx; } } } return a; } void SplashXPathScanner::drawRectangle(Guchar *line, int xMin, int xMax, SplashCoord y0, SplashCoord y1, SplashCoord x0, SplashCoord x1) { SplashCoord dy, a; int xx0, xx1, x; xx0 = splashFloor(x0); if (xx0 < xMin) { xx0 = xMin; } xx1 = splashFloor(x1); if (xx1 > xMax) { xx1 = xMax; } dy = y1 - y0; for (x = xx0; x <= xx1; ++x) { a = dy; if ((SplashCoord)x < x0) { a -= (x0 - x) * dy; } if ((SplashCoord)(x + 1) > x1) { a -= ((SplashCoord)(x + 1) - x1) * dy; } addArea(line, x, a); } } void SplashXPathScanner::sortActiveSegs() { SplashXPathSeg *seg0, *seg1; int i, j, k; if (activeSegs->getLength() < 2) { return; } seg0 = (SplashXPathSeg *)activeSegs->get(0); for (i = 1; i < activeSegs->getLength(); ++i) { seg1 = (SplashXPathSeg *)activeSegs->get(i); if (SplashXPathSeg::cmpX(seg0, seg1) > 0) { for (j = i - 1; j > 0; --j) { if (SplashXPathSeg::cmpX((SplashXPathSeg *)activeSegs->get(j - 1), seg1) <= 0) { break; } } for (k = i; k > j; --k) { activeSegs->put(k, activeSegs->get(k-1)); } activeSegs->put(j, seg1); } else { seg0 = seg1; } } } void SplashXPathScanner::insertActiveSeg(SplashXPathSeg *seg) { int i; for (i = 0; i < activeSegs->getLength(); ++i) { if (SplashXPathSeg::cmpX(seg, (SplashXPathSeg *)activeSegs->get(i)) < 0) { break; } } activeSegs->insert(i, seg); } xpdf-3.04/splash/Makefile.in0000644000076400007640000000414612341430012015245 0ustar dereknderekn#======================================================================== # # Splash library Makefile # # Copyright 2003 Glyph & Cog, LLC # #======================================================================== SHELL = /bin/sh srcdir = @srcdir@ VPATH = @srcdir@ GOOSRCDIR = $(srcdir)/../goo GOOLIBDIR = ../goo FOFISRCDIR = $(srcdir)/../fofi FOFILIBDIR = ../fofi CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @freetype2_CFLAGS@ CXX = @CXX@ AR = @AR@ RANLIB = @RANLIB@ LIBPREFIX = @LIBPREFIX@ #------------------------------------------------------------------------ .SUFFIXES: .cc .cc.o: $(CXX) $(CXXFLAGS) -c $< #------------------------------------------------------------------------ CXX_SRC = \ $(srcdir)/Splash.cc \ $(srcdir)/SplashBitmap.cc \ $(srcdir)/SplashClip.cc \ $(srcdir)/SplashFTFont.cc \ $(srcdir)/SplashFTFontEngine.cc \ $(srcdir)/SplashFTFontFile.cc \ $(srcdir)/SplashFont.cc \ $(srcdir)/SplashFontEngine.cc \ $(srcdir)/SplashFontFile.cc \ $(srcdir)/SplashFontFileID.cc \ $(srcdir)/SplashPath.cc \ $(srcdir)/SplashPattern.cc \ $(srcdir)/SplashScreen.cc \ $(srcdir)/SplashState.cc \ $(srcdir)/SplashXPath.cc \ $(srcdir)/SplashXPathScanner.cc #------------------------------------------------------------------------ all: $(LIBPREFIX)splash.a #------------------------------------------------------------------------ SPLASH_OBJS = \ Splash.o \ SplashBitmap.o \ SplashClip.o \ SplashFTFont.o \ SplashFTFontEngine.o \ SplashFTFontFile.o \ SplashFont.o \ SplashFontEngine.o \ SplashFontFile.o \ SplashFontFileID.o \ SplashPath.o \ SplashPattern.o \ SplashScreen.o \ SplashState.o \ SplashXPath.o \ SplashXPathScanner.o $(LIBPREFIX)splash.a: $(SPLASH_OBJS) rm -f $(LIBPREFIX)splash.a $(AR) $(LIBPREFIX)splash.a $(SPLASH_OBJS) $(RANLIB) $(LIBPREFIX)splash.a #------------------------------------------------------------------------ clean: rm -f $(SPLASH_OBJS) $(LIBPREFIX)splash.a #------------------------------------------------------------------------ depend: $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep -include Makefile.dep xpdf-3.04/splash/SplashClip.h0000644000076400007640000000727612341430012015422 0ustar dereknderekn//======================================================================== // // SplashClip.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHCLIP_H #define SPLASHCLIP_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" #include "SplashMath.h" class SplashPath; class SplashXPath; class SplashXPathScanner; class SplashBitmap; //------------------------------------------------------------------------ enum SplashClipResult { splashClipAllInside, splashClipAllOutside, splashClipPartial }; //------------------------------------------------------------------------ // SplashClip //------------------------------------------------------------------------ class SplashClip { public: // Create a clip, for the given rectangle. SplashClip(int hardXMinA, int hardYMinA, int hardXMaxA, int hardYMaxA); // Copy a clip. SplashClip *copy() { return new SplashClip(this); } ~SplashClip(); // Reset the clip to a rectangle. void resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // Intersect the clip with a rectangle. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // Interesect the clip with . SplashError clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, GBool eoA); // Tests a rectangle against the clipping region. Returns one of: // - splashClipAllInside if the entire rectangle is inside the // clipping region, i.e., all pixels in the rectangle are // visible // - splashClipAllOutside if the entire rectangle is outside the // clipping region, i.e., all the pixels in the rectangle are // clipped // - splashClipPartial if the rectangle is part inside and part // outside the clipping region SplashClipResult testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax, GBool strokeAdjust); // Clip a scan line. Modifies line[] by multiplying with clipping // shape values for one scan line: ([x0, x1], y). void clipSpan(Guchar *line, int y, int x0, int x1, GBool strokeAdjust); // Like clipSpan(), but uses the values 0 and 255 only. // Returns true if there are any non-zero values in the result // (i.e., returns false if the entire line is clipped out). GBool clipSpanBinary(Guchar *line, int y, int x0, int x1, GBool strokeAdjust); // Get the rectangle part of the clip region. SplashCoord getXMin() { return xMin; } SplashCoord getXMax() { return xMax; } SplashCoord getYMin() { return yMin; } SplashCoord getYMax() { return yMax; } // Get the rectangle part of the clip region, in integer coordinates. int getXMinI(GBool strokeAdjust); int getXMaxI(GBool strokeAdjust); int getYMinI(GBool strokeAdjust); int getYMaxI(GBool strokeAdjust); // Get the number of arbitrary paths used by the clip region. int getNumPaths() { return length; } private: SplashClip(SplashClip *clip); void grow(int nPaths); void updateIntBounds(GBool strokeAdjust); int hardXMin, hardYMin, // coordinates cannot fall outside of hardXMax, hardYMax; // [hardXMin, hardXMax), [hardYMin, hardYMax) SplashCoord xMin, yMin, // current clip bounding rectangle xMax, yMax; // (these coordinates may be adjusted if // stroke adjustment is enabled) int xMinI, yMinI, xMaxI, yMaxI; GBool intBoundsValid; // true if xMinI, etc. are valid GBool intBoundsStrokeAdjust; // value of strokeAdjust used to compute // xMinI, etc. SplashXPath **paths; Guchar *eo; SplashXPathScanner **scanners; int length, size; Guchar *buf; }; #endif xpdf-3.04/splash/Splash.cc0000644000076400007640000052423612341430012014750 0ustar dereknderekn//======================================================================== // // Splash.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include "gmem.h" #include "SplashErrorCodes.h" #include "SplashMath.h" #include "SplashBitmap.h" #include "SplashState.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashFont.h" #include "SplashGlyphBitmap.h" #include "Splash.h" //------------------------------------------------------------------------ #define splashAAGamma 0.67 // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle ((SplashCoord)0.55228475) #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } // Clip x to lie in [0, 255]. static inline Guchar clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; } // Used by drawImage and fillImageMask to divide the target // quadrilateral into sections. struct ImageSection { int y0, y1; // actual y range int ia0, ia1; // vertex indices for edge A int ib0, ib1; // vertex indices for edge B SplashCoord xa0, ya0, xa1, ya1; // edge A SplashCoord dxdya; // slope of edge A SplashCoord xb0, yb0, xb1, yb1; // edge B SplashCoord dxdyb; // slope of edge B }; //------------------------------------------------------------------------ // SplashPipe //------------------------------------------------------------------------ #define splashPipeMaxStages 9 struct SplashPipe { // source pattern SplashPattern *pattern; // source alpha and color Guchar aInput; SplashColor cSrcVal; // special cases and result color GBool noTransparency; GBool shapeOnly; SplashPipeResultColorCtrl resultColorCtrl; // non-isolated group correction // (this is only used when Splash::composite() is called to composite // a non-isolated group onto the backdrop) GBool nonIsolatedGroup; // the "run" function void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorNoAlphaBlendCMYK #endif }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaNoBlendCMYK #endif }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaBlendCMYK #endif }; //------------------------------------------------------------------------ static void blendXor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = src[i] ^ dest[i]; } } //------------------------------------------------------------------------ // modified region //------------------------------------------------------------------------ void Splash::clearModRegion() { modXMin = bitmap->getWidth(); modYMin = bitmap->getHeight(); modXMax = -1; modYMax = -1; } inline void Splash::updateModX(int x) { if (x < modXMin) { modXMin = x; } if (x > modXMax) { modXMax = x; } } inline void Splash::updateModY(int y) { if (y < modYMin) { modYMin = y; } if (y > modYMax) { modYMax = y; } } //------------------------------------------------------------------------ // pipeline //------------------------------------------------------------------------ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup) { pipe->pattern = NULL; // source color if (pattern && pattern->isStatic()) { pattern->getColor(0, 0, pipe->cSrcVal); pipe->pattern = NULL; } else { pipe->pattern = pattern; } // source alpha pipe->aInput = aInput; // special cases pipe->noTransparency = aInput == 255 && !state->softMask && !usesShape && !state->inNonIsolatedGroup && !state->inKnockoutGroup && !nonIsolatedGroup && state->overprintMask == 0xffffffff; pipe->shapeOnly = aInput == 255 && !state->softMask && usesShape && !state->inNonIsolatedGroup && !state->inKnockoutGroup && !nonIsolatedGroup && state->overprintMask == 0xffffffff; // result color if (pipe->noTransparency) { // the !state->blendFunc case is handled separately in pipeRun pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode]; } else if (!state->blendFunc) { pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode]; } else { pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode]; } // non-isolated group correction pipe->nonIsolatedGroup = nonIsolatedGroup; // select the 'run' function pipe->run = &Splash::pipeRun; if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { if (bitmap->mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono1; } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono8; } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleRGB8; } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleBGR8; #if SPLASH_CMYK } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleCMYK8; #endif } } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) { if (bitmap->mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunShapeMono1; } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeMono8; } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeRGB8; } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeBGR8; #if SPLASH_CMYK } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeCMYK8; #endif } } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && usesShape && !(state->inNonIsolatedGroup && groupBackBitmap->alpha) && !state->inKnockoutGroup && !state->blendFunc && !pipe->nonIsolatedGroup) { if (bitmap->mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono1; } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono8; } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAARGB8; } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAABGR8; #if SPLASH_CMYK } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAACMYK8; #endif } } } // general case void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar *shapePtr2; Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; SplashColor cSrc, cDest, cBlend; Guchar shapeVal, cResult0, cResult1, cResult2, cResult3; int cSrcStride, shapeStride, x, lastX, t, i; SplashColorPtr destColorPtr; Guchar destColorMask; Guchar *destAlphaPtr; SplashColorPtr color0Ptr; Guchar color0Mask; Guchar *alpha0Ptr; SplashColorPtr softMaskPtr; #if SPLASH_CMYK SplashColor cSrc2, cDest2; #endif if (cSrcPtr && !pipe->pattern) { cSrcStride = bitmapComps; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (shapePtr) { shapePtr2 = shapePtr; shapeStride = 1; for (; x0 <= x1; ++x0) { if (*shapePtr2) { break; } cSrcPtr += cSrcStride; ++shapePtr2; } } else { shapeVal = 0xff; shapePtr2 = &shapeVal; shapeStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; if (bitmap->mode == splashModeMono1) { destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = 0x80 >> (x0 & 7); } else { destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps]; destColorMask = 0; // make gcc happy } if (bitmap->alpha) { destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; } else { destAlphaPtr = NULL; } if (state->softMask) { softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0]; } else { softMaskPtr = NULL; } if (state->inKnockoutGroup) { if (bitmap->mode == splashModeMono1) { color0Ptr = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + ((groupBackX + x0) >> 3)]; color0Mask = 0x80 >> ((groupBackX + x0) & 7); } else { color0Ptr = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + (groupBackX + x0) * bitmapComps]; color0Mask = 0; // make gcc happy } } else { color0Ptr = NULL; color0Mask = 0; // make gcc happy } if (state->inNonIsolatedGroup && groupBackBitmap->alpha) { alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y) * groupBackBitmap->width + (groupBackX + x0)]; } else { alpha0Ptr = NULL; } for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr2; if (!shape) { if (bitmap->mode == splashModeMono1) { destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); } else { destColorPtr += bitmapComps; } if (destAlphaPtr) { ++destAlphaPtr; } if (softMaskPtr) { ++softMaskPtr; } if (color0Ptr) { if (bitmap->mode == splashModeMono1) { color0Ptr += color0Mask & 1; color0Mask = (color0Mask << 7) | (color0Mask >> 1); } else { color0Ptr += bitmapComps; } } if (alpha0Ptr) { ++alpha0Ptr; } cSrcPtr += cSrcStride; shapePtr2 += shapeStride; continue; } lastX = x; //----- source color // static pattern: handled in pipeInit // fixed color: handled in pipeInit // dynamic pattern if (pipe->pattern) { pipe->pattern->getColor(x, y, pipe->cSrcVal); } if (pipe->noTransparency && !state->blendFunc) { //----- write destination pixel switch (bitmap->mode) { case splashModeMono1: cResult0 = state->grayTransfer[cSrcPtr[0]]; if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= ~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); break; case splashModeMono8: *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; break; case splashModeRGB8: destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr += 3; break; case splashModeBGR8: destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; destColorPtr += 4; break; #endif } if (destAlphaPtr) { *destAlphaPtr++ = 255; } } else { // if (noTransparency && !blendFunc) //----- read destination pixel // (or backdrop color, for knockout groups) if (color0Ptr) { switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00; color0Ptr += color0Mask & 1; color0Mask = (color0Mask << 7) | (color0Mask >> 1); break; case splashModeMono8: cDest[0] = *color0Ptr++; break; case splashModeRGB8: cDest[0] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[2] = color0Ptr[2]; color0Ptr += 3; break; case splashModeBGR8: cDest[2] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[0] = color0Ptr[2]; color0Ptr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: cDest[0] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[2] = color0Ptr[2]; cDest[3] = color0Ptr[3]; color0Ptr += 4; break; #endif } } else { switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00; break; case splashModeMono8: cDest[0] = *destColorPtr; break; case splashModeRGB8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; break; case splashModeBGR8: cDest[0] = destColorPtr[2]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[0]; break; #if SPLASH_CMYK case splashModeCMYK8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; cDest[3] = destColorPtr[3]; break; #endif } } if (destAlphaPtr) { aDest = *destAlphaPtr; } else { aDest = 0xff; } //----- overprint for (i = 0; i < bitmapComps; ++i) { #if SPLASH_CMYK if (state->overprintMask & (1 << i)) { cSrc[i] = cSrcPtr[i]; } else { cSrc[i] = div255(aDest * cDest[i]); } #else cSrc[i] = cSrcPtr[i]; #endif } //----- source alpha if (softMaskPtr) { if (shapePtr) { aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape); } else { aSrc = div255(pipe->aInput * *softMaskPtr++); } } else if (shapePtr) { aSrc = div255(pipe->aInput * shape); } else { aSrc = pipe->aInput; } //----- non-isolated group correction if (pipe->nonIsolatedGroup) { // This path is only used when Splash::composite() is called to // composite a non-isolated group onto the backdrop. In this // case, shape is the source (group) alpha. t = (aDest * 255) / shape - aDest; switch (bitmap->mode) { #if SPLASH_CMYK case splashModeCMYK8: cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255); #endif case splashModeRGB8: case splashModeBGR8: cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255); cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255); case splashModeMono1: case splashModeMono8: cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255); break; } } //----- blend function if (state->blendFunc) { #if SPLASH_CMYK if (bitmap->mode == splashModeCMYK8) { // convert colors to additive cSrc2[0] = 0xff - cSrc[0]; cSrc2[1] = 0xff - cSrc[1]; cSrc2[2] = 0xff - cSrc[2]; cSrc2[3] = 0xff - cSrc[3]; cDest2[0] = 0xff - cDest[0]; cDest2[1] = 0xff - cDest[1]; cDest2[2] = 0xff - cDest[2]; cDest2[3] = 0xff - cDest[3]; (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode); // convert result back to subtractive cBlend[0] = 0xff - cBlend[0]; cBlend[1] = 0xff - cBlend[1]; cBlend[2] = 0xff - cBlend[2]; cBlend[3] = 0xff - cBlend[3]; } else #endif (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); } //----- result alpha and non-isolated group element correction // alphaI = alpha_i // alphaIm1 = alpha_(i-1) if (pipe->noTransparency) { alphaI = alphaIm1 = aResult = 255; } else if (alpha0Ptr) { if (color0Ptr) { // non-isolated, knockout aResult = aSrc; alpha0 = *alpha0Ptr++; alphaI = aSrc + alpha0 - div255(aSrc * alpha0); alphaIm1 = alpha0; } else { // non-isolated, non-knockout aResult = aSrc + aDest - div255(aSrc * aDest); alpha0 = *alpha0Ptr++; alphaI = aResult + alpha0 - div255(aResult * alpha0); alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest); } } else { if (color0Ptr) { // isolated, knockout aResult = aSrc; alphaI = aSrc; alphaIm1 = 0; } else { // isolated, non-knockout aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; alphaIm1 = aDest; } } //----- result color cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy switch (pipe->resultColorCtrl) { case splashPipeResultColorNoAlphaBlendMono: cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])]; break; case splashPipeResultColorNoAlphaBlendRGB: cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])]; cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + aDest * cBlend[1])]; cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + aDest * cBlend[2])]; break; #if SPLASH_CMYK case splashPipeResultColorNoAlphaBlendCMYK: cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])]; cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] + aDest * cBlend[1])]; cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] + aDest * cBlend[2])]; cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] + aDest * cBlend[3])]; break; #endif case splashPipeResultColorAlphaNoBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI]; } break; case splashPipeResultColorAlphaNoBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI]; cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI]; cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI]; } break; #if SPLASH_CMYK case splashPipeResultColorAlphaNoBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI]; cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI]; cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI]; cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) / alphaI]; } break; #endif case splashPipeResultColorAlphaBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI]; } break; case splashPipeResultColorAlphaBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI]; cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI]; cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI]; } break; #if SPLASH_CMYK case splashPipeResultColorAlphaBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI]; cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI]; cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI]; cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + aSrc * ((255 - alphaIm1) * cSrc[3] + alphaIm1 * cBlend[3]) / 255) / alphaI]; } break; #endif } //----- write destination pixel switch (bitmap->mode) { case splashModeMono1: if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= ~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); break; case splashModeMono8: *destColorPtr++ = cResult0; break; case splashModeRGB8: destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; break; case splashModeBGR8: destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; break; #endif } if (destAlphaPtr) { *destAlphaPtr++ = aResult; } } // if (noTransparency && !blendFunc) cSrcPtr += cSrcStride; shapePtr2 += shapeStride; } // for (x ...) updateModX(lastX); } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono1 && !bitmap->alpha) { void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = 0x80 >> (x0 & 7); for (x = x0; x <= x1; ++x) { //----- write destination pixel cResult0 = state->grayTransfer[cSrcPtr[0]]; if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= ~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono8 && bitmap->alpha) { void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeRGB8 && bitmap->alpha) { void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr += 3; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeBGR8 && bitmap->alpha) { void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr += 3; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } #if SPLASH_CMYK // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && bitmap->alpha) { void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; destColorPtr += 4; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } #endif // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeMono1 && !bitmap->alpha void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = 0x80 >> (x0 & 7); for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; //----- source alpha aSrc = shape; //----- result color // note: aDest = alphaI = aResult = 0xff cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrcPtr[0])]; //----- write destination pixel if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= ~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeMono8 && bitmap->alpha void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { ++destColorPtr; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = *destColorPtr; aDest = *destAlphaPtr; //----- source alpha aSrc = shape; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrcPtr[0]) / alphaI)]; } //----- write destination pixel *destColorPtr++ = cResult0; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeRGB8 && bitmap->alpha void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; aDest = *destAlphaPtr; //----- source alpha aSrc = shape; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrcPtr[0]) / alphaI)]; cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrcPtr[1]) / alphaI)]; cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrcPtr[2]) / alphaI)]; } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeBGR8 && bitmap->alpha void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[2]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[0]; aDest = *destAlphaPtr; //----- source alpha aSrc = shape; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrcPtr[0]) / alphaI)]; cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrcPtr[1]) / alphaI)]; cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrcPtr[2]) / alphaI)]; } //----- write destination pixel destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #if SPLASH_CMYK // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && bitmap->alpha void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2, cSrc3; Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 4; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; cDest3 = destColorPtr[3]; aDest = *destAlphaPtr; //----- overprint if (state->overprintMask & 1) { cSrc0 = cSrcPtr[0]; } else { cSrc0 = div255(aDest * cDest0); } if (state->overprintMask & 2) { cSrc1 = cSrcPtr[1]; } else { cSrc1 = div255(aDest * cDest1); } if (state->overprintMask & 4) { cSrc2 = cSrcPtr[2]; } else { cSrc2 = div255(aDest * cDest2); } if (state->overprintMask & 8) { cSrc3 = cSrcPtr[3]; } else { cSrc3 = div255(aDest * cDest3); } //----- source alpha aSrc = shape; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI)]; cResult1 = state->cmykTransferM[(Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI)]; cResult2 = state->cmykTransferY[(Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI)]; cResult3 = state->cmykTransferK[(Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI)]; } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #endif // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono1 && !bitmap->alpha void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = 0x80 >> (x0 & 7); for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result color // note: aDest = alphaI = aResult = 0xff cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrcPtr[0])]; //----- write destination pixel if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= ~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (destColorMask << 7) | (destColorMask >> 1); cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono8 && bitmap->alpha void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { ++destColorPtr; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = *destColorPtr; aDest = *destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrcPtr[0]) / alphaI)]; } //----- write destination pixel *destColorPtr++ = cResult0; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeRGB8 && bitmap->alpha void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; aDest = *destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrcPtr[0]) / alphaI)]; cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrcPtr[1]) / alphaI)]; cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrcPtr[2]) / alphaI)]; } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeBGR8 && bitmap->alpha void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[2]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[0]; aDest = *destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrcPtr[0]) / alphaI)]; cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrcPtr[1]) / alphaI)]; cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrcPtr[2]) / alphaI)]; } //----- write destination pixel destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #if SPLASH_CMYK // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeCMYK8 && bitmap->alpha void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2, cSrc3; Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 4; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; cDest3 = destColorPtr[3]; aDest = *destAlphaPtr; //----- overprint if (state->overprintMask & 1) { cSrc0 = cSrcPtr[0]; } else { cSrc0 = div255(aDest * cDest0); } if (state->overprintMask & 2) { cSrc1 = cSrcPtr[1]; } else { cSrc1 = div255(aDest * cDest1); } if (state->overprintMask & 4) { cSrc2 = cSrcPtr[2]; } else { cSrc2 = div255(aDest * cDest2); } if (state->overprintMask & 8) { cSrc3 = cSrcPtr[3]; } else { cSrc3 = div255(aDest * cDest3); } //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[(Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI)]; cResult1 = state->cmykTransferM[(Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI)]; cResult2 = state->cmykTransferY[(Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI)]; cResult3 = state->cmykTransferK[(Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI)]; } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #endif //------------------------------------------------------------------------ // Transform a point from user space to device space. inline void Splash::transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo) { // [ m[0] m[1] 0 ] // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] // [ m[4] m[5] 1 ] *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; } //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreenParams *screenParams) { int i; bitmap = bitmapA; bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams); scanBuf = (Guchar *)gmalloc(bitmap->width); if (vectorAntialias) { for (i = 0; i <= 255; ++i) { aaGamma[i] = (Guchar)splashRound( splashPow((SplashCoord)i / (SplashCoord)255, splashAAGamma) * 255); } } minLineWidth = 0; clearModRegion(); debugMode = gFalse; } Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreen *screenA) { int i; bitmap = bitmapA; bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA); scanBuf = (Guchar *)gmalloc(bitmap->width); if (vectorAntialias) { for (i = 0; i <= 255; ++i) { aaGamma[i] = (Guchar)splashRound( splashPow((SplashCoord)i / (SplashCoord)255, splashAAGamma) * 255); } } minLineWidth = 0; clearModRegion(); debugMode = gFalse; } Splash::~Splash() { while (state->next) { restoreState(); } delete state; gfree(scanBuf); } //------------------------------------------------------------------------ // state read //------------------------------------------------------------------------ SplashCoord *Splash::getMatrix() { return state->matrix; } SplashPattern *Splash::getStrokePattern() { return state->strokePattern; } SplashPattern *Splash::getFillPattern() { return state->fillPattern; } SplashScreen *Splash::getScreen() { return state->screen; } SplashBlendFunc Splash::getBlendFunc() { return state->blendFunc; } SplashCoord Splash::getStrokeAlpha() { return state->strokeAlpha; } SplashCoord Splash::getFillAlpha() { return state->fillAlpha; } SplashCoord Splash::getLineWidth() { return state->lineWidth; } int Splash::getLineCap() { return state->lineCap; } int Splash::getLineJoin() { return state->lineJoin; } SplashCoord Splash::getMiterLimit() { return state->miterLimit; } SplashCoord Splash::getFlatness() { return state->flatness; } SplashCoord *Splash::getLineDash() { return state->lineDash; } int Splash::getLineDashLength() { return state->lineDashLength; } SplashCoord Splash::getLineDashPhase() { return state->lineDashPhase; } GBool Splash::getStrokeAdjust() { return state->strokeAdjust; } SplashClip *Splash::getClip() { return state->clip; } SplashBitmap *Splash::getSoftMask() { return state->softMask; } GBool Splash::getInNonIsolatedGroup() { return state->inNonIsolatedGroup; } GBool Splash::getInKnockoutGroup() { return state->inKnockoutGroup; } //------------------------------------------------------------------------ // state write //------------------------------------------------------------------------ void Splash::setMatrix(SplashCoord *matrix) { memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); } void Splash::setStrokePattern(SplashPattern *strokePattern) { state->setStrokePattern(strokePattern); } void Splash::setFillPattern(SplashPattern *fillPattern) { state->setFillPattern(fillPattern); } void Splash::setScreen(SplashScreen *screen) { state->setScreen(screen); } void Splash::setBlendFunc(SplashBlendFunc func) { state->blendFunc = func; } void Splash::setStrokeAlpha(SplashCoord alpha) { state->strokeAlpha = alpha; } void Splash::setFillAlpha(SplashCoord alpha) { state->fillAlpha = alpha; } void Splash::setLineWidth(SplashCoord lineWidth) { state->lineWidth = lineWidth; } void Splash::setLineCap(int lineCap) { state->lineCap = lineCap; } void Splash::setLineJoin(int lineJoin) { state->lineJoin = lineJoin; } void Splash::setMiterLimit(SplashCoord miterLimit) { state->miterLimit = miterLimit; } void Splash::setFlatness(SplashCoord flatness) { if (flatness < 1) { state->flatness = 1; } else { state->flatness = flatness; } } void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase) { state->setLineDash(lineDash, lineDashLength, lineDashPhase); } void Splash::setStrokeAdjust(GBool strokeAdjust) { state->strokeAdjust = strokeAdjust; } void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { state->clipResetToRect(x0, y0, x1, y1); } SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { return state->clipToRect(x0, y0, x1, y1); } SplashError Splash::clipToPath(SplashPath *path, GBool eo) { return state->clipToPath(path, eo); } void Splash::setSoftMask(SplashBitmap *softMask) { state->setSoftMask(softMask); } void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA, int groupBackXA, int groupBackYA, GBool nonIsolated, GBool knockout) { groupBackBitmap = groupBackBitmapA; groupBackX = groupBackXA; groupBackY = groupBackYA; state->inNonIsolatedGroup = nonIsolated; state->inKnockoutGroup = knockout; } void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray) { state->setTransfer(red, green, blue, gray); } void Splash::setOverprintMask(Guint overprintMask) { state->overprintMask = overprintMask; } //------------------------------------------------------------------------ // state save/restore //------------------------------------------------------------------------ void Splash::saveState() { SplashState *newState; newState = state->copy(); newState->next = state; state = newState; } SplashError Splash::restoreState() { SplashState *oldState; if (!state->next) { return splashErrNoSave; } oldState = state; state = state->next; delete oldState; return splashOk; } //------------------------------------------------------------------------ // drawing operations //------------------------------------------------------------------------ void Splash::clear(SplashColorPtr color, Guchar alpha) { SplashColorPtr row, p; Guchar mono; int x, y; switch (bitmap->mode) { case splashModeMono1: mono = (color[0] & 0x80) ? 0xff : 0x00; if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), mono, -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); } break; case splashModeMono8: if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } break; case splashModeRGB8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; } row += bitmap->rowSize; } } break; case splashModeBGR8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[2]; *p++ = color[1]; *p++ = color[0]; } row += bitmap->rowSize; } } break; #if SPLASH_CMYK case splashModeCMYK8: if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; *p++ = color[3]; } row += bitmap->rowSize; } } break; #endif } if (bitmap->alpha) { memset(bitmap->alpha, alpha, bitmap->width * bitmap->height); } updateModX(0); updateModY(0); updateModX(bitmap->width - 1); updateModY(bitmap->height - 1); } SplashError Splash::stroke(SplashPath *path) { SplashPath *path2, *dPath; SplashCoord t0, t1, t2, t3, w, w2; if (debugMode) { printf("stroke [dash:%d] [width:%.2f]:\n", state->lineDashLength, (double)state->lineWidth); dumpPath(path); } opClipRes = splashClipAllOutside; if (path->length == 0) { return splashErrEmptyPath; } path2 = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { dPath = makeDashedPath(path2); delete path2; path2 = dPath; if (path2->length == 0) { delete path2; return splashErrEmptyPath; } } // Compute an approximation of the transformed line width. // Given a CTM of [m0 m1], // [m2 m3] // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else // use min{|m1|,|m2|}. // This handles the common cases -- [s 0 ] and [0 s] -- // [0 +/-s] [+/-s 0] // well, and still does something reasonable for the uncommon // case transforms. t0 = splashAbs(state->matrix[0]); t1 = splashAbs(state->matrix[1]); t2 = splashAbs(state->matrix[2]); t3 = splashAbs(state->matrix[3]); if (t0 * t3 >= t1 * t2) { w = (t0 < t3) ? t0 : t3; } else { w = (t1 < t2) ? t1 : t2; } w2 = w * state->lineWidth; // if there is a min line width set, and the transformed line width // is smaller, use the min line width if (w > 0 && w2 < minLineWidth) { strokeWide(path2, minLineWidth / w); } else if (bitmap->mode == splashModeMono1) { // in monochrome mode, use 0-width lines for any transformed line // width <= 1 -- lines less than 1 pixel wide look too fat without // antialiasing if (w2 < 1.001) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth); } } else { // in gray and color modes, only use 0-width lines if the line // width is explicitly set to 0 if (state->lineWidth == 0) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth); } } delete path2; return splashOk; } void Splash::strokeNarrow(SplashPath *path) { SplashPipe pipe; SplashXPath *xPath; SplashXPathSeg *seg; int x0, x1, y0, y1, xa, xb, y; SplashCoord dxdy; SplashClipResult clipRes; int nClipRes[3]; int i; nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse); pipeInit(&pipe, state->strokePattern, (Guchar)splashRound(state->strokeAlpha * 255), gTrue, gFalse); for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { if (seg->y0 <= seg->y1) { y0 = splashFloor(seg->y0); y1 = splashFloor(seg->y1); x0 = splashFloor(seg->x0); x1 = splashFloor(seg->x1); } else { y0 = splashFloor(seg->y1); y1 = splashFloor(seg->y0); x0 = splashFloor(seg->x1); x1 = splashFloor(seg->x0); } if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, x0 <= x1 ? x1 : x0, y1, state->strokeAdjust)) != splashClipAllOutside) { if (y0 == y1) { if (x0 <= x1) { drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); } } else { dxdy = seg->dxdy; y = state->clip->getYMinI(state->strokeAdjust); if (y0 < y) { y0 = y; x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); } y = state->clip->getYMaxI(state->strokeAdjust); if (y1 > y) { y1 = y; x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); } if (x0 <= x1) { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 + 1; } if (xa == xb) { drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside); } xa = xb; } } else { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 - 1; } if (xa == xb) { drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside); } xa = xb; } } } } ++nClipRes[clipRes]; } if (nClipRes[splashClipPartial] || (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { opClipRes = splashClipPartial; } else if (nClipRes[splashClipAllInside]) { opClipRes = splashClipAllInside; } else { opClipRes = splashClipAllOutside; } delete xPath; } void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip) { int x; x = state->clip->getXMinI(state->strokeAdjust); if (x > x0) { x0 = x; } x = state->clip->getXMaxI(state->strokeAdjust); if (x < x1) { x1 = x; } if (x0 > x1) { return; } for (x = x0; x <= x1; ++x) { scanBuf[x] = 0xff; } if (!noClip) { if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) { return; } } (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL); } void Splash::strokeWide(SplashPath *path, SplashCoord w) { SplashPath *path2; path2 = makeStrokePath(path, w, gFalse); fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); delete path2; } SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness) { SplashPath *fPath; SplashCoord flatness2; Guchar flag; int i; fPath = new SplashPath(); #if USE_FIXEDPOINT flatness2 = flatness; #else flatness2 = flatness * flatness; #endif i = 0; while (i < path->length) { flag = path->flags[i]; if (flag & splashPathFirst) { fPath->moveTo(path->pts[i].x, path->pts[i].y); ++i; } else { if (flag & splashPathCurve) { flattenCurve(path->pts[i-1].x, path->pts[i-1].y, path->pts[i ].x, path->pts[i ].y, path->pts[i+1].x, path->pts[i+1].y, path->pts[i+2].x, path->pts[i+2].y, matrix, flatness2, fPath); i += 3; } else { fPath->lineTo(path->pts[i].x, path->pts[i].y); ++i; } if (path->flags[i-1] & splashPathClosed) { fPath->close(); } } } return fPath; } void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath) { SplashCoord cx[splashMaxCurveSplits + 1][3]; SplashCoord cy[splashMaxCurveSplits + 1][3]; int cNext[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, tx, ty, d1, d2; int p1, p2, p3; // initial segment p1 = 0; p2 = splashMaxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; xx1 = cx[p1][1]; yy1 = cy[p1][1]; xx2 = cx[p1][2]; yy2 = cy[p1][2]; p2 = cNext[p1]; xr3 = cx[p2][0]; yr3 = cy[p2][0]; // compute the distances (in device space) from the control points // to the midpoint of the straight line (this is a bit of a hack, // but it's much faster than computing the actual distances to the // line) transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); transform(matrix, xx1, yy1, &tx, &ty); #if USE_FIXEDPOINT d1 = splashDist(tx, ty, mx, my); #else dx = tx - mx; dy = ty - my; d1 = dx*dx + dy*dy; #endif transform(matrix, xx2, yy2, &tx, &ty); #if USE_FIXEDPOINT d2 = splashDist(tx, ty, mx, my); #else dx = tx - mx; dy = ty - my; d2 = dx*dx + dy*dy; #endif // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { fPath->lineTo(xr3, yr3); p1 = p2; // otherwise, subdivide the curve } else { xl1 = splashAvg(xl0, xx1); yl1 = splashAvg(yl0, yy1); xh = splashAvg(xx1, xx2); yh = splashAvg(yy1, yy2); xl2 = splashAvg(xl1, xh); yl2 = splashAvg(yl1, yh); xr2 = splashAvg(xx2, xr3); yr2 = splashAvg(yy2, yr3); xr1 = splashAvg(xh, xr2); yr1 = splashAvg(yh, yr2); xr0 = splashAvg(xl2, xr1); yr0 = splashAvg(yl2, yr1); // add the new subdivision points p3 = (p1 + p2) / 2; cx[p1][1] = xl1; cy[p1][1] = yl1; cx[p1][2] = xl2; cy[p1][2] = yl2; cNext[p1] = p3; cx[p3][0] = xr0; cy[p3][0] = yr0; cx[p3][1] = xr1; cy[p3][1] = yr1; cx[p3][2] = xr2; cy[p3][2] = yr2; cNext[p3] = p2; } } } SplashPath *Splash::makeDashedPath(SplashPath *path) { SplashPath *dPath; SplashCoord lineDashTotal; SplashCoord lineDashStartPhase, lineDashDist, segLen; SplashCoord x0, y0, x1, y1, xa, ya; GBool lineDashStartOn, lineDashOn, newPath; int lineDashStartIdx, lineDashIdx; int i, j, k; lineDashTotal = 0; for (i = 0; i < state->lineDashLength; ++i) { lineDashTotal += state->lineDash[i]; } // Acrobat simply draws nothing if the dash array is [0] if (lineDashTotal == 0) { return new SplashPath(); } lineDashStartPhase = state->lineDashPhase; i = splashFloor(lineDashStartPhase / lineDashTotal); lineDashStartPhase -= (SplashCoord)i * lineDashTotal; lineDashStartOn = gTrue; lineDashStartIdx = 0; if (lineDashStartPhase > 0) { while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { lineDashStartOn = !lineDashStartOn; lineDashStartPhase -= state->lineDash[lineDashStartIdx]; ++lineDashStartIdx; } } dPath = new SplashPath(); // process each subpath i = 0; while (i < path->length) { // find the end of the subpath for (j = i; j < path->length - 1 && !(path->flags[j] & splashPathLast); ++j) ; // initialize the dash parameters lineDashOn = lineDashStartOn; lineDashIdx = lineDashStartIdx; lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; // process each segment of the subpath newPath = gTrue; for (k = i; k < j; ++k) { // grab the segment x0 = path->pts[k].x; y0 = path->pts[k].y; x1 = path->pts[k+1].x; y1 = path->pts[k+1].y; segLen = splashDist(x0, y0, x1, y1); // process the segment while (segLen > 0) { if (lineDashDist >= segLen) { if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; } dPath->lineTo(x1, y1); } lineDashDist -= segLen; segLen = 0; } else { xa = x0 + (lineDashDist / segLen) * (x1 - x0); ya = y0 + (lineDashDist / segLen) * (y1 - y0); if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; } dPath->lineTo(xa, ya); } x0 = xa; y0 = ya; segLen -= lineDashDist; lineDashDist = 0; } // get the next entry in the dash array if (lineDashDist <= 0) { lineDashOn = !lineDashOn; if (++lineDashIdx == state->lineDashLength) { lineDashIdx = 0; } lineDashDist = state->lineDash[lineDashIdx]; newPath = gTrue; } } } i = j + 1; } return dPath; } SplashError Splash::fill(SplashPath *path, GBool eo) { if (debugMode) { printf("fill [eo:%d]:\n", eo); dumpPath(path); } return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); } SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha) { SplashPipe pipe; SplashPath *path2; SplashXPath *xPath; SplashXPathScanner *scanner; int xMin, yMin, xMax, yMax, x, y, t; SplashClipResult clipRes; if (path->length == 0) { return splashErrEmptyPath; } if (pathAllOutside(path)) { opClipRes = splashClipAllOutside; return splashOk; } path2 = tweakFillPath(path); xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue); if (path2 != path) { delete path2; } xMin = xPath->getXMin(); yMin = xPath->getYMin(); xMax = xPath->getXMax(); yMax = xPath->getYMax(); if (xMin > xMax || yMin > yMax) { delete xPath; return splashOk; } scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); // check clipping if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin > xMax || yMin > yMax) { delete scanner; delete xPath; return splashOk; } pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255), gTrue, gFalse); // draw the spans if (vectorAntialias && !inShading) { for (y = yMin; y <= yMax; ++y) { scanner->getSpan(scanBuf, y, xMin, xMax); if (clipRes != splashClipAllInside) { state->clip->clipSpan(scanBuf, y, xMin, xMax, state->strokeAdjust); } for (x = xMin; x <= xMax; ++x) { scanBuf[x] = aaGamma[scanBuf[x]]; } (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } else { for (y = yMin; y <= yMax; ++y) { scanner->getSpanBinary(scanBuf, y, xMin, xMax); if (clipRes != splashClipAllInside) { state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, state->strokeAdjust); } (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } opClipRes = clipRes; delete scanner; delete xPath; return splashOk; } // Applies various tweaks to a fill path: // (1) add stroke adjust hints to a filled rectangle // (2) applies a minimum width to a zero-width filled rectangle (so // stroke adjustment works correctly // (3) convert a degenerate fill ('moveto lineto fill' and 'moveto // lineto closepath fill') to a minimum-width filled rectangle // // These tweaks only apply to paths with a single subpath. // // Returns either the unchanged input path or a new path (in which // case the returned path must be deleted by the caller). SplashPath *Splash::tweakFillPath(SplashPath *path) { SplashPath *path2; SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w; int n; if (!state->strokeAdjust || path->hints) { return path; } n = path->getLength(); if (!((n == 2) || (n == 3 && path->flags[1] == 0) || (n == 4 && path->flags[1] == 0 && path->flags[2] == 0) || (n == 5 && path->flags[1] == 0 && path->flags[2] == 0 && path->flags[3] == 0))) { return path; } path2 = path; // degenerate fill (2 or 3 points) or rectangle of (nearly) zero // width --> replace with a min-width rectangle and hint if (n == 2 || (n == 3 && (path->flags[0] & splashPathClosed)) || (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) || ((n == 4 || (n == 5 && (path->flags[0] & splashPathClosed))) && ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 && splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 && splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) || (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 && splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 && splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) { wx = state->matrix[0] + state->matrix[2]; wy = state->matrix[1] + state->matrix[3]; w = sqrt(wx*wx + wy*wy); if (w < 0.001) { w = 0; } else { // min width is 0.1 -- this constant is minWidth * sqrt(2) w = (SplashCoord)0.1414 / w; } xx0 = path->pts[0].x; yy0 = path->pts[0].y; if (n <= 3) { xx1 = path->pts[1].x; yy1 = path->pts[1].y; } else { xx1 = path->pts[2].x; yy1 = path->pts[2].y; } dx = xx1 - xx0; dy = yy1 - yy0; d = sqrt(dx * dx + dy * dy); if (d < 0.001) { d = 0; } else { d = w / d; } dx *= d; dy *= d; path2 = new SplashPath(); path2->moveTo(xx0 + dy, yy0 - dx); path2->lineTo(xx1 + dy, yy1 - dx); path2->lineTo(xx1 - dy, yy1 + dx); path2->lineTo(xx0 - dy, yy0 + dx); path2->close(gTrue); path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); // unclosed rectangle --> close and hint } else if (n == 4 && !(path->flags[0] & splashPathClosed)) { path2->close(gTrue); path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); // closed rectangle --> hint } else if (n == 5 && (path->flags[0] & splashPathClosed)) { path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); } return path2; } GBool Splash::pathAllOutside(SplashPath *path) { SplashCoord xMin1, yMin1, xMax1, yMax1; SplashCoord xMin2, yMin2, xMax2, yMax2; SplashCoord x, y; int xMinI, yMinI, xMaxI, yMaxI; int i; xMin1 = xMax1 = path->pts[0].x; yMin1 = yMax1 = path->pts[0].y; for (i = 1; i < path->length; ++i) { if (path->pts[i].x < xMin1) { xMin1 = path->pts[i].x; } else if (path->pts[i].x > xMax1) { xMax1 = path->pts[i].x; } if (path->pts[i].y < yMin1) { yMin1 = path->pts[i].y; } else if (path->pts[i].y > yMax1) { yMax1 = path->pts[i].y; } } transform(state->matrix, xMin1, yMin1, &x, &y); xMin2 = xMax2 = x; yMin2 = yMax2 = y; transform(state->matrix, xMin1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMin1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } xMinI = splashFloor(xMin2); yMinI = splashFloor(yMin2); xMaxI = splashFloor(xMax2); yMaxI = splashFloor(yMax2); return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI, state->strokeAdjust) == splashClipAllOutside; } SplashError Splash::xorFill(SplashPath *path, GBool eo) { SplashPipe pipe; SplashXPath *xPath; SplashXPathScanner *scanner; int xMin, yMin, xMax, yMax, y, t; SplashClipResult clipRes; SplashBlendFunc origBlendFunc; if (path->length == 0) { return splashErrEmptyPath; } if (pathAllOutside(path)) { opClipRes = splashClipAllOutside; return splashOk; } xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); xMin = xPath->getXMin(); yMin = xPath->getYMin(); xMax = xPath->getXMax(); yMax = xPath->getYMax(); if (xMin > xMax || yMin > yMax) { delete xPath; return splashOk; } scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); // check clipping if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin > xMax || yMin > yMax) { delete scanner; delete xPath; return splashOk; } origBlendFunc = state->blendFunc; state->blendFunc = &blendXor; pipeInit(&pipe, state->fillPattern, 255, gTrue, gFalse); // draw the spans for (y = yMin; y <= yMax; ++y) { scanner->getSpanBinary(scanBuf, y, xMin, xMax); if (clipRes != splashClipAllInside) { state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, state->strokeAdjust); } (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } state->blendFunc = origBlendFunc; } opClipRes = clipRes; delete scanner; delete xPath; return splashOk; } SplashError Splash::fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font) { SplashGlyphBitmap glyph; SplashCoord xt, yt; int x0, y0, xFrac, yFrac; SplashError err; if (debugMode) { printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", (double)x, (double)y, c, c, c); } transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); xFrac = splashFloor((xt - x0) * splashFontFraction); y0 = splashFloor(yt); yFrac = splashFloor((yt - y0) * splashFontFraction); if (!font->getGlyph(c, xFrac, yFrac, &glyph)) { return splashErrNoGlyph; } err = fillGlyph2(x0, y0, &glyph); if (glyph.freeData) { gfree(glyph.data); } return err; } SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph) { SplashCoord xt, yt; int x0, y0; transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); y0 = splashFloor(yt); return fillGlyph2(x0, y0, glyph); } SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { SplashPipe pipe; SplashClipResult clipRes; Guchar alpha; Guchar *p; int xMin, yMin, xMax, yMax; int x, y, xg, yg, xx, t; xg = x0 - glyph->x; yg = y0 - glyph->y; xMin = xg; xMax = xg + glyph->w - 1; yMin = yg; yMax = yg + glyph->h - 1; if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (clipRes == splashClipAllInside) { if (glyph->aa) { p = glyph->data; for (y = yMin; y <= yMax; ++y) { (this->*pipe.run)(&pipe, xMin, xMax, y, glyph->data + (y - yMin) * glyph->w, NULL); } } else { p = glyph->data; for (y = yMin; y <= yMax; ++y) { for (x = xMin; x <= xMax; x += 8) { alpha = *p++; for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00; alpha <<= 1; } } (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } else { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin <= xMax && yMin <= yMax) { if (glyph->aa) { for (y = yMin; y <= yMax; ++y) { p = glyph->data + (y - yg) * glyph->w + (xMin - xg); memcpy(scanBuf + xMin, p, xMax - xMin + 1); state->clip->clipSpan(scanBuf, y, xMin, xMax, state->strokeAdjust); (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } else { for (y = yMin; y <= yMax; ++y) { p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3) + ((xMin - xg) >> 3); alpha = *p++; xx = (xMin - xg) & 7; alpha <<= xx; for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) { scanBuf[x] = (alpha & 0x80) ? 255 : 0; alpha <<= 1; } for (; x <= xMax; x += 8) { alpha = *p++; for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0; alpha <<= 1; } } state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, state->strokeAdjust); (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } } } opClipRes = clipRes; return splashOk; } void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax, int *xyMinI, int *xyMaxI) { if (state->strokeAdjust) { splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI); } else { *xyMinI = splashFloor(xyMin); *xyMaxI = splashFloor(xyMax); if (*xyMaxI <= *xyMinI) { *xyMaxI = *xyMinI + 1; } } } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; GBool minorAxisZero; SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; if (debugMode) { printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; // rough estimate of size of scaled mask t0 = splashAbs(mat[0]); t1 = splashAbs(mat[1]); wSize = t0 > t1 ? t0 : t1; t0 = splashAbs(mat[2]); t1 = splashAbs(mat[3]); hSize = t0 > t1 ? t0 : t1; // stream-mode upscaling -- this is slower, so we only use it if the // upscaled mask is large (in which case clipping should remove many // pixels) if (wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) { upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate); // scaling only } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus horizontal flip } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus horizontal and vertical flips } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // all other cases } else { arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate); } return splashOk; } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. void Splash::upscaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashClipResult clipRes; SplashPipe pipe; Guchar *unscaledImage, *p; SplashCoord xMin, yMin, xMax, yMax, t; SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; SplashCoord ix, iy, sx, sy, pix0, pix1; int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt; // compute the bbox of the target quadrilateral xMin = xMax = mat[4]; t = mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } getImageBounds(xMin, xMax, &xMinI, &xMaxI); yMin = yMax = mat[5]; t = mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } getImageBounds(yMin, yMax, &yMinI, &yMaxI); // clipping clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } if (clipRes != splashClipAllInside) { if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { xMinI = tt; } if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { xMaxI = tt; } if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { yMinI = tt; } if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { yMaxI = tt; } } // invert the matrix det = mat[0] * mat[3] - mat[1] * mat[2]; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } det = (SplashCoord)1 / det; mi0 = det * mat[3] * srcWidth; mi1 = -det * mat[1] * srcHeight; mi2 = -det * mat[2] * srcWidth; mi3 = det * mat[0] * srcHeight; mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; // grab the image unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight); for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) { (*src)(srcData, p); for (x = 0; x < srcWidth; ++x) { p[x] *= 255; } } // draw it pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (y = yMinI; y < yMaxI; ++y) { for (x = xMinI; x < xMaxI; ++x) { ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; if (interpolate) { if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { x0 = splashFloor(ix - 0.5); x1 = x0 + 1; sx = (ix - 0.5) - x0; y0 = splashFloor(iy - 0.5); y1 = y0 + 1; sy = (iy - 0.5) - y0; if (x0 < 0) { x0 = 0; } if (x1 >= srcWidth) { x1 = srcWidth - 1; } if (y0 < 0) { y0 = 0; } if (y1 >= srcHeight) { y1 = srcHeight - 1; } pix0 = ((SplashCoord)1 - sx) * unscaledImage[y0 * srcWidth + x0] + sx * unscaledImage[y0 * srcWidth + x1]; pix1 = ((SplashCoord)1 - sx) * unscaledImage[y1 * srcWidth + x0] + sx * unscaledImage[y1 * srcWidth + x1]; scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } else { scanBuf[x] = 0; } } else { x0 = splashFloor(ix); y0 = splashFloor(iy); if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { scanBuf[x] = unscaledImage[y0 * srcWidth + x0]; } else { scanBuf[x] = 0; } } } if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } } (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL); } gfree(unscaledImage); } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; SplashPipe pipe; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = splashRound(vx[0]); xMax = splashRound(vx[0]); yMin = splashRound(vy[0]); yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; } else if (t0 > xMax) { xMax = t0; } t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; } else if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && vy[(i-1) & 3] < vy[(i+1) & 3]) { i = (i-1) & 3; } if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { section[0].y0 = splashRound(vy[i]); section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; section[0].ib0 = (i+1) & 3; section[0].ib1 = (i+2) & 3; } else { section[0].ia0 = (i+1) & 3; section[0].ia1 = (i+2) & 3; section[0].ib0 = i; section[0].ib1 = (i+3) & 3; } nSections = 1; } else { section[0].y0 = splashRound(vy[i]); section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i+2) & 3; if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[0].ia1 = section[2].ia0 = (i+1) & 3; section[0].ib1 = section[2].ib0 = (i+3) & 3; } else { section[0].ia1 = section[2].ia0 = (i+3) & 3; section[0].ib1 = section[2].ib0 = (i+1) & 3; } if (vy[(i+1) & 3] < vy[(i+3) & 3]) { section[1].y0 = splashRound(vy[(i+1) & 3]); section[2].y0 = splashRound(vy[(i+3) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = (i+1) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i+3) & 3; section[1].ib0 = (i+1) & 3; section[1].ib1 = (i+2) & 3; } } else { section[1].y0 = splashRound(vy[(i+3) & 3]); section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; section[1].ib0 = (i+3) & 3; section[1].ib1 = (i+2) & 3; } else { section[1].ia0 = (i+3) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } // scan all pixels inside the target region for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = splashRound(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); xb = splashRound(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); if (xa > xb) { continue; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } // check the scanBuf bounds if (xa >= bitmap->width || xb < 0) { continue; } if (xa < 0) { xa = 0; } if (xb > bitmap->width) { xb = bitmap->width; } // get the scan line for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } scanBuf[x] = scaledMask->data[yy * scaledWidth + xx]; } // clip the scan line if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, state->strokeAdjust); } } // draw the scan line (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL); } } delete scaledMask; } // Scale an image mask into a SplashBitmap. SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, gFalse); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleMaskYdXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYdXu(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleMaskYuXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (interpolate) { scaleMaskYuXuI(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYuXu(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } return dest; } void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint *pixBuf; Guint pix; Guchar *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d0 = (255 << 23) / (yStep * xp); d1 = (255 << 23) / (yStep * (xp + 1)); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += pixBuf[xx++]; } // (255 * pix) / xStep * yStep pix = (pix * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix; } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint *pixBuf; Guint pix; Guchar *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d = (255 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = pixBuf[x]; // (255 * pix) / yStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix; } } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint pix; Guchar *destPtr0, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; destPtr0 = dest->data; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; d0 = (255 << 23) / xp; d1 = (255 << 23) / (xp + 1); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += lineBuf[xx++]; } // (255 * pix) / xStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + i * scaledWidth + x; *destPtr = (Guchar)pix; } } destPtr0 += yStep * scaledWidth; } gfree(lineBuf); } void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint pix; Guchar *destPtr0, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; destPtr0 = dest->data; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; xx = 0; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = lineBuf[x] ? 255 : 0; // store the pixel for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + i * scaledWidth + xx + j; *destPtr++ = (Guchar)pix; } } xx += xStep; } destPtr0 += yStep * scaledWidth; } gfree(lineBuf); } void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf0, *lineBuf1, *tBuf; Guchar pix; SplashCoord yr, xr, ys, xs, ySrc, xSrc; int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x; Guchar *destPtr; // ratios yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; // allocate buffers lineBuf0 = (Guchar *)gmalloc(scaledWidth); lineBuf1 = (Guchar *)gmalloc(scaledWidth); // read first two rows (*src)(srcData, lineBuf0); if (srcHeight > 1) { (*src)(srcData, lineBuf1); yBuf = 1; } else { memcpy(lineBuf1, lineBuf0, srcWidth); yBuf = 0; } // interpolate first two rows for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } lineBuf0[x] = (Guchar)(int) ((xs * lineBuf0[xSrc0] + ((SplashCoord)1 - xs) * lineBuf0[xSrc1]) * 255); lineBuf1[x] = (Guchar)(int) ((xs * lineBuf1[xSrc0] + ((SplashCoord)1 - xs) * lineBuf1[xSrc1]) * 255); } destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // compute vertical interpolation parameters ySrc = yr * y; ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); ySrc1 = ySrc0 + 1; ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); if (ySrc0 < 0) { ySrc0 = 0; ys = 1; } if (ySrc1 >= srcHeight) { ySrc1 = srcHeight - 1; ys = 0; } // read another row (if necessary) if (ySrc1 > yBuf) { tBuf = lineBuf0; lineBuf0 = lineBuf1; lineBuf1 = tBuf; (*src)(srcData, lineBuf1); // interpolate the row for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } lineBuf1[x] = (Guchar)(int) ((xs * lineBuf1[xSrc0] + ((SplashCoord)1 - xs) * lineBuf1[xSrc1]) * 255); } ++yBuf; } // do the vertical interpolation for (x = 0; x < scaledWidth; ++x) { pix = (Guchar)(int)(ys * lineBuf0[x] + ((SplashCoord)1 - ys) * lineBuf1[x]); // store the pixel *destPtr++ = pix; } } gfree(lineBuf1); gfree(lineBuf0); } void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; int w, h, x0, x1, y0, y1, y, t; w = src->getWidth(); h = src->getHeight(); pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (clipRes == splashClipAllInside) { for (y = 0; y < h; ++y) { (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->getDataPtr() + y * w, NULL); } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->getDataPtr() + (y - yDest) * w + (x0 - xDest), x1 - x0); if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL); } } } } SplashError Splash::drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat, GBool interpolate) { GBool ok; SplashBitmap *scaledImg; SplashClipResult clipRes; GBool minorAxisZero; SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; int nComps; if (debugMode) { printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check color modes ok = gFalse; // make gcc happy nComps = 0; // make gcc happy switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: ok = srcMode == splashModeMono8; nComps = 1; break; case splashModeRGB8: case splashModeBGR8: ok = srcMode == splashModeRGB8; nComps = 3; break; #if SPLASH_CMYK case splashModeCMYK8: ok = srcMode == splashModeCMYK8; nComps = 4; break; #endif default: ok = gFalse; break; } if (!ok) { return splashErrModeMismatch; } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; // rough estimate of size of scaled image t0 = splashAbs(mat[0]); t1 = splashAbs(mat[1]); wSize = t0 > t1 ? t0 : t1; t0 = splashAbs(mat[2]); t1 = splashAbs(mat[3]); hSize = t0 > t1 ? t0 : t1; // stream-mode upscaling -- this is slower, so we only use it if the // upscaled image is large (in which case clipping should remove // many pixels) if (wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) { upscaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate); // scaling only } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus horizontal flip } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus horizontal and vertical flips } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // all other cases } else { arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate); } return splashOk; } void Splash::upscaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate) { SplashClipResult clipRes; SplashPipe pipe; SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11; Guchar *unscaledAlpha, *alphaPtr; SplashCoord xMin, yMin, xMax, yMax, t; SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; SplashCoord ix, iy, sx, sy, pix0, pix1; int rowSize, xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i; // compute the bbox of the target quadrilateral xMin = xMax = mat[4]; t = mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } getImageBounds(xMin, xMax, &xMinI, &xMaxI); yMin = yMax = mat[5]; t = mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } getImageBounds(yMin, yMax, &yMinI, &yMaxI); // clipping clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } if (clipRes != splashClipAllInside) { if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { xMinI = tt; } if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { xMaxI = tt; } if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { yMinI = tt; } if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { yMaxI = tt; } } // invert the matrix det = mat[0] * mat[3] - mat[1] * mat[2]; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } det = (SplashCoord)1 / det; mi0 = det * mat[3] * srcWidth; mi1 = -det * mat[1] * srcHeight; mi2 = -det * mat[2] * srcWidth; mi3 = det * mat[0] * srcHeight; mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; // grab the image if (srcWidth > INT_MAX / nComps) { rowSize = -1; } else { rowSize = srcWidth * nComps; } unscaledImage = (SplashColorPtr)gmallocn(srcHeight, rowSize); if (srcAlpha) { unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth); for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha; y < srcHeight; ++y, p += rowSize, alphaPtr += srcWidth) { (*src)(srcData, p, alphaPtr); } } else { unscaledAlpha = NULL; for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth * nComps) { (*src)(srcData, p, NULL); } } // draw it pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps); pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (y = yMinI; y < yMaxI; ++y) { p = pixelBuf; for (x = xMinI; x < xMaxI; ++x) { ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; if (interpolate) { if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { x0 = splashFloor(ix - 0.5); x1 = x0 + 1; sx = (ix - 0.5) - x0; y0 = splashFloor(iy - 0.5); y1 = y0 + 1; sy = (iy - 0.5) - y0; if (x0 < 0) { x0 = 0; } if (x1 >= srcWidth) { x1 = srcWidth - 1; } if (y0 < 0) { y0 = 0; } if (y1 >= srcHeight) { y1 = srcHeight - 1; } q00 = &unscaledImage[(y0 * srcWidth + x0) * nComps]; q01 = &unscaledImage[(y0 * srcWidth + x1) * nComps]; q10 = &unscaledImage[(y1 * srcWidth + x0) * nComps]; q11 = &unscaledImage[(y1 * srcWidth + x1) * nComps]; for (i = 0; i < nComps; ++i) { pix0 = ((SplashCoord)1 - sx) * *q00++ + sx * *q01++; pix1 = ((SplashCoord)1 - sx) * *q10++ + sx * *q11++; *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } if (srcAlpha) { pix0 = ((SplashCoord)1 - sx) * unscaledAlpha[y0 * srcWidth + x0] + sx * unscaledAlpha[y0 * srcWidth + x1]; pix1 = ((SplashCoord)1 - sx) * unscaledAlpha[y1 * srcWidth + x0] + sx * unscaledAlpha[y1 * srcWidth + x1]; scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } else { scanBuf[x] = 0xff; } } else { for (i = 0; i < nComps; ++i) { *p++ = 0; } scanBuf[x] = 0; } } else { x0 = splashFloor(ix); y0 = splashFloor(iy); if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { q = &unscaledImage[(y0 * srcWidth + x0) * nComps]; for (i = 0; i < nComps; ++i) { *p++ = *q++; } if (srcAlpha) { scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0]; } else { scanBuf[x] = 0xff; } } else { for (i = 0; i < nComps; ++i) { *p++ = 0; } scanBuf[x] = 0; } } } if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } } (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf); } gfree(pixelBuf); gfree(unscaledImage); gfree(unscaledAlpha); } void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate) { SplashBitmap *scaledImg; SplashClipResult clipRes; SplashPipe pipe; SplashColorPtr pixelBuf; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = splashRound(vx[0]); xMax = splashRound(vx[0]); yMin = splashRound(vy[0]); yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; } else if (t0 > xMax) { xMax = t0; } t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; } else if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in drawImage return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && vy[(i-1) & 3] < vy[(i+1) & 3]) { i = (i-1) & 3; } if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { section[0].y0 = splashRound(vy[i]); section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; section[0].ib0 = (i+1) & 3; section[0].ib1 = (i+2) & 3; } else { section[0].ia0 = (i+1) & 3; section[0].ia1 = (i+2) & 3; section[0].ib0 = i; section[0].ib1 = (i+3) & 3; } nSections = 1; } else { section[0].y0 = splashRound(vy[i]); section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i+2) & 3; if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[0].ia1 = section[2].ia0 = (i+1) & 3; section[0].ib1 = section[2].ib0 = (i+3) & 3; } else { section[0].ia1 = section[2].ia0 = (i+3) & 3; section[0].ib1 = section[2].ib0 = (i+1) & 3; } if (vy[(i+1) & 3] < vy[(i+3) & 3]) { section[1].y0 = splashRound(vy[(i+1) & 3]); section[2].y0 = splashRound(vy[(i+3) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = (i+1) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i+3) & 3; section[1].ib0 = (i+1) & 3; section[1].ib1 = (i+2) & 3; } } else { section[1].y0 = splashRound(vy[(i+3) & 3]); section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; section[1].ib0 = (i+3) & 3; section[1].ib1 = (i+2) & 3; } else { section[1].ia0 = (i+3) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps); // scan all pixels inside the target region for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = splashRound(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); xb = splashRound(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); if (xa > xb) { continue; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } // check the scanBuf bounds if (xa >= bitmap->width || xb < 0) { continue; } if (xa < 0) { xa = 0; } if (xb > bitmap->width) { xb = bitmap->width; } // clip the scan line memset(scanBuf + xa, 0xff, xb - xa); if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, state->strokeAdjust); } } // draw the scan line for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } // get the color scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps); // apply alpha if (srcAlpha) { scanBuf[x] = div255(scanBuf[x] * scaledImg->alpha[yy * scaledWidth + xx]); } } (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf); } } gfree(pixelBuf); delete scaledImg; } // Scale an image into a SplashBitmap. SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (interpolate) { scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } return dest; } void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint *pixBuf, *alphaPixBuf; Guint pix0, pix1, pix2; #if SPLASH_CMYK Guint pix3; #endif Guint alpha; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = NULL; alphaPixBuf = NULL; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d0 = (1 << 23) / (yStep * xp); d1 = (1 << 23) / (yStep * (xp + 1)); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } switch (srcMode) { case splashModeMono8: // compute the final pixel pix0 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx++]; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; break; case splashModeRGB8: // compute the final pixel pix0 = pix1 = pix2 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx+1]; pix2 += pixBuf[xx+2]; xx += 3; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix2; break; #if SPLASH_CMYK case splashModeCMYK8: // compute the final pixel pix0 = pix1 = pix2 = pix3 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx+1]; pix2 += pixBuf[xx+2]; pix3 += pixBuf[xx+3]; xx += 4; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; pix3 = (pix3 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix2; *destPtr++ = (Guchar)pix3; break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // bgr8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaPixBuf[xxa]; } // alpha / xStep * yStep alpha = (alpha * d) >> 23; *destAlphaPtr++ = (Guchar)alpha; } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); } void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint *pixBuf, *alphaPixBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = NULL; alphaPixBuf = NULL; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d = (1 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel for (i = 0; i < nComps; ++i) { // pixBuf[] / yStep pix[i] = (pixBuf[x * nComps + i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; } break; case splashModeRGB8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { // alphaPixBuf[] / yStep alpha = (alphaPixBuf[x] * d) >> 23; for (i = 0; i < xStep; ++i) { *destAlphaPtr++ = (Guchar)alpha; } } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); } void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); } else { alphaLineBuf = NULL; } // init y scale Bresenham yt = 0; destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; d0 = (1 << 23) / xp; d1 = (1 << 23) / (xp + 1); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = 0; } for (i = 0; i < xStep; ++i) { for (j = 0; j < nComps; ++j, ++xx) { pix[j] += lineBuf[xx]; } } for (i = 0; i < nComps; ++i) { // pix[] / xStep pix[i] = (pix[i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; } break; case splashModeRGB8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaLineBuf[xxa]; } // alpha / xStep alpha = (alpha * d) >> 23; for (i = 0; i < yStep; ++i) { destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; *destAlphaPtr = (Guchar)alpha; } } } destPtr0 += yStep * scaledWidth * nComps; if (srcAlpha) { destAlphaPtr0 += yStep * scaledWidth; } } gfree(alphaLineBuf); gfree(lineBuf); } void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); } else { alphaLineBuf = NULL; } // init y scale Bresenham yt = 0; destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; xx = 0; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = lineBuf[x * nComps + i]; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (Guchar)pix[0]; } } break; case splashModeRGB8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = alphaLineBuf[x]; for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; *destAlphaPtr = (Guchar)alpha; } } } xx += xStep; } destPtr0 += yStep * scaledWidth * nComps; if (srcAlpha) { destAlphaPtr0 += yStep * scaledWidth; } } gfree(alphaLineBuf); gfree(lineBuf); } void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf; Guchar pix[splashMaxColorComps]; SplashCoord yr, xr, ys, xs, ySrc, xSrc; int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i; Guchar *destPtr, *destAlphaPtr; // ratios yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; // allocate buffers lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps); lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); if (srcAlpha) { alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth); alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); } else { alphaLineBuf0 = NULL; alphaLineBuf1 = NULL; } // read first two rows (*src)(srcData, lineBuf0, alphaLineBuf0); if (srcHeight > 1) { (*src)(srcData, lineBuf1, alphaLineBuf1); yBuf = 1; } else { memcpy(lineBuf1, lineBuf0, srcWidth * nComps); if (srcAlpha) { memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth); } yBuf = 0; } // interpolate first two rows for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } for (i = 0; i < nComps; ++i) { lineBuf0[x*nComps+i] = (Guchar)(int) (xs * lineBuf0[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * lineBuf0[xSrc1*nComps+i]); lineBuf1[x*nComps+i] = (Guchar)(int) (xs * lineBuf1[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * lineBuf1[xSrc1*nComps+i]); } if (srcAlpha) { alphaLineBuf0[x] = (Guchar)(int) (xs * alphaLineBuf0[xSrc0] + ((SplashCoord)1 - xs) * alphaLineBuf0[xSrc1]); alphaLineBuf1[x] = (Guchar)(int) (xs * alphaLineBuf1[xSrc0] + ((SplashCoord)1 - xs) * alphaLineBuf1[xSrc1]); } } destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // compute vertical interpolation parameters ySrc = yr * y; ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); ySrc1 = ySrc0 + 1; ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); if (ySrc0 < 0) { ySrc0 = 0; ys = 1; } if (ySrc1 >= srcHeight) { ySrc1 = srcHeight - 1; ys = 0; } // read another row (if necessary) if (ySrc1 > yBuf) { tBuf = lineBuf0; lineBuf0 = lineBuf1; lineBuf1 = tBuf; tBuf = alphaLineBuf0; alphaLineBuf0 = alphaLineBuf1; alphaLineBuf1 = tBuf; (*src)(srcData, lineBuf1, alphaLineBuf1); // interpolate the row for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } for (i = 0; i < nComps; ++i) { lineBuf1[x*nComps+i] = (Guchar)(int)(xs * lineBuf1[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * lineBuf1[xSrc1*nComps+i]); } if (srcAlpha) { alphaLineBuf1[x] = (Guchar)(int)(xs * alphaLineBuf1[xSrc0] + ((SplashCoord)1 - xs) * alphaLineBuf1[xSrc1]); } } ++yBuf; } // do the vertical interpolation for (x = 0; x < scaledWidth; ++x) { for (i = 0; i < nComps; ++i) { pix[i] = (Guchar)(int)(ys * lineBuf0[x*nComps+i] + ((SplashCoord)1 - ys) * lineBuf1[x*nComps+i]); } // store the pixel switch (srcMode) { case splashModeMono8: *destPtr++ = pix[0]; break; case splashModeRGB8: *destPtr++ = pix[0]; *destPtr++ = pix[1]; *destPtr++ = pix[2]; break; #if SPLASH_CMYK case splashModeCMYK8: *destPtr++ = pix[0]; *destPtr++ = pix[1]; *destPtr++ = pix[2]; *destPtr++ = pix[3]; break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { *destAlphaPtr++ = (Guchar)(int) (ys * alphaLineBuf0[x] + ((SplashCoord)1 - ys) * alphaLineBuf1[x]); } } } gfree(alphaLineBuf1); gfree(alphaLineBuf0); gfree(lineBuf1); gfree(lineBuf0); } void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; Guchar *p0, *p1; int w; w = width * nComps; lineBuf = (Guchar *)gmalloc(w); for (p0 = img->data, p1 = img->data + (height - 1) * w; p0 < p1; p0 += w, p1 -= w) { memcpy(lineBuf, p0, w); memcpy(p0, p1, w); memcpy(p1, lineBuf, w); } if (img->alpha) { for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width; p0 < p1; p0 += width, p1 -= width) { memcpy(lineBuf, p0, width); memcpy(p0, p1, width); memcpy(p1, lineBuf, width); } } gfree(lineBuf); } void Splash::horizFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; SplashColorPtr p0, p1, p2; int w, x, y, i; w = width * nComps; lineBuf = (Guchar *)gmalloc(w); for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) { memcpy(lineBuf, p0, w); p1 = p0; p2 = lineBuf + (w - nComps); for (x = 0; x < width; ++x) { for (i = 0; i < nComps; ++i) { p1[i] = p2[i]; } p1 += nComps; p2 -= nComps; } } if (img->alpha) { for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) { memcpy(lineBuf, p0, width); p1 = p0; p2 = lineBuf + (width - 1); for (x = 0; x < width; ++x) { *p1++ = *p2--; } } } gfree(lineBuf); } void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; int w, h, x0, y0, x1, y1, y; // split the image into clipped and unclipped regions w = src->getWidth(); h = src->getHeight(); if (clipRes == splashClipAllInside) { x0 = 0; y0 = 0; x1 = w; y1 = h; } else { if (state->clip->getNumPaths()) { x0 = x1 = w; y0 = y1 = h; } else { if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { x0 = 0; } if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { y0 = 0; } if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { x1 = w; } if (x1 < x0) { x1 = x0; } if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { y1 = h; } if (y1 < y0) { y1 = y0; } } } // draw the unclipped region if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); if (srcAlpha) { for (y = y0; y < y1; ++y) { (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, src->getAlphaPtr() + y * w + x0, src->getDataPtr() + y * src->getRowSize() + x0 * bitmapComps); } } else { for (y = y0; y < y1; ++y) { (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, NULL, src->getDataPtr() + y * src->getRowSize() + x0 * bitmapComps); } } } // draw the clipped regions if (y0 > 0) { blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); } if (y1 < h) { blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); } if (x0 > 0 && y0 < y1) { blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); } if (x1 < w && y0 < y1) { blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, w - x1, y1 - y0); } } void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashPipe pipe; int y; if (xDest < 0) { xSrc -= xDest; w += xDest; xDest = 0; } if (xDest + w > bitmap->width) { w = bitmap->width - xDest; } if (yDest < 0) { ySrc -= yDest; h += yDest; yDest = 0; } if (yDest + h > bitmap->height) { h = bitmap->height - yDest; } if (w <= 0 || h <= 0) { return; } pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (srcAlpha) { for (y = 0; y < h; ++y) { memcpy(scanBuf + xDest, src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc, w); if (vectorAntialias) { state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + xDest, src->getDataPtr() + (ySrc + y) * src->getRowSize() + xSrc * bitmapComps); } } else { for (y = 0; y < h; ++y) { memset(scanBuf + xDest, 0xff, w); if (vectorAntialias) { state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + xDest, src->getDataPtr() + (ySrc + y) * src->getRowSize() + xSrc * bitmapComps); } } } SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated) { SplashPipe pipe; int x0, x1, y0, y1, y, t; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), !noClip || src->alpha != NULL, nonIsolated); if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc, src->getDataPtr() + (ySrc + y) * src->getRowSize() + xSrc * bitmapComps); } } else { for (y = 0; y < h; ++y) { (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, src->getDataPtr() + (ySrc + y) * src->getRowSize() + xSrc * bitmapComps); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->getAlphaPtr() + (ySrc + y - yDest) * src->getWidth() + (xSrc + x0 - xDest), x1 - x0); if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, state->strokeAdjust)) { continue; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, src->getDataPtr() + (ySrc + y - yDest) * src->getRowSize() + (xSrc + x0 - xDest) * bitmapComps); } } else { for (y = y0; y < y1; ++y) { memset(scanBuf + x0, 0xff, x1 - x0); if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, state->strokeAdjust)) { continue; } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + x0, src->getDataPtr() + (ySrc + y - yDest) * src->getRowSize() + (xSrc - xDest) * bitmapComps); } } } } return splashOk; } void Splash::compositeBackground(SplashColorPtr color) { SplashColorPtr p; Guchar *q; Guchar alpha, alpha1, c, color0, color1, color2; #if SPLASH_CMYK Guchar color3; #endif int x, y, mask; switch (bitmap->mode) { case splashModeMono1: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; mask = 0x80; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; alpha1 = 255 - alpha; c = (*p & mask) ? 0xff : 0x00; c = div255(alpha1 * color0 + alpha * c); if (c & 0x80) { *p |= mask; } else { *p &= ~mask; } if (!(mask >>= 1)) { mask = 0x80; ++p; } } } break; case splashModeMono8: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); ++p; } } break; case splashModeRGB8: case splashModeBGR8: color0 = color[0]; color1 = color[1]; color2 = color[2]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); p += 3; } } break; #if SPLASH_CMYK case splashModeCMYK8: color0 = color[0]; color1 = color[1]; color2 = color[2]; color3 = color[3]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); p[3] = div255(alpha1 * color3 + alpha * p[3]); p += 4; } } break; #endif } memset(bitmap->alpha, 255, bitmap->width * bitmap->height); } SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, q; int x, y, mask, srcMask; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } switch (bitmap->mode) { case splashModeMono1: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; mask = 0x80 >> (xDest & 7); q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; srcMask = 0x80 >> (xSrc & 7); for (x = 0; x < w; ++x) { if (*q & srcMask) { *p |= mask; } else { *p &= ~mask; } if (!(mask >>= 1)) { mask = 0x80; ++p; } if (!(srcMask >>= 1)) { srcMask = 0x80; ++q; } } } break; case splashModeMono8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; q = &src->data[(ySrc + y) * src->rowSize + xSrc]; memcpy(p, q, w); } break; case splashModeRGB8: case splashModeBGR8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; memcpy(p, q, 3 * w); } break; #if SPLASH_CMYK case splashModeCMYK8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; memcpy(p, q, 4 * w); } break; #endif } if (bitmap->alpha) { for (y = 0; y < h; ++y) { q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest]; memset(q, 0, w); } } return splashOk; } SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, GBool flatten) { SplashPath *pathIn, *dashPath, *pathOut; SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; SplashCoord crossprod, dotprod, miter, m; GBool first, last, closed; int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; int left0, left1, left2, right0, right1, right2, join0, join1, join2; int leftFirst, rightFirst, firstPt; pathOut = new SplashPath(); if (path->length == 0) { return pathOut; } if (flatten) { pathIn = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { dashPath = makeDashedPath(pathIn); delete pathIn; pathIn = dashPath; if (pathIn->length == 0) { delete pathIn; return pathOut; } } } else { pathIn = path; } subpathStart0 = subpathStart1 = 0; // make gcc happy seg = 0; // make gcc happy closed = gFalse; // make gcc happy left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy leftFirst = rightFirst = firstPt = 0; // make gcc happy i0 = 0; for (i1 = i0; !(pathIn->flags[i1] & splashPathLast) && i1 + 1 < pathIn->length && pathIn->pts[i1+1].x == pathIn->pts[i1].x && pathIn->pts[i1+1].y == pathIn->pts[i1].y; ++i1) ; while (i1 < pathIn->length) { if ((first = pathIn->flags[i0] & splashPathFirst)) { subpathStart0 = i0; subpathStart1 = i1; seg = 0; closed = pathIn->flags[i0] & splashPathClosed; } j0 = i1 + 1; if (j0 < pathIn->length) { for (j1 = j0; !(pathIn->flags[j1] & splashPathLast) && j1 + 1 < pathIn->length && pathIn->pts[j1+1].x == pathIn->pts[j1].x && pathIn->pts[j1+1].y == pathIn->pts[j1].y; ++j1) ; } else { j1 = j0; } if (pathIn->flags[i1] & splashPathLast) { if (first && state->lineCap == splashLineCapRound) { // special case: zero-length subpath with round line caps --> // draw a circle pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y + (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y - (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->close(); } i0 = j0; i1 = j1; continue; } last = pathIn->flags[j1] & splashPathLast; if (last) { k0 = subpathStart1 + 1; } else { k0 = j1 + 1; } for (k1 = k0; !(pathIn->flags[k1] & splashPathLast) && k1 + 1 < pathIn->length && pathIn->pts[k1+1].x == pathIn->pts[k1].x && pathIn->pts[k1+1].y == pathIn->pts[k1].y; ++k1) ; // compute the deltas for segment (i1, j0) #if USE_FIXEDPOINT // the 1/d value can be small, which introduces significant // inaccuracies in fixed point mode d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d; dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d; #else d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); #endif wdx = (SplashCoord)0.5 * w * dx; wdy = (SplashCoord)0.5 * w * dy; // draw the start cap pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); if (i0 == subpathStart0) { firstPt = pathOut->length - 1; } if (first && !closed) { switch (state->lineCap) { case splashLineCapButt: pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapRound: pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, pathIn->pts[i0].y + wdx - bezierCircle * wdy, pathIn->pts[i0].x - wdx - bezierCircle * wdy, pathIn->pts[i0].y - wdy + bezierCircle * wdx, pathIn->pts[i0].x - wdx, pathIn->pts[i0].y - wdy); pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, pathIn->pts[i0].y - wdy - bezierCircle * wdx, pathIn->pts[i0].x + wdy - bezierCircle * wdx, pathIn->pts[i0].y - wdx - bezierCircle * wdy, pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapProjecting: pathOut->lineTo(pathIn->pts[i0].x - wdx - wdy, pathIn->pts[i0].y + wdx - wdy); pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, pathIn->pts[i0].y - wdx - wdy); pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; } } else { pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); } // draw the left side of the segment rectangle left2 = pathOut->length - 1; pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // draw the end cap if (last && !closed) { switch (state->lineCap) { case splashLineCapButt: pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapRound: pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, pathIn->pts[j0].y - wdx + bezierCircle * wdy, pathIn->pts[j0].x + wdx + bezierCircle * wdy, pathIn->pts[j0].y + wdy - bezierCircle * wdx, pathIn->pts[j0].x + wdx, pathIn->pts[j0].y + wdy); pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, pathIn->pts[j0].y + wdy + bezierCircle * wdx, pathIn->pts[j0].x - wdy + bezierCircle * wdx, pathIn->pts[j0].y + wdx + bezierCircle * wdy, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapProjecting: pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, pathIn->pts[j0].y - wdx + wdy); pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, pathIn->pts[j0].y + wdx + wdy); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; } } else { pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // draw the right side of the segment rectangle // (NB: if stroke adjustment is enabled, the closepath operation MUST // add a segment because this segment is used for a hint) right2 = pathOut->length - 1; pathOut->close(state->strokeAdjust); // draw the join join2 = pathOut->length; if (!last || closed) { // compute the deltas for segment (j1, k0) #if USE_FIXEDPOINT // the 1/d value can be small, which introduces significant // inaccuracies in fixed point mode d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d; dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d; #else d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); #endif wdxNext = (SplashCoord)0.5 * w * dxNext; wdyNext = (SplashCoord)0.5 * w * dyNext; // compute the join parameters crossprod = dx * dyNext - dy * dxNext; dotprod = -(dx * dxNext + dy * dyNext); if (dotprod > 0.9999) { // avoid a divide-by-zero -- set miter to something arbitrary // such that sqrt(miter) will exceed miterLimit (and m is never // used in that situation) // (note: the comparison value (0.9999) has to be less than // 1-epsilon, where epsilon is the smallest value // representable in the fixed point format) miter = (state->miterLimit + 1) * (state->miterLimit + 1); m = 0; } else { miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); if (miter < 1) { // this can happen because of floating point inaccuracies miter = 1; } m = splashSqrt(miter - 1); } // round join if (state->lineJoin == splashLineJoinRound) { pathOut->moveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w, pathIn->pts[j0].y); pathOut->curveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w, pathIn->pts[j0].y + bezierCircle2 * w, pathIn->pts[j0].x + bezierCircle2 * w, pathIn->pts[j0].y + (SplashCoord)0.5 * w, pathIn->pts[j0].x, pathIn->pts[j0].y + (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[j0].x - bezierCircle2 * w, pathIn->pts[j0].y + (SplashCoord)0.5 * w, pathIn->pts[j0].x - (SplashCoord)0.5 * w, pathIn->pts[j0].y + bezierCircle2 * w, pathIn->pts[j0].x - (SplashCoord)0.5 * w, pathIn->pts[j0].y); pathOut->curveTo(pathIn->pts[j0].x - (SplashCoord)0.5 * w, pathIn->pts[j0].y - bezierCircle2 * w, pathIn->pts[j0].x - bezierCircle2 * w, pathIn->pts[j0].y - (SplashCoord)0.5 * w, pathIn->pts[j0].x, pathIn->pts[j0].y - (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[j0].x + bezierCircle2 * w, pathIn->pts[j0].y - (SplashCoord)0.5 * w, pathIn->pts[j0].x + (SplashCoord)0.5 * w, pathIn->pts[j0].y - bezierCircle2 * w, pathIn->pts[j0].x + (SplashCoord)0.5 * w, pathIn->pts[j0].y); } else { pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); // angle < 180 if (crossprod < 0) { pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); // miter join inside limit if (state->lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, pathIn->pts[j0].y + wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // angle >= 180 } else { pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // miter join inside limit if (state->lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, pathIn->pts[j0].y - wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } pathOut->close(); } // add stroke adjustment hints if (state->strokeAdjust) { if (seg == 0 && !closed) { if (state->lineCap == splashLineCapButt) { pathOut->addStrokeAdjustHint(firstPt, left2 + 1, firstPt, firstPt + 1); if (last) { pathOut->addStrokeAdjustHint(firstPt, left2 + 1, left2 + 1, left2 + 2); } } else if (state->lineCap == splashLineCapProjecting) { if (last) { pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, firstPt + 1, firstPt + 2); pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, left2 + 2, left2 + 3); } else { pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1, firstPt + 1, firstPt + 2); } } } if (seg >= 1) { if (seg >= 2) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, left2); } else { pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); } pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); } left0 = left1; left1 = left2; right0 = right1; right1 = right2; join0 = join1; join1 = join2; if (seg == 0) { leftFirst = left2; rightFirst = right2; } if (last) { if (seg >= 2) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, pathOut->length - 1); } else { pathOut->addStrokeAdjustHint(left1, right1, firstPt, pathOut->length - 1); } if (closed) { pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst); pathOut->addStrokeAdjustHint(left1, right1, rightFirst + 1, rightFirst + 1); pathOut->addStrokeAdjustHint(leftFirst, rightFirst, left1 + 1, right1); pathOut->addStrokeAdjustHint(leftFirst, rightFirst, join1, pathOut->length - 1); } if (!closed && seg > 0) { if (state->lineCap == splashLineCapButt) { pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1, left1 + 1, left1 + 2); } else if (state->lineCap == splashLineCapProjecting) { pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2, left1 + 2, left1 + 3); } } } } i0 = j0; i1 = j1; ++seg; } if (pathIn != path) { delete pathIn; } return pathOut; } void Splash::dumpPath(SplashPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", i, (double)path->pts[i].x, (double)path->pts[i].y, (path->flags[i] & splashPathFirst) ? " first" : "", (path->flags[i] & splashPathLast) ? " last" : "", (path->flags[i] & splashPathClosed) ? " closed" : "", (path->flags[i] & splashPathCurve) ? " curve" : ""); } } void Splash::dumpXPath(SplashXPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, path->segs[i].count); } } xpdf-3.04/splash/SplashFontEngine.cc0000644000076400007640000002112112341430012016706 0ustar dereknderekn//======================================================================== // // SplashFontEngine.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #ifndef _WIN32 # include #endif #include "gmem.h" #include "GString.h" #include "SplashMath.h" #include "SplashFTFontEngine.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" #include "SplashFont.h" #include "SplashFontEngine.h" #ifdef VMS #if (__VMS_VER < 70000000) extern "C" int unlink(char *filename); #endif #endif //------------------------------------------------------------------------ // SplashFontEngine //------------------------------------------------------------------------ SplashFontEngine::SplashFontEngine( #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H GBool enableFreeType, Guint freeTypeFlags, #endif GBool aa) { int i; for (i = 0; i < splashFontCacheSize; ++i) { fontCache[i] = NULL; } #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (enableFreeType) { ftEngine = SplashFTFontEngine::init(aa, freeTypeFlags); } else { ftEngine = NULL; } #endif } SplashFontEngine::~SplashFontEngine() { int i; for (i = 0; i < splashFontCacheSize; ++i) { if (fontCache[i]) { delete fontCache[i]; } } #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (ftEngine) { delete ftEngine; } #endif } SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) { SplashFontFile *fontFile; int i; for (i = 0; i < splashFontCacheSize; ++i) { if (fontCache[i]) { fontFile = fontCache[i]->getFontFile(); if (fontFile && fontFile->getID()->matches(id)) { return fontFile; } } } return NULL; } SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { fontFile = ftEngine->loadType1Font(idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif enc); } #endif #if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (deleteFile) { unlink(fontFile ? fontFile->fileName->getCString() : fileName); } #endif return fontFile; } SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { fontFile = ftEngine->loadType1CFont(idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif enc); } #endif #if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (deleteFile) { unlink(fontFile ? fontFile->fileName->getCString() : fileName); } #endif return fontFile; } SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { fontFile = ftEngine->loadOpenTypeT1CFont(idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif enc); } #endif #if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (deleteFile) { unlink(fontFile ? fontFile->fileName->getCString() : fileName); } #endif return fontFile; } SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf #else char *fileName, GBool deleteFile #endif ) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { fontFile = ftEngine->loadCIDFont(idA, #if LOAD_FONTS_FROM_MEM fontBuf #else fileName, deleteFile #endif ); } #endif #if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (deleteFile) { unlink(fontFile ? fontFile->fileName->getCString() : fileName); } #endif return fontFile; } SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int *codeToGID, int codeToGIDLen) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { fontFile = ftEngine->loadOpenTypeCFFFont(idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif codeToGID, codeToGIDLen); } #endif #if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (deleteFile) { unlink(fontFile ? fontFile->fileName->getCString() : fileName); } #endif return fontFile; } SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int fontNum, int *codeToGID, int codeToGIDLen, char *fontName) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { fontFile = ftEngine->loadTrueTypeFont(idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif fontNum, codeToGID, codeToGIDLen); } #endif if (!fontFile) { gfree(codeToGID); } #if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (deleteFile) { unlink(fontFile ? fontFile->fileName->getCString() : fileName); } #endif return fontFile; } SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, SplashCoord *textMat, SplashCoord *ctm) { SplashCoord mat[4]; SplashFont *font; int i, j; mat[0] = textMat[0] * ctm[0] + textMat[1] * ctm[2]; mat[1] = -(textMat[0] * ctm[1] + textMat[1] * ctm[3]); mat[2] = textMat[2] * ctm[0] + textMat[3] * ctm[2]; mat[3] = -(textMat[2] * ctm[1] + textMat[3] * ctm[3]); if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.01)) { // avoid a singular (or close-to-singular) matrix mat[0] = 0.01; mat[1] = 0; mat[2] = 0; mat[3] = 0.01; } font = fontCache[0]; if (font && font->matches(fontFile, mat, textMat)) { return font; } for (i = 1; i < splashFontCacheSize; ++i) { font = fontCache[i]; if (font && font->matches(fontFile, mat, textMat)) { for (j = i; j > 0; --j) { fontCache[j] = fontCache[j-1]; } fontCache[0] = font; return font; } } font = fontFile->makeFont(mat, textMat); if (fontCache[splashFontCacheSize - 1]) { delete fontCache[splashFontCacheSize - 1]; } for (j = splashFontCacheSize - 1; j > 0; --j) { fontCache[j] = fontCache[j-1]; } fontCache[0] = font; return font; } xpdf-3.04/splash/Splash.h0000644000076400007640000003676512341430012014617 0ustar dereknderekn//======================================================================== // // Splash.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASH_H #define SPLASH_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" #include "SplashClip.h" class Splash; class SplashBitmap; struct SplashGlyphBitmap; class SplashState; class SplashPattern; class SplashScreen; class SplashPath; class SplashXPath; class SplashFont; struct SplashPipe; //------------------------------------------------------------------------ // Retrieves the next line of pixels in an image mask. Normally, // fills in * and returns true. If the image stream is // exhausted, returns false. typedef GBool (*SplashImageMaskSource)(void *data, SplashColorPtr pixel); // Retrieves the next line of pixels in an image. Normally, fills in // * and returns true. If the image stream is exhausted, // returns false. typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine, Guchar *alphaLine); //------------------------------------------------------------------------ enum SplashPipeResultColorCtrl { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, #if SPLASH_CMYK splashPipeResultColorNoAlphaBlendCMYK, #endif splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, #if SPLASH_CMYK splashPipeResultColorAlphaNoBlendCMYK, #endif splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaBlendCMYK #endif }; //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ class Splash { public: // Create a new rasterizer object. Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreenParams *screenParams = NULL); Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreen *screenA); ~Splash(); //----- state read SplashCoord *getMatrix(); SplashPattern *getStrokePattern(); SplashPattern *getFillPattern(); SplashScreen *getScreen(); SplashBlendFunc getBlendFunc(); SplashCoord getStrokeAlpha(); SplashCoord getFillAlpha(); SplashCoord getLineWidth(); int getLineCap(); int getLineJoin(); SplashCoord getMiterLimit(); SplashCoord getFlatness(); SplashCoord *getLineDash(); int getLineDashLength(); SplashCoord getLineDashPhase(); GBool getStrokeAdjust(); SplashClip *getClip(); SplashBitmap *getSoftMask(); GBool getInNonIsolatedGroup(); GBool getInKnockoutGroup(); //----- state write void setMatrix(SplashCoord *matrix); void setStrokePattern(SplashPattern *strokeColor); void setFillPattern(SplashPattern *fillColor); void setScreen(SplashScreen *screen); void setBlendFunc(SplashBlendFunc func); void setStrokeAlpha(SplashCoord alpha); void setFillAlpha(SplashCoord alpha); void setLineWidth(SplashCoord lineWidth); void setLineCap(int lineCap); void setLineJoin(int lineJoin); void setMiterLimit(SplashCoord miterLimit); void setFlatness(SplashCoord flatness); // the array will be copied void setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase); void setStrokeAdjust(GBool strokeAdjust); // NB: uses transformed coordinates. void clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses transformed coordinates. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses untransformed coordinates. SplashError clipToPath(SplashPath *path, GBool eo); void setSoftMask(SplashBitmap *softMask); void setInTransparencyGroup(SplashBitmap *groupBackBitmapA, int groupBackXA, int groupBackYA, GBool nonIsolated, GBool knockout); void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); void setOverprintMask(Guint overprintMask); //----- state save/restore void saveState(); SplashError restoreState(); //----- drawing operations // Fill the bitmap with . This is not subject to clipping. void clear(SplashColorPtr color, Guchar alpha = 0x00); // Stroke a path using the current stroke pattern. SplashError stroke(SplashPath *path); // Fill a path using the current fill pattern. SplashError fill(SplashPath *path, GBool eo); // Fill a path, XORing with the current fill pattern. SplashError xorFill(SplashPath *path, GBool eo); // Draw a character, using the current fill pattern. SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font); // Draw a glyph, using the current fill pattern. This function does // not free any data, i.e., it ignores glyph->freeData. SplashError fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph); // Draws an image mask using the fill color. This will read // lines of pixels from , starting with the top line. "1" // pixels will be drawn with the current fill color; "0" pixels are // transparent. The matrix: // [ mat[0] mat[1] 0 ] // [ mat[2] mat[3] 0 ] // [ mat[4] mat[5] 1 ] // maps a unit square to the desired destination for the image, in // PostScript style: // [x' y' 1] = [x y 1] * mat // Note that the Splash y axis points downward, and the image source // is assumed to produce pixels in raster order, starting from the // top line. SplashError fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, GBool glyphMode, GBool interpolate); // Draw an image. This will read lines of pixels from // , starting with the top line. These pixels are assumed to // be in the source mode, . If is true, the // alpha values returned by are used; otherwise they are // ignored. The following combinations of source and target modes // are supported: // source target // ------ ------ // Mono8 Mono1 -- with dithering // Mono8 Mono8 // RGB8 RGB8 // BGR8 RGB8 // CMYK8 CMYK8 // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat, GBool interpolate); // Composite a rectangular region from onto this Splash // object. SplashError composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated); // Composite this Splash object onto a background color. The // background alpha is assumed to be 1. void compositeBackground(SplashColorPtr color); // Copy a rectangular region from onto the bitmap belonging to // this Splash object. The destination alpha values are all set to // zero. SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h); //----- misc // Construct a path for a stroke, given the path to be stroked and // the line width . All other stroke parameters are taken from // the current state. If is true, this function will // first flatten the path and handle the linedash. SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, GBool flatten = gTrue); // Return the associated bitmap. SplashBitmap *getBitmap() { return bitmap; } // Set the minimum line width. void setMinLineWidth(SplashCoord w) { minLineWidth = w; } // Get a bounding box which includes all modifications since the // last call to clearModRegion. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) { *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; } // Clear the modified region bounding box. void clearModRegion(); // Get clipping status for the last drawing operation subject to // clipping. SplashClipResult getClipRes() { return opClipRes; } // Toggle debug mode on or off. void setDebugMode(GBool debugModeA) { debugMode = debugModeA; } #if 1 //~tmp: turn off anti-aliasing temporarily void setInShading(GBool sh) { inShading = sh; } #endif private: void pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup); void pipeRun(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void updateModX(int x); void updateModY(int y); void strokeNarrow(SplashPath *path); void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); void strokeWide(SplashPath *path, SplashCoord w); SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness); void flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath); SplashPath *makeDashedPath(SplashPath *xPath); SplashError fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha); SplashPath *tweakFillPath(SplashPath *path); GBool pathAllOutside(SplashPath *path); SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); void getImageBounds(SplashCoord xyMin, SplashCoord xyMax, int *xyMinI, int *xyMaxI); void upscaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate); void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate); SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate); void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYdXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes); void upscaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate); void arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate); SplashBitmap *scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate); void scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYdXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXuI(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void vertFlipImage(SplashBitmap *img, int width, int height, int nComps); void horizFlipImage(SplashBitmap *img, int width, int height, int nComps); void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, SplashClipResult clipRes); void blitImageClipped(SplashBitmap *src, GBool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; static int pipeNonIsoGroupCorrection[]; SplashBitmap *bitmap; int bitmapComps; SplashState *state; Guchar *scanBuf; SplashBitmap // for transparency groups, this is the bitmap *groupBackBitmap; // containing the alpha0/color0 values int groupBackX, groupBackY; // offset within groupBackBitmap Guchar aaGamma[256]; SplashCoord minLineWidth; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; GBool vectorAntialias; GBool inShading; GBool debugMode; }; #endif xpdf-3.04/splash/SplashFontFile.h0000644000076400007640000000264112341430012016230 0ustar dereknderekn//======================================================================== // // SplashFontFile.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFONTFILE_H #define SPLASHFONTFILE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "SplashTypes.h" class GString; class SplashFontEngine; class SplashFont; class SplashFontFileID; //------------------------------------------------------------------------ // SplashFontFile //------------------------------------------------------------------------ class SplashFontFile { public: virtual ~SplashFontFile(); // Create a new SplashFont, i.e., a scaled instance of this font // file. virtual SplashFont *makeFont(SplashCoord *mat, SplashCoord *textMat) = 0; // Get the font file ID. SplashFontFileID *getID() { return id; } // Increment the reference count. void incRefCnt(); // Decrement the reference count. If the new value is zero, delete // the SplashFontFile object. void decRefCnt(); protected: SplashFontFile(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA #else char *fileNameA, GBool deleteFileA #endif ); SplashFontFileID *id; #if LOAD_FONTS_FROM_MEM GString *fontBuf; #else GString *fileName; GBool deleteFile; #endif int refCnt; friend class SplashFontEngine; }; #endif xpdf-3.04/splash/SplashXPathScanner.h0000644000076400007640000000405012341430012017054 0ustar dereknderekn//======================================================================== // // SplashXPathScanner.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHXPATHSCANNER_H #define SPLASHXPATHSCANNER_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" class GList; class SplashXPath; //------------------------------------------------------------------------ // SplashXPathScanner //------------------------------------------------------------------------ class SplashXPathScanner { public: // Create a new SplashXPathScanner object. must be sorted. SplashXPathScanner(SplashXPath *xPathA, GBool eoA, int yMinA, int yMaxA); ~SplashXPathScanner(); // Compute shape values for a scan line. Fills in line[] with shape // values for one scan line: ([x0, x1], y). The values are in [0, // 255]. void getSpan(Guchar *line, int y, int x0, int x1); // Like getSpan(), but uses the values 0 and 255 only. Writes 255 // for all pixels which include non-zero area inside the path. void getSpanBinary(Guchar *line, int y, int x0, int x1); private: inline void addArea(Guchar *line, int x, SplashCoord a); void drawTrapezoid(Guchar *line, int xMin, int xMax, SplashCoord y0, SplashCoord y1, SplashCoord xa0, SplashCoord xa1, SplashCoord dydxa, SplashCoord xb0, SplashCoord xb1, SplashCoord dydxb); SplashCoord areaLeft(int xp, SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord dydx); SplashCoord areaRight(int xp, SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord dydx); void drawRectangle(Guchar *line, int xMin, int xMax, SplashCoord y0, SplashCoord y1, SplashCoord x0, SplashCoord x1); void sortActiveSegs(); void insertActiveSeg(SplashXPathSeg *seg); SplashXPath *xPath; GBool eo; int yMin, yMax; GList *activeSegs; // [SplashXPathSeg] int nextSeg; int yNext; }; #endif xpdf-3.04/splash/SplashClip.cc0000644000076400007640000002720312341430012015550 0ustar dereknderekn//======================================================================== // // SplashClip.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "SplashErrorCodes.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashClip.h" //------------------------------------------------------------------------ // Compute x * y / 255, where x and y are in [0, 255]. static inline Guchar mul255(Guchar x, Guchar y) { int z; z = (int)x * (int)y; return (Guchar)((z + (z >> 8) + 0x80) >> 8); } //------------------------------------------------------------------------ // SplashClip //------------------------------------------------------------------------ SplashClip::SplashClip(int hardXMinA, int hardYMinA, int hardXMaxA, int hardYMaxA) { int w; hardXMin = hardXMinA; hardYMin = hardYMinA; hardXMax = hardXMaxA; hardYMax = hardYMaxA; xMin = hardXMin; yMin = hardYMin; xMax = hardXMax; yMax = hardYMax; intBoundsValid = gFalse; paths = NULL; eo = NULL; scanners = NULL; length = size = 0; if ((w = hardXMax + 1) <= 0) { w = 1; } buf = (Guchar *)gmalloc(w); } SplashClip::SplashClip(SplashClip *clip) { int w, i; hardXMin = clip->hardXMin; hardYMin = clip->hardYMin; hardXMax = clip->hardXMax; hardYMax = clip->hardYMax; xMin = clip->xMin; yMin = clip->yMin; xMax = clip->xMax; yMax = clip->yMax; xMinI = clip->xMinI; yMinI = clip->yMinI; xMaxI = clip->xMaxI; yMaxI = clip->yMaxI; intBoundsValid = clip->intBoundsValid; intBoundsStrokeAdjust = clip->intBoundsStrokeAdjust; length = clip->length; size = clip->size; paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *)); eo = (Guchar *)gmallocn(size, sizeof(Guchar)); scanners = (SplashXPathScanner **) gmallocn(size, sizeof(SplashXPathScanner *)); for (i = 0; i < length; ++i) { paths[i] = clip->paths[i]->copy(); eo[i] = clip->eo[i]; scanners[i] = new SplashXPathScanner(paths[i], eo[i], yMinI, yMaxI); } if ((w = splashCeil(xMax)) <= 0) { w = 1; } buf = (Guchar *)gmalloc(w); } SplashClip::~SplashClip() { int i; for (i = 0; i < length; ++i) { delete paths[i]; delete scanners[i]; } gfree(paths); gfree(eo); gfree(scanners); gfree(buf); } void SplashClip::grow(int nPaths) { if (length + nPaths > size) { if (size == 0) { size = 32; } while (size < length + nPaths) { size *= 2; } paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *)); eo = (Guchar *)greallocn(eo, size, sizeof(Guchar)); scanners = (SplashXPathScanner **) greallocn(scanners, size, sizeof(SplashXPathScanner *)); } } void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { int w, i; for (i = 0; i < length; ++i) { delete paths[i]; delete scanners[i]; } gfree(paths); gfree(eo); gfree(scanners); gfree(buf); paths = NULL; eo = NULL; scanners = NULL; length = size = 0; if (x0 < x1) { xMin = x0; xMax = x1; } else { xMin = x1; xMax = x0; } if (y0 < y1) { yMin = y0; yMax = y1; } else { yMin = y1; yMax = y0; } intBoundsValid = gFalse; if ((w = splashCeil(xMax)) <= 0) { w = 1; } buf = (Guchar *)gmalloc(w); } SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { if (x0 < x1) { if (x0 > xMin) { xMin = x0; intBoundsValid = gFalse; } if (x1 < xMax) { xMax = x1; intBoundsValid = gFalse; } } else { if (x1 > xMin) { xMin = x1; intBoundsValid = gFalse; } if (x0 < xMax) { xMax = x0; intBoundsValid = gFalse; } } if (y0 < y1) { if (y0 > yMin) { yMin = y0; intBoundsValid = gFalse; } if (y1 < yMax) { yMax = y1; intBoundsValid = gFalse; } } else { if (y1 > yMin) { yMin = y1; intBoundsValid = gFalse; } if (y0 < yMax) { yMax = y0; intBoundsValid = gFalse; } } return splashOk; } SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, GBool eoA) { SplashXPath *xPath; SplashCoord t; xPath = new SplashXPath(path, matrix, flatness, gTrue); // check for an empty path if (xPath->length == 0) { xMin = yMin = 1; xMax = yMax = 0; intBoundsValid = gFalse; delete xPath; return splashOk; } // check for a rectangle if (xPath->length == 4 && xPath->segs[0].y0 == xPath->segs[0].y1 && xPath->segs[1].x0 == xPath->segs[1].x1 && xPath->segs[2].x0 == xPath->segs[2].x1 && xPath->segs[3].y0 == xPath->segs[3].y1) { clipToRect(xPath->segs[1].x0, xPath->segs[0].y0, xPath->segs[2].x0, xPath->segs[3].y0); delete xPath; return splashOk; } if (xPath->length == 4 && xPath->segs[0].x0 == xPath->segs[0].x1 && xPath->segs[1].y0 == xPath->segs[1].y1 && xPath->segs[2].x0 == xPath->segs[2].x1 && xPath->segs[3].y0 == xPath->segs[3].y1) { clipToRect(xPath->segs[0].x0, xPath->segs[1].y0, xPath->segs[2].x0, xPath->segs[3].y0); delete xPath; return splashOk; } if (xPath->length == 4 && xPath->segs[0].x0 == xPath->segs[0].x1 && xPath->segs[1].x0 == xPath->segs[1].x1 && xPath->segs[2].y0 == xPath->segs[2].y1 && xPath->segs[3].y0 == xPath->segs[3].y1) { clipToRect(xPath->segs[0].x0, xPath->segs[2].y0, xPath->segs[1].x0, xPath->segs[3].y0); delete xPath; return splashOk; } grow(1); paths[length] = xPath; eo[length] = (Guchar)eoA; if ((t = xPath->getXMin()) > xMin) { xMin = t; } if ((t = xPath->getYMin()) > yMin) { yMin = t; } if ((t = xPath->getXMax() + 1) < xMax) { xMax = t; } if ((t = xPath->getYMax() + 1) < yMax) { yMax = t; } intBoundsValid = gFalse; scanners[length] = new SplashXPathScanner(xPath, eoA, splashFloor(yMin), splashCeil(yMax) - 1); ++length; return splashOk; } SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax, GBool strokeAdjust) { // In general, this function tests the rectangle: // x = [rectXMin, rectXMax + 1) (note: coords are ints) // y = [rectYMin, rectYMax + 1) // against the clipping region: // x = [xMin, xMax) (note: coords are fp) // y = [yMin, yMax) if (strokeAdjust && length == 0) { // special case for stroke adjustment with a simple clipping // rectangle -- the clipping region is: // x = [xMinI, xMaxI + 1) // y = [yMinI, yMaxI + 1) updateIntBounds(strokeAdjust); if (xMinI > xMaxI || yMinI > yMaxI) { return splashClipAllOutside; } if (rectXMax + 1 <= xMinI || rectXMin >= xMaxI + 1 || rectYMax + 1 <= yMinI || rectYMin >= yMaxI + 1) { return splashClipAllOutside; } if (rectXMin >= xMinI && rectXMax <= xMaxI && rectYMin >= yMinI && rectYMax <= yMaxI) { return splashClipAllInside; } } else { if (xMin >= xMax || yMin >= yMax) { return splashClipAllOutside; } if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin >= xMax || (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin >= yMax) { return splashClipAllOutside; } if (length == 0 && (SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax) { return splashClipAllInside; } } return splashClipPartial; } void SplashClip::clipSpan(Guchar *line, int y, int x0, int x1, GBool strokeAdjust) { SplashCoord d; int x0a, x1a, x, i; updateIntBounds(strokeAdjust); //--- clip to the integer rectangle if (y < yMinI || y > yMaxI || x1 < xMinI || x0 > xMaxI) { memset(line + x0, 0, x1 - x0 + 1); return; } if (x0 > xMinI) { x0a = x0; } else { x0a = xMinI; memset(line + x0, 0, x0a - x0); } if (x1 < xMaxI) { x1a = x1; } else { x1a = xMaxI; memset(line + x1a + 1, 0, x1 - x1a); } if (x0a > x1a) { return; } //--- clip to the floating point rectangle // (if stroke adjustment is disabled) if (!strokeAdjust) { // clip left edge (xMin) if (x0a == xMinI) { d = (SplashCoord)(xMinI + 1) - xMin; line[x0a] = (Guchar)(int)((SplashCoord)line[x0a] * d); } // clip right edge (xMax) if (x1a == xMaxI) { d = xMax - (SplashCoord)xMaxI; line[x1a] = (Guchar)(int)((SplashCoord)line[x1a] * d); } // clip top edge (yMin) if (y == yMinI) { d = (SplashCoord)(yMinI + 1) - yMin; for (x = x0a; x <= x1a; ++x) { line[x] = (Guchar)(int)((SplashCoord)line[x] * d); } } // clip bottom edge (yMax) if (y == yMaxI) { d = yMax - (SplashCoord)yMaxI; for (x = x0a; x <= x1a; ++x) { line[x] = (Guchar)(int)((SplashCoord)line[x] * d); } } } if (length == 0) { return; } //--- clip to the paths for (i = 0; i < length; ++i) { scanners[i]->getSpan(buf, y, x0a, x1a); for (x = x0a; x <= x1a; ++x) { line[x] = mul255(line[x], buf[x]); } } } GBool SplashClip::clipSpanBinary(Guchar *line, int y, int x0, int x1, GBool strokeAdjust) { int x0a, x1a, x0b, x1b, x, i; Guchar any; updateIntBounds(strokeAdjust); if (y < yMinI || y > yMaxI || x1 < xMinI || x0 > xMaxI) { if (x0 <= x1) { memset(line + x0, 0, x1 - x0 + 1); } return gFalse; } if (x0 > xMinI) { x0a = x0; } else { x0a = xMinI; memset(line + x0, 0, x0a - x0); } if (x1 < xMaxI) { x1a = x1; } else { x1a = xMaxI; memset(line + x1a + 1, 0, x1 - x1a); } if (x0a > x1a) { return gFalse; } if (length == 0) { for (x = x0a; x <= x1a; ++x) { if (line[x]) { return gTrue; } } return gFalse; } any = 0; for (i = 0; i < length; ++i) { scanners[i]->getSpanBinary(buf, y, x0a, x1a); for (x0b = x0a; x0b <= x1a && !buf[x0b]; ++x0b) ; if (x0a < x0b) { memset(line + x0a, 0, x0b - x0a); } for (x1b = x1a; x1b >= x0b && !buf[x1b]; --x1b) ; if (x1b < x1a) { memset(line + x1b + 1, 0, x1a - x1b); } for (x = x0b; x <= x1b; ++x) { line[x] &= buf[x]; any |= line[x]; } } return any != 0; } int SplashClip::getXMinI(GBool strokeAdjust) { updateIntBounds(strokeAdjust); return xMinI; } int SplashClip::getXMaxI(GBool strokeAdjust) { updateIntBounds(strokeAdjust); return xMaxI; } int SplashClip::getYMinI(GBool strokeAdjust) { updateIntBounds(strokeAdjust); return yMinI; } int SplashClip::getYMaxI(GBool strokeAdjust) { updateIntBounds(strokeAdjust); return yMaxI; } void SplashClip::updateIntBounds(GBool strokeAdjust) { if (intBoundsValid && strokeAdjust == intBoundsStrokeAdjust) { return; } if (strokeAdjust && length == 0) { splashStrokeAdjust(xMin, xMax, &xMinI, &xMaxI); splashStrokeAdjust(yMin, yMax, &yMinI, &yMaxI); } else { xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); xMaxI = splashCeil(xMax); yMaxI = splashCeil(yMax); } if (xMinI < hardXMin) { xMinI = hardXMin; } if (yMinI < hardYMin) { yMinI = hardYMin; } if (xMaxI > hardXMax) { xMaxI = hardXMax; } if (yMaxI > hardYMax) { yMaxI = hardYMax; } // the clipping code uses [xMinI, xMaxI] instead of [xMinI, xMaxI) --xMaxI; --yMaxI; intBoundsValid = gTrue; intBoundsStrokeAdjust = strokeAdjust; } xpdf-3.04/splash/SplashFTFontEngine.cc0000644000076400007640000002064012341430012017145 0ustar dereknderekn//======================================================================== // // SplashFTFontEngine.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #ifndef _WIN32 # include #endif #include "gmem.h" #include "GString.h" #include "gfile.h" #include "FoFiTrueType.h" #include "FoFiType1C.h" #include "SplashFTFontFile.h" #include "SplashFTFontEngine.h" #include FT_MODULE_H #ifdef FT_CFF_DRIVER_H # include FT_CFF_DRIVER_H #endif #ifdef VMS #if (__VMS_VER < 70000000) extern "C" int unlink(char *filename); #endif #endif //------------------------------------------------------------------------ static void fileWrite(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } #if LOAD_FONTS_FROM_MEM static void gstringWrite(void *stream, const char *data, int len) { ((GString *)stream)->append(data, len); } #endif //------------------------------------------------------------------------ // SplashFTFontEngine //------------------------------------------------------------------------ SplashFTFontEngine::SplashFTFontEngine(GBool aaA, Guint flagsA, FT_Library libA) { FT_Int major, minor, patch; aa = aaA; flags = flagsA; lib = libA; // as of FT 2.1.8, CID fonts are indexed by CID instead of GID FT_Library_Version(lib, &major, &minor, &patch); useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); } SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA, Guint flagsA) { FT_Library libA; if (FT_Init_FreeType(&libA)) { return NULL; } return new SplashFTFontEngine(aaA, flagsA, libA); } SplashFTFontEngine::~SplashFTFontEngine() { FT_Done_FreeType(lib); } SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif enc, gTrue); } SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif enc, gFalse); } SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif const char **enc) { FoFiTrueType *ff; #if LOAD_FONTS_FROM_MEM GString *fontBuf2; #else GString *tmpFileName; FILE *tmpFile; #endif SplashFontFile *ret; #if LOAD_FONTS_FROM_MEM if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), 0, gTrue))) { #else if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) { #endif return NULL; } if (ff->isHeadlessCFF()) { #if LOAD_FONTS_FROM_MEM fontBuf2 = new GString(); ff->convertToType1(NULL, enc, gFalse, &gstringWrite, fontBuf2); delete ff; ret = SplashFTFontFile::loadType1Font(this, idA, fontBuf2, enc, gFalse); if (ret) { delete fontBuf; } else { delete fontBuf2; } #else tmpFileName = NULL; if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { delete ff; return NULL; } ff->convertToType1(NULL, enc, gFalse, &fileWrite, tmpFile); delete ff; fclose(tmpFile); ret = SplashFTFontFile::loadType1Font(this, idA, tmpFileName->getCString(), gTrue, enc, gFalse); if (ret) { if (deleteFile) { unlink(fileName); } } else { unlink(tmpFileName->getCString()); } delete tmpFileName; #endif } else { delete ff; ret = SplashFTFontFile::loadType1Font(this, idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif enc, gFalse); } return ret; } SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf #else char *fileName, GBool deleteFile #endif ) { FoFiType1C *ff; int *cidToGIDMap; int nCIDs; SplashFontFile *ret; // check for a CFF font if (useCIDs) { cidToGIDMap = NULL; nCIDs = 0; #if LOAD_FONTS_FROM_MEM } else if ((ff = FoFiType1C::make(fontBuf->getCString(), fontBuf->getLength()))) { #else } else if ((ff = FoFiType1C::load(fileName))) { #endif cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); delete ff; } else { cidToGIDMap = NULL; nCIDs = 0; } ret = SplashFTFontFile::loadCIDFont(this, idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif cidToGIDMap, nCIDs); if (!ret) { gfree(cidToGIDMap); } return ret; } SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int *codeToGID, int codeToGIDLen) { FoFiTrueType *ff; #if LOAD_FONTS_FROM_MEM GString *fontBuf2; #else GString *tmpFileName; FILE *tmpFile; #endif char *cffStart; int cffLength; int *cidToGIDMap; int nCIDs; SplashFontFile *ret; #if LOAD_FONTS_FROM_MEM if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), 0, gTrue))) { #else if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) { #endif return NULL; } cidToGIDMap = NULL; nCIDs = 0; if (ff->isHeadlessCFF()) { if (!ff->getCFFBlock(&cffStart, &cffLength)) { return NULL; } #if LOAD_FONTS_FROM_MEM fontBuf2 = new GString(cffStart, cffLength); if (!useCIDs) { cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); } ret = SplashFTFontFile::loadCIDFont(this, idA, fontBuf2, cidToGIDMap, nCIDs); if (ret) { delete fontBuf; } else { delete fontBuf2; } #else tmpFileName = NULL; if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { delete ff; return NULL; } fwrite(cffStart, 1, cffLength, tmpFile); fclose(tmpFile); if (!useCIDs) { cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); } ret = SplashFTFontFile::loadCIDFont(this, idA, tmpFileName->getCString(), gTrue, cidToGIDMap, nCIDs); if (ret) { if (deleteFile) { unlink(fileName); } } else { unlink(tmpFileName->getCString()); } delete tmpFileName; #endif } else { if (!codeToGID && !useCIDs && ff->isOpenTypeCFF()) { cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); } ret = SplashFTFontFile::loadCIDFont(this, idA, #if LOAD_FONTS_FROM_MEM fontBuf, #else fileName, deleteFile, #endif codeToGID ? codeToGID : cidToGIDMap, codeToGID ? codeToGIDLen : nCIDs); } delete ff; if (!ret) { gfree(cidToGIDMap); } return ret; } SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBuf, #else char *fileName, GBool deleteFile, #endif int fontNum, int *codeToGID, int codeToGIDLen) { FoFiTrueType *ff; #if LOAD_FONTS_FROM_MEM GString *fontBuf2; #else GString *tmpFileName; FILE *tmpFile; #endif SplashFontFile *ret; #if LOAD_FONTS_FROM_MEM if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), fontNum))) { #else if (!(ff = FoFiTrueType::load(fileName, fontNum))) { #endif return NULL; } #if LOAD_FONTS_FROM_MEM fontBuf2 = new GString; ff->writeTTF(&gstringWrite, fontBuf2); #else tmpFileName = NULL; if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { delete ff; return NULL; } ff->writeTTF(&fileWrite, tmpFile); fclose(tmpFile); #endif delete ff; ret = SplashFTFontFile::loadTrueTypeFont(this, idA, #if LOAD_FONTS_FROM_MEM fontBuf2, #else tmpFileName->getCString(), gTrue, #endif 0, codeToGID, codeToGIDLen); #if LOAD_FONTS_FROM_MEM if (ret) { delete fontBuf; } else { delete fontBuf2; } #else if (ret) { if (deleteFile) { unlink(fileName); } } else { unlink(tmpFileName->getCString()); } delete tmpFileName; #endif return ret; } #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H xpdf-3.04/splash/SplashPath.h0000644000076400007640000000704112341430012015415 0ustar dereknderekn//======================================================================== // // SplashPath.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHPATH_H #define SPLASHPATH_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" //------------------------------------------------------------------------ // SplashPathPoint //------------------------------------------------------------------------ struct SplashPathPoint { SplashCoord x, y; }; //------------------------------------------------------------------------ // SplashPath.flags //------------------------------------------------------------------------ // first point on each subpath sets this flag #define splashPathFirst 0x01 // last point on each subpath sets this flag #define splashPathLast 0x02 // if the subpath is closed, its first and last points must be // identical, and must set this flag #define splashPathClosed 0x04 // curve control points set this flag #define splashPathCurve 0x08 //------------------------------------------------------------------------ // SplashPathHint //------------------------------------------------------------------------ struct SplashPathHint { int ctrl0, ctrl1; int firstPt, lastPt; }; //------------------------------------------------------------------------ // SplashPath //------------------------------------------------------------------------ class SplashPath { public: // Create an empty path. SplashPath(); // Copy a path. SplashPath *copy() { return new SplashPath(this); } ~SplashPath(); // Append to . void append(SplashPath *path); // Start a new subpath. SplashError moveTo(SplashCoord x, SplashCoord y); // Add a line segment to the last subpath. SplashError lineTo(SplashCoord x, SplashCoord y); // Add a third-order (cubic) Bezier curve segment to the last // subpath. SplashError curveTo(SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3); // Close the last subpath, adding a line segment if necessary. If // is true, this adds a line segment even if the current // point is equal to the first point in the subpath. SplashError close(GBool force = gFalse); // Add a stroke adjustment hint. The controlling segments are // and (where segments are identified by their first // point), and the points to be adjusted are .. . void addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt); // Add (, ) to every point on this path. void offset(SplashCoord dx, SplashCoord dy); // Get the points on the path. int getLength() { return length; } void getPoint(int i, SplashCoord *x, SplashCoord *y, Guchar *f) { *x = pts[i].x; *y = pts[i].y; *f = flags[i]; } // Get the current point. GBool getCurPt(SplashCoord *x, SplashCoord *y); private: SplashPath(SplashPath *path); void grow(int nPts); GBool noCurrentPoint() { return curSubpath == length; } GBool onePointSubpath() { return curSubpath == length - 1; } GBool openSubpath() { return curSubpath < length - 1; } SplashPathPoint *pts; // array of points Guchar *flags; // array of flags int length, size; // length/size of the pts and flags arrays int curSubpath; // index of first point in last subpath SplashPathHint *hints; // list of hints int hintsLength, hintsSize; friend class SplashXPath; friend class Splash; }; #endif xpdf-3.04/splash/SplashFTFontFile.h0000644000076400007640000000435512341430012016466 0ustar dereknderekn//======================================================================== // // SplashFTFontFile.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFTFONTFILE_H #define SPLASHFTFONTFILE_H #include #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include FT_FREETYPE_H #include "SplashFontFile.h" class SplashFontFileID; class SplashFTFontEngine; //------------------------------------------------------------------------ // SplashFTFontFile //------------------------------------------------------------------------ class SplashFTFontFile: public SplashFontFile { public: static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif const char **encA, GBool useLightHintingA); static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif int *codeToGIDA, int codeToGIDLenA); static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif int fontNum, int *codeToGIDA, int codeToGIDLenA); virtual ~SplashFTFontFile(); // Create a new SplashFTFont, i.e., a scaled instance of this font // file. virtual SplashFont *makeFont(SplashCoord *mat, SplashCoord *textMat); private: SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, #if LOAD_FONTS_FROM_MEM GString *fontBufA, #else char *fileNameA, GBool deleteFileA, #endif FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, GBool trueTypeA, GBool useLightHintingA); SplashFTFontEngine *engine; FT_Face face; int *codeToGID; int codeToGIDLen; GBool trueType; GBool useLightHinting; friend class SplashFTFont; }; #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #endif xpdf-3.04/splash/Makefile.dep0000644000076400007640000000000012341430012015370 0ustar derekndereknxpdf-3.04/splash/SplashXPath.h0000644000076400007640000000637012341430012015551 0ustar dereknderekn//======================================================================== // // SplashXPath.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHXPATH_H #define SPLASHXPATH_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" class SplashPath; struct SplashXPathPoint; struct SplashPathHint; //------------------------------------------------------------------------ #define splashMaxCurveSplits (1 << 10) //------------------------------------------------------------------------ // SplashXPathSeg //------------------------------------------------------------------------ struct SplashXPathSeg { SplashCoord x0, y0; // first endpoint (y0 <= y1) SplashCoord x1, y1; // second endpoint SplashCoord dxdy; // slope: delta-x / delta-y SplashCoord dydx; // slope: delta-y / delta-x int count; // EO/NZWN counter increment //----- used by SplashXPathScanner SplashCoord xCur0, xCur1; // current x values #if HAVE_STD_SORT static bool cmpY(const SplashXPathSeg &seg0, const SplashXPathSeg &seg1) { return seg0.y0 < seg1.y0; } #else static int cmpY(const void *seg0, const void *seg1) { SplashCoord cmp; cmp = ((SplashXPathSeg *)seg0)->y0 - ((SplashXPathSeg *)seg1)->y0; return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0; } #endif static int cmpX(SplashXPathSeg *seg0, SplashXPathSeg *seg1) { SplashCoord cmp; if ((cmp = seg0->xCur0 - seg1->xCur0) == 0) { cmp = seg0->dxdy - seg1->dxdy; } return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0; } static int cmpXi(const void *p0, const void *p1) { return cmpX(*(SplashXPathSeg **)p0, *(SplashXPathSeg **)p1); } }; //------------------------------------------------------------------------ // SplashXPath //------------------------------------------------------------------------ class SplashXPath { public: // Expands (converts to segments) and flattens (converts curves to // lines) . Transforms all points from user space to device // space, via . If is true, closes all open // subpaths. SplashXPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, GBool closeSubpaths); // Copy an expanded path. SplashXPath *copy() { return new SplashXPath(this); } ~SplashXPath(); int getXMin() { return xMin; } int getXMax() { return xMax; } int getYMin() { return yMin; } int getYMax() { return yMax; } private: SplashXPath(SplashXPath *xPath); void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void strokeAdjust(SplashXPathPoint *pts, SplashPathHint *hints, int nHints); void grow(int nSegs); void addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1); void addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); SplashXPathSeg *segs; int length, size; // length and size of segs array int xMin, xMax; int yMin, yMax; friend class SplashXPathScanner; friend class SplashClip; friend class Splash; }; #endif xpdf-3.04/splash/SplashFontFileID.h0000644000076400007640000000125712341430012016447 0ustar dereknderekn//======================================================================== // // SplashFontFileID.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFONTFILEID_H #define SPLASHFONTFILEID_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" //------------------------------------------------------------------------ // SplashFontFileID //------------------------------------------------------------------------ class SplashFontFileID { public: SplashFontFileID(); virtual ~SplashFontFileID(); virtual GBool matches(SplashFontFileID *id) = 0; }; #endif xpdf-3.04/splash/SplashState.cc0000644000076400007640000001541612341430012015744 0ustar dereknderekn//======================================================================== // // SplashState.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmem.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashClip.h" #include "SplashBitmap.h" #include "SplashState.h" //------------------------------------------------------------------------ // SplashState //------------------------------------------------------------------------ // number of components in each color mode int splashColorModeNComps[] = { 1, 1, 3, 3 #if SPLASH_CMYK , 4 #endif }; SplashState::SplashState(int width, int height, GBool vectorAntialias, SplashScreenParams *screenParams) { SplashColor color; int i; matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; matrix[4] = 0; matrix[5] = 0; memset(&color, 0, sizeof(SplashColor)); strokePattern = new SplashSolidColor(color); fillPattern = new SplashSolidColor(color); screen = new SplashScreen(screenParams); blendFunc = NULL; strokeAlpha = 1; fillAlpha = 1; lineWidth = 1; lineCap = splashLineCapButt; lineJoin = splashLineJoinMiter; miterLimit = 10; flatness = 1; lineDash = NULL; lineDashLength = 0; lineDashPhase = 0; strokeAdjust = gFalse; clip = new SplashClip(0, 0, width, height); clipIsShared = gFalse; softMask = NULL; deleteSoftMask = gFalse; inNonIsolatedGroup = gFalse; inKnockoutGroup = gFalse; for (i = 0; i < 256; ++i) { rgbTransferR[i] = (Guchar)i; rgbTransferG[i] = (Guchar)i; rgbTransferB[i] = (Guchar)i; grayTransfer[i] = (Guchar)i; cmykTransferC[i] = (Guchar)i; cmykTransferM[i] = (Guchar)i; cmykTransferY[i] = (Guchar)i; cmykTransferK[i] = (Guchar)i; } overprintMask = 0xffffffff; next = NULL; } SplashState::SplashState(int width, int height, GBool vectorAntialias, SplashScreen *screenA) { SplashColor color; int i; matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; matrix[4] = 0; matrix[5] = 0; memset(&color, 0, sizeof(SplashColor)); strokePattern = new SplashSolidColor(color); fillPattern = new SplashSolidColor(color); screen = screenA->copy(); blendFunc = NULL; strokeAlpha = 1; fillAlpha = 1; lineWidth = 1; lineCap = splashLineCapButt; lineJoin = splashLineJoinMiter; miterLimit = 10; flatness = 1; lineDash = NULL; lineDashLength = 0; lineDashPhase = 0; strokeAdjust = gFalse; clip = new SplashClip(0, 0, width, height); clipIsShared = gFalse; softMask = NULL; deleteSoftMask = gFalse; inNonIsolatedGroup = gFalse; inKnockoutGroup = gFalse; for (i = 0; i < 256; ++i) { rgbTransferR[i] = (Guchar)i; rgbTransferG[i] = (Guchar)i; rgbTransferB[i] = (Guchar)i; grayTransfer[i] = (Guchar)i; cmykTransferC[i] = (Guchar)i; cmykTransferM[i] = (Guchar)i; cmykTransferY[i] = (Guchar)i; cmykTransferK[i] = (Guchar)i; } overprintMask = 0xffffffff; next = NULL; } SplashState::SplashState(SplashState *state) { memcpy(matrix, state->matrix, 6 * sizeof(SplashCoord)); strokePattern = state->strokePattern->copy(); fillPattern = state->fillPattern->copy(); screen = state->screen->copy(); blendFunc = state->blendFunc; strokeAlpha = state->strokeAlpha; fillAlpha = state->fillAlpha; lineWidth = state->lineWidth; lineCap = state->lineCap; lineJoin = state->lineJoin; miterLimit = state->miterLimit; flatness = state->flatness; if (state->lineDash) { lineDashLength = state->lineDashLength; lineDash = (SplashCoord *)gmallocn(lineDashLength, sizeof(SplashCoord)); memcpy(lineDash, state->lineDash, lineDashLength * sizeof(SplashCoord)); } else { lineDash = NULL; lineDashLength = 0; } lineDashPhase = state->lineDashPhase; strokeAdjust = state->strokeAdjust; clip = state->clip; clipIsShared = gTrue; softMask = state->softMask; deleteSoftMask = gFalse; inNonIsolatedGroup = state->inNonIsolatedGroup; inKnockoutGroup = state->inKnockoutGroup; memcpy(rgbTransferR, state->rgbTransferR, 256); memcpy(rgbTransferG, state->rgbTransferG, 256); memcpy(rgbTransferB, state->rgbTransferB, 256); memcpy(grayTransfer, state->grayTransfer, 256); memcpy(cmykTransferC, state->cmykTransferC, 256); memcpy(cmykTransferM, state->cmykTransferM, 256); memcpy(cmykTransferY, state->cmykTransferY, 256); memcpy(cmykTransferK, state->cmykTransferK, 256); overprintMask = state->overprintMask; next = NULL; } SplashState::~SplashState() { delete strokePattern; delete fillPattern; delete screen; gfree(lineDash); if (!clipIsShared) { delete clip; } if (deleteSoftMask && softMask) { delete softMask; } } void SplashState::setStrokePattern(SplashPattern *strokePatternA) { delete strokePattern; strokePattern = strokePatternA; } void SplashState::setFillPattern(SplashPattern *fillPatternA) { delete fillPattern; fillPattern = fillPatternA; } void SplashState::setScreen(SplashScreen *screenA) { delete screen; screen = screenA; } void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA, SplashCoord lineDashPhaseA) { gfree(lineDash); lineDashLength = lineDashLengthA; if (lineDashLength > 0) { lineDash = (SplashCoord *)gmallocn(lineDashLength, sizeof(SplashCoord)); memcpy(lineDash, lineDashA, lineDashLength * sizeof(SplashCoord)); } else { lineDash = NULL; } lineDashPhase = lineDashPhaseA; } void SplashState::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { if (clipIsShared) { clip = clip->copy(); clipIsShared = gFalse; } clip->resetToRect(x0, y0, x1, y1); } SplashError SplashState::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { if (clipIsShared) { clip = clip->copy(); clipIsShared = gFalse; } return clip->clipToRect(x0, y0, x1, y1); } SplashError SplashState::clipToPath(SplashPath *path, GBool eo) { if (clipIsShared) { clip = clip->copy(); clipIsShared = gFalse; } return clip->clipToPath(path, matrix, flatness, eo); } void SplashState::setSoftMask(SplashBitmap *softMaskA) { if (deleteSoftMask) { delete softMask; } softMask = softMaskA; deleteSoftMask = gTrue; } void SplashState::setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray) { int i; memcpy(rgbTransferR, red, 256); memcpy(rgbTransferG, green, 256); memcpy(rgbTransferB, blue, 256); memcpy(grayTransfer, gray, 256); for (i = 0; i < 256; ++i) { cmykTransferC[i] = 255 - rgbTransferR[255 - i]; cmykTransferM[i] = 255 - rgbTransferG[255 - i]; cmykTransferY[i] = 255 - rgbTransferB[255 - i]; cmykTransferK[i] = 255 - grayTransfer[255 - i]; } } xpdf-3.04/splash/SplashState.h0000644000076400007640000000652512341430012015607 0ustar dereknderekn//======================================================================== // // SplashState.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHSTATE_H #define SPLASHSTATE_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" class SplashPattern; class SplashScreen; class SplashClip; class SplashBitmap; class SplashPath; //------------------------------------------------------------------------ // line cap values //------------------------------------------------------------------------ #define splashLineCapButt 0 #define splashLineCapRound 1 #define splashLineCapProjecting 2 //------------------------------------------------------------------------ // line join values //------------------------------------------------------------------------ #define splashLineJoinMiter 0 #define splashLineJoinRound 1 #define splashLineJoinBevel 2 //------------------------------------------------------------------------ // SplashState //------------------------------------------------------------------------ class SplashState { public: // Create a new state object, initialized with default settings. SplashState(int width, int height, GBool vectorAntialias, SplashScreenParams *screenParams); SplashState(int width, int height, GBool vectorAntialias, SplashScreen *screenA); // Copy a state object. SplashState *copy() { return new SplashState(this); } ~SplashState(); // Set the stroke pattern. This does not copy . void setStrokePattern(SplashPattern *strokePatternA); // Set the fill pattern. This does not copy . void setFillPattern(SplashPattern *fillPatternA); // Set the screen. This does not copy . void setScreen(SplashScreen *screenA); // Set the line dash pattern. This copies the array. void setLineDash(SplashCoord *lineDashA, int lineDashLengthA, SplashCoord lineDashPhaseA); void clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); SplashError clipToPath(SplashPath *path, GBool eo); // Set the soft mask bitmap. void setSoftMask(SplashBitmap *softMaskA); // Set the transfer function. void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); private: SplashState(SplashState *state); SplashCoord matrix[6]; SplashPattern *strokePattern; SplashPattern *fillPattern; SplashScreen *screen; SplashBlendFunc blendFunc; SplashCoord strokeAlpha; SplashCoord fillAlpha; SplashCoord lineWidth; int lineCap; int lineJoin; SplashCoord miterLimit; SplashCoord flatness; SplashCoord *lineDash; int lineDashLength; SplashCoord lineDashPhase; GBool strokeAdjust; SplashClip *clip; GBool clipIsShared; SplashBitmap *softMask; GBool deleteSoftMask; GBool inNonIsolatedGroup; GBool inKnockoutGroup; Guchar rgbTransferR[256], rgbTransferG[256], rgbTransferB[256]; Guchar grayTransfer[256]; Guchar cmykTransferC[256], cmykTransferM[256], cmykTransferY[256], cmykTransferK[256]; Guint overprintMask; SplashState *next; // used by Splash class friend class Splash; }; #endif xpdf-3.04/splash/SplashXPath.cc0000644000076400007640000002554712341430012015716 0ustar dereknderekn//======================================================================== // // SplashXPath.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #if HAVE_STD_SORT #include #endif #include "gmem.h" #include "SplashMath.h" #include "SplashPath.h" #include "SplashXPath.h" //------------------------------------------------------------------------ struct SplashXPathPoint { SplashCoord x, y; }; struct SplashXPathAdjust { int firstPt, lastPt; // range of points GBool vert; // vertical or horizontal hint SplashCoord x0a, x0b, // hint boundaries xma, xmb, x1a, x1b; SplashCoord x0, x1, xm; // adjusted coordinates }; //------------------------------------------------------------------------ // Transform a point from user space to device space. inline void SplashXPath::transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo) { // [ m[0] m[1] 0 ] // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] // [ m[4] m[5] 1 ] *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; } //------------------------------------------------------------------------ // SplashXPath //------------------------------------------------------------------------ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, GBool closeSubpaths) { SplashXPathPoint *pts; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp; SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP; int curSubpath, i; // transform the points pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint)); for (i = 0; i < path->length; ++i) { transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y); } // do stroke adjustment if (path->hints) { strokeAdjust(pts, path->hints, path->hintsLength); } segs = NULL; length = size = 0; x0 = y0 = xsp = ysp = 0; // make gcc happy curSubpath = 0; i = 0; while (i < path->length) { // first point in subpath - skip it if (path->flags[i] & splashPathFirst) { x0 = pts[i].x; y0 = pts[i].y; xsp = x0; ysp = y0; curSubpath = i; ++i; } else { // curve segment if (path->flags[i] & splashPathCurve) { x1 = pts[i].x; y1 = pts[i].y; x2 = pts[i+1].x; y2 = pts[i+1].y; x3 = pts[i+2].x; y3 = pts[i+2].y; addCurve(x0, y0, x1, y1, x2, y2, x3, y3, flatness, (path->flags[i-1] & splashPathFirst), (path->flags[i+2] & splashPathLast), !closeSubpaths && (path->flags[i-1] & splashPathFirst) && !(path->flags[i-1] & splashPathClosed), !closeSubpaths && (path->flags[i+2] & splashPathLast) && !(path->flags[i+2] & splashPathClosed)); x0 = x3; y0 = y3; i += 3; // line segment } else { x1 = pts[i].x; y1 = pts[i].y; addSegment(x0, y0, x1, y1); x0 = x1; y0 = y1; ++i; } // close a subpath if (closeSubpaths && (path->flags[i-1] & splashPathLast) && (pts[i-1].x != pts[curSubpath].x || pts[i-1].y != pts[curSubpath].y)) { addSegment(x0, y0, xsp, ysp); } } } gfree(pts); #if HAVE_STD_SORT std::sort(segs, segs + length, SplashXPathSeg::cmpY); #else qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY); #endif if (length == 0) { xMin = yMin = xMax = yMax = 0; } else { if (segs[0].x0 < segs[0].x1) { xMinFP = segs[0].x0; xMaxFP = segs[0].x1; } else { xMinFP = segs[0].x1; xMaxFP = segs[0].x0; } yMinFP = segs[0].y0; yMaxFP = segs[0].y1; for (i = 1; i < length; ++i) { if (segs[i].x0 < xMinFP) { xMinFP = segs[i].x0; } else if (segs[i].x0 > xMaxFP) { xMaxFP = segs[i].x0; } if (segs[i].x1 < xMinFP) { xMinFP = segs[i].x1; } else if (segs[i].x1 > xMaxFP) { xMaxFP = segs[i].x1; } if (segs[i].y1 > yMaxFP) { yMaxFP = segs[i].y1; } } xMin = splashFloor(xMinFP); yMin = splashFloor(yMinFP); xMax = splashFloor(xMaxFP); yMax = splashFloor(yMaxFP); } } void SplashXPath::strokeAdjust(SplashXPathPoint *pts, SplashPathHint *hints, int nHints) { SplashXPathAdjust *adjusts, *adjust; SplashPathHint *hint; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3; SplashCoord adj0, adj1, d; int xi0, xi1; int i, j; // set up the stroke adjustment hints adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust)); for (i = 0; i < nHints; ++i) { hint = &hints[i]; x0 = pts[hint->ctrl0 ].x; y0 = pts[hint->ctrl0 ].y; x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y; x2 = pts[hint->ctrl1 ].x; y2 = pts[hint->ctrl1 ].y; x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y; if (x0 == x1 && x2 == x3) { adjusts[i].vert = gTrue; adj0 = x0; adj1 = x2; } else if (y0 == y1 && y2 == y3) { adjusts[i].vert = gFalse; adj0 = y0; adj1 = y2; } else { goto done; } if (adj0 > adj1) { x0 = adj0; adj0 = adj1; adj1 = x0; } d = adj1 - adj0; if (d > 0.04) { d = 0.01; } else { d *= 0.25; } adjusts[i].x0a = adj0 - d; adjusts[i].x0b = adj0 + d; adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d; adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d; adjusts[i].x1a = adj1 - d; adjusts[i].x1b = adj1 + d; splashStrokeAdjust(adj0, adj1, &xi0, &xi1); adjusts[i].x0 = (SplashCoord)xi0; // the "minus epsilon" thing here is needed when vector // antialiasing is turned off -- otherwise stroke adjusted lines // will touch an extra pixel on one edge adjusts[i].x1 = (SplashCoord)xi1 - 0.001; adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); adjusts[i].firstPt = hint->firstPt; adjusts[i].lastPt = hint->lastPt; } // perform stroke adjustment for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) { for (j = adjust->firstPt; j <= adjust->lastPt; ++j) { if (adjust->vert) { x0 = pts[j].x; if (x0 > adjust->x0a && x0 < adjust->x0b) { pts[j].x = adjust->x0; } else if (x0 > adjust->xma && x0 < adjust->xmb) { pts[j].x = adjust->xm; } else if (x0 > adjust->x1a && x0 < adjust->x1b) { pts[j].x = adjust->x1; } } else { y0 = pts[j].y; if (y0 > adjust->x0a && y0 < adjust->x0b) { pts[j].y = adjust->x0; } else if (y0 > adjust->xma && y0 < adjust->xmb) { pts[j].y = adjust->xm; } else if (y0 > adjust->x1a && y0 < adjust->x1b) { pts[j].y = adjust->x1; } } } } done: gfree(adjusts); } SplashXPath::SplashXPath(SplashXPath *xPath) { length = xPath->length; size = xPath->size; segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg)); memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg)); xMin = xPath->xMin; yMin = xPath->yMin; xMax = xPath->xMax; yMax = xPath->yMax; } SplashXPath::~SplashXPath() { gfree(segs); } // Add space for more segments void SplashXPath::grow(int nSegs) { if (length + nSegs > size) { if (size == 0) { size = 32; } while (size < length + nSegs) { size *= 2; } segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg)); } } void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1) { SplashCoord cx[splashMaxCurveSplits + 1][3]; SplashCoord cy[splashMaxCurveSplits + 1][3]; int cNext[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, d1, d2, flatness2; int p1, p2, p3; #if USE_FIXEDPOINT flatness2 = flatness; #else flatness2 = flatness * flatness; #endif // initial segment p1 = 0; p2 = splashMaxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; xx1 = cx[p1][1]; yy1 = cy[p1][1]; xx2 = cx[p1][2]; yy2 = cy[p1][2]; p2 = cNext[p1]; xr3 = cx[p2][0]; yr3 = cy[p2][0]; // compute the distances from the control points to the // midpoint of the straight line (this is a bit of a hack, but // it's much faster than computing the actual distances to the // line) mx = (xl0 + xr3) * 0.5; my = (yl0 + yr3) * 0.5; #if USE_FIXEDPOINT d1 = splashDist(xx1, yy1, mx, my); d2 = splashDist(xx2, yy2, mx, my); #else dx = xx1 - mx; dy = yy1 - my; d1 = dx*dx + dy*dy; dx = xx2 - mx; dy = yy2 - my; d2 = dx*dx + dy*dy; #endif // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { addSegment(xl0, yl0, xr3, yr3); p1 = p2; // otherwise, subdivide the curve } else { xl1 = (xl0 + xx1) * 0.5; yl1 = (yl0 + yy1) * 0.5; xh = (xx1 + xx2) * 0.5; yh = (yy1 + yy2) * 0.5; xl2 = (xl1 + xh) * 0.5; yl2 = (yl1 + yh) * 0.5; xr2 = (xx2 + xr3) * 0.5; yr2 = (yy2 + yr3) * 0.5; xr1 = (xh + xr2) * 0.5; yr1 = (yh + yr2) * 0.5; xr0 = (xl2 + xr1) * 0.5; yr0 = (yl2 + yr1) * 0.5; // add the new subdivision points p3 = (p1 + p2) / 2; cx[p1][1] = xl1; cy[p1][1] = yl1; cx[p1][2] = xl2; cy[p1][2] = yl2; cNext[p1] = p3; cx[p3][0] = xr0; cy[p3][0] = yr0; cx[p3][1] = xr1; cy[p3][1] = yr1; cx[p3][2] = xr2; cy[p3][2] = yr2; cNext[p3] = p2; } } } void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { grow(1); if (y0 <= y1) { segs[length].x0 = x0; segs[length].y0 = y0; segs[length].x1 = x1; segs[length].y1 = y1; segs[length].count = 1; } else { segs[length].x0 = x1; segs[length].y0 = y1; segs[length].x1 = x0; segs[length].y1 = y0; segs[length].count = -1; } #if USE_FIXEDPOINT if (y0 == y1 || x0 == x1 || !FixedPoint::divCheck(x1 - x0, y1 - y0, &segs[length].dxdy) || !FixedPoint::divCheck(y1 - y0, x1 - x0, &segs[length].dydx)) { segs[length].dxdy = 0; segs[length].dydx = 0; } #else if (y0 == y1 || x0 == x1) { segs[length].dxdy = 0; segs[length].dydx = 0; } else { segs[length].dxdy = (x1 - x0) / (y1 - y0); if (segs[length].dxdy == 0) { segs[length].dydx = 0; } else { segs[length].dydx = 1 / segs[length].dxdy; } } #endif ++length; } xpdf-3.04/splash/SplashTypes.h0000644000076400007640000000743212341430012015631 0ustar dereknderekn//======================================================================== // // SplashTypes.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHTYPES_H #define SPLASHTYPES_H #include #include "gtypes.h" //------------------------------------------------------------------------ // coordinates //------------------------------------------------------------------------ #if USE_FIXEDPOINT #include "FixedPoint.h" typedef FixedPoint SplashCoord; #else typedef double SplashCoord; #endif //------------------------------------------------------------------------ // antialiasing //------------------------------------------------------------------------ #define splashAASize 4 //------------------------------------------------------------------------ // colors //------------------------------------------------------------------------ enum SplashColorMode { splashModeMono1, // 1 bit per component, 8 pixels per byte, // MSbit is on the left splashModeMono8, // 1 byte per component, 1 byte per pixel splashModeRGB8, // 1 byte per component, 3 bytes per pixel: // RGBRGB... splashModeBGR8 // 1 byte per component, 3 bytes per pixel: // BGRBGR... #if SPLASH_CMYK , splashModeCMYK8 // 1 byte per component, 4 bytes per pixel: // CMYKCMYK... #endif }; // number of components in each color mode // (defined in SplashState.cc) extern int splashColorModeNComps[]; // max number of components in any SplashColor #define splashMaxColorComps 3 #if SPLASH_CMYK # undef splashMaxColorComps # define splashMaxColorComps 4 #endif typedef Guchar SplashColor[splashMaxColorComps]; typedef Guchar *SplashColorPtr; // RGB8 static inline Guchar splashRGB8R(SplashColorPtr rgb8) { return rgb8[0]; } static inline Guchar splashRGB8G(SplashColorPtr rgb8) { return rgb8[1]; } static inline Guchar splashRGB8B(SplashColorPtr rgb8) { return rgb8[2]; } // BGR8 static inline Guchar splashBGR8R(SplashColorPtr bgr8) { return bgr8[2]; } static inline Guchar splashBGR8G(SplashColorPtr bgr8) { return bgr8[1]; } static inline Guchar splashBGR8B(SplashColorPtr bgr8) { return bgr8[0]; } #if SPLASH_CMYK // CMYK8 static inline Guchar splashCMYK8C(SplashColorPtr cmyk8) { return cmyk8[0]; } static inline Guchar splashCMYK8M(SplashColorPtr cmyk8) { return cmyk8[1]; } static inline Guchar splashCMYK8Y(SplashColorPtr cmyk8) { return cmyk8[2]; } static inline Guchar splashCMYK8K(SplashColorPtr cmyk8) { return cmyk8[3]; } #endif static inline void splashColorCopy(SplashColorPtr dest, SplashColorPtr src) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; #if SPLASH_CMYK dest[3] = src[3]; #endif } static inline void splashColorXor(SplashColorPtr dest, SplashColorPtr src) { dest[0] ^= src[0]; dest[1] ^= src[1]; dest[2] ^= src[2]; #if SPLASH_CMYK dest[3] ^= src[3]; #endif } //------------------------------------------------------------------------ // blend functions //------------------------------------------------------------------------ typedef void (*SplashBlendFunc)(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm); //------------------------------------------------------------------------ // screen parameters //------------------------------------------------------------------------ enum SplashScreenType { splashScreenDispersed, splashScreenClustered, splashScreenStochasticClustered }; struct SplashScreenParams { SplashScreenType type; int size; int dotRadius; SplashCoord gamma; SplashCoord blackThreshold; SplashCoord whiteThreshold; }; //------------------------------------------------------------------------ // error results //------------------------------------------------------------------------ typedef int SplashError; #endif xpdf-3.04/splash/SplashFTFont.h0000644000076400007640000000257112341430012015664 0ustar dereknderekn//======================================================================== // // SplashFTFont.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFTFONT_H #define SPLASHFTFONT_H #include #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include FT_FREETYPE_H #include "SplashFont.h" class SplashFTFontFile; //------------------------------------------------------------------------ // SplashFTFont //------------------------------------------------------------------------ class SplashFTFont: public SplashFont { public: SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, SplashCoord *textMatA); virtual ~SplashFTFont(); // Munge xFrac and yFrac before calling SplashFont::getGlyph. virtual GBool getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap); // Rasterize a glyph. The and values are the same // as described for getGlyph. virtual GBool makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap); // Return the path for a glyph. virtual SplashPath *getGlyphPath(int c); private: FT_Size sizeObj; FT_Matrix matrix; FT_Matrix textMatrix; SplashCoord textScale; }; #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H #endif xpdf-3.04/splash/SplashFont.h0000644000076400007640000000660412341430012015433 0ustar dereknderekn//======================================================================== // // SplashFont.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHFONT_H #define SPLASHFONT_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "SplashTypes.h" struct SplashGlyphBitmap; struct SplashFontCacheTag; class SplashFontFile; class SplashPath; //------------------------------------------------------------------------ // Fractional positioning uses this many bits to the right of the // decimal points. #define splashFontFractionBits 2 #define splashFontFraction (1 << splashFontFractionBits) #define splashFontFractionMul \ ((SplashCoord)1 / (SplashCoord)splashFontFraction) //------------------------------------------------------------------------ // SplashFont //------------------------------------------------------------------------ class SplashFont { public: SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, SplashCoord *textMatA, GBool aaA); // This must be called after the constructor, so that the subclass // constructor has a chance to compute the bbox. void initCache(); virtual ~SplashFont(); SplashFontFile *getFontFile() { return fontFile; } // Return true if matches the specified font file and matrix. GBool matches(SplashFontFile *fontFileA, SplashCoord *matA, SplashCoord *textMatA) { return fontFileA == fontFile && matA[0] == mat[0] && matA[1] == mat[1] && matA[2] == mat[2] && matA[3] == mat[3] && textMatA[0] == textMat[0] && textMatA[1] == textMat[1] && textMatA[2] == textMat[2] && textMatA[3] == textMat[3]; } // Get a glyph - this does a cache lookup first, and if not found, // creates a new bitmap and adds it to the cache. The and // values are splashFontFractionBits bits each, representing // the numerators of fractions in [0, 1), where the denominator is // splashFontFraction = 1 << splashFontFractionBits. Subclasses // should override this to zero out xFrac and/or yFrac if they don't // support fractional coordinates. virtual GBool getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap); // Rasterize a glyph. The and values are the same // as described for getGlyph. virtual GBool makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap) = 0; // Return the path for a glyph. virtual SplashPath *getGlyphPath(int c) = 0; // Return the font transform matrix. SplashCoord *getMatrix() { return mat; } // Return the glyph bounding box. void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; } protected: SplashFontFile *fontFile; SplashCoord mat[4]; // font transform matrix // (text space -> device space) SplashCoord textMat[4]; // text transform matrix // (text space -> user space) GBool aa; // anti-aliasing int xMin, yMin, xMax, yMax; // glyph bounding box Guchar *cache; // glyph bitmap cache SplashFontCacheTag * // cache tags cacheTags; int glyphW, glyphH; // size of glyph bitmaps int glyphSize; // size of glyph bitmaps, in bytes int cacheSets; // number of sets in cache int cacheAssoc; // cache associativity (glyphs per set) }; #endif xpdf-3.04/splash/SplashFontFileID.cc0000644000076400007640000000110712341430012016577 0ustar dereknderekn//======================================================================== // // SplashFontFileID.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "SplashFontFileID.h" //------------------------------------------------------------------------ // SplashFontFileID //------------------------------------------------------------------------ SplashFontFileID::SplashFontFileID() { } SplashFontFileID::~SplashFontFileID() { } xpdf-3.04/splash/SplashScreen.h0000644000076400007640000000331212341430012015735 0ustar dereknderekn//======================================================================== // // SplashScreen.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHSCREEN_H #define SPLASHSCREEN_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" //------------------------------------------------------------------------ // SplashScreen //------------------------------------------------------------------------ class SplashScreen { public: SplashScreen(SplashScreenParams *params); SplashScreen(SplashScreen *screen); ~SplashScreen(); SplashScreen *copy() { return new SplashScreen(this); } // Return the computed pixel value (0=black, 1=white) for the gray // level at (, ). int test(int x, int y, Guchar value) { int xx, yy; xx = x & sizeM1; yy = y & sizeM1; return value < mat[(yy << log2Size) + xx] ? 0 : 1; } // Returns true if value is above the white threshold or below the // black threshold, i.e., if the corresponding halftone will be // solid white or black. GBool isStatic(Guchar value) { return value < minVal || value >= maxVal; } private: void buildDispersedMatrix(int i, int j, int val, int delta, int offset); void buildClusteredMatrix(); int distance(int x0, int y0, int x1, int y1); void buildSCDMatrix(int r); Guchar *mat; // threshold matrix int size; // size of the threshold matrix int sizeM1; // size - 1 int log2Size; // log2(size) Guchar minVal; // any pixel value below minVal generates // solid black Guchar maxVal; // any pixel value above maxVal generates // solid white }; #endif xpdf-3.04/splash/SplashFont.cc0000644000076400007640000001101612341430012015562 0ustar dereknderekn//======================================================================== // // SplashFont.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmem.h" #include "SplashMath.h" #include "SplashGlyphBitmap.h" #include "SplashFontFile.h" #include "SplashFont.h" //------------------------------------------------------------------------ // font cache size parameters #define splashFontCacheAssoc 8 #define splashFontCacheMaxSets 8 #define splashFontCacheSize (128*1024) //------------------------------------------------------------------------ struct SplashFontCacheTag { int c; short xFrac, yFrac; // x and y fractions int mru; // valid bit (0x80000000) and MRU index int x, y, w, h; // offset and size of glyph }; //------------------------------------------------------------------------ // SplashFont //------------------------------------------------------------------------ SplashFont::SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, SplashCoord *textMatA, GBool aaA) { fontFile = fontFileA; fontFile->incRefCnt(); mat[0] = matA[0]; mat[1] = matA[1]; mat[2] = matA[2]; mat[3] = matA[3]; textMat[0] = textMatA[0]; textMat[1] = textMatA[1]; textMat[2] = textMatA[2]; textMat[3] = textMatA[3]; aa = aaA; cache = NULL; cacheTags = NULL; xMin = yMin = xMax = yMax = 0; } void SplashFont::initCache() { int i; // this should be (max - min + 1), but we add some padding to // deal with rounding errors glyphW = xMax - xMin + 3; glyphH = yMax - yMin + 3; if (aa) { glyphSize = glyphW * glyphH; } else { glyphSize = ((glyphW + 7) >> 3) * glyphH; } // set up the glyph pixmap cache cacheAssoc = splashFontCacheAssoc; for (cacheSets = splashFontCacheMaxSets; cacheSets > 1 && cacheSets * cacheAssoc * glyphSize > splashFontCacheSize; cacheSets >>= 1) ; cache = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize); cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc, sizeof(SplashFontCacheTag)); for (i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = i & (cacheAssoc - 1); } } SplashFont::~SplashFont() { fontFile->decRefCnt(); if (cache) { gfree(cache); } if (cacheTags) { gfree(cacheTags); } } GBool SplashFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap) { SplashGlyphBitmap bitmap2; int size; Guchar *p; int i, j, k; // no fractional coordinates for large glyphs or non-anti-aliased // glyphs if (!aa || glyphH > 50) { xFrac = yFrac = 0; } // check the cache i = (c & (cacheSets - 1)) * cacheAssoc; for (j = 0; j < cacheAssoc; ++j) { if ((cacheTags[i+j].mru & 0x80000000) && cacheTags[i+j].c == c && (int)cacheTags[i+j].xFrac == xFrac && (int)cacheTags[i+j].yFrac == yFrac) { bitmap->x = cacheTags[i+j].x; bitmap->y = cacheTags[i+j].y; bitmap->w = cacheTags[i+j].w; bitmap->h = cacheTags[i+j].h; for (k = 0; k < cacheAssoc; ++k) { if (k != j && (cacheTags[i+k].mru & 0x7fffffff) < (cacheTags[i+j].mru & 0x7fffffff)) { ++cacheTags[i+k].mru; } } cacheTags[i+j].mru = 0x80000000; bitmap->aa = aa; bitmap->data = cache + (i+j) * glyphSize; bitmap->freeData = gFalse; return gTrue; } } // generate the glyph bitmap if (!makeGlyph(c, xFrac, yFrac, &bitmap2)) { return gFalse; } // if the glyph doesn't fit in the bounding box, return a temporary // uncached bitmap if (bitmap2.w > glyphW || bitmap2.h > glyphH) { *bitmap = bitmap2; return gTrue; } // insert glyph pixmap in cache if (aa) { size = bitmap2.w * bitmap2.h; } else { size = ((bitmap2.w + 7) >> 3) * bitmap2.h; } p = NULL; // make gcc happy for (j = 0; j < cacheAssoc; ++j) { if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) { cacheTags[i+j].mru = 0x80000000; cacheTags[i+j].c = c; cacheTags[i+j].xFrac = (short)xFrac; cacheTags[i+j].yFrac = (short)yFrac; cacheTags[i+j].x = bitmap2.x; cacheTags[i+j].y = bitmap2.y; cacheTags[i+j].w = bitmap2.w; cacheTags[i+j].h = bitmap2.h; p = cache + (i+j) * glyphSize; memcpy(p, bitmap2.data, size); } else { ++cacheTags[i+j].mru; } } *bitmap = bitmap2; bitmap->data = p; bitmap->freeData = gFalse; if (bitmap2.freeData) { gfree(bitmap2.data); } return gTrue; } xpdf-3.04/splash/SplashGlyphBitmap.h0000644000076400007640000000136612341430012016745 0ustar dereknderekn//======================================================================== // // SplashGlyphBitmap.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASHGLYPHBITMAP_H #define SPLASHGLYPHBITMAP_H #include #include "gtypes.h" //------------------------------------------------------------------------ // SplashGlyphBitmap //------------------------------------------------------------------------ struct SplashGlyphBitmap { int x, y, w, h; // offset and size of glyph GBool aa; // anti-aliased: true means 8-bit alpha // bitmap; false means 1-bit Guchar *data; // bitmap data GBool freeData; // true if data memory should be freed }; #endif xpdf-3.04/splash/SplashPath.cc0000644000076400007640000001074312341430012015556 0ustar dereknderekn//======================================================================== // // SplashPath.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmem.h" #include "SplashErrorCodes.h" #include "SplashPath.h" //------------------------------------------------------------------------ // SplashPath //------------------------------------------------------------------------ // A path can be in three possible states: // // 1. no current point -- zero or more finished subpaths // [curSubpath == length] // // 2. one point in subpath // [curSubpath == length - 1] // // 3. open subpath with two or more points // [curSubpath < length - 1] SplashPath::SplashPath() { pts = NULL; flags = NULL; length = size = 0; curSubpath = 0; hints = NULL; hintsLength = hintsSize = 0; } SplashPath::SplashPath(SplashPath *path) { length = path->length; size = path->size; pts = (SplashPathPoint *)gmallocn(size, sizeof(SplashPathPoint)); flags = (Guchar *)gmallocn(size, sizeof(Guchar)); memcpy(pts, path->pts, length * sizeof(SplashPathPoint)); memcpy(flags, path->flags, length * sizeof(Guchar)); curSubpath = path->curSubpath; if (path->hints) { hintsLength = hintsSize = path->hintsLength; hints = (SplashPathHint *)gmallocn(hintsSize, sizeof(SplashPathHint)); memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint)); } else { hints = NULL; hintsLength = hintsSize = 0; } } SplashPath::~SplashPath() { gfree(pts); gfree(flags); gfree(hints); } // Add space for more points. void SplashPath::grow(int nPts) { if (length + nPts > size) { if (size == 0) { size = 32; } while (size < length + nPts) { size *= 2; } pts = (SplashPathPoint *)greallocn(pts, size, sizeof(SplashPathPoint)); flags = (Guchar *)greallocn(flags, size, sizeof(Guchar)); } } void SplashPath::append(SplashPath *path) { int i; curSubpath = length + path->curSubpath; grow(path->length); for (i = 0; i < path->length; ++i) { pts[length] = path->pts[i]; flags[length] = path->flags[i]; ++length; } } SplashError SplashPath::moveTo(SplashCoord x, SplashCoord y) { if (onePointSubpath()) { return splashErrBogusPath; } grow(1); pts[length].x = x; pts[length].y = y; flags[length] = splashPathFirst | splashPathLast; curSubpath = length++; return splashOk; } SplashError SplashPath::lineTo(SplashCoord x, SplashCoord y) { if (noCurrentPoint()) { return splashErrNoCurPt; } flags[length-1] &= ~splashPathLast; grow(1); pts[length].x = x; pts[length].y = y; flags[length] = splashPathLast; ++length; return splashOk; } SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3) { if (noCurrentPoint()) { return splashErrNoCurPt; } flags[length-1] &= ~splashPathLast; grow(3); pts[length].x = x1; pts[length].y = y1; flags[length] = splashPathCurve; ++length; pts[length].x = x2; pts[length].y = y2; flags[length] = splashPathCurve; ++length; pts[length].x = x3; pts[length].y = y3; flags[length] = splashPathLast; ++length; return splashOk; } SplashError SplashPath::close(GBool force) { if (noCurrentPoint()) { return splashErrNoCurPt; } if (force || curSubpath == length - 1 || pts[length - 1].x != pts[curSubpath].x || pts[length - 1].y != pts[curSubpath].y) { lineTo(pts[curSubpath].x, pts[curSubpath].y); } flags[curSubpath] |= splashPathClosed; flags[length - 1] |= splashPathClosed; curSubpath = length; return splashOk; } void SplashPath::addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt) { if (hintsLength == hintsSize) { hintsSize = hintsLength ? 2 * hintsLength : 8; hints = (SplashPathHint *)greallocn(hints, hintsSize, sizeof(SplashPathHint)); } hints[hintsLength].ctrl0 = ctrl0; hints[hintsLength].ctrl1 = ctrl1; hints[hintsLength].firstPt = firstPt; hints[hintsLength].lastPt = lastPt; ++hintsLength; } void SplashPath::offset(SplashCoord dx, SplashCoord dy) { int i; for (i = 0; i < length; ++i) { pts[i].x += dx; pts[i].y += dy; } } GBool SplashPath::getCurPt(SplashCoord *x, SplashCoord *y) { if (noCurrentPoint()) { return gFalse; } *x = pts[length - 1].x; *y = pts[length - 1].y; return gTrue; } xpdf-3.04/splash/SplashPattern.cc0000644000076400007640000000177512341430012016304 0ustar dereknderekn//======================================================================== // // SplashPattern.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include "SplashMath.h" #include "SplashScreen.h" #include "SplashPattern.h" //------------------------------------------------------------------------ // SplashPattern //------------------------------------------------------------------------ SplashPattern::SplashPattern() { } SplashPattern::~SplashPattern() { } //------------------------------------------------------------------------ // SplashSolidColor //------------------------------------------------------------------------ SplashSolidColor::SplashSolidColor(SplashColorPtr colorA) { splashColorCopy(color, colorA); } SplashSolidColor::~SplashSolidColor() { } void SplashSolidColor::getColor(int x, int y, SplashColorPtr c) { splashColorCopy(c, color); } xpdf-3.04/README0000644000076400007640000003573312341430012012574 0ustar derekndereknXpdf ==== version 3.04 2014-may-28 The Xpdf software and documentation are copyright 1996-2014 Glyph & Cog, LLC. Email: derekn@foolabs.com WWW: http://www.foolabs.com/xpdf/ The PDF data structures, operators, and specification are copyright 1985-2006 Adobe Systems Inc. What is Xpdf? ------------- Xpdf is an open source viewer for Portable Document Format (PDF) files. (These are also sometimes also called 'Acrobat' files, from the name of Adobe's PDF software.) The Xpdf project also includes a PDF text extractor, PDF-to-PostScript converter, and various other utilities. Xpdf runs under the X Window System on UNIX and OS/2. The non-X components (pdftops, pdftotext, etc.) also run on Windows and Mac OSX systems and should run on pretty much any system with a decent C++ compiler. Xpdf will run on 32-bit and 64-bit machines. License & Distribution ---------------------- Xpdf is licensed under the GNU General Pulbic License (GPL), version 2 or 3. This means that you can distribute derivatives of Xpdf under any of the following: - GPL v2 only - GPL v3 only - GPL v2 or v3 The Xpdf source package includes the text of both GPL versions: COPYING for GPL v2, COPYING3 for GPL v3. Please note that Xpdf is NOT licensed under "any later version" of the GPL, as I have no idea what those versions will look like. If you are redistributing unmodified copies of Xpdf (or any of the Xpdf tools) in binary form, you need to include all of the documentation: README, man pages (or help files), COPYING, and COPYING3. If you want to incorporate the Xpdf source code into another program (or create a modified version of Xpdf), and you are distributing that program, you have two options: release your program under the GPL (v2 and/or v3), or purchase a commercial Xpdf source license. If you're interested in commercial licensing, please see the Glyph & Cog web site: http://www.glyphandcog.com/ Compatibility ------------- Xpdf is developed and tested on Linux. In addition, it has been compiled by others on Solaris, AIX, HP-UX, Digital Unix, Irix, and numerous other Unix implementations, as well as OS/2. It should work on pretty much any system which runs X11 and has Unix-like libraries. You'll need ANSI C++ and C compilers to compile it. The non-X components of Xpdf (pdftops, pdftotext, pdfinfo, pdffonts, pdfdetach, pdftoppm, and pdfimages) can also be compiled on Windows and Mac OSX systems. See the Xpdf web page for details. If you compile Xpdf for a system not listed on the web page, please let me know. If you're willing to make your binary available by ftp or on the web, I'll be happy to add a link from the Xpdf web page. I have decided not to host any binaries I didn't compile myself (for disk space and support reasons). If you can't get Xpdf to compile on your system, send me email and I'll try to help. Xpdf has been ported to the Acorn, Amiga, BeOS, and EPOC. See the Xpdf web page for links. Getting Xpdf ------------ The latest version is available from: http://www.foolabs.com/xpdf/ or: ftp://ftp.foolabs.com/pub/xpdf/ Source code and several precompiled executables are available. Announcements of new versions are posted to comp.text.pdf and emailed to a list of people. If you'd like to receive email notification of new versions, just let me know. Running Xpdf ------------ To run xpdf, simply type: xpdf file.pdf To generate a PostScript file, hit the "print" button in xpdf, or run pdftops: pdftops file.pdf To generate a plain text file, run pdftotext: pdftotext file.pdf There are five additional utilities (which are fully described in their man pages): pdfinfo -- dumps a PDF file's Info dictionary (plus some other useful information) pdffonts -- lists the fonts used in a PDF file along with various information for each font pdfdetach -- lists or extracts embedded files (attachments) from a PDF file pdftoppm -- converts a PDF file to a series of PPM/PGM/PBM-format bitmaps pdfimages -- extracts the images from a PDF file Command line options and many other details are described in the man pages: xpdf(1), etc. All of these utilities read an optional configuration file: see the xpdfrc(5) man page. Upgrading from Xpdf 3.02 (and earlier) -------------------------------------- The font configuration system has been changed. Previous versions used mostly separate commands to configure fonts for display and for PostScript output. As of 3.03, configuration options that make sense for both display and PS output have been unified. The following xpdfrc commands have been removed: * displayFontT1, displayFontTT: replaced with fontFile * displayNamedCIDFontT1, displayNamedCIDFontTT: replaced with fontFile * displayCIDFontT1, displayCIDFontTT: replaced with fontFileCC * psFont: replaced with psResidentFont * psNamedFont16: replaced with psResidentFont16 * psFont16: replaced with psResidentFontCC See the xpdfrc(5) man page for more information on the new commands. Pdftops will now embed external 16-bit fonts (configured with the fontFileCC command) when the PDF file refers to a non-embedded font. It does not do any subsetting (yet), so the resulting PS files will be large. Compiling Xpdf -------------- See the separate file, INSTALL. Bugs ---- If you find a bug in Xpdf, i.e., if it prints an error message, crashes, or incorrectly displays a document, and you don't see that bug listed here, please send me email, with a pointer (URL, ftp site, etc.) to the PDF file. Third-Party Libraries --------------------- Xpdf uses the following libraries: * FreeType [http://www.freetype.org/] * libpng [http://www.libpng.com/pub/png/libpng.html] (used by pdftohtml) * zlib [http://zlib.net/] (used by pdftohtml) Acknowledgments --------------- Thanks to: * Patrick Voigt for help with the remote server code. * Patrick Moreau, Martin P.J. Zinser, and David Mathog for the VMS port. * David Boldt and Rick Rodgers for sample man pages. * Brendan Miller for the icon idea. * Olly Betts for help testing pdftotext. * Peter Ganten for the OS/2 port. * Michael Richmond for the Win32 port of pdftops and pdftotext and the xpdf/cygwin/XFree86 build instructions. * Frank M. Siegert for improvements in the PostScript code. * Leo Smiers for the decryption patches. * Rainer Menzner for creating t1lib, and for helping me adapt it to xpdf. * Pine Tree Systems A/S for funding the OPI and EPS support in pdftops. * Easy Software Products for funding several improvements to the PostScript output code. * Tom Kacvinsky for help with FreeType and for being my interface to the FreeType team. * Theppitak Karoonboonyanan for help with Thai support. * Leonard Rosenthol for help and contributions on a bunch of things. * Alexandros Diamantidis and Maria Adaloglou for help with Greek support. * Lawrence Lai for help with the CJK Unicode maps. Various people have contributed modifications made for use by the pdftex project: * Han The Thanh * Martin Schröder of ArtCom GmbH References ---------- Adobe Systems Inc., _PDF Reference, sixth edition: Adobe Portable Document Format version 1.7_. http://www.adobe.com/devnet/pdf/pdf_reference.html [The manual for PDF version 1.7.] Adobe Systems Inc., "Errata for the PDF Reference, sixth edition, version 1.7", October 16, 2006. http://www.adobe.com/devnet/pdf/pdf_reference.html [The errata for the PDF 1.7 spec.] Adobe Systems Inc., _PostScript Language Reference_, 3rd ed. Addison-Wesley, 1999, ISBN 0-201-37922-8. [The official PostScript manual.] Adobe Systems, Inc., _The Type 42 Font Format Specification_, Adobe Developer Support Technical Specification #5012. 1998. http://partners.adobe.com/asn/developer/pdfs/tn/5012.Type42_Spec.pdf [Type 42 is the format used to embed TrueType fonts in PostScript files.] Adobe Systems, Inc., _Adobe CMap and CIDFont Files Specification_, Adobe Developer Support Technical Specification #5014. 1995. http://www.adobe.com/supportservice/devrelations/PDFS/TN/5014.CIDFont_Spec.pdf [CMap file format needed for Japanese and Chinese font support.] Adobe Systems, Inc., _Adobe-Japan1-4 Character Collection for CID-Keyed Fonts_, Adobe Developer Support Technical Note #5078. 2000. http://partners.adobe.com/asn/developer/PDFS/TN/5078.CID_Glyph.pdf [The Adobe Japanese character set.] Adobe Systems, Inc., _Adobe-GB1-4 Character Collection for CID-Keyed Fonts_, Adobe Developer Support Technical Note #5079. 2000. http://partners.adobe.com/asn/developer/pdfs/tn/5079.Adobe-GB1-4.pdf [The Adobe Chinese GB (simplified) character set.] Adobe Systems, Inc., _Adobe-CNS1-3 Character Collection for CID-Keyed Fonts_, Adobe Developer Support Technical Note #5080. 2000. http://partners.adobe.com/asn/developer/PDFS/TN/5080.CNS_CharColl.pdf [The Adobe Chinese CNS (traditional) character set.] Adobe Systems Inc., _Supporting the DCT Filters in PostScript Level 2_, Adobe Developer Support Technical Note #5116. 1992. http://www.adobe.com/supportservice/devrelations/PDFS/TN/5116.PS2_DCT.PDF [Description of the DCTDecode filter parameters.] Adobe Systems Inc., _Open Prepress Interface (OPI) Specification - Version 2.0_, Adobe Developer Support Technical Note #5660. 2000. http://partners.adobe.com/asn/developer/PDFS/TN/5660.OPI_2.0.pdf Adobe Systems Inc., CMap files. ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/ [The actual CMap files for the 16-bit CJK encodings.] Adobe Systems Inc., Unicode glyph lists. http://partners.adobe.com/asn/developer/type/unicodegn.html http://partners.adobe.com/asn/developer/type/glyphlist.txt http://partners.adobe.com/asn/developer/type/corporateuse.txt http://partners.adobe.com/asn/developer/type/zapfdingbats.txt [Mappings between character names to Unicode.] Adobe Systems Inc., OpenType Specification v. 1.4. http://partners.adobe.com/public/developer/opentype/index_spec.html [The OpenType font format spec.] Aldus Corp., _OPI: Open Prepress Interface Specification 1.3_. 1993. http://partners.adobe.com/asn/developer/PDFS/TN/OPI_13.pdf Anonymous, RC4 source code. ftp://ftp.ox.ac.uk/pub/crypto/misc/rc4.tar.gz ftp://idea.sec.dsi.unimi.it/pub/crypt/code/rc4.tar.gz [This is the algorithm used to encrypt PDF files.] T. Boutell, et al., "PNG (Portable Network Graphics) Specification, Version 1.0". RFC 2083. [PDF uses the PNG filter algorithms.] CCITT, "Information Technology - Digital Compression and Coding of Continuous-tone Still Images - Requirements and Guidelines", CCITT Recommendation T.81. http://www.w3.org/Graphics/JPEG/ [The official JPEG spec.] A. Chernov, "Registration of a Cyrillic Character Set". RFC 1489. [Documentation for the KOI8-R Cyrillic encoding.] Roman Czyborra, "The ISO 8859 Alphabet Soup". http://czyborra.com/charsets/iso8859.html [Documentation on the various ISO 859 encodings.] L. Peter Deutsch, "ZLIB Compressed Data Format Specification version 3.3". RFC 1950. [Information on the general format used in FlateDecode streams.] L. Peter Deutsch, "DEFLATE Compressed Data Format Specification version 1.3". RFC 1951. [The definition of the compression algorithm used in FlateDecode streams.] Morris Dworkin, "Recommendation for Block Cipher Modes of Operation", National Institute of Standards, NIST Special Publication 800-38A, 2001. [The cipher block chaining (CBC) mode used with AES in PDF files.] Federal Information Processing Standards Publication 197 (FIPS PUBS 197), "Advanced Encryption Standard (AES)", November 26, 2001. [AES encryption, used in PDF 1.6.] Jim Flowers, "X Logical Font Description Conventions", Version 1.5, X Consortium Standard, X Version 11, Release 6.1. ftp://ftp.x.org/pub/R6.1/xc/doc/hardcopy/XLFD/xlfd.PS.Z [The official specification of X font descriptors, including font transformation matrices.] Foley, van Dam, Feiner, and Hughes, _Computer Graphics: Principles and Practice_, 2nd ed. Addison-Wesley, 1990, ISBN 0-201-12110-7. [Colorspace conversion functions, Bezier spline math.] Robert L. Hummel, _Programmer's Technical Reference: Data and Fax Communications_. Ziff-Davis Press, 1993, ISBN 1-56276-077-7. [CCITT Group 3 and 4 fax decoding.] ISO/IEC, _Information technology -- Lossy/lossless coding of bi-level images_. ISO/IEC 14492, First edition (2001-12-15). http://webstore.ansi.org/ [The official JBIG2 standard. The final draft of this spec is available from http://www.jpeg.org/jbighomepage.html.] ISO/IEC, _Information technology -- JPEG 2000 image coding system -- Part 1: Core coding system_. ISO/IEC 15444-1, First edition (2000-12-15). http://webstore.ansi.org/ [The official JPEG 2000 standard. The final committee draft of this spec is available from http://www.jpeg.org/JPEG2000.html, but there were changes made to the bitstream format between that draft and the published spec.] ITU, "Standardization of Group 3 facsimile terminals for document transmission", ITU-T Recommendation T.4, 1999. ITU, "Facsimile coding schemes and coding control functions for Group 4 facsimile apparatus", ITU-T Recommendation T.6, 1993. http://www.itu.int/ [The official Group 3 and 4 fax standards - used by the CCITTFaxDecode stream, as well as the JBIG2Decode stream.] B. Kaliski, "PKCS #5: Password-Based Cryptography Specification, Version 2.0". RFC 2898. [Defines the padding scheme used with AES encryption in PDF files.] Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, "Practical Fast 1-D DCT Algorithms with 11 Multiplications". IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991. [The fast IDCT algorithm used in the DCTDecode filter.] Microsoft, _TrueType 1.0 Font Files_, rev. 1.66. 1995. http://www.microsoft.com/typography/tt/tt.htm [The TrueType font spec (in MS Word format, naturally).] V. Ostromoukhov, R.D. Hersch, "Stochastic Clustered-Dot Dithering", Conf. Color Imaging: Device-Independent Color, Color Hardcopy, and Graphic Arts IV, 1999, SPIE Vol. 3648, 496-505. http://diwww.epfl.ch/w3lsp/publications/colour/scd.html [The stochastic dithering algorithm used in Xpdf.] P. Peterlin, "ISO 8859-2 (Latin 2) Resources". http://sizif.mf.uni-lj.si/linux/cee/iso8859-2.html [This is a web page with all sorts of useful Latin-2 character set and font information.] Charles Poynton, "Color FAQ". http://www.inforamp.net/~poynton/ColorFAQ.html [The mapping from the CIE 1931 (XYZ) color space to RGB.] R. Rivest, "The MD5 Message-Digest Algorithm". RFC 1321. [MD5 is used in PDF document encryption.] Thai Industrial Standard, "Standard for Thai Character Codes for Computers", TIS-620-2533 (1990). http://www.nectec.or.th/it-standards/std620/std620.htm [The TIS-620 Thai encoding.] Unicode Consortium, "Unicode Home Page". http://www.unicode.org/ [Online copy of the Unicode spec.] W3C Recommendation, "PNG (Portable Network Graphics) Specification Version 1.0". http://www.w3.org/Graphics/PNG/ [Defines the PNG image predictor.] Gregory K. Wallace, "The JPEG Still Picture Compression Standard". ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz [Good description of the JPEG standard. Also published in CACM, April 1991, and submitted to IEEE Transactions on Consumer Electronics.] F. Yergeau, "UTF-8, a transformation format of ISO 10646". RFC 2279. [A commonly used Unicode encoding.] xpdf-3.04/CHANGES0000644000076400007640000033742112341430012012706 0ustar dereknderekn0.2 (95-dec-12) --------------- First public release. 0.3 (96-jan-13) --------------- LZW patent workaround. Implemented inline images. Fixed (mostly) disjoint polygon fills. Added remote server stuff. Added page number on command line. Fixed problem with font encodings which caused character misalignment. Fixed inverted CCITT decoding and inverted image mask drawing. Now compiles under gcc 2.7.x (ignore those stupid 'unused parameter' warnings). Many minor bug fixes and optimizations. 0.4 (96-apr-24) --------------- Implemented DCT filter. Implemented PostScript output; wrote pdftops program. Implemented links. Implemented font rotation -- I was wrong: X11R6 servers *do* support font rotation (by specifying a matrix in place of a size). Added bindings for Home/End, Page Up/Down, arrow keys. Added initialZoom resource and -z option. Added geometry resource and -g option. Fixed image size off-by-one bug. Fixed bug where page content is reference to an array of streams. Cleaned up uninitialized variables which were causing random problems on various platforms. Manually skip whitespace before calling atoi() for startxref. Replaced calls to XrmCombineFileDatabase() with calls to XrmGetFileDatabase() and XrmMergeDatabases() so it will work under older versions of X. Fixed problem with reading multiple xref tables in updated PDF files. Check for encryption and print appropriate error message. Rudimentary dithering of images. Fixed bug in CCITTFax filter (pass mode followed by horizontal mode). Optimized drawImage() and drawImageMask(). Changed several things to ease porting: - changed '__inline' to 'inline' (to adhere to the ANSI standard) - surrounded interface/implementation pragmas with #ifdef _GNUC__ - got rid of empty array initializer lists - moved Operator type definition from Gfx.cc to Gfx.h - renamed String, uint, etc. - ability to uncompress to file (NO_POPEN and USE_GZIP flags) - added definitions of XK_Page_Up/Down and XPointer for old versions of X For VMS port: - use correct Xdefaults name for VMS, get rid of ltkGetHomeDir() - added '#include ' before all X includes - renamed files with multiple periods in their names Fixed window resizing infinite oscillation bug. Fixed problem with string-type (as opposed to stream-type) indexed color space lookup tables (which are used in inline images). If an X font is not found, try smaller and then larger sizes (this is useful for old or broken X servers which can't scale bitmap fonts). Added -rgb (color cube size) option. Various minor bug fixes. 0.5 (96-may-23) --------------- Fixed bug in LTKWindow which broke the remote server mode. Fixed PostScript output: - doesn't seg fault if file is unwritable. - incorrect DSC comment - need colon in '%%Page:'. - use 'imagemask' command for masks. - output filters in the correct order. - Stream::isBinary() checks the next-to-bottom, not top, stream. - if page width > height, rotate it 90 degrees. - if page is larger than paper size, scale it down. Set default MediaBox to 8.5" x 11" to deal with non-compliant PDF files which don't specify a MediaBox. Added DEBUG_MEM stuff in gmem.c and gmempp.cc. Fixed memory leaks: - LTKWindow didn't delete the LTKBox. - LinkAction needs a virtual destructor. Use $(RANLIB) variable in goo/Makefile and ltk/Makefile. Allocate image data after calling XCreateImage, using image->bytes_per_line -- works in 24-bit mode now. DCTStream rounds width of rowBuf lines up to the next multiple of mcuWidth, so last MCU doesn't run off end of buffer. Increase size of block (from 255 to 1024 bytes) read at end of file to search for 'startxref'. Skip past garbage at start of file, look for '%PDF'. Moved more compiler options out of Makefiles into Makefile.config. Top-level Makefile uses '$(MAKE)' instead of 'make' for making subdirectories. Space/PageDown/Next and Backspace/PageUp/Previous now moves to next/previous page if already scrolled to bottom/top of current page. 0.5a (96-jul-09) ---------------- [not a public release] For PDF 1.2 (a.k.a. Amber, a.k.a. Acrobat 3) support: - look for trailer after first xref instead of at end of file. Deal with font subsets by converting character names of the form 'Cnnnn' to the appropriate character from the standard encoding. Extract encoding from embedded Type 1 fonts. Kludge to fill one-pixel thick polygons. Changed X font encoding to use endash for hyphen (gets rid of too-long hyphens). Handle Resources key in Pages dictionaries (needed for pstoedit output). Fix comment handling in Parser (needed for pstoedit output). Move Bezier curve conversion from GfxState to XOutputDev; look at flatness parameter in GfxState. Change all of the path functions in XOutputDev (stroke, fill, clip) to use the same path transformation/conversion function. Rewrote PostScript output driver as a subclass of OutputDev; removed duplicated code (ps_ functions) from Gfx. Fixed bug in xref code with small (< 1024 bytes) PDF files. Implemented BX/EX operators. Added PDFDoc class. 0.6 (96-nov-12) --------------- Add support for PostScript output to stdout (-) and to a command (|lpr); added -ps option and psFile resource. Decryption is implemented but not included in the distribution due to legal restrictions: the decryption algorithm is a trade secret of RSA, Inc., and the U.S.A. still has bogus export controls on cryptography software. Added .xpdfrc config file: - Added fontmap parameter: user can map PDF font names to X fonts. - Added fontpath parameter: search for Type 1 font if encoding is not in PDF file. Incremental display: display is updated after every 200 commands. Added forward and backward by-10-page buttons. Links: - Implement links with "Launch" actions that point to PDF files. - Draw borders around links. - Handle links with named destinations. - GoToR links specify a page number instead of a page reference. Optimizations: - Rewrote Stream to use buffering, and added lookChar() functions; rewrote Lexer to take advantage of this. - Use hash tables for name->code mapping in font encodings. - Made XOutputDev::doCurve() iterative, changed /2 to *0.5, and changed the flatness test. Added file name to window title. Implemented RunLength filter. Implemented forms. Convert ObjType to an enum. Changed isDict("Pages") to isDict() (in Catalog.cc) to deal with incorrect PDF files. Changed color selection so that very pale colors don't map to white. Fixed bug in CCITTFax filter (multiple make-up codes). In GString::clear(): need to set length to 0 before calling resize(). Base initial window size on first displayed page, not page 1; deal correctly with rotated pages. Added ltkGetIntResource() and LTKApp::getIntResource(). PostScript output fixes: - Escape backslashes in strings. - When doing ASCII85 encoding, keep both chars of EOF marker ('~>') on same line. - Add extra line '%-EOD-' after image data streams; call wrapper functions for image and imagemask which look for this line -- this should fix the 'too much data in stream' bug. - Font tags can be reused for different fonts on different pages -- so use font object reference (number/generation) instead. Initialize character widths to zero (this caused crashes on OSF/1). Handle image masks which go outside of pixmap. Makefile.config changes: - Remove -o in C++ compile rule. - Add $(AR) variable. Code which read char widths from font dictionary read all but the last width. Add 'return 0;' to main() in xpdf and pdftops. Allow fonts to use StandardEncoding. Convert man pages to VMS help files. 0.7 (97-may-28) --------------- Implemented FlateDecode filter (for PDF 1.2). Basic xref table reconstruction for damaged files New pdftotext program converts PDF to plain text. Implemented menus in LTK; added a menu to xpdf. Added open and save functions; allow xpdf to start without any PDF file. Implemented text find. Implemented text select/copy. Change mouse cursor when it's over a link. Embed Type 1 fonts in PostScript output. Moved rotate functions to menu; added quit to menu. Fixed stroke color bug in PostScript output (was using fill color instead of stroke color; this sometimes caused lines to be missing (white) in PostScript output). Support Launch-type links -- pops up a dialog before executing anything. Expects the A (action) dictionary to contain a Unix dictionary with F (file) and P (paremeter) keys just like the Win dictionary. A moveto op all by itself should just be discarded, instead of generating a subpath with one point (this was causing seg faults). Balanced parentheses in strings don't need to be escaped. Tj operator in PostScript prolog didn't check for zero when dividing by length of string. Implemented selection in LTK; TextIn widgets support dragging/copy/ paste. Handle font subsets that use hex character codes. Added icon pixmap; added the XPMLIB and NO_XPM variables to Makefile.config. Fixed subtle bug in use of horizontal scaling parameter (it affects only the width of drawn characters, not positioning done in text space). Memory testing (with DEBUG_MEM): - gmalloc now fills memory blocks with garbage to catch unitialized fields. - gfree fills memory blocks with garbage to catch uses of freed blocks. Handle image masks which go off the pixmap on the top and/or left. Fixed inline functions which had two return statements (to make the HP, SCO, and other cfront-based compilers happy). Fixed bug which caused seg faults when following a link to a different file (info in LinkGoto object was used after link was deleted by loadFile). If page content is an array of streams, the streams are concatenated; objects and commands can span multiple streams. If file open fails, try lower-casing and upper-casing the file name. Commands should end when lexer sees a '/' character. GString::append(char *, int) was broken. Changed LTKScrollingCanvas redraw to be more efficient: copy as much as possible from window before copying from off-screen pixmap. Ignore gs (set extended graphics state) operator. Handle colorspaces (CalGray/RGB are treated as DeviceGray/RGB; the weird colorspaces are not yet implemented). Named destinations (for links) can be strings as well as names; deal with the names tree in the catalog. Clip to the page CropBox. Added '-q' to gzip options (to suppress warnings, in case user has -v in GZIP env var). Added 'include Makefile.config' to top-level Makefile. Added INSTALL variable to Makefile.config; used in top-level Makefile. Always initialize LinkDest left/bottom/top/right/zoom fields (bogus floating point values were causing crashes on Alpha). Added Makefile.config options for Digital Unix (DEC compilers), HP-UX (HP compilers), SCO Unix, and Evans & Sutherland ES/OS. Added flag to set stream mode in fopen call for VMS. Rewrote Link module. Pages with no contents shouldn't cause an error message. In PostScript output: pdfImM needs to set fill color before doing imagemask. If font doesn't specify character widths, use widths from built-in font, based on font flags. Fixed LTK scrollbar to delay before repeating and to control the period between repeats. Removed window/widget copy() methods (they were untested and unused). Unknown filter types produce a single error message instead of a stream of errors. Added a dummy target in top-level Makefile so making individual executables (e.g., 'make pdftops') should now work. Added optional xpdf-flip.ltk with buttons on right side instead of bottom of window. 0.7a (98-feb-22) ---------------- Moved find command from menu to toolbar button ('f' key still works). Support TrueColor visuals. Added a -cmap option and a installCmap resource to install a private colormap. Mouse button 2 pans the window. Selecting a URI link now executes a configurable command (from the urlCommand resource). Added a "link info" display which shows the URL or file for the link under the mouse. Don't draw (or convert to PostScript) text drawn in render modes 3 and 7 -- this is invisible text, used by Acrobat Capture; this text is still passed to the TextPage object so that selection works. Recognize (and quietly ignore) marked content operators (BMC, BDC, EMC, MP, DP). Recognize new color-setting operators (scn, SCN). Added A4_PAPER option. Embed external Type 1 font files (this currently only works with PFA files). Added "-level1" option (in xpdf and pdftops) to generate Level 1 PostScript. Setup autoconf -- replaced Makefile.config. Added SELECT_TAKES_INT flag, and use configure to autodetect (for HP-UX). Fixed appendToPath() to behave reasonably when appending ".." to root directory. Fixed array size in FlateStream::compHuffmanCodes() (was causing xpdf to crash under OSF/1). ASCII85Stream, ASCIIHexStream, and DCTStream didn't check for EOF and could run past the end of the stream in damaged files. Handle hex escapes (#xx) in names. Still allow the name /# for backward-compatibility. Check for NULL characters in encoding array in GfxFont.cc (was calling strcmp() with NULL which crashed under Solaris). PageAttrs::PageAttrs() didn't initialize crop box boundaries. Changed uses of lookup() to lookupNF() in XRef.cc. Fixed type checking of operators which take a variable number of args. Gfx::buildImageStream() doesn't need to check for parser (since I got rid of the bogus array-of-command thing). XOutputFont matches on font reference instead of font tag (similar to PSOutputDev fix). Fixed bug in position calculation for multi-char substitutions in XOutputDev. Cleaned up local variables which hid class variables. Optimized variable length decoding in CCITTFaxStream. Set link border width to zero if Border dictionary entry is missing. Throw away zero-length strings in TextOutputDev -- they don't have valid xMin/xMax values. Swapped order of XLIBS and XPMLIB in xpdf/Makefile. Deleted 'LTKApp::' in function declaration in LTKApp.h. Changed '(XKeyEvent *)&event' to '&event.xkey' in LTKApp.cc. Check that the link rectangle coordinates are in the correct order, and swap if necessary. TextOutputDev didn't set text to NULL, which caused pdftotext to segfault if it couldn't open it's output file. Fixed a hash table search bug in GfxFontEncoding::getCharCode(). Cleaned up colorspace code: rewrote GfxColorSpace and added GfxImageColorMap; cleaned up PSOutputDev::doImage. Handle named colorspaces in images. Correctly set the default color after a colorspace change. Old setcolor operators now set the colorspace. Fixed bug with uncompressed blocks in FlateStream. Fixed bug with fixed Huffman code table in FlateStream. Added hash table of X windows (for LTKWindow and LTKWidget) to LTKApp and replaced calls to XQueryTree with hash table searches -- this avoids a roundtrip to the server for each event and also fixes the problem where XQueryTree crashed if the window no longer existed (with leftover events from a destroyed window). (Thanks to Yair Lenga for the suggestion.) Create a new GC for selection -- xor black and white (instead of LTK foreground and background). Fixed crash with blank lines in .xpdfrc. Allow spaces in font descriptors in fontmap lines in .xpdfrc. Check for bogus object number in XRef::fetch(). Use MacRomanEncoding for TrueType fonts that don't specify an encoding. Certain PDF generators apparently don't include FontDescriptors for Arial, TimesNewRoman, and CourierNew -- set GfxFont flags appropriately. Fixed a bug in width guessing in GfxFont -- sans serif and serif were swapped. Rewrote XRef::readXRef() to avoid using a parser to read the xref entries. Added NO_TEXT_SELECT option. Ignore APPn/COM/etc. markers in DCT streams. Replaced select() with XMultiplexInput() in LTKApp.cc for VMS. Handle WM_DELETE_WINDOW protocol -- if you ask the window manager to delete the xpdf window, xpdf will exit cleanly; other windows/dialogs are simply closed. Optimized DCT decoder; switched to integer arithmetic. The "/Type /Annots" field in an annotation dictionary is optional. Check for null nameTree in Catalog::findDest(). In XOutputDev, search user font map before default font map. Added "normal" SETWIDTH parameter to all font descriptors in XOutputDev (some systems have a narrow-width Helvetica font). Added FOPEN_READ_BIN and FOPEN_WRITE_BIN to support Win32. Added a hack which allows better font substitution for some Type 3 fonts. Also allow character names of the form /nn and /nnn. Added and to LTKApp.cc (needed by AIX and IRIX for bzero() declaration for FD_ZERO). 0.80 (98-nov-27) ---------------- Support for some Japanese fonts (Type 0 fonts using the Adobe-Japan1-2 character collection, horizontal only). Added pdfinfo application. Added pdftopbm application. Added pdfimages application. Added -papercolor option and .paperColor resource. Fixed divide-by-zero problem in XOutputDev Type 3 font matrix kludge. Font subset char names can be 'Bxx' as well as 'Cxx' and 'Gxx'. Fixed bug in color space conversion in DCTStream filter (YCC->RGB was correct, YCCK->CMYK was broken). Added XRef::getDocInfo() and PDFDoc::getDocInfo() to support pdfinfo. Optimized GfxImageColorMap. Lexer::getStream(), getPos(), and setPos() check for null stream. Decryption code now does strings as well as streams. ASCII85 decoder rounds short tuples up instead of down. CropBox and MediaBox can be non-integers. PostScript output: - Use a rectangle operator. - Call setpagedevice with page size. - Insert %%PageOrientation comments. - Add paper size flags (-paperw and -paperh) to xpdf and pdftops. - If HAVE_POPEN is not defined, and user tries to print to '|...', the PSOutputDev destructor tried to write to the PS file. - Added support for forms (pdftops -form). Removed error messages for empty paths in stroke, fill, etc. operators. Don't allow flatnesses less than 1 in XOutputDev (this speeds up rendering a little bit when there are lots of tiny curves). Moved the font subset character name guessing from GfxFont to XOutputDev and TextOutputDev - now these files print correctly. Cast argument to XFree() to XPointer; add XPointer definition where necessary (portability fixes). Various minor VMS fixes. Changes to configure script and Makefiles: - Print a warning if X is missing. - Use C++ when checking select() argument type (HP-UX). - Use 0 instead of NULL when checking select(). - Default to gcc instead of c++. - Get rid of AC_C_INLINE -- this is meant for C, not C++. - Changed -USE_GZIP to -DUSE_GZIP. - Added ability to compile ouside of the source tree. - Added .cc.o rule to {goo,ltk,xpdf}/Makefile.in. - Added @LIBS@ to XLIBS in xpdf/Makefile.in. - In top-level Makefile.in: added '-' to clean commands; added distclean rule. - Create install directories. - Use INSTALL_DATA (instead of INSTALL) for man pages. - Changed xpdf-ltk.h rule to avoid leaving an empty file when ltkbuild fails. - Change things so that by default, ltkbuild is not built and xpdf-ltk.h is not rebuilt. - Use AM_PROG_CC_STDC to add compiler flags for ANSI C. - Modify autoconf's builtin macros to check for xlC. - Use Steve Robbins' smr_CHECK_LIB to test for pixmap library (Xpm) -- this should fix the problems on systems that have the library but not the include file. - Added better test for sys/select.h, sys/bsdtypes.h, strings.h, bstring.h. - New VMS make scripts from Martin P.J. Zinser. - Moved dependences into Makefile.in -- this gets rid of problems with 'cc -MM' (which is gcc-specific) and 'include Makefile.dep' (which isn't supported by all make implementations). Also changed all non-system include files to '#include "..."' (from '<...>'). Tweaked the TextOutputDev heuristics slightly. Modify Gfx to use a stack of resources -- this is necessary for Form XObjects, which can define their own local resources; also modified PSOutputDev to dump fonts used by forms. Look for excessively large MediaBox (compared to CropBox) and shrink it to CropBox. Minor fix to scrolling when dragging a selection. Various patches for pdftex and Win32 support. Deal with Separation colorspaces by using their alternate colorspace entry. Added PBMOutputDev for pdftopbm application. Added ImageOutputDev for pdfimages application. Separated XOutputDev into LTKOutputDev + XOutputDev. Added support for 1-D and mixed 1-D/2-D (Group 3) decoding to CCITTDecode filter. Added resetImage(), getImagePixel(), and skipImageLine() to Stream class; used these in XOutputDev, PSOutputDev, and ImageOutputDev. Implemented predictor for LZW and Flate filters. In pdfImM1 in PSOutputDev prolog: div should be idiv. Changed output from printUsage() function in parseargs to look nicer. 0.90 (99-aug-02) ---------------- Added Type 1/1C font rendering, using t1lib. Added "backward" and "forward" buttons. Added fit-page and fit-page-width zoom factors; replaced zoom-in and zoom-out buttons with a zoom popup menu. Type 1C fonts are converted to Type 1 and embedded in PostScript. Support vertical Japanese text. Added Japanese text support (EUC-JP) to pdftotext. Bumped PDF version to 1.3. Added stub functions for ri and sh operators. (But there are still some missing 1.3 features.) Added -raw option to pdftotext. Minor changes to allow compiling under MS Visual C++ 5.0. Top-level makefile: changed 'mkdir -p' to '-mkdir -p'. Configure script: added X_CFLAGS to smr_CHECK_LIB(Xpm). Added Xpm_CFLAGS to xpdf/Makefile.in (this is needed to get the -I for the xpm includes). Rewrote code that handles font encodings; added support for Type 1C fonts. In the setpagedevice dictionary in PostScript output - added a /Policies entry which tells the PS interpreter to scale the page to fit the available paper. Changed PageUp behavior slightly: move to bottom (instead of top) of previous page. TextPage used character's dx (width + char space) instead of just its width. Read base URI from document Catalog (for URI-type links). Minor change to configure script to avoid using 'unset'. Fixed bugs in CropBox inheritance. Fixed a bug in resource stack handling for form objects. Display forms even if they have a missing/incorrect FormType. Fixed a bug in stream predictors -- the predictor parameters (width, comps, bits) don't need to match the actual image parameters. Completely rearranged the predictor code. Fixed PostScript output to correctly handle stream predictors. Don't segfault on empty (zero-page) documents. Added the xpdf.viKeys feature. Added the ffi and ffl ligatures to XOutputDev and TextOutputDev. Pdftotext and pdfimages now check okToCopy(). Added a '-q' flag to all programs (except pdfinfo) to suppress messages and errors. Deal with DeviceN colorspaces by using their alternate colorspace entry. Change PostScript output so setpagedevice is only called once, at the very beginning of the document (to avoid problems on duplex printers). Changes to configure script and makefiles for DOS/DJGPP. FontEncoding::getCharCode() looked for (code>0) instead of (code>=0). Added keypad arrow keys, etc. to xpdf. Minor changes to gfile.{h,cpp} () to compile under VC++. Fixed CCITTFaxStream to correctly handle all parameters. Modifications to gfile.{h,cc} for Acorn. Some minor changes for OS/2. Added 'SHELL = /bin/sh' to Makefiles. Compare file version to pdfVersionNum+0.0001 to avoid floating point precision problems. Added LDFLAGS to Makefiles. Removed strip entirely from configure and Makefiles. Fixed a bug in choosing the correct DCTStream color transform. 0.91 (2000-aug-14) ------------------ Added TrueType font rendering, using FreeType. Support for Chinese fonts (Type 0 fonts using the Adobe-GB1-2 character collection). Decryption code is included with the main distribution (now that the US export regulations are a little bit less irrational). Added (very basic) support for generating PostScript with Japanese fonts -- only tested with ghostscript so far. Added support for generating EPS files (pdftops -eps). Much-improved image filtering in xpdf (for downsampling and for transforms other than 0/90/180/270-degree rotations). Implemented a basic full-screen (presentation) mode (xpdf -fullscreen). (There is currently no way to switch between window and full-screen modes on the fly -- this will be fixed in a later release.) Added "reload" menu item. Do a better job with anti-aliased Type 1 fonts on non-white backgrounds. Handle Lab color spaces. Handle non-null user passwords. Avoid security hole with tmpnam()/fopen() -- added openTempFile() in goo/gfile.cc. [Thanks to Joseph S. Myers for pointing this out.] Filter out quote marks (' and ") in URLs before running urlCommand to avoid a potential security hole. [Thanks to Frank Doepper for pointing this out.] Fixed TrueColor detection to look at the complete list of available visuals, not just the default visual. In gfile.h, changed NAMLEN(dirent) macro to NAMLEN(d). Removed copyright character from start-up banners. In the open and save dialogs, if the open/save button is pressed with no file name, the dialog is not canceled. Added Xpm_CFLAGS to ltk/Makefile. XOutputDev::updateLineAttrs was using dashLength before it was defined. In top-level Makefile.in, use INSTALL_PROGRAM instead of INSTALL. In man page, recommend -q instead of -err in .mailcap. Changes for GNOME / Bonobo support: - Separated Stream classes into BaseStream and FilterStream trees. - Got rid of all FileStream-specific stuff. - Added a PDFDoc constructor that takes a BaseStream* instead of a file name. Allow newlines inside strings (Photoshop does this). Don't require whitespace between tokens in consecutive content streams (for pages that specify an array of streams). Look at MissingWidth when constructing font character widths array. Fixed a bug that caused incorrect PostScript output for images that use 8-bit indexed color spaces with < 256 colors in the palette. Handle case where embedded font file is bad (this was seg faulting). Minor changes for Windows/pdftex. Work around a bug in PDF files from the IBM patent server. Fixed bugs in PostScript form generation: use pdfStartPage instead of pdfSetup; problem with inline images. Minor bug fix in FlateStream::loadFixedCodes(). Added %%DocumentMedia and %%PageMedia comments to the PostScript so that gsview (a Windows frontend for ghostscript) gets the right paper size. Draw AcroForm fields that have appearance annotations. Bounds check gray, CMYK, and RGB values (in GfxColor). Moved the link border drawing code into Page (from PDFDoc). Minor modifications for pdftohtml. PSOutputDev: use the Type 3 font scaling kludge from XOutputDev. Separation color spaces were handled incorrectly in images. Fixed a bug with form bounding boxes. Modified the t1lib support -- replace libt1x code with my own code. Type 1 and TrueType fonts are now handled similarly, and clipping works on Type 1 fonts. Don't print copyright banner (xpdf); add -v switch to get copyright and version info (all apps); get rid of -err switch (xpdf). Automatically reload the PDF file if it has been changed, i.e., if the modification time is different. Fixed a memory (malloc size) bug in CCITTFaxStream. Fixed two bugs in FontEncoding::hash() -- handle zero-length character names (which were found in a (buggy?) PDF file), and handle character names with high-bit-set characters (use unsigned ints). Added PDFDoc::isLinearized() and corresponding code in pdfinfo.cc. Handle files with an incorrect page count in the Pages dictionary (FOP, from the Apache project produces a page count of 0). Handle TrueType equivalents to the Base14 fonts (Arial, TimesNewRoman, CourierNew) -- Adobe's tools use these names without embedding the fonts. Tweaked the Type 3 font sizing kludge. Changed pdfimages (ImageOutputDev) so it doesn't output JPEG files for 4-component color spaces, since these seem to confuse most image viewers. Added support for generating OPI comments (pdftops -opi). In XOutputDev::drawImage() and drawImageMask(), check for images that are completely off-page. Use the provided alternate or a default (DeviceGray/RGB/CMYK) color space for ICCBased color spaces. Incorporated MacOS-specific code from Leonard Rosenthol. Configure script switches to C++ for the strings.h/bstring.h test. Gfx::opRestore() calls clearPath() to handle (apparently) buggy PDF files produced by FreeHand. The /Type field in most dictionaries is optional (PDF 1.3 change). Move printCommands variable definition into Gfx.cc. If page is smaller than paper, center the PostScript output. Fix a minor bug in the SELECT_TAKES_INT detection in the configure script. TextOutputDev filters out control characters. Changed enough occurrences of 'char *' to 'const char *' to keep gcc 2.95 from barfing. Support for Latin-2 and Latin-5 in pdftotext (however, this will only work if the PDF file contains correct font encodings, which seems to be rare). TextOutputDev converts "eightoldstyle" to "eight", etc. Don't use the return value from sprintf() -- most systems return the length, but some return the string. Minor fixes for SunOS 4. Configure script looks for both select() and fd_set in sys/select.h. Configure script checks for gethostbyname() in -lbsd (for LynxOS). Fix missing closepath bug in PostScript output. Change PostScript portrait/landscape mode selection so it only uses landscape if the page width is greater than the paper width. Tweaked the VMS code in makePathAbsolute(). 0.91a (2000-oct-11) ------------------- Implemented separable CMYK PostScript output (the -level1sep switch to pdftops). Implemented Pattern color spaces with tiling patterns (polygon fills only). Implemented Stamp annotations. Implemented Named link actions. Fixed a really dumb bug in the TrueColor code in SFont (which affects both Type 1 and TrueType font rendering on 16-bit displays). Rewrote the GfxColorSpace / GfxColor code. Switched from djgppcfg to dj_make.bat (from Michael Richmond). Bug in the Type 1 encoding parser -- couldn't handle lines of the form 'dup NNN/name put' (with no space between the code and the name). Fixed the mkstemp() test in configure.in -- switched from AC_TRY_COMPILE to AC_TRY_LINK and added . Added DESTDIR to top-level Makefile.in. Fixed an incorrect OPI comment in PSOutputDev. Minor tweak to the CCITTFax code to avoid writing past the end of an array on an invalid data stream. Xpdf crashed if the user selected 'reload' when no document was loaded. Look for character names of the form "xx" (two hex digits with no leading alphabetic char) and 'cNNN' (decimal digits with a leading alphabetic char that happens to be a hex digit). FlateStream didn't correctly handle zero-length streams. Xref reconstruction didn't handle the case where the opening "<<" immediately followed "trailer" with no intervening whitespace. Fix the %%DocumentSuppliedResources comment in EPS output. Scale annotations to fit their rectangles. Added Stream::close() to handle cases (e.g., patterns) where a Stream object is used multiple times before it is deleted. Added the topLevel arg to Gfx::go() so it doesn't call out->dump() for every pattern element (and form). Rearranged the GfxResources class. Clean up white space handling in Lexer. Make the dpi parameter to PDFDoc::displayPage etc. a double - this avoids margin gaps with fit-page and fit-width. Fix a rounding problem in xpdf.cc that was causing the window to sometimes be one pixel too small. Fixed a minor bug in dealing with Base-14 TrueType font names. Fixed Lab -> RGB color space conversion. Added support for opacity values (from PDF 1.4) to GfxState and OutputDev. [Thanks to Leonard Rosenthol.] Implemented type 2 functions; rearranged the Function class hierarchy. 0.91b (2000-oct-29) ------------------- Print a warning about Type 3 fonts (XOutputDev, PSOutputDev). Added the scroll lock behavior to 'n' and 'p' keys in xpdf. Change FileStream buffer size to a #define'd constant. Renamed Pattern to GfxPattern to avoid clashes with Windows and MacOS types. Added CNS (Big5) Chinese font support (CHINESE_CNS_SUPPORT); renamed CHINESE_SUPPORT to CHINESE_GB_SUPPORT. 0.91c (2000-nov-19) ------------------- Fix an endianness problem in the Type 1 font code which resulted in an incorrect display with "-t1lib plain" on big-endian systems. CCITTFax stream decoder will skip over extra zero bits at end of line, even if EncodedByteAlign flag wasn't set. Added Big5 support to pdftotext (with CHINESE_CNS_SUPPORT enabled). Fixed a typo in the CNS/Big5 encoding translation table. Change the form code in PSOutputDev to store images in arrays of strings. The xref reconstruction (for damaged files) now also looks for 'endstream' tags, and the parser uses this information when setting up stream objects. In pdfinfo, convert Unicode chars in the 00xx range into 8-bit chars; print a warning if there are any other Unicode chars. 0.92 (2000-dec-03) ------------------ Fixed %%BeginResource comment (for xpdf procset) in PostScript output. Added "-title" switch and "xpdf.title" resource to set the window title. Check for in addition to . Upgraded the configure script to smr_macros 0.2.4 - this should fix a bug where configure wasn't correctly finding t1lib. 0.92a (2000-dec-17) ------------------- Added 'extern "C" { ... }' in various places for ANSI C++ compliance. Tweaked the code that figures out DPI for fit-to-page and fit-to-width modes. Fixed the image transformation code in XOutputDev -- no more missing lines. Implemented color key image masking in XOutputDev. 0.92b (2001-jan-07) ------------------- Fixed a bug in the error-checking code in the Separation/DeviceN color space parsing functions. [Thanks to Lidia Mirkin.] Added wheel mouse support (mouse buttons 4 and 5). [Thanks to Thorsten Schreiner.] Added preliminary support for FreeType 2 (disabled by default). 0.92c (2001-jun-04) ------------------- Fixed a bug in the new image transformation code. Look for character names of the form "", instead of looking for names beginning with a few specific letters. T1FontFile::T1FontFile wasn't initializing vars, and ~T1FontFile wasn't checking before calling T1_DeleteFont -- this caused crashes if it tried to open a nonexistent font file. Catalog::Catalog didn't set baseURI to NULL early enough. Tweak the check for strings.h in the configure script. Yet another fix for the image rotation code in XOutputDev -- off-by-one problem when upsampling. Handle Type 1/1C encodings when using FreeType 2. Allow FreeType2 to render user-supplied Type 1 base fonts. Opening a new file from full-screen mode tried to scroll. Fixed a bug in GfxFont constructor (missing check for NULL base font name). Don't crash if a Type 1 font's FontBBox is non-integer. Pdfinfo prints page size. Tweak for the alpha hack in T1Font/TTFont: sample the middle pixel instead of the top-left pixel. Automatically activate the text input widget in the find window. Changed a Japanese char code mapping in XOutputDev and TextOutputDev: period was being incorrectly mapped to small circle (end-of-sentence character). Add the 0/+/-/z/w key bindings to control the zoom setting. Fixed ImageOutputDev (pdfimages) to correctly handle inline image masks. Extract ascent/descent info from font descriptor. 0.92d (2001-jun-26) ------------------- Embed TrueType fonts in PostScript output. (Added a "-noembtt" flag to pdftops.) Extract encoding from TrueType fonts. Moved Function classes to a separate file (Function.h/cc). Implemented multi-dimensional sampled Functions. Implemented Type 4 (PostScript calculator) Functions. For Type 0 fonts, FontDescriptor is in descendant font, not parent. [Thanks to Lidia Mirkin.] Added the "-htmlmeta" option to pdftotext. In TextOutputDev, when computing the number of blank lines to insert, do a sanity check on the result. If both FlateDecode and some other filter (e.g., DCTDecode) were applied to an image stream, getPSFilter() crashed instead of just returning NULL. Handle the /Identity function. 0.92e (2001-aug-23) ------------------- Widths in font dict should override built-in font widths. Changed "rotate left/right" menu items to "rotate clockwise/counterclockwise". The link parsing code choked if the Border array was incorrect (too short). Modified PSOutputDev to output CMYK for fill/stroke colors. 0.93 (2001-oct-25) ------------------ Implement PDF 1.4 (128-bit) decryption. Bump supported PDF version number to 1.4. Text output for Simplified Chinese. [Thanks to Cheung Siu Fai.] Read an app-defaults file for Xpdf. Read a system-wide config file (/etc/xpdfrc) if ~/.xpdfrc doesn't exist. Accept and verify owner password; if correct, allow all actions. Added a "-level2sep" option to pdftops to generate Level 2 separable PostScript. The PostScript separation convention operators are used to handle custom (spot) colors. [Thanks to Thomas Freitag for help on this.] Add support for FreeType 2 to the configure script. Warning: this requires FT 2.0.5 or newer. Fixed the bounding rectangle overlap test in the disconnected subpath fill hack in XOutputDev. Stupid typo in font name table in PSOutputDev. Changing the zoom setting with a keyboard shortcut didn't update the displayed setting. Modified the mouse wheel support and added the second wheel (mouse buttons 6 and 7). [Thanks to Michal Pasternak.] Character and word spacing is affected by horizontal scaling (display and PS output). [Thanks to Eddy Ng.] Rotation specified by the text matrix, character spacing, and horizontal scaling interacted incorrectly (display and PS output). Some broken Type 1/1C fonts have a zero BBox -- kludge around this by assuming a largeish BBox. Handle PDF files with an incorrect (too small) xref table size. Allow "-?" and "--help" as aliases for "-h" (all apps). Correctly handle unescaped parens in strings in Lexer. Fixed a bug in LTK where a menu got posted multiple times if you right clicked while a page was being rendered. Removed a comma inside a string in configure.in. Kludge around broken PDF files that use char 32 but encode it as .notdef instead of space. Clean up various compiler warnings: use constructor args like "fooA" if there is a field named "foo". Everything now compiles cleanly under gcc 2.91.66, 2.95.2, and 3.0.1. Page objects now read all of the page rectangles (MediaBox, CropBox, BleedBox, TrimBox, ArtBox), as requested by the pdfTeX folks. Added a new PDFRectangle struct to hold these. Use XOutputDev's Type 3 font size hack in TextOutputDev too, so it does a little better job of extracting text in Type 3 fonts. Modify pdfimages to write one-bit images as PBM files. Work around a bug in cygwin's implementation of fseek. 0.93a (2001-nov-21) ------------------- Implemented the sh (shaded fill) operator for the axial shading type. Minor fixes to avoid compiler warnings. Cleaned up global variables -- moved many into instance vars and function args. Minor fixes for OS/2. Fix the system config file path for VMS. Fix an uninitialized var in XOutputDev that caused crashes on Alphas. Don't incrementally update the display in full-screen mode. For Type 1/1C fonts, use the FontBBox from the PDF FontDescriptor (instead of the one in the font file) if present -- this avoids problems with fonts that have non-standard FontMatrixes. Add the Euro character to WinAnsiEncoding. Track the bounding box of the clip region to make rendering patterns more efficient. Fix openTempFile() for Win32. 0.93b (2001-dec-11) ------------------- Added a duplex option to PSOutputDev and a -duplex switch to pdftops. Added XRef::PDFgetDocInfoNF() for pdftex project. Updated the VMS build script. 0.93c (2001-dec-12) ------------------- Completely rewrote the code that handles font encodings: - everything is Unicode-based - 16-bit fonts are handled much more cleanly - text output encoding can be set more flexibly New .xpdfrc config files. 1.00 (2002-feb-01) ------------------ More work on the font encoding rewrite: - use the ToUnicode font dict entry - pdfinfo and pdftotext (with '-htmlmeta') convert info strings to the selected text encoding Added key bindings for forward ('v') and backward ('b'). Added the pdffonts program which lists the fonts used in a PDF file. Fixed several problems in the TrueType font embedding code (for PostScript output). Accept named destination on command line. Added several new items to pdfinfo: file size, PDF version, tagged (yes or no), XML metadata (with the -meta option). Pdftops didn't get the portrait/landscape setting correct for PDF files with rotated pages. The TrueTypeFontFile class (including the Type 42 converter) now understands cmap format 6. Improved the "about" window -- mention the GPL, add a list of key bindings. Added Zcaron and zcaron characters to WinAnsiEncoding. The '0' keyboard shortcut didn't update the zoom popup menu. Handle the complete list of alternate names for the Base14 fonts. Fixed substitute font scaling in XOutputDev - scale only the width, not the height. Implemented stitching (type 3) functions. Handle the case of moveto/closepath/clip, which defines an empty clipping region. Move dependences into separate Makefile.dep files; get rid of the distdepend target. Move all of the configure-script-generated -D options out of the Makefiles and into a top-level .h file (aconf.h). Cleaned up the FreeType 1/2 detection code in the configure script. Pdfinfo prints dates in a more readable format. Fixed a bug in the Paeth image predictor. Handle annotations with multiple states. Another workaround for buggy X servers: clip points that are way out of bounds. Added libpaper support (for Debian). Generate PostScript DSC resource comments for PS (not just EPS) files. The save and restore (q/Q) operators shouldn't save/restore the path. Performance optimization: disable pattern drawing in TextOutputDev. 1.00a (2002-feb-25) ------------------- Added an optimized special case for one-bit images in XOutputDev. Implemented CID TrueType font embedding; added a psEmbedCIDTrueType option. The initialZoom X resource was broken. The reverse MacRoman encoding should return 32 for "space" (not 202, which is an alternate encoding). Tweaks to the FreeType 2 support: only disable hinting if the bytecode interpreter is disabled (i.e., disable autohinting but not bytecode hinting); add some padding to the glyph cache for CJK fonts. Added level3 and level3Sep options for the psLevel setting and corresponding -level3 and -level3Sep options to pdftops. Added a -level2 option to pdftops for consistency. Avoid a divide by zero in pdftotext. [Thanks to William Bader.] Added a Greek language support package. [Thanks to Alexandros Diamantidis and Maria Adaloglou.] Don't bother trying to extract a "builtin" encoding from a TrueType font. Accept either a page number or a page reference in a link destination. Update the fontFixedWidth flag in GfxFont after reading the char widths (used by the Acorn RiscOS port). Removed yet another (illegal but not caught by gcc) class specified from a .h file. Avoid using snprintf - it's not available everywhere. Improved the CMYK->RGB transform. Use mkstemps where available. 1.01 (2002-may-20) ------------------ Implemented Type 3 fonts. Implemented PostScript CID font embedding; added a psEmbedCIDPostScriptFonts option. Implemented PostScript 16-bit font substitution; added psNamedFont16 and psFont16 options. Moved the initialZoom setting from X resources to the xpdfrc file. Implemented the radial shading type in the sh (shaded fill) operator. [Thanks to Mike Sweet.] Added an 'include' command to the xpdfrc format. Added the displayNamedCIDFontX option so different fonts can be used within one character collection. Added a simple reverse video mode (-rv switch, xpdf.reverseVideo resource). Implemented stroked text in XOutputDev (with t1lib and FreeType2). [Thanks to Leonard Rosenthol.] Implemented stroked text in PSOutputDev. Added a built-in Unicode map for UCS-2. New key binding in xpdf: 'g' activates the page number text field. PSOutputDev will now embed external TrueType fonts in addition to external Type 1 fonts. The psEmbedType1Fonts and psEmbedTrueTypeFonts options were missing the "Fonts" suffix. Documentation in xpdf.1 for -freetype option was wrong. Added the Big5ascii Unicode map to the Chinese-traditional support package (maps 7-bit ASCII straight through). [Thanks to Lawrence Lai.] Modified the EUC-CN and EUC-JP encodings to pass 7-bit ASCII straight through. [Thanks to Lawrence Lai.] Avoid a divide by zero in XOutputDev. [Thanks to Simon Burge.] Remove old code in openTempFile that removed an extension from the name returned by tmpnam. Tweak the scrolling behavior when switching pages. [Thanks to Case Jones.] In the code that guesses character names (for font subsets), also handle names of the form 'ABnnn'. [Thanks to Colin Granville.] Fix the transform code for annotations. Improved the CMap file parser to handle more general PostScript lexical conventions. Added '-enc' option to pdfinfo. Added the small caps and oldstyle numbers from Adobe's Unicode corporate use area to the Latin1 and ASCII7 Unicode maps. The code in TextOutputDev that guesses Type 3 font size could generate a zero size, which resulted in div-by-zero errors. Various tools (including Adobe's) occasionally embed Type 1 fonts but label them Type 1C - so check for a '%!' at the start. Some tools embed Type 1C fonts with an extra whitespace char at the beginning - just skip over it. Fixed a typo in the Simplified Chinese add-to-xpdfrc file. Updates to dj_make.bat and the djgpp build instructions. Added a Turkish language support package. Updated VMS build scripts. [Thanks to Martin Zinser.] Modify the incremental display update code to redraw less often if most of the commands are vector graphics, as opposed to text and images. Tweak the Type 1 font bbox code to look at the bboxes in both the PDF font object and the embedded font file. Fixed the ETenms-B5-H CMap file (for traditional Chinese) to map the Latin characters to their proportional versions. Added an optional displayCIDFontX entry for one of the Arphic TrueType fonts in the traditional Chinese 'add-to-xpdfrc' file. Remove leading '-' on include statements in Makefiles. Added psASCIIHex parameter. Added the GBK Unicode map to the simplified Chinese language pack. Pdftotext now opens the text file in binary mode to avoid Microsoft's annoying automatic end-of-line translation stuff. Added an executeCommand function in goo/gfile.cc. [Thanks to Mikhail Kruk.] The %ALDImagePosition OPI comment was wrong if the page was scaled to a different paper size. The OPI code was saving the default transform matrix before calling setpagedevice, which can change the matrix. Fixed a crash when an inline image dictionary contains garbage. Upgraded to autoconf 2.53. Use unsigned int file offsets, to allow access to PDF files in the 2-4 GB size range; use fseek64/ftell64 if available. Fixed two floating point exception cases that came up with broken PDF files. Avoid a crash when printing an error message regarding an unnamed font. Default link border width should be 1. [Thanks to Michael Pfeiffer.] Minor tweak to build with FreeType 2.1.0. Handle "weird" characters in PostScript font names. PSOutputDev now handles PostScript XObjects. Added several more page attributes for the pdftex project. Transferred the copyright to Glyph & Cog, LLC. 2.00 (2002-nov-04) ------------------ Switched to the Motif toolkit. Support multiple open documents (in separate windows). Added document outlines to the viewer. Modified the text extraction (placement) algorithm. Implemented the JBIG2 decoder. Added a Latin2 language support package. Added support for movie annotations. Switched back to native LZW decompression code. Text extraction from Type 3 fonts was (partly) broken. The owner password checking code was missing a step in the case of 128-bit encryption. Added the 'printCommands' option to the xpdfrc file. Added key binding for '?' to bring up the about/help dialog. In TextOutputDev, ignore any text that's outside the page bounding box. Text extraction throws away "tiny" characters after the first 20000 per page, to avoid really slow runtimes with PDF files that use special fonts to do shading or cross-hatching; added the 'textKeepTinyChars' option to disable this behavior. Text extraction discards duplicated text (fake boldface, shadow effects). Added ctrl-F as a key binding for find. Added a "find next" function, bound to ctrl-G. Added ctrl-P as a key binding for print. Modified the DCT decoder to handle progressive and non-interleaved JPEG streams. Added key bindings for ctrl-Home and ctrl-End. Allow the initialZoom setting to be made in either the xpdfrc file or as an X resource. Added a Hebrew language support package. [Thanks to Roy Arav.] The "make distclean" target now creates (empty) Makefile.dep files in the three subdirectories. Initialize XRef::ownerPasswordOk. Correctly handle stroking of Type 3 fonts in PSOutputDev. Generate correct PostScript for fonts with "weird" character names (e.g., "("). Generate correct PostScript for images using Indexed color spaces with DeviceN base color spaces. Added lowercase Roman numerals to ISO-2022-CN.unicodeMap (simplified Chinese support package). Tweak the image scaling code to better handle flipped (top-bottom and/or left-right) images. Generate correct PostScript code for inline images and images in Type 3 fonts which are too large for a single PS string. Correctly handle indexed color spaces whose base color spaces have component ranges other than [0,1]. Optimized the DCT decoder. Fixed mistakes in the list of key bindings in the about/help dialog. Optimized the Flate decoder. Add literal names for punctuation and digits to the Unicode name table. Cygwin's popen wants mode "r", not "rb". Fixed a bug in the Type 4 function parser (the "if" operator wasn't parsed correctly). Fix a bug in PS output for TrueType fonts with no PDF encoding. Make the bbox size in FTFont more liberal (to avoid problems with fonts that have incorrect bboxes). Reverse the colors in PBM files generated by pdfimages, so the common case (an image mask filled with black) comes out correct. Add fseeko/ftello support which is basically identical to fseek64/ftell64. [Thanks to Nassib Nassar.] Modified column assignment in text extractor to account for characters that convert to multiple characters in the output encoding. Fix TrueType fonts which have an incorrect cmap table length. Work around a pragma bug in the version of gcc that ships with MacOS X 10.2. [Thanks to Frank Siegert and Andrew Stone.] Fix a problem that was causing an infinite loop when a damaged content stream contains an 'ID' command inside a dictionary. Handle the case where systempapername() returns NULL (libpaper support). Handle fonts which are defined directly in the font resource dictionary rather than as separate objects. Track process colors in Level 1 separable PostScript. Pdfinfo now checks the return value from mktime to avoid seg faults in flakey strftime implementations. If duplex is not enabled in PostScript output, leave the duplex setting alone, allowing the spooler to insert its own setting. Added three missing fclose calls. Change the default encoding for TrueType fonts (used when the PDF file doesn't specify an encoding) from MacRomanEncoding to WinAnsiEncoding. Move X_CFLAGS to the end of the list in CXXFLAGS (in Makefile.in) to avoid some of the FreeType2 include path problems. Fixed an obscure bug in the LZW decoder. [Thanks to Martin Schroeder.] Fixed a bug in decryption when using the newer (PDF 1.4) algorithm with shorter-than-128-bit keys. Minor optimization for image data streams: the ImageStream class can return an entire buffered line. 2.01 (2002-dec-05) ------------------ Redesigned the text extraction process: - process the text into "reading order" - added a "-layout" flag to pdftotext to switch back to the old style, where physical layout is maintained - use of the "-raw" flag is no longer recommended Added the -reload option for xpdf (in remote mode). Added support for external CID fonts; added the displayCIDFontT1 and displayNamedCIDFontT1 commands to the xpdfrc file. Handle the case of moveto/newpath/clip, which defines an empty clipping region (just like moveto/closepath/clip). Accept XYZ link destinations with missing array elements. Fix some problems with state save/restore triggered by Type 3 fonts that reference other fonts. Accept bogus font names based on "Symbol": Symbol,{Bold,Italic, BoldItalic}. Fixed color and font resource names in the xpdf man page. Was using delete instead of gfree in OutlineItem::~OutlineItem. Set the busy cursor in the find dialog while searching. Map variants of the copyright, trademark, and registered trademark symbols to the proper Unicode codes, not to Adobe's corporate use area codes. Fixed a floating point exception bug in TextOutputDev (check for a too-small denominator). Fixed a typo in TextOutputDev, in the code that generating blank lines to add vertical whitespace. Config files whose last line didn't end with a LF (or CR+LF) weren't being handled correctly. The code that handled CIDToGIDMaps in Type 2 CIDFonts was broken. Check the per-glyph bounding box in Type 3 fonts, and don't try to cache glyphs with bogus bboxes. Allow ToUnicode CMaps to use fewer than four hex digits in the Unicode char indexes. Added multithreading protection to the GlobalParams class. Fixed a bug in end-of-stream detection with the TIFF predictor. Added some characters to MacRomanEncoding to match up with Apple's definition. 2.02 (2003-mar-24) ------------------ Rewrote the text extractor code that assembles words into lines to better handle vertically overlapping lines. Add the "match" option for paper size (in PostScript output). Added support for external 16-bit TrueType fonts; added the displayCIDFontTT and displayNamedCIDFontTT commands to the xpdfrc file. Added an Arabic language support package. Added the Windows-1255 encoding to the Hebrew language package. A missing NULL check was causing a crash when closing the file in a single window (which clears out the window, but leaves it open). Deal with TrueType fonts whose glyph data is out of order - this affected both FreeType rasterization and PostScript generation. Munge font names in PSOutputDev to avoid names that are problematic for ghostscript because they start with an out-of-limits number (e.g., 1e999foo). Modify the TrueType font encoding deciphering algorithm in yet another attempt to match up with Acrobat's behavior. Bounds check the indexHigh value in indexed color spaces. The text extractor no longer bothers trying to get an average character width for Type 3 fonts, since it generally doesn't work very well (because Type 3 metrics are unreliable). Don't crash if the user hits ctrl-G ("find again") before doing a find. Set the button pixmap foreground color correctly. Handle text drawn backward on 180 degree rotated pages. Added a magic call to XtUngrabButton after calling XmCreatePopupMenu which appears to prevent some very odd problems (idea taken from the DDD source code). Fix the MacOS X fix (needed to include ). Fixed a bunch of Motif 1.x / X11R5 incompatibilities. [Thanks to William Bader and Albert Chin-A-Young.] Fixed various bugs in previously untested code in the JBIG2 decoder. Modify the XPDFCore destructor to avoid a bogus warning message from OpenMotif 2.2. Modified the Type 1C font parser to do proper bounds checking. Fixed the bounds checking in the TrueType font parser. Text extractor shouldn't do block merging in physical layout mode. Fixed a problem in PSOutputDev in level2sep mode with images in a Separation color space and with a non-default Decode array. Text extraction with "-raw" was concatenating lines from the bottom of one column and the top of the next. Handle Type 1C subroutines in the font converters. Correctly handle progressive JPEG images whose scans are slightly different sizes (e.g., the Y scan rounds up to a multiple of 8 pixels and the Cb/Cr scans round up to 16 pixels). Avoid a potential divide-by-zero problem in TextOutputDev. Modified the T1Font and FTFont modules to correctly handle glyphs that are larger than the font's claimed bounding box. Tweak dupMaxDeltaX parameter in TextOutputDev to avoid triggering on double characters. Improved detection in pdfinfo for ISO paper sizes. [Thanks to Hartmut Henkel.] Xpdf wasn't responding to the TARGETS atom, which prevented pasting the selection into various applications. [Thanks to Phillip Ezolt.] Handle XObjects with recursive references in their Resources dictionaries (in PSOutputDev). Change PSOutputDev to deal with invalid PDF files that use non-embedded TrueType fonts with no encoding. Check for undersized Widths arrays in fonts. Add bounds checking code to Array class. Updated VMS build scripts. [Thanks to Martin Zinser.] Tweak the TrueType font handling code (again): - char codes in symbolic fonts may or may not be offset by 0xf000 - discard empty tables because they sometimes confuse FreeType Fixed bounds checking in the Flate decoder. Removed a bogus error message for exponential functions without explicit C0/C1 values. [Thanks to Hartmut Henkel.] Handle the other Unicode cmap type (platform=0) in TrueType fonts. Added support for the SGI Motif horizontal paned window widget. [Thanks to Felix Ritter.] Ignore extra elements in link destination arrays. Accept external Type 1 font files with a suffix of ".ps" or no suffix at all. Add a bounds check in the DCT decoder. Added instructions for building xpdf.exe under cygwin/XFree86. Tweaked the word separation parameter for raw-mode text extraction. 2.03 (2003-oct-10) ------------------ Rewrote the text extractor to: - do a better job with rotated text; - handle right-to-left scripts; - be faster. Changed the zoom setting to use a percentage (relative to 72 dpi) instead of a zoom "factor". If the PDF file has an outline, open the outline pane initially. Added -f and -l options to pdfinfo; print multiple page sizes. The HAVE_XTAPPSETEXITFLAG test in XPDFApp.cc was backwards. The BitsPerComponent entry is optional in image mask objects. Render any annotation with an appearance stream, instead of just Widget and Stamp annotations. Fix a bug in the TrueType font checker: the test for an unsorted 'loca' table was wrong. Modify the TrueType cmap selection algorithm yet again to try to match Adobe's behavior. Changed sqrt(2) to sqrt(2.0) in pdfinfo.cc to make various compilers happy. Fixed a deadlock problem (when MULTITHREADING is set); cleaned up some other problems with the locking code. Fixed a bug in the interpolation code for type 0 (sampled) functions. Implemented type 1 (function-based) shaded fills. Fixed some stupid bugs in the JBIG2 decoder (introduced with the previous optimization work). Fixed a typo in the code that parses vertical font metrics for CID fonts that was causing a seg fault. Fixed a couple of bugs that were causing seg faults with badly damaged PDF files. Limit the number of nested Forms to avoid infinite recursion (in buggy PDF files). Add a special case for rectangular clip regions - make sure these don't drop pixels on the right and bottom edges. Tell FreeType not to use glyph bitmaps when in anti-aliased mode. Read all of the border style info for links. All of the shaded fill types now do at least one bisection to avoid problems when the colors at the endpoints of the domain are the same. If the Length2 parameter for an embedded Type 1 font was incorrect (too small), pdftops was losing font data. Deal with (broken) DCT streams that use the same component ID number for different components. The MediaBox page attribute was not being inherited correctly. Fixed a bug in the Type 1C font converter related to local subroutines. The Type 1C -> Type 1 font converter was allocating the font dictionary one slot too small. Added a missing private dictionary entry to Type 1 fonts generated by the Type 1C converter. [Thanks to Michael Shell.] Fixed bugs in the tiling pattern fill code. Try the TrueType 0xf000 char code offset hack for the MacRoman encoding too (in addition to MS Symbol). Update the font metrics info for the Base 14 fonts to include the Euro character. SECURITY HOLE: Escape various characters in URLs before running a web browser (or movie viewer). [Fixed in 2.02p11] SECURITY HOLE: In the dialog used to verify "launch" links, provide a scrolling view if the command to be run is excessively long. [Fixed in 2.02p11] Added an option to disable insertion of page breaks (form feed characters) in extracted text (pdftotext -nopgbrk; xpdfrc "textPageBreaks" option). Check for 8-bit fonts that specify an out-of-range FirstChar or LastChar. Correctly handle an obsolete Type 2 charstring op (in the Type 1C-to-Type 1 font converter). [Thanks to Helge Blischke.] Use the font encoding info to fill in holes in the ToUnicode map. Added character names for Bulgarian (in the Cyrillic support pacakage) and Greek. Handle clipping to text in xpdf and pdftops. Fix color space detection in DCT decoder. [Thanks to Dwight Kelly.] Added the "unicodeToUnicode" xpdfrc option, intended (initially) for Arabic support. Handle the case in PSOutputDev where two font objects refer to the same embedded TrueType font, but with different encodings. [Thanks to Frank Siegert.] Kill any pre-existing path before drawing a form (or annotation). Save state before rendering page content; restore state afterward. Fix Stream::reset/close to work correctly with encoder streams; fix PSOutputDev to use Stream::close consistently. Fix a seg fault when hitting the 'back' button after closing a file. GfxState::getStrokeGray was returning the fill gray value (this only affected Level 1 PS output). Change PSOutputDev to reuse dictionaries in Level 1 mode (since Level 1 PS interpreters don't do garbage collection). [Thanks to Frank Siegert.] PSOutputDev was generating incorrect translations for landscape-mode pages. Implemented shading pattern color spaces. PSOutputDev wasn't correctly handling Type 3 fonts which used image resources (as opposed to inline images). [Thanks to Frank Siegert.] The fix from 1.00 which clipped out-of-bounds points was a bit too aggressive. Do proper Floyd-Steinberg dithering in XOutputDev. Don't automatically check for a null owner password (to match Adobe's behavior). Allow the FlateDecode filter in Level 3 PostScript output. Fixed small bugs in the Type 1C -> Type 1 converter and Type 1C -> Type 0 converter. [Thanks to Tom Kacvinsky.] Work around another weird Motif problem with the right button menu (which was sometimes causing the menu to not be displayed). Make the code that handles fonts defined directly in the resource dict more robust. Add a brief description of the outline pane to the xpdf man page. Ignore extra operands to content stream operators. Fixed a bug in the CCITTFax decoder. Allow the Count entry in a Pages dictionary to be a real number (because some PDF generators actually do this). Shading pattern fills weren't being clipped correctly. Incorrect shallow copies in GfxRadialShading and StitchingFunction. The StitchingFunction destructor wasn't checking for funcs being NULL. Change the TrueType code-to-GID mapping code so it looks at the TrueType 'post' table. Set the print command in the print dialog once at startup, don't change it each time a file is (re)loaded. Generate the %%BoundingBox comment in regular PostScript files (not just EPS files). Fixed a bug in the Unicode CMap parser. 3.00 (2004-jan-22) ------------------ New PDF rasterizer ("Splash"). Added support for PDF 1.5: - JPX (JPEG 2000) decoder - XRef streams - object streams - DeviceN color spaces with up to 32 components - Added new CMaps to the CJK language support packages Replaced pdftopbm with pdftoppm (which can generate PBM, PGM, and PPM files). Reorganized the font file parser code into a new library ("Fofi"). Removed support for FreeType 1.x. Removed support for X server fonts - Xpdf (and pdftoppm) will now search for the URW fonts (from ghostscript). Changed the "-t1lib" and "-freetype" switches; replaced the "t1libControl" and "freetypeControl" config file options with "enableT1lib", "enableFreeType", and "antialias". Added the "-box" option to pdfinfo. Added imageable area support to PSOutputDev (for CUPS); added the "psImageableArea" config file option. Added the "-nocrop", "-expand", "-noshrink", and "-nocenter" switches to pdftops; added the "psCrop", "psExpandSmaller", "psShrinkLarger", and "psCenter" config file options. Dictionary size was in PostScript code generated for Type 3 fonts. The PS code generated for images in Type 3 characters was broken. Tweaked the text extractor. Accept xref entries that are one byte too short (matching Adobe's behavior). Change things so "xpdf -h" and "xpdf -v" work if DISPLAY isn't set. Fix a problem in the damaged file repair code that handles the trailer dictionary. Use the "Last" entries in "Outlines" objects - this avoids a problem with PDF files generated by buggy software that, e.g., sets the last item's Next pointer to point to itself. PSOutputDev was not handling DeviceN color spaces correctly in Level 2 images. Fixed a stupid little bug that broke PS output for JBIG2 images. Work around a Lesstif bug: set up an extra callback so hitting in the find dialog performs a search. [Thanks to Elliott Hughes.] Pdftops was crashing on zero page PDF files. Add an AC_PREREQ call to configure.in. Change the 'find' dialog so the text entry box resizes with the dialog. Skip extraneous zero bits at the start of a CCITTFax stream. The PostScript text clipping operator was missing a 'newpath'. [Thanks to Frank Siegert.] Fix a bug in tiling patterns with bboxes that don't start at (0,0). Fix a bug in Type 3 font handling with rotated text. The tiled pattern fill code was destroying the current path, which broke the fill+stroke operators when the fill color space was a tiled pattern. ICCBased color spaces don't always set their Ranges values correctly, so just use the values from the alternate color space. Modified GHash to accept int or void* - this avoids some conversion warnings. Check for missing Type 3 CharProcs - avoid a segfault. Pdffonts now marks all Type 3 fonts as embedded. Outline entries with no Title string weren't being handled correctly, resulting in segfaults. PSOutputDev now forces the text horizontal scale factor to be non-zero to avoid singular font matrices in the PS code. Tweaked the error recovery in the CCITTFax decoder. The LZW/Flate predictor should treat any Predictor value (in the stream dictionary) >= 10 identically. PSOutputDev and pdffonts check for NULL font objects (which can happen, e.g., because of missing CMap files). Swap the left and right mouse wheel button numbers. EPS output ("pdftops -eps") now uses the CropBox instead of the MediaBox as the EPS bounding box. 3.01 (2005-aug-17) ------------------ Added the continuous view mode, including the '-cont' switch and the 'continuousView' config file option. At high zoom levels, don't rasterize the entire page - this avoids problems running out of memory. Added "search backward" and "match case" options to the find dialog. Support explicitly masked images and soft masked images. Add support to DCTStream for 16-bit quant tables. Don't segfault if the user clicks on an outline entry with a broken destination. Changed the makefiles and configure script to skip building pdftoppm (in addition to xpdf) if X, Motif, or FreeType is not found; changed the error message in the configure script to match. Move an inline function in JArithmeticDecoder.cc to avoid compiler errors. Fixed a bug in the rasterizer that was sometimes causing infinite loops with round line caps on vertical lines. Various rasterizer optimizations. Look for intermediate resize events - try to avoid lagging when the user is doing an opaque resize. The FormType key in Form XObjects is optional. Handle external 16-bit TrueType fonts correctly, using the Unicode cmap. Add class declarations to TextOutputDev.h to work with stricter C++ compilers. Support FreeType's weird include file stuff (ft2build.h, etc.). Fixed a bug handling empty paths. Fixed a text positioning problem in PostScript output. Handle TrueType collections in FoFiTrueType.cc. FoFiTrueType constructor was reporting a failure if the post table was bad - this should be non-fatal. Type 1 font parser was missing a NULL test. Mask chars passed to isdigit in goo/parseargs.c to avoid problems with signed chars. Added more error checking to the CCITTFax decoder. Fixed a bug (computing the MCU size) in the DCT decoder. Change a test in the Splash stroke code to avoid x86 floating point weirdness. Reorganized the decryption code to allow security handler plugins; removed the NO_DECRYPTION #ifdefs. Added a plugin interface, initially just for security handlers. Support color key masked images and explicitly masked images in PS output (Level 2 only). When checking for aliases of the Base 14 fonts, ignore spaces in the specified font name. Handle encrypted PDF files that are missing the file ID string. Handle tiling patterns more efficiently in the PostScript output. Rewrote the code that handles color spaces in PostScript output. Fixed a bug in the Type 1C font parser - zero-length indexes (and zero-length names) weren't handled correctly. Handle shaded fills more efficiently in the PostScript output. Implement the remaining shading types (4-7). Rearranged the Splash color modes. Add the EarlyChange parameter to LZWStream when generating PostScript. Check for zero values in line dash arrays in PSOutputDev. Fixed an uninitialized variable in JArithmeticDecoder which was causing crashes. Treat unknown CMap names as identity mappings (to match Adobe's behavior). Fixed bugs in the XRef parser related to XRef streams in updated files. Added a missing call to FT_Done_Glyph which was causing a memory leak. [Thanks to Dave Formanek.] Fixed a bug in text copying that was causing the last word to be dropped on some pages. Tweaked the image width/height computation in Splash::drawImage and Splash::fillImageMask to make striped images work better. Ignore minus signs in the middle of numbers (to match Adobe's behavior). Missing '%s' in format strings for dates in pdftotext '-htmlmeta' mode. Change the TrueType code-to-GID mapping code so it looks at the standard name-to-Unicode mapping before the ToUnicode mapping defined in the font object. Added a matteColor setting (command line option and X resource). Tweaked the CMYK->RGB transform. Fix some problems in tracking the character position (to match up with Adobe's highlight file format). Handle moveto/closepath/stroke correctly. Check for singular text matrices and font size of zero in PSOutputDev. Clip PS output to the size of the page (avoiding any gibberish that lies outside the MediaBox, in the case where the MediaBox is smaller than the paper). If the line dash element in an annotation's Border array is of an invalid type (i.e., not an array), don't draw the link at all (this matches Adobe's behavior). Don't remap small caps and oldstyle glyphs in the name-to-Unicode table - it messes up TrueType font encodings. Pdftoppm wasn't setting the paper color correctly in mono and gray modes (this only showed up on big-endian machines). Missing NULL check was causing crashes when attempting to read non-PDF files that happened to contain the string '%PDF'. Fixed a problem in the text extractor that was breaking up words. Handle vertical text (CJK fonts) in PS output with TrueType fonts that are missing the vertical metrics tables. Handle the case where a font object and the corresponding embedded font are different types. Handle basic crypt filter functionality. Added more value checking in the XRef parser, to avoid potential security problems. Updated the CJK language support packages: replaced the displayCIDFontX references with displayCIDFontTT; added pointers to free TrueType fonts. Added a missing error message when SplashOutputDev can't parse an embedded TrueType font file. PDFCore and TextOutputDev now correctly handle searching for Unicode strings, including real Unicode case-folding. Throw away tiling pattern fills that are completely outside the clip region. The JPEG 2000 inverse reversible multiple component transform code was wrong. Fixed some bugs in shading pattern fills: clipping was wrong, and background color was not implemented. Added tool tips for the toolbar buttons. Decrease the max depth of recursive patch mesh filling if the pattern has a large number of patches. Highlight the find text whenever the find dialog is mapped. Handle page boundary boxes with reversed coordinates. Fixed a bug in the text extractor code that handles duplicated text. Optimization work on SampledFunction::transform(). Use the CropBox instead of the MediaBox as the display region. Dither for PseudoColor (8-bit) displays. Fix a bug in DCTStream that was causing an infinite loop with corrupted DCT image data. Fix a bug in the ToUnicode CMap parser. Fix a bug in the text extractor - negative font sizes weren't being handled correctly. Fix a bug in the text extractor - in certain cases, out-of-bounds text could cause crashes (generally only in damaged PDF files). Fix a read-past-end-of-array bug in the JBIG2 decoder. Fix a case where pdftops was generating lines longer than 255 chars. Optimize redraws - don't regenerate the XImage every time redrawRect is called. The ASCII85 decoder wasn't skipping whitespace properly. Optimize text extraction: skip (non-inline) image setup entirely. Added initial transparency support (stroke/fill alpha and blend mode). Added support for the overprint setting in PostScript output. Fixed various buffer overflow bugs. Handle negative font sizes and horizontal scaling correctly - this affected PSOutputDev for all text operators, as well as the TJ operator for all OutputDevs. Fixed a buffer overflow in the CCITTFax decoder. Fixed an out-of-order entry in the list of font name aliases. Fixed a backward loop in the PostScriptFunction code. Treat a zero-length base URI the same way as a nonexistent base URI. Add a divide-by-zero check in TextOutputDev (the problem was happening in cases of mixed horizontal and vertical text). PSOutputDev wasn't rounding the page bounding box coordinates correctly. Support the SOF1 marker in DCT (JPEG) image streams. Minor changes to GlobalParams.h and JPXStream.h because some compilers don't like anonymous structs inside anonymous unions. Xpdf now complains about a negative page number. Changed GString::cmp and GString::cmpN to correctly handle '\0' chars in the middle of strings. Fixed the radial shading code; corrected the handling of the 'extend' parameters. Added the gmallocn and greallocn functions. Fixed a bug in the TIFF image component predictor which shows up with components that are not 1 or 8 bits wide. Optimized FlateStream::loadFixedCodes(). For non-embedded Base-14 fonts, don't use the ascent/descent/bbox values from the FontDescriptor - various PDF generators get them wrong. Fixed a bug in the text extractor - words on the same line (especially in tables) were being split vertically onto multiple lines. Automatically select the correct radio button ("print with command" vs. "print to file") in the print dialog. Don't create the "open" and "save as" dialogs until needed - this avoids stat-ing every file in the directory at startup. Changed the Big5 and Big5ascii encodings (in the traditional Chinese language support package) to include characters from the Unicode database (which aren't mentioned in the Adobe character collection documentation). Added the '-pagecrop' switch to pdftops. Tweaked the RGB->gray and CMYK->gray conversion functions to match the PDF spec. The JPEG 2000 decoder wasn't correctly handling codeblocks split across multiple packets/layers. Fixed a typecast that caused compile errors on 64-bit systems. The CMap parser wasn't handling the 'cidchar' construct. Handle the case in PSOutputDev where two font objects refer to the same embedded 16-bit TrueType font, but with different CIDToGIDMaps. Changed the configure script to report more accurate warnings when it can't find X / Motif / FreeType. Encryption with revision=2 always uses a 40-bit key, regardless of the specified Length value. Yet another minor change to the TrueType font encoding deciphering algorithm. Don't completely invalidate the Catalog if one (or more) of the page objects are bogus -- just skip over those pages. Removed the workaround in pdftops for too-small Length2 values in Type 1 fonts -- it was causing problems on various PostScript printers. Started adding error checking to the JBIG2 decoder (this is nowhere near complete yet). Extended the "unicodeToUnicode" config option to also apply to CID fonts. Added the narrow Latin characters to the Adobe-Korea1.cidToUnicode file in the Korean language support package. Fixed the code that handles page rotation in PSOutputDev. When converting a Type 1C glyph to a Type 1 glyph, insert closepath operators as appropriate. Check for a sane 'loca' table in TrueType fonts (FoFiTrueType::parse). Fix PSOutputDev to correctly handle the case of an empty name in a font encoding. 3.02 (2007-feb-27) ------------------ Added anti-aliasing for vector graphics; added the vectorAntialias xpdfrc option; added the "-aaVector" switch to xpdf and pdftoppm. Implemented stroke adjustment (always enabled by default, ignoring the SA parameter, to match Adobe's behavior), and added the strokeAdjust xpdfrc command. Support PDF 1.6 and PDF 1.7. Added support for AES decryption. Added support for OpenType fonts (only tested with 8-bit CFF data so far). Added user-configurable key/mouse bindings - the bind/unbind xpdfrc commands. Cleaned up the full-screen mode code and added the ability to toggle it on the fly (the default key binding is alt-f). Pdfimages with the -j option now writes JPEG files for 1-component (grayscale) DCT images, in addition to 3-component (RGB) images. Fixed bugs in handling sampled (type 0) functions with 32-bit samples. Fixed some things to support DeviceN color spaces with up to 32 colorants. Pdftops now constructs the %%Creator and %%Title DSC comments from the relevant information in the PDF Info dictionary. Tweak the TrueType font encoding deciphering algorithm. Added the "mapUnkownCharNames" xpdfrc option. Fix a bug (that only showed up with certain window managers) in the intermediate resize event optimization. [Thanks to Michael Rogers.] Check for a broken/missing embedded font (this was causing xpdf to crash). Added support for transfer functions in PostScript output. Be a bit more tolerant of Link destinations that contain null values for positioning parameters. Use ordered dot dithering instead of clustered dot dithering at resolutions below 300 dpi (for monochrome output). Fixed security holes (bounds checking issues) in several places. Don't bother creating a SplashFont (allocating memory) for fonts that are only used for hidden text - this avoids problems with fonts of unreasonably large sizes. Clipping in TextOutputDev was off for characters on the left edge of the page. The scn and SCN operators weren't correctly handling colors with more than four components. FoFiType1::writeEncoded wasn't always correctly finding the end of the encoding. Use the ColorTransform parameter in the DCTDecode stream dictionary. Type 3 fonts are allowed to have a bbox of [0 0 0 0], which means "unspecified" -- don't issue error messages in that case. Perform the transform (to device space) in Splash instead of in SplashOutputDev -- this is needed to correctly handle round joins and caps on stroked paths. PSOutputDev now rasterizes any pages that use transparency. Limit the crop, bleed, trim, and art boxes to the edges of the media box (per the PDF spec). Change GString to increase the allocation increment by powers of two. Handle whitespace in hex strings in CMap files/streams. Use strings instead of names for separation colorant names in PSOutputDev. For explicitly masked images where the mask is higher resolution than the image, use the soft mask code. Avoid problems with very large x-steps in the PostScript output for tiling pattern fills. Avoid a divide-by-zero in stitching functions which have a subfunction with empty bounds. Honor the "Hidden", "NoView", and "Print" flags on annotations. Rewrote the pixel rendering code in Splash to use a single set of pixel pipeline functions. Added support for transparency groups and soft masks. Fixed the transparency blend functions to match the addendum published by Adobe. Changed Splash/SplashBitmap to store alpha in a separate plane. Setting the color space now selects the correct default color for that color space. Remove the mutex lock from GlobalParams::getErrQuiet() to avoid a deadlock when parseCIDToUnicode() or parseUnicodeToUnicode() calls it from inside a locked section. Added error checking (on the argument count) in the sc/SC/scn/SCN operators. Skip over notdef glyphs in TrueType fonts (which sometimes get drawn as little boxes), to match Adobe's behavior. Painting operations in a Separation color space with the "None" colorant or a DeviceN color space with all colorants set to "None" never mark the page. Fixed an obscure bug in the JPX decoder - it wasn't reading the extra stuffing byte in the case where the last byte of a packet header was 0xff. Change the TrueType font parser (FoFiTrueType) to change the glyph count rather than report an error if the 'loca' table is too small. Fixed a couple of bugs in the JBIG2 decoder. Added stochastic clustered dot dithering. Added the screenType, screenSize, screenDotRadius, screenGamma, screenBlackThreshold, and screenWhiteThreshold xpdfrc settings. PSOutputDev now correctly handles invalid Type 3 charprocs which don't start with a d0 or d1 operator FreeType 2.2.x support - get rid of the FT_INTERNAL_OBJECTS_H include, and add some 'const' declarations. Handle PDFDocEncoding in Info dictionary strings. Tweak the xref repair code - ignore whitespace at the start of lines when looking for objects. Added the "-exec" switch to xpdf. Removed the xpdf.viKeys X resource. Changed the color key / explicit masked image code in PSOutputDev to generate better PS code, including a Level 3 option. Tweaked the DEBUG_MEM code for performance. Move the JBIG2 global stream reading code into reset() instead of the constructor - this way, pdftotext doesn't end up reading the global stream. Added the "-preload" option to pdftops and the psPreload xpdfrc command. Added the "zoom to selection" command (on the popup menu). Fix a bug (in xpdf/pdftoppm/pdftops) with tiling patterns whose bbox size is different from their xStep/yStep. Implemented stroke with pattern color spaces. Following a link to a page whose CropBox was different from the MediaBox was resulting in an incorrect scroll position. Parse truncated date strings from the Info dictionary correctly. Change FoFiType1 to handle Type 1 fonts with two /Encoding keys. Extend the PSOutputDev shaded fill code to handle DeviceCMYK shaded fills in level2sep and level3sep modes. Detect infinite loops in the Page tree. Optimized the ASCII85Encoder code. Tweaked the text extractor to do a better job of lining up rows of text. Leave images compressed (or re-compress them with RLE) in PostScript output when setting up images for forms and Type 3 fonts (or with -preload). Extend FoFiType1 to handle Type 1 fonts with octal character codes in their encodings. Use a custom string formatter to avoid problems with locale-based decimal formatting (commas instead of periods) in PS output. Allow comments in PostScript-type functions. Change the TrueType font parser (FoFiTrueType) to delete glyf table entries that are too short. 3.03 (2011-aug-15) ------------------ Added the "fixed pitch" text extraction mode. Modified "pdftops -paper match" to handle PDF files with different-sized pages, i.e., it will now select the matching paper size on a page-by-page basis. Add ability for pdftoppm to write to stdout. Added the pdfdetach tool. Implemented 256-bit AES decryption. Commented out the t1lib section in the configure script -- t1lib has some potential security holes, and hasn't been updated in years. Redesigned the font configuration xpdfrc commands: removed the displayFontT1, displayFontTT, displayNamedCIDFontT1, displayCIDFontT1, displayNamedCIDFontTT, displayCIDFontTT, psFont, psNamedFont16, and psFont16 commands; added the fontFile, fontFileCC, psResidentFont, psResidentFont16, and psResidentFontCC commands. Switched from GPLv2 to dual v2/v3 licensing. Performance: cache tiling patterns. Implemented text fills with pattern color spaces. Rewrote the image and image mask rendering code to be more accurate and faster. Fixed a bug in PDFCore that could sometimes cause crashes at high zoom levels. Implemented embedded CMap streams. Added the 'setSelection' command. Added the 'rotateCCW' and 'rotateCW' commands. Added the 'psFontPassthrough' xpdfrc command. Added the 'launchCommand' xpdfrc command. Implemented alpha-type soft masks. Added "Form: AcroForm|XFA|none" to pdfinfo output. Added support for image masks filled with pattern color spaces. Text search wasn't finding all occurrences of a string on rotated pages (i.e., pages where the primary rotation, as displayed, was not horizontal). The text extractor now uses "ActualText" spans if they are present. Modified PSOutputDev so it always check for transparency; if Splash is not available, it now prints a warning. Handle the blending color space for soft masks. Added the disableFreeTypeHinting xpdfrc option. Added the psAlwaysRasterize xpdfrc option. Added support for transfer functions in the rasterizer. Optimized the JPEG 2000 decoder to use less memory. Do fill adjustment (similar to stroke adjustment) on simple rectangular fills. Added the antialiasPrinting xpdfrc setting. Added '%i', '%j', and '%k' (mouse pointer page and position) to the available options for the 'run' command. Links with the underlined border style were being drawn with the lines over, instead of under, the text. Add #include to XPDFTree.cc (to avoid problems with certain compilers). Change XRef::fetch() to avoid infinite loops caused by odd damage to the xref table (e.g., where a stream object's "Length" value is an indirect reference that points to another stream object). Minor fix in GString.cc to deal with an error in newer C++ compilers (pow() can take int or double args). Handle embedded fonts which are declared with the wrong font type, including 8-bit fonts declared as CID fonts and vice versa; this was causing various problems, including crashes and invalid PostScript output. In text extractor, don't drop horizontally overlapping words onto a separate line. The numbers in the operand to the TJ operator should be multiplied by the current horizontal scaling parameter. Fixed a bug in the Type 1C-to-Type 1 font converter -- need to escape strings in the font dictionary. The zero-font-size check in PSOutputDev.cc was broken. Fixes for the form field appearance regeneration code: handle Unicode strings (by downconverting to Latin1); check for "True" in addition to "Yes" as a button value. Modify XPDFTree to limit the widget height to 32767 pixels -- this avoids crashes with very large outlines. Modify FoFiType1 to handle PFB headers in Type 1 font files. Allow image mask decode arrays to be [1.0 0.0] in addition to [1 0]. Tweak the form field appearance regeneration code. PSOutputDev now sets up resources for DR dictionaries in forms (in case the form field appearances are regenerated). For TrueType fonts which are marked symbolic, the cmaps should be ignored. Change the handling of numeric characters in Unicode text output -- they are now treated as left-to-right, which isn't strictly correct, but does result in correct visual formatting. Modify FoFiTrueType to handle bogus loca table entries where the offset is past the end of the glyf table. Fixed shading pattern fills to do clipping and background fills correctly. Change the code that reads the page tree to be more flexible with regard to improperly constructed trees. Changed the PostScript header comment from "Produced by xpdf/pdftops x.yy" to "XpdfVersion: x.yy" to make it (mostly) DSC-compliant. Fixed PSOutputDev to handle page rotation correctly where the PDF page is rotated 90 or 270 degrees and the page width (before rotation) is greater than its height (and vice versa for 0 or 180 degrees). Unbalanced save/restores inside form XObjects (which can be happen in damaged or incorrectly constructed PDF files) could cause crashes. The CCITTFax decoder could go into an infinite loop on certain types of corrupt input. Added the "drawAnnotations" xpdfrc command. Added the "psUncompressPreloadedImages" xpdfrc command. Escape newlines and other non-printable characters in the custom color DSC comments in PostScript output. Added the tilingPatternFill and *ShadedFill functions to PreScanOutputDev to speed it up. Too many elements in an image's Decode array should not be a fatal error. Fixed a buffer overflow security hole in StreamPredictor. Empty pages (zero-length content streams and null contents) were causing crashes in continuous view mode. Handle line dash arrays that start with a zero element correctly. PreScanOutputDev was not correctly detecting all transparency - in some places, it was looking only at the blending mode, and not at the opacity setting. Force halftone screen size to be a power of 2. This allows optimizing the halftoning code. Fixed a bug in the JBIG2 MMR decoder (and also in the CCITTFax decoder) that was causing array references with negative indexes. Fixed a bug in the transparency code that was causing memory overruns. Fixed a 64-bit bug in the ASCII85 encoder. The focusToPageNum was crashing in full-screen mode - it should simply do nothing. Added '%p' (current page number) to the available options for the 'run' command. Tweak the behavior with PDF files that have pages of different widths in continuous mode: scroll horizontally to avoid blank space on the left side (when changing pages via entering a page number or clicking on a link). A closepath followed by a lineto should create a new subpath for the lineto. Fixed a buffer overflow in the CCITTFax decoder. Adobe Acrobat appears to ignore the flatness setting when rasterizing curves, so Xpdf now does the same. (Oddly, Acrobat passes the flatness setting through to PostScript output, which ends up making the PS file look different from the PDF file - so Xpdf continues to pass flatness through to PS files, too.) Pdfimages now ignores tiling pattern fills (for performance reasons). Fixed a bug in PDFCore that could sometimes cause crashes at high zoom levels. Use std::sort (with functors) in place of qsort (if available) - this can be significantly faster. Hitting "close" while Xpdf is in full-screen mode (with only one file open) was crashing. Tweak the TrueType font encoding deciphering algorithm. Rewrote the CCITTFax decoder inner loop - this fixes a security hole. Fixed two security holes (missing bounds checks) in the DCT decoder. Do the correct zooming for "Fit" and "FitR" destinations. Implement the rotation value in the form field appearance regeneration code. When PSOutputDev rasterizes a page (because it contains transparency), the image is broken into stripes if it is above a size threshold. Don't clip the other page boxes to the MediaBox at the intermediate (Pages) nodes; only do it at the leaf (Page) nodes - the other boxes can be specified before the MediaBox is specified. Split several special cases off from Splash::pipeRun, for performance. Add a sanity check for excessively large font sizes (which caused problems because Xpdf tried to allocate memory for a font cache). Fixed a bug in the GfxCIDFont constructor involving reading the vertical metrics. Rewrote the code that handles annotation transforms - it was not handling non-rectangular transforms correctly. Tweak the Type 3 bbox code to allow some slack on the left/bottom edges, as well as the top/right edges. Fixed a bug that was causing problems rendering Type 3 fonts to 1-bit monochrome output. Handle the case where AES padding is invalid. Changed XRef::getNumObjects to return the number of used entries, instead of the same value as returned by XRef::getSize(). Ignore bogus entries in the TrueType table directory instead of immediately giving up on the font. Fixed a bug in the radial shading code that could cause divide-by-zero errors. Tweaked the TrueType font fixup code to deal with an extra zero entry at the end of the loca table. Increased font cache sizes to improve performance. Tweaked the TrueType notdef kludge to skip the notdef glyph only if it was caused by a character for which a Unicode/MacRoman mapping could not be found. Added another font size sanity check, this time for Type 3 glyphs. Added initial support for optional content (layers) - no GUI yet. For CID fonts that specify the 'Adobe-Identity' or 'Adobe-UCS' collection, use an identity mapping for char code-to-Unicode conversion. Updated the error function to take a category argument, and to use GString::format instead of printf; added an error callback. The PDF spec claims that names are limited to 127 chars, but Distiller 8 will produce longer names, and Acrobat 8 will accept longer names -- Xpdf will now handle them, too. Change the Catalog code so it doesn't load the entire page tree at startup (this only helps the command line tools - the viewer scans all of the pages at startup). Clip opacity values to the range [0,1]. Handle glyph names of the form 'unixxxx' (similar to Ann, Axx, etc.). Resolution of rasterized pages in PostScript output was not being computed correctly (resulting in overly large images). Extend the mapUnknownCharNames config command to cover CID fonts. Zero-length segments in the middle of stroked paths were not being handled correctly -- they should be completely ignored, except for the special case of a zero-length path with round line caps. Various optimizations to the Splash rasterizer. Allow "Identity" and "Default" as the transfer function in soft masks. Tweaked the ToUnicode CMap parser to allow <00xx> char codes for 8-bit fonts. TextPage::clear() was not clearing the lists of underlines and links. Changed the CCITTFax decoder to correctly handle the interaction between the EndOfLine and EncodedByteAlign parameters. Fixed a bug where xpdf wouldn't go to a destination specified on the command line, if continuous mode was enabled (xpdf -cont file.pdf +foo). Fixed a bug in the FreeType interface code which was causing incorrect positioning/sizing of characters. Tweaked the FreeType interface code - use 'light' hinting with Type 1 fonts (because it generally looks better). Tweak the Windows font searching code to handle "Black" fonts. Fixed a bug in the PostScript-type function parser -- real numbers that start with a decimal point weren't being handled correctly. Changed the way filled-and-stroked text is handled -- use Splash to do both the fill and the stroke (rather than using the font ending to do the fill and Splash to do the stroke); also turn off stroke adjustment when drawing stroked text. Ignore generation numbers on compressed objects (to match Adobe's behavior). Changed the PostScript PageSize policy from 3 to 6 (choose next largest paper size, don't rescale). Check that the mask values for color key masked images are in range -- both Ghostscript and Distiller choke on the PostScript if invalid mask colors are specified. Fixed a bug in generic region decoding in the JBIG2 decoder. Fixed the lexer to handle large real numbers correctly. Pdftops wasn't correctly handling tiling patterns that use both fill and stroke operators. Report an error if the Size array in a sampled function includes any zeros. The PostScriptFunction.codeString field wasn't being initialized correctly. Invalid object streams were not being handled correctly. Check for loops in xref tables. Handle the case where a content stream ends after the 'ID' operator which starts inline image data. Check for invalid object streams (i.e., an object stream can't be inside another object stream. Add a recursion limit to the object parser - this avoids stack overflows with various sorts of damaged PDF files. Handle various parameter settings in the extended graphics state dictionary. An invalid document outline (missing fields) could cause a crash. Added an overprint preview mode (for CMYK output only). Correctly handle FitH/BH/V/BV link destinations that contain null values for positioning parameters. If the PDF file doesn't define a BaseURI, set one based on the location of the PDF file -- this allows relative links to be handled correctly. Use ResusableStreamDecode when generating Level 3 PostScript for an explicitly masked image. Tweak the Type 1 font parser to handle encodings with multiple characters on one line. Invert subtractive color components before passing them to the blending function. Fix an invalid array access in SplashOutputDev::setSoftMask() in CMYK mode. A PS interpreter may attempt to read past the end of a preloaded image, so we need to check for that. Fixed several overflow/uninit bugs in JBIG2Stream. Fix the CCITTFax decoder to correctly find end-of-file (RTC/EOFB) markers when the EndOfLine parameter is false. Don't limit the startxref offset to 10 digits - some PDF generators use extra leading zeros. Use the "DV" (default value) field in text annotations if the "V" (value) field is missing. Increase the number of digits printed for floating point numbers in PostScript output -- it was running into numerical accuracy problems on large pages. Fixed integer overflow bugs in Catalog.cc and GfxFont.cc. Zero-length tables in TrueType fonts should be treated as missing. ToUnicode CMaps map char codes to Unicode; .cidToUnicode files map CIDs to Unicode -- ToUnicodeCMaps were being handled incorrectly. Added the "psRasterResolution" and "psRasterMono" xpdfrc commands. Added code to FoFiTrueType to check for entries in the table directory with bogus tags -- this handles the case where the number of tables given in the header is too high. Negative shift values in the PostScript bitshift operator were being handled incorrectly. GfxICCBasedColorSpace was not correctly bounds-checking the number of components. Check SampledFunction input values for NaNs. Fix a divide-by-zero when the page width or height is zero. Fix a bug positioning text in PostScript output - if the last char in a string has an incorrect width in the PDF font object, that messes up the total string width, so we have to position individual chars. In PostScript output, if CID font substitution fails, drop all text in that font. Handle PDF files that set the stream/string decryption filters to Identity (i.e., no encryption). Avoid passing a zero font size to FreeType. Tweak raw mode in the text extractor to handle words on the same line drawn in the wrong order. Pdftops was generating a singular transform matrix for annotations whose bounding boxes had zero width or height. Handle embedded OpenType CFF fonts with CIDToGIDMaps. Remove the old kludge from PSOutputDev that was converting char 32 from ".notdef" to "space". Allow CCITTFax images to be more than 32k pixels wide. Modified the CMap parser to handle usecmap with Identity-H/V. Added some parameter checking in the JPX decoder. Added checks for infinite loops in PDF objects - for color spaces, functions, optional content, and outline items. Add support for mouse button bindings up to button 32 (old limit was 7). Fixed a bug in the decryption code for revision 3 with keyLength < 16 -- the owner password was not being handled correctly. [Thanks to Matthias Franz.] Optimize SampledFunction: pull index computation code out of the transform function; cache the last transform. Tweaked the font naming algorithm in PSOutputDev. Treat mirrored (as opposed to rotated) text the same as upright text in pdftotext. Fixed buffer overflows in Splash and SplashBitmap. Check for bogus character codes (e.g., ) in ToUnicode CMaps. The radial shading code (in both Gfx.cc and PSOutputDev.cc) was not computing the s bounds properly. Drop empty subpaths in clip and fill (but not stroke) operations - this can significantly speed up clip performance in the weird case where a PDF file does " re m h W n". Added code to FoFiTrueType to check for an invalid loca format field in the head table. The axial shading code (in Gfx.cc, but not PSOutputDev.cc) was not computing the incremental polygon vertices correctly. Set the character width in TextOutputDev to something sensible for vertical fonts. Added basic support to the text extractor for vertical writing mode. The non-interactive tools (pdftotext, pdftops) now free Page objects after using them, avoiding performance problems with pages that have huge resource dictionaries. Check for line dash arrays like [0], and draw nothing at all (to match Acrobat's behavior). Add a sanity check for the ascent/descent values in FontDescriptors. Single-point paths with a line dash were causing a crash. Correctly handle Level 3 PostScript output with masked images inside patterns. Tweaked the xref repair code so that it runs if the catalog is messed up (in addition to running if the xref table is damaged). If Indexed color space tables (streams or strings) are too small, reduce the max index instead of throwing away the color space. Change the CMap parser to allocate memory only when it sees a mapping, not when it sees a 'codespacerange' declaration -- this avoids allocating huge amounts of memory for CMaps with large, unused codespaceranges. In monochrome mode, treat lines with width <= 1 as hairlines (to match Acrobat's behavior). Cache the last transform for PostScript-type functions. Added the "-rawdates" option to pdfinfo. Optimized ImageStream::getLine(). Fixed the Hue, Saturation, Color, and Luminosity blend functions to match Adobe's spec. Fixed the non-isolated group compositing computation. Skip extraneous unused data at the end of JBIG2 segments. Change the algorithm that stroke/fill adjustment uses so that the edges of adjacent strokes/fills line up. Do stroke adjustment on end caps when cap style is butt or projecting. Fixed a security hole: Gfx.parser was not being initialized to NULL. Fixed a security hole: integer bounds check in the Type 1 encoding parser in FoFiType1.cc. If an embedded font object is invalid or non-existent, do font substitution (same as if there were no embedded font). TextOutputDev was reusing an old font in the case where the font changed but the font size and character positioning stayed the same. When starting a transparency group, copy the fill/stroke colors from the graphics state. Tweaked the fixed-point code. When a TrueType font is declared resident (with a psFont command), don't munge the encoding. Look for URIs starting with "www." and treat them as absolute "http:" URIs, not as relative URIs (to match Adobe's behavior). Added code to FoFiTrueType to check for a zero-entry cmap table. Tweaked the font substitution code to do a better job of scaling the substituted font. Require at least two splits in the axial shading color bisection. Optimized JBIG2Stream::readGenericBitmap(). JPXStream wasn't correctly handling row padding for images with fewer than 8 bits per component. Optimized the ToUnicode CMap parser. Added a "whole words only" option to text searches. Check for valid component parameters in the DCT decoder. Implement embedding of external 16-bit fonts (without subsetting) in PostScript output. Added the minLineWidth xpdfrc command. Added warning messages for font substitutions. 3.04 (2014-may-28) ------------------ New text extractor. Added the pdftohtml tool. Added the pdftopng tool. New trapezoid-based rasterizer core (for performance). Generate appearance streams for Line, PolyLine, and Polygon annotations. Added the closeWindowOrQuit command, and changed the default binding for ctrl-W from closeWindow to closeWindowOrQuit. Implemented the new AES-256 mode (R=6, Acrobat X). Add an object cache. Added a small cache for object streams. Modify PSOutputDev to use LZW compression instead of RLE, with a fallback to RLE if the "psLZW no" setting is given. Pdfinfo now prints page rotation info. Modified ImageOutputDev, used by pdfimages, to output the masks and soft masks used when drawing images. Remove non-printable characters from error output, just in case they might cause problems for the terminal program. Added initial support for Code3of9 bar codes in XFA forms. Added the mapExtTrueTypeFontsViaUnicode xpdfrc command. Apply stroke adjustment to rectangular images and clipping regions (in addition to strokes and fills). Decode JPEG 2000 images at less than full resolution if the full res image isn't needed (i.e., if the raw image is higher resolution than the output). Implemented knockout groups. Removed t1lib support. Added support for images with 16-bit components. Rewrote the Dict class to use a hash table; as a side effect, this handles dictionaries with multiple definitions for a key, which are in violation of the spec, but Acrobat appears to handle. The transformed line width computation -- used to implement the minLineWidth setting, and the hairline threshold in monochrome mode -- was incorrect. Pdftops was not correctly handling the case where it couldn't find a 16-bit font -- this led to crashes and/or invalid PostScript. A bug in FlateStream::getBlock() was causing problems with narrow images. Use the correct _WIN32 define instead of WIN32. Use copy-on-write for the clip path in SplashState (when doing gsave), for performance. Added a Solaris-specific entry to the ghostscript font search path. SplashState was initializing line width to 0 instead of 1. Abort processing on a content stream after getting 500 errors (undefined operator, wrong number of args) -- this avoids very long processing time for malicious PDF files using bogus RLE encoded content streams. Added the psUseCropBoxAsPage xpdfrc option; "pdftops -pagecrop" now sets psUseCropBoxAsPage; "pdftops -pagecrop -paper match" now uses the CropBox as the page size. Re-architected the AcroForm support code into a separate AcroForm module. Fixed the handling of overprinting/transparency interaction, using the CompatibleOverprint blend mode. The TIFF predictor code for the 1-bit-per-pixel case was broken. For triangle and patch mesh shadings (types 4-7) with color functions, interpolate the function parameter not the color. Check the fontFile/fontDir commands before (instead of after) doing Base-14 substitution in PS output. Correctly handle non-embedded TrueType fonts that have an Identity ToUnicode mapping (display and PS output were failing). Added support for XFA form rendering, including an "enableXFA" xpdfrc setting. Handle PFB Type 1 fonts when generating PostScript output. Unwind any extraneous saved graphics state at the end of the page (before drawing annotations). Added some integer overflow checks in the GString class. Handle 16-bit components in JPEG 2000 images. ActualText spans can end without a valid font, in which case TextPage::beginWord was crashing. The Domain entry in function shadings wasn't being parsed correctly. Fixed a bug in the JPEG decoder - successive approximation (progressive mode) coefficients weren't being handled correctly. Added a better infinite loop test to the xref parser. When generating PostScript, merge reused TrueType fonts (if their code-to-GID mappings are the same). Tweak the Gouraud triangle shaded fill code to end the recursive splitting if the triangles get sufficiently small. Do bilinear interpolation when upsampling images. When skipping extraneous image data from an inline image, look for EI instead of just EI. When writing to stdout on Windows, pdftoppm now sets the file mode to binary. [Thanks to Robert Frunzke.] Accept strings as well as names for the BaseFont entry in font objects. Removed the TEXTOUT_WORD_LIST config option (with the new text extractor, this is always enabled). Fixed a bug in the JBIG2 decoder (the TPGD context for template #3 in readGenericBitmap was incorrect). Rewrote the PostScriptFunction code for performance. Handle 8-bit OpenType CFF fonts that are missing required tables in the OpenType wrapper. Handle tiling patterns with reversed coordinates in their bounding boxes. Added support for 64-bit file offsets, i.e., PDF files larger than 2GB. Optimize the code that rasterizes pattern-filled image masks. Added support for Mac OS X system fonts (Base-14 only). The backdrop color in luminosity-type soft mask groups was not being handled correctly. Modified behavior of "pdftops -paper match -duplex ..." - it will now duplex consecutive same-sized pages. Tweak the handling of degenerate fills ('moveto lineto fill') to match Adobe. Don't honor the OPM=1 setting with ICCBased CMYK color spaces. Whole-word searches were treating certain punctuation (Unicode number separators and terminators) as part of the word, e.g., searching for "foo" would not match "foo,". Use the TextString class everywhere it makes sense. Removed the unnecessary segment sort in Splash (performance optimization). Handle hyperlinks that use Widget-type annotations. Fix up the integer overflow checks to avoid issues with clever compilers. [Thanks to Nickolai Zeldovich.] Correctly handle streams with missing Length entries in damaged PDF files. Added a compile-time option (LOAD_FONTS_FROM_MEM) to load fonts from memory rather than temporary files on disk. Added the psRasterSliceSize xpdfrc option. Fixed a case in the JPEG 2000 arithmetic decoder where extra data is present in packet i, and needs to be saved for use in packet i+1. Fixed a bug in the JPEG 2000 decoder related to images with fewer than 8 bits per component. Handle the case in PSOutputDev where slice size overflows a 32-bit integer. Add (partial) support for TrueType cmap format 2. Always pass FT_LOAD_NO_BITMAP to FreeType -- bitmaps apparently fail with rotated characters. Support fonts specified in ExtGState dictionaries. Annotations with empty Border arrays should not draw a border. Fix the CMap parser to handle large CID ranges. Check for Type 3 CharProcs that call q or Q before the d0/d1 operator, and treat them as uncacheable. Invert the selection color when starting in reverse video mode. Device{Gray,RGB,CMYK} cannot be mapped via a resource dict. Changed the PS output for masked images (explicit and color key masking): use a plain old clip path instead of rectclip to avoid array overflows. Check the StemSnapH/V arrays when converting Type 1C fonts to Type 1 - if there are any duplicate or out-of-order values, skip that StemSnapH/V array. Added the psMinLineWidth xpdfrc setting. Fix an obscure issue in converting TrueType fonts to Type 42, related to empty glyph descriptions (12 zero bytes). Pdftops now reports an error if there were any I/O errors writing to the PS output file. Fix vertical text (CJK fonts) in PS output -- offset the character origin correctly. Increased the number of digits used by pdfimages for the image number from three to four. Handle right-to-left (e.g., Arabic) ligatures correctly in the text extractor. Added the -loc and -locPS options to pdffonts. Extend the object parser recursion limit to cover Stream::addFilters() / Stream::makeFilters() - to avoid another possibility of stack overflow. Disable FreeType autohinting, because it can fail badly with font subsets that use invalid glyph names -- except in the case of Type 1 fonts, which look much better with light autohinting. Modified the rasterizer pipeline functions to process a scan line at a time (for performance). Removed VMS build support (it hasn't been updated in ages). Removed pdftotext's '-htmlmeta' option (use pdftohtml instead). PSOutputDev's font/form setup code, and pdffonts, were not scanning soft mask groups in ExtGState dictionaries. Invalid DCT input (e.g., from a damaged PDF file) could overflow the dctClip array. When upsampling an image mask or image with a large resulting image size, do it in stream mode instead of prescaling the whole image (to avoid running out of memory). Added infinite loop detection to pdffonts. xpdf-3.04/COPYING30000644000076400007640000010451312341430012013023 0ustar dereknderekn GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . xpdf-3.04/configure.in0000644000076400007640000002740412341430012014221 0ustar dereknderekndnl Process this file with autoconf to produce a configure script. dnl Copyright 1998-2013 Glyph & Cog, LLC AC_PREREQ(2.57) AC_INIT(xpdf/Gfx.cc) AC_CONFIG_HEADER(aconf.h) dnl ##### Optional features. AC_ARG_ENABLE([a4-paper], AS_HELP_STRING([--enable-a4-paper], [use A4 paper size instead of Letter for PostScript output])) AS_IF([test "x$enable_a4_paper" = "xyes"], [AC_DEFINE(A4_PAPER)]) AC_ARG_ENABLE([no-text-select], AS_HELP_STRING([--enable-no-text-select], [do not allow text selection])) AS_IF([test "x$enable_no_text_select" = "xyes"], [AC_DEFINE(NO_TEXT_SELECT)]) AC_ARG_ENABLE([opi], AS_HELP_STRING([--enable-opi], [include support for OPI comments])) AS_IF([test "x$enable_opi" = "xyes"], [AC_DEFINE(OPI_SUPPORT)]) AC_ARG_ENABLE([multithreaded], AS_HELP_STRING([--enable-multithreaded], [include support for multithreading])) AS_IF([test "x$enable_multithreaded" = "xyes"], [AC_DEFINE(MULTITHREADED)]) AC_ARG_ENABLE([exceptions], AS_HELP_STRING([--enable-exceptions], [use C++ exceptions])) AS_IF([test "x$enable_exceptions" = "xyes"], [AC_DEFINE(USE_EXCEPTIONS)]) AC_ARG_ENABLE([fixedpoint], AS_HELP_STRING([--enable-fixedpoint], [use fixed point (instead of floating point) arithmetic])) AS_IF([test "x$enable_fixedpoint" = "xyes"], [AC_DEFINE(USE_FIXEDPOINT)]) AC_ARG_ENABLE([cmyk], AS_HELP_STRING([--enable-cmyk], [include support for CMYK rasterization])) AS_IF([test "x$enable_cmyk" = "xyes"], [AC_DEFINE(SPLASH_CMYK)]) AC_ARG_WITH([appdef-dir], AS_HELP_STRING([--with-appdef-dir], [set app-defaults directory])) AS_IF([test "x$with_appdef_dir" != "xno"], [AC_DEFINE_UNQUOTED(APPDEFDIR, "$with_appdef_dir")]) dnl ##### Path to xpdfrc. dnl This ugly kludge to get the sysconfdir path is needed because dnl autoconf doesn't actually set the prefix variable until later. if test "$sysconfdir" = '${prefix}/etc'; then if test "x$prefix" = xNONE; then system_xpdfrc="$ac_default_prefix/etc/xpdfrc" else system_xpdfrc="$prefix/etc/xpdfrc" fi else system_xpdfrc="$sysconfdir/xpdfrc" fi AC_DEFINE_UNQUOTED(SYSTEM_XPDFRC, "$system_xpdfrc") dnl ##### Checks for programs. AC_PROG_CC AC_ISC_POSIX AC_PROG_CC_STDC #if test -z "$CXX" -a "$CC" = "gcc"; then # CXX="gcc" #fi AC_PROG_CXX AC_PROG_INSTALL AC_PROG_RANLIB dnl ##### Default values for Unix. EXE="" LIBPREFIX="lib" AR="ar rc" UP_DIR="" dnl ##### Check for OS/2. AC_CACHE_CHECK([for OS/2 (with EMX)], xpdf_cv_sys_os2, [AC_TRY_COMPILE([], [__EMX__], xpdf_cv_sys_os2=yes, xpdf_cv_sys_os2=no)]) if test "$xpdf_cv_sys_os2" = yes; then EXE=".exe" LIBPREFIX="" AR="ar -rc" fi dnl ##### Check for DOS (with DJGPP). AC_CACHE_CHECK([for DOS (with DJGPP)], xpdf_cv_sys_dos, [AC_TRY_COMPILE([], [__DJGPP__], xpdf_cv_sys_dos=yes, xpdf_cv_sys_dos=no)]) if test "$xpdf_cv_sys_dos" = yes; then EXE=".exe" LIBPREFIX="lib" AR="ar -rc" UP_DIR="../" fi dnl ##### Do substitutions. AC_SUBST(EXE) AC_SUBST(LIBPREFIX) AC_SUBST(AR) AC_SUBST(UP_DIR) dnl ##### Checks for header files. AC_PATH_XTRA AC_HEADER_DIRENT dnl ##### Switch over to C++. This will make the checks below a little dnl ##### bit stricter (requiring function prototypes in include files). dnl ##### (99% of xpdf is written in C++.) AC_LANG_CPLUSPLUS dnl ##### Check for extra libraries needed by X. (LynxOS needs this.) AC_CHECK_FUNC(gethostbyname) if test $ac_cv_func_gethostbyname = no; then AC_CHECK_LIB(bsd, gethostbyname, X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd") fi dnl ##### Look for header that defines select() and fd_set. AC_MSG_CHECKING([select() and fd_set in sys/select.h and sys/bsdtypes.h]) AC_TRY_COMPILE([#include #include #include #include ], [fd_set fds; select(0, NULL, NULL, NULL, NULL);], xpdf_ok=yes, xpdf_ok=no) if test $xpdf_ok = yes; then AC_MSG_RESULT([not needed]) else AC_TRY_COMPILE([#include #include #include #include #include ], [fd_set fds; select(0, NULL, NULL, NULL, NULL);], xpdf_ok=yes, xpdf_ok=no) if test $xpdf_ok = yes; then AC_DEFINE(HAVE_SYS_SELECT_H) AC_MSG_RESULT([need sys/select.h]) else AC_TRY_COMPILE([#include #include #include #include #include ], [fd_set fds; select(0, NULL, NULL, NULL, NULL);], xpdf_ok=yes, xpdf_ok=no) if test $xpdf_ok = yes; then AC_DEFINE(HAVE_SYS_BSDTYPES_H) AC_MSG_RESULT([need sys/bsdtypes.h]) else AC_MSG_RESULT([problem]) fi fi fi dnl ##### Look for header that defines FD_ZERO. AC_MSG_CHECKING([FD_ZERO and strings.h or bstring.h]) AC_TRY_COMPILE([#include #include #ifdef HAVE_SYS_SELECT_H #include #endif], [fd_set fds; FD_ZERO(&fds);], xpdf_ok=yes, xpdf_ok=no) if test $xpdf_ok = yes; then AC_MSG_RESULT([not needed]) else AC_TRY_COMPILE([#include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif], [fd_set fds; FD_ZERO(&fds);], xpdf_ok=yes, xpdf_ok=no) if test $xpdf_ok = yes; then AC_DEFINE(HAVE_STRINGS_H) AC_MSG_RESULT([need strings.h]) else AC_TRY_COMPILE([#include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif], [fd_set fds; FD_ZERO(&fds);], xpdf_ok=yes, xpdf_ok=no) if test $xpdf_ok = yes; then AC_DEFINE(HAVE_BSTRING_H) AC_MSG_RESULT([need bstring.h]) else AC_MSG_RESULT([problem]) fi fi fi dnl ##### Look for rewinddir. AC_CHECK_FUNCS(rewinddir) if test $ac_cv_func_rewinddir = no; then AC_CHECK_LIB(cposix, rewinddir) fi dnl ##### Checks for library functions. AC_CHECK_FUNCS(popen) dnl # This should use 'AC_CHECK_FUNCS(mkstemp)' but that fails if dnl # the mkstemp exists in the library but isn't declared in the dnl # include file (e.g., in cygwin 1.1.2). AC_CACHE_CHECK([for mkstemp], xpdf_cv_func_mkstemp, [AC_TRY_LINK([#include #include ], [mkstemp("foo");], xpdf_cv_func_mkstemp=yes, xpdf_cv_func_mkstemp=no)]) if test "$xpdf_cv_func_mkstemp" = yes; then AC_DEFINE(HAVE_MKSTEMP) fi dnl # Check for mkstemps, just like mkstemp. AC_CACHE_CHECK([for mkstemps], xpdf_cv_func_mkstemps, [AC_TRY_LINK([#include #include ], [mkstemps("foo", 0);], xpdf_cv_func_mkstemps=yes, xpdf_cv_func_mkstemps=no)]) if test "$xpdf_cv_func_mkstemps" = yes; then AC_DEFINE(HAVE_MKSTEMPS) fi dnl ##### Check select argument type: on HP-UX before version 10, select dnl ##### takes (int *) instead of (fd_set *). AC_CACHE_CHECK([whether select takes fd_set arguments], xpdf_cv_func_select_arg, [AC_TRY_COMPILE([#include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif], [fd_set fds; select(1, &fds, &fds, &fds, 0);], xpdf_cv_func_select_arg=yes, xpdf_cv_func_select_arg=no)]) if test "$xpdf_cv_func_select_arg" != yes; then AC_DEFINE(SELECT_TAKES_INT) fi dnl ##### Check for std::sort. AC_CACHE_CHECK([for std::sort], xpdf_cv_func_std_sort, [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include struct functor { bool operator()(const int &i0, const int &i1) { return i0 < i1; } };]], [[int a[100]; std::sort(a, a+100, functor());]])], xpdf_cv_func_std_sort=yes, xpdf_cv_func_std_sort=no)]) if test "$xpdf_cv_func_std_sort" = yes; then AC_DEFINE(HAVE_STD_SORT) fi dnl ##### Back to C for the library tests. AC_LANG_C dnl ##### Check for fseeko/ftello or fseek64/ftell64 dnl The LARGEFILE and FSEEKO macros have to be called in C, not C++, mode. AC_SYS_LARGEFILE AC_FUNC_FSEEKO AC_CHECK_FUNCS(fseek64, xpdf_cv_func_fseek64=yes, xpdf_cv_func_fseek64=no) AC_CHECK_FUNCS(ftell64, xpdf_cv_func_ftell64=yes, xpdf_cv_func_ftell64=no) if test "$xpdf_cv_func_fseek64" = yes -a "$xpdf_cv_func_ftell64" = yes; then AC_DEFINE(HAVE_FSEEK64) fi dnl ##### Check for libXpm. if test -z "$no_x"; then smr_CHECK_LIB(Xpm, Xpm, [pixmap library - used only for icon], XpmCreatePixmapFromData, X11/xpm.h, $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS) AC_SUBST(Xpm_LIBS) AC_SUBST(Xpm_CFLAGS) fi dnl ##### Check for Motif (libXm). if test -z "$no_x"; then dnl # XextAddDisplay isn't defined in any header file, so we provide a dnl # bogus prototype (so the compiler doesn't complain) and a bogus dnl # header file (so the smr macro doesn't break). smr_CHECK_LIB(Xext, Xext, [Motif library], XextAddDisplay, X11/Xlib.h, $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS, [int XextAddDisplay();]) AC_SUBST(Xext_LIBS) AC_SUBST(Xext_CFLAGS) smr_CHECK_LIB(Xp, Xp, [Motif library], XpStartPage, X11/extensions/Print.h, $X_LIBS $X_PRE_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS) AC_SUBST(Xp_LIBS) AC_SUBST(Xp_CFLAGS) smr_CHECK_LIB(Xt, Xt, [Motif library], XtAppInitialize, X11/Intrinsic.h, $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS) AC_SUBST(Xt_LIBS) AC_SUBST(Xt_CFLAGS) smr_CHECK_LIB(Xm, Xm, [Motif library], XmCreateForm, Xm/XmAll.h, $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS) AC_SUBST(Xm_LIBS) AC_SUBST(Xm_CFLAGS) smr_CHECK_LIB(Sgm, Sgm, [SGI Motif library], SgCreateHorzPanedWindow, Sgm/HPanedW.h, $Xm_LIBS $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS) AC_SUBST(Sgm_LIBS) AC_SUBST(Sgm_CFLAGS) dnl # check for XtAppSetExitFlag, which didn't exist prior to X11R6 (?) if test "x$smr_have_Xt_library" = xyes; then AC_CHECK_LIB(Xt, XtAppSetExitFlag, AC_DEFINE(HAVE_XTAPPSETEXITFLAG), , [$Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11]) fi fi dnl ##### Check for FreeType 2.x. dnl ##### (Note: FT_Get_Name_Index was added in FT 2.0.5, and is dnl ##### the reason that Xpdf requires 2.0.5+.) smr_CHECK_LIB(freetype2, freetype, [FreeType2 font rasterizer - version 2.0.5+], FT_Get_Name_Index, ft2build.h, -lm) AC_SUBST(freetype2_LIBS) AC_SUBST(freetype2_CFLAGS) if test "x$smr_have_freetype2_library" = xyes; then AC_DEFINE(HAVE_FREETYPE_FREETYPE_H) AC_DEFINE(HAVE_SPLASH) fi dnl ##### Check for libpng. smr_CHECK_LIB(libpng, png, [PNG library], png_write_row, png.h, -lz) AC_SUBST(libpng_LIBS) AC_SUBST(libpng_CFLAGS) dnl ##### Check for libpaper (Debian). smr_CHECK_LIB(libpaper, paper, [Debian libpaper], paperinit, paper.h) AC_SUBST(libpaper_LIBS) AC_SUBST(libpaper_CFLAGS) dnl ##### Disable X-specific stuff in top-level Makefile. if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_library" != xyes; then X="#" XPDF_TARGET="all-no-x" else X="" XPDF_TARGET="all" fi AC_SUBST(X) AC_SUBST(XPDF_TARGET) dnl ##### Extra libraries. EXTRA_LIBS= EXTRA_CFLAGS= AC_SUBST(EXTRA_LIBS) AC_SUBST(EXTRA_CFLAGS) dnl ##### Write the makefiles. AC_OUTPUT(Makefile goo/Makefile fofi/Makefile splash/Makefile xpdf/Makefile) dnl ##### Warn user if X is missing. if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_library" != xyes; then if test -n "$no_x"; then AC_MSG_WARN([Couldn't find X]); fi if test "x$smr_have_Xm_library" != xyes; then AC_MSG_WARN([Couldn't find Motif]); fi if test "x$smr_have_freetype2_library" != xyes; then AC_MSG_WARN([Couldn't find FreeType]); fi AC_MSG_WARN([-- You will be able to compile pdftops, pdftotext, pdfinfo, pdffonts, pdfdetach, and pdfimages, but not xpdf or pdftoppm]) fi dnl ##### Warn user if libpng is missing. if test "x$smr_have_libpng_library" != xyes; then AC_MSG_WARN([Couldn't find libpng -- you will not be able to build pdftohtml or pdftopng]) fi