plan9fox/sys/lib/ghostscript/pdf_main.ps
2011-03-30 19:35:09 +03:00

1337 lines
44 KiB
PostScript

% Copyright (C) 1994, 2000 Aladdin Enterprises. All rights reserved.
%
% This software is provided AS-IS with no warranty, either express or
% implied.
%
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
%
% For more information about licensing, please refer to
% http://www.ghostscript.com/licensing/. For information on
% commercial licensing, go to http://www.artifex.com/licensing/ or
% contact Artifex Software, Inc., 101 Lucas Valley Road #110,
% San Rafael, CA 94903, U.S.A., +1(415)492-9861.
% $Id: pdf_main.ps,v 1.100 2005/09/23 18:21:23 ray Exp $
% pdf_main.ps
% PDF file- and page-level operations.
/.setlanguagelevel where { pop 2 .setlanguagelevel } if
.currentglobal true .setglobal
/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
pdfdict begin
% Patch in an obsolete variable used by some third-party software.
/#? false def
% Test whether the current output device handles pdfmark.
/.writepdfmarkdict 1 dict dup /pdfmark null put readonly def
/.writepdfmarks { % - .writepdfmarks <bool>
currentdevice //.writepdfmarkdict .getdeviceparams
mark eq { false } { pop pop true } ifelse
systemdict /DOPDFMARKS known or
} bind def
% For simplicity, we use a single interpretation dictionary for all
% PDF graphics execution, even though this is too liberal.
/pdfopdict mark
objopdict { } forall
drawopdict { } forall
/endstream { exit } bind
(%%EOF) cvn { exit } bind % for filters
% PDF 1.1 operators
/BX { /BXlevel BXlevel 1 add store } bind
/EX { /BXlevel BXlevel 1 sub store } bind
/PS { cvx exec } bind
% PDF 1.2 operators
/BMC { pop } bind
/BDC { pop pop } bind
/EMC { }
/MP { pop } bind
/DP { pop pop } bind
.dicttomark readonly def
% ======================== Main program ======================== %
end % pdfdict
userdict begin
/defaultfontname /Times-Roman def
% Make sure the registered encodings are loaded, so we don't run the risk
% that some of the indices for their names will overflow the packed
% representation. (Yes, this is a hack.)
SymbolEncoding pop
DingbatsEncoding pop
% Redefine 'run' so it recognizes PDF files.
systemdict begin
/.runps /run load def
/run {
dup type /filetype ne { (r) file } if
% skip leading whitespace characters (actually anything less than or equal to <sp>)
{ dup ( ) .peekstring not { false exit } if
dup 0 get 32 le { pop dup read pop pop } { true exit } ifelse
} loop
{
(%) eq {
dup ( ) .peekstring {
(%PDF-) eq {
dup (%stdin) (r) file eq {
% Copy PDF from stdin to temporary file then run it.
null (w+) //systemdict /.tempfile get exec exch 3 1 roll
% stack: tempname stdin tempfile
64000 string
{
% stack: tempname stdin tempfile string
2 index 1 index readstring
exch 3 index exch writestring
not { exit } if
}
loop
pop exch closefile
% stack: tempname tempfile
dup 0 setfileposition
dup runpdf
closefile deletefile
} {
runpdf
} ifelse
} {
cvx .runps % doesn't start with %PDF-
} ifelse
} {
pop cvx .runps % didn't read 5 characters
} ifelse
} {
cvx .runps % didn't start with %
} ifelse
} {
pop closefile % file was empty
} ifelse
} bind odef
currentdict /runpdfstring .undef
/runpdfbegin { % <file> runpdf -
userdict begin
% It turns out that the PDF interpreter uses memory more
% effectively if it is run under at least one level of save.
% This is counter-intuitive, and we don't understand why it happens,
% but the improvement is significant.
/PDFTopSave save def
0 setobjectformat
/Page# null def
/Page null def
/DSCPageCount 0 def
/PDFSave null def
GS_PDF_ProcSet begin
pdfdict begin
pdfopen begin
Trailer /Root oget /Pages oget /CropBox knownoget
{ oforce_array normrect mark /CropBox 3 -1 roll /PAGES pdfmark
}
if
/FirstPage where
{ pop FirstPage dup pdfpagecount gt
{ (\nRequested FirstPage is greater than the number of pages in the file: ) print
pdfpagecount = flush
} if
} {
1
} ifelse
/LastPage where { pop LastPage pdfpagecount .min } { pdfpagecount } ifelse
1 index 1 index gt
{ ( No pages will be processed \(FirstPage > LastPage\).) = flush }
{ QUIET not
{ (Processing pages ) print 1 index =only ( through ) print dup =only
(.) = flush
}
if
}
ifelse
} bind def
/dopdfpages { % firstpage# lastpage# dopdfpages -
<< /PDFScanRules true >> setuserparams % set scanning rules for PDF vs. PS
1 exch
{ dup /Page# exch store
QUIET not { (Page ) print dup == flush } if
pdfgetpage pdfshowpage
} for
<< /PDFScanRules null >> setuserparams % restore scanning rules for PS
} bind def
/runpdfend {
Repaired { printrepaired } if
currentdict pdfclose
end % temporary dict
end % pdfdict
end % GS_PDF_ProcSet
PDFTopSave restore
end % userdict
} bind def
/runpdf { % <file> runpdf -
runpdfbegin
dopdfpages
runpdfend
} bind def
end % systemdict
% Redefine the procedure that the C code uses for running piped input.
% It is OK to use { (%stdin) run } here, because a startjob cannot occur.
/.runstdin {
{ (%stdin) run } execute0
} bind def
end % userdict
pdfdict begin
% ======================== File parsing ======================== %
% Read the cross-reference and trailer sections.
/traileropdict mark
(<<) cvn { mark } bind
(>>) cvn { { .dicttomark } stopped {
( **** File has unbalanced >> in trailer.\n) pdfformaterror
} if } bind
([) cvn { mark } bind % ditto
(]) cvn dup load
% /true true % see .pdfexectoken in pdf_base.ps
% /false false % ibid.
% /null null % ibid.
/R { /resolveR cvx 3 packedarray cvx } bind % see Objects below
/startxref /exit load
.dicttomark readonly def
% Because of EOL conversion, lines with fixed contents might be followed
% by one or more blanks.
/lineeq % <filestr> <conststr> lineeq <bool>
{ anchorsearch
{ pop { ( ) anchorsearch not { () eq exit } if pop } loop }
{ pop false }
ifelse
} bind def
/linene { lineeq not } bind def
% Read original version (pre PDF 1.5) of the xref table.
% Note: The position is the location of 'xref'. The current PDFfile
% position is just after the 'XREF'.
/readorigxref % <pos> readorigxref <trailerdict>
{
pop % We do not need the position.
0 % Initialize xref table error counter
{ PDFfile token pop % first object # or trailer
dup /trailer eq { pop exit } if
PDFfile pdfstring readline pop
token pop % entry count
% remaining must be whitespace only (otherwise this xref Size was invalid.
exch dup length 0 ne {
false 1 index { 32 gt { pop true exit } if } forall {
( **** Warning: xref subsection header has extra characters.\n)
pdfformaterror
/setxrefentry cvx /syntaxerror signalerror
} if
} if
pop % remove last
% This section might be adding new objects:
% ensure that Objects and Generations are big enough.
% stack: <err count> <first obj> <entry count>
2 copy add growPDFobjects
{ % stack: <err count> <obj num>
% Read xref line
PDFfile 20 string readstring pop % always read 20 chars.
token pop % object position
exch token pop % generation #
exch token pop % n or f
exch % stack: <err count> <obj#> <loc> <gen#> <tag> <remainder of line>
dup length 0 ne {
% check to make sure trailing garbage is just white space
dup { 32 gt { 5 -1 roll 1 add 5 1 roll } if } forall % bump error count on garbage
} if
pop % Stack: <err count> <obj#> <loc> <gen#> <tag>
dup /n eq { % xref line tag is /n
pop % pop dup of line tag
Objects 3 index lget null eq { % later update might have set it
0 3 1 roll % Set ObjectStream object number = 0
setxrefentry % Save xref entry
3 -1 roll pop % Remove ObjectStream object onumber
} if
}
{ % xref line tag was not /n
/f ne % verify that the tag was /f
{ /setxrefentry cvx /syntaxerror signalerror
} if
} ifelse
pop pop % pop <obj location> and <gen num>
% stack: <err count> <obj num>
1 add % increment object number
} repeat
pop % pop <obj #>
} loop
0 ne {
( **** Warning: length of some xref entries is not equal to 20 bytes.\n)
pdfformaterror
} if
PDFfile traileropdict .pdfrun
} bind def
% This dicitonary is used to read the xref dictionary. It should work for
% reading any dictionary. dictlevelcount must contain 0.
/xrefopdict mark
(<<) cvn { /dictlevelcount dictlevelcount 1 add def mark } bind
(>>) cvn { .dicttomark /dictlevelcount dictlevelcount 1 sub def
dictlevelcount 0 eq { exit} if } bind
([) cvn { mark } bind % ditto
(]) cvn dup load
% /true true % see .pdfexectoken in pdf_base.ps
% /false false % ibid.
% /null null % ibid.
/R { /resolveR cvx 3 packedarray cvx } bind % see Objects below
.dicttomark readonly def
% Get a variable length positive integer value from a stream. A value
% of zero is returned if the count is zero.
/getintn { % <stream> <count> getintn int
0 exch { 256 mul 1 index read pop add } repeat
exch pop % Discard stream
} bind def
% This array contains handlers for processing the different types of
% entries in the XRef stream.
% Stack: <Xrefdict> <xref stream> <Index array> <pair loc> <obj num>
% <field 2> <field 3>
% The handlers leave the stack unchanged.
/xref15entryhandlers [
{ % XRef entry type 0 - free or f type xref entry
% (free ) print
% (obj num: ) print 2 index pdfstring cvs print ( ) print
% (loc: ) print 1 index pdfstring cvs print ( ) print
% (gen: ) print dup === flush
} bind % Do nothing for free xref entries
% XRef entry type 1 - normal or n type xref entry
{ % field 2 = obj loc, field 3 = gen num
% (normal ) print
% (obj num: ) print 2 index pdfstring cvs print ( ) print
% (loc: ) print 1 index pdfstring cvs print ( ) print
% (gen: ) print dup === flush
0 3 1 roll % set stream number = 0
setxrefentry
3 -1 roll pop % remove stream number
} bind
% XRef entry type 2 - compressed object type xref entry
{ % field 2 = object stream num, field 3 = index into object stream
% (Compressed objects: ) print
% (obj num: ) print 2 index pdfstring cvs print ( ) print
% (field 2: ) print 1 index pdfstring cvs print ( ) print
% (field 3: ) print dup === flush
0 setxrefentry pop % set generation number = 0
} bind
] def
% Read the PDF 1.5 version of the xref table.
% Note: The position is the location of the start of the dictionary object
% In PDF 1.5, the XRef dictionary also serves as the trailer dictionary
/readpdf15xref % <pos> readpdf15xref <trailerdict>
{
PDFfile exch setfileposition % move to start of object
% Get object number, revision, and 'obj' and discard
PDFfile token pop pop
PDFfile token pop pop
PDFfile token pop pop
% Get the XRef dicitionary
/dictlevelcount 0 def PDFfile xrefopdict .pdfrun
% Verify that we have an XRef dictionary
dup /Type get /XRef ne {
/readpdf15xref cvx /syntaxerror signalerror
} if
% Ensure that we we have room in the objects array, etc.
dup /Size get growPDFobjects
% Create a stream for the XRef data
PDFfile token pop pop % Skip over 'stream'
dup stream false resolvestream
% Stack: <XRefdict> <xref stream>
% The Index array defines the ranges of object numbers in the
% XRef stream. Each value pair is consists of starting object
% number and the count of consecutive objects.
% Get the Index array, if present
1 index /Index .knownget not { % If no Index array ...
[ 0 3 index /Size get ] % Default = [ 0 Size ]
} if
% Loop through the Index array
0 2 2 index length 1 sub {
% Get start and end of object range
2 copy get % Start of the range
dup 3 index 3 index 1 add get % Number of entries in range
% Loop through the range of object numbers
add 1 sub 1 exch { % Form end of range, set increment = 1
% Stack: <Xrefdict> <xref stream> <Index array> <pair loc> <obj num>
% Get xref parameters. Note: The number of bytes for each parameter
% is defined by the entries in the W array.
4 index /W get aload pop % Get W array values
% The first field indicates type of entry. Get first field value.
% If the num. of bytes for field 1 is 0 then default field value is 1
3 -1 roll dup 0 eq { pop 1 } { 6 index exch getintn } ifelse
% Get the handler for the xref entry type. We will execute the
% handler after we get the other two field values.
xref15entryhandlers exch get
3 -1 roll 6 index exch getintn % Get second field
3 -1 roll 6 index exch getintn % Get third field
3 -1 roll exec % Execute Xref entry handler
pop pop pop % Remove field values and obj num
} for % Loop through Xref entries
pop % Remove Index array pair loc
} for % Loop through Index array entries
pop pop % Remove Index array and xref stream
} bind def
% Read the cross-reference table.
% <pos> is the position either from the startxref statement or the /Prev
% entry in the prior trailer dictionary.
/readxref % <pos> readxref <trailerdict>
{
PDFoffset add PDFfile exch setfileposition
% In some PDF files, this position actually points to
% white space before the xref line. Skip over this here.
{
PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
} loop
dup % Make copy of the file position (before last char was read).
PDFfile exch setfileposition
% The PDF specification says that the 'xref' must be on a line
% by itself. The code here formerly used readline and linene to
% check this. However, Acrobat Reader only requires the line to
% begin with 'xref', and there are enough applications producing
% non-compliant PDF files that we have to do this too.
PDFfile pdfstring 0 4 getinterval readstring pop
(xref) eq
{ readorigxref } % 'xref' -> original xref table
{ readpdf15xref } % otherwise assume PDF 1.5 xref stream
ifelse
} bind def
% Open a PDF file and read the header, trailer, and cross-reference.
/pdfopen { % <file> pdfopen <dict>
% Color space substitution in PDF is handled somewhat differently
% than in PostScript. A given device color space will be substituted
% if the corresponding "Default..." entry exists in the Page's
% Resource dictionary (which might be inhereted); there is no
% UseCIEColor to enable/disable color mapping.
%
% This behavior is achieved by always setting UseCIEColor to true
% in the page device dictionary. If the value of this parameter was
% originally false (i.e.: the output device does not perform color
% space substitution by default), the instances DefaultGray,
% DefaultRGB, and DefaultCMYK of the (local) ColorSpace category
% are redefined to be DeviceGray, DeviceRGB, and DeviceCMYK,
% respectively. This is not done if UseCIEColor is true by default,
% as in that case color substitution is presumably desired even
% if the file does not request it.
currentpagedevice /UseCIEColor .knownget dup { pop } if not
{ .currentglobal false .setglobal
/DefaultGray { /DeviceGray } cvlit /ColorSpace defineresource pop
/DefaultRGB { /DeviceRGB } cvlit /ColorSpace defineresource pop
/DefaultCMYK { /DeviceCMYK } cvlit /ColorSpace defineresource pop
.setglobal
}
if
pdfopenfile begin
pdfopencache
.writepdfmarks {
% Copy bookmarks (outline) to the output.
Trailer /Root oget /Outlines knownoget {
/First knownoget {
{ dup writeoutline /Next knownoget not { exit } if } loop
} if
} if
} if % end .writepdfmarks
currentdict end
} bind def
% Verify that each entry in the xref table is pointing at an object with
% the correct object number and generation number.
/verify_xref % - verify_xref -
{ 1 1 Objects llength 1 sub % stack: 1 1 <number of objects - 1>
{ % Check if the object is free (i.e. not used). The values in
% Generations is the generation number plus 1. If the value in
% Generations is zero then the object is free.
Generations 1 index lget % Get the genration number
0 ne { % Skip if object number is free
ObjectStream 1 index lget % Check if object is in objectstream
0 eq { % We only check objects not in an objectstream
{ % Use stop context since we may get an error if object is invalid
dup Objects exch lget % Get the object location
PDFoffset add PDFfile exch setfileposition
true % Stack: <obj num> <true>
PDFfile token pop % Read object number from file
2 index eq { % Verify object number
PDFfile token pop % Read generation number from file
Generations 3 index % Get specified generaton number
lget 1 sub % Gen numbs are stored with 1 added.
eq { % Verify generation number
PDFfile token pop /obj eq { % Verify 'obj' text
pop false % We have valid object, do not rebuild
} if
} if
} if
} .internalstopped
{ true } if % If we stop then we need to rebuild
{
( **** Warning: File has an invalid xref entry: )
pdfformaterror
pdfstring cvs pdfformaterror
(. Rebuilding xref table.\n) pdfformaterror
search_objects
exit
} if % If the entry is invalid
} if % If not in an object stream
} if % If object entry is not free
pop % Remove object number
} for
} bind odef
/pdfopencache { % - pdfopencache -
% Create and initialize some caches.
/PageCount pdfpagecount def
/PageNumbers PageCount 65534 .min dict def
/PageIndex PageCount 65534 .min array def
} bind def
/pdfopenfile { % <file> pdfopenfile <dict>
pdfdict readonly pop % can't do it any earlier than this
15 dict begin
/LocalResources 0 dict def
/DefaultQstate //null def % establish binding
/Printed where { pop } {
% Guess whether the output device is a printer.
/Printed currentpagedevice /OutputFile known def
} ifelse
/PSLevel1 where { pop } { /PSLevel1 false def } ifelse
% NB: PDFfile is used outside of the PDF code to determine that a
% PDF job is being processed; to not change or hide this key.
cvlit /PDFfile exch def
/PDFsource PDFfile def
/Repaired false def
currentglobal true .setglobal globaldict begin
/TTFWarnList 0 dict def /UndefProcList 0 dict def
end .setglobal
PDFfile dup 0 setfileposition pdfstring readstring
not {/pdfopen cvx /syntaxerror signalerror} if
(%PDF-) search not {/pdfopen cvx /syntaxerror signalerror} if
length /PDFoffset exch def pop
% some badly formed PDF's (Visioneer) have something other than EOL
% after the version number. If we get an error, shorten the string
% and try again.
false exch % error encountered
{ { cvr } stopped
{ exch pop true exch 0 1 index length 1 sub dup 0 eq
{ pop 0 exit } if % exit if string now empty
getinterval % trim character from right end and retry
}
{ exch {
( **** Warning: PDF version number not followed by EOL.\n)
pdfformaterror
}
if exit
}
ifelse
} loop
/PDFversion exch def
% Read the last cross-reference table.
count /pdfemptycount exch def
/Trailer << >> def % Initialize to an emptry dict.
{ initPDFobjects findxref readxref } .internalstopped {
recover_xref_data % Read failed. Attempt to recover xref data.
search_trailer % Search for the primary trailer
} {
/Trailer exch def % Save trailer dict after first xref table
% Read any previous cross-reference tables. When we are done,
% verify that the entries in the xref tables are valid if NoVerifyXref
% is not defined.
Trailer
{ /Prev knownoget not { % If no previous xref table then ...
/NoVerifyXref where { pop } { verify_xref } ifelse exit
} if
{ readxref } .internalstopped {
recover_xref_data % Read failed. Attempt to recover xref data.
exit % Exit loop since recover gets all obj data.
} if % If readxref stopped
% The PDF spec. says that each trailer dict should contain the required
% entries. However we have seen a PDF file that only has a Prev entry in
% the initial trailer dict. Acrobat complains but it accepts these files.
% To work with these files, we are copying any entries which we find in
% a previous trailer dict which are not present in the initial dict.
dup {
Trailer 2 index known {
pop pop % discard if key already present
} {
Trailer 3 1 roll put % add key if not present
} ifelse
} forall
} loop % Loop to previous trailer
} ifelse % Ifelse readxref stopped
Trailer /Encrypt knownoget {
pop
pdf_process_Encrypt % signal error
} if
currentdict end
} bind def
% Look for [\r\n]%%EO from the current position of the file.
% Return the position of %%EO if found or -1 .
/findeof { % <file> find_eof <file> <position>
-1 exch
{
dup bytesavailable 4 lt { exit } if
dup 0 (%%EO) /SubFileDecode filter flushfile
dup dup fileposition 5 sub setfileposition
dup 5 string readstring not { pop exit } if
dup (\r%%EO) eq exch (\n%%EO) eq or {
dup fileposition 4 sub
3 1 roll exch pop
} if
} loop
exch
} bind def
% Skip backward over the %%EOF at the end of the PDF file, and read
% the preceding startxref line. The PDF specification unambiguously
% requires that the %%EOF appear on a line by itself, and that the
% startxref and the following position value appear on separate lines;
% however, some applications truncate the %%EOF to %%EO, and/or put the
% startxref and the following value on the same line.
% There seems to be no limit on the amount of garbage that can be
% appended to the PDF file. Current record (60K) belongs to
% PDF-Out (v 2.0 - 35). We start the search for %%EO from the last 1024
% bytes and continue from the beginning of the file.
/findxref { % - findxref <xrefpos>
PDFfile dup dup dup 0 setfileposition bytesavailable
dup /PDFfilelen exch def
% Find the last %%EOF string (within 1024 bytes)
1024 sub PDFoffset .max
setfileposition findeof % search the last 1024 bytes
dup 0 le {
pop
dup PDFoffset setfileposition findeof % search from the beginnibg
dup 0 le {
( **** Error: Cannot find a %%EOF marker anywhere in the file.\n)
pdfformaterror
/findxref cvx /syntaxerror signalerror
} if
} if
dup 3 1 roll setfileposition
% Stack: eofpos
% Check for whether this is, in fact, a valid PDF file.
dup PDFfilelen exch sub dup dup 7 gt exch 5 lt or {
pop true
} {
string PDFfile exch readstring pop
dup (%%EOF\n) eq exch dup (%%EOF\r) eq
exch dup (%%EOF\r\n) eq exch (%%EOF) eq or or or not
} ifelse {
( **** Warning: File has a corrupted %%EOF marker, or garbage after %%EOF.\n)
pdfformaterror
} if
PDFfile exch setfileposition
% Now read the startxref and xref start position.
prevline token not { null } if dup type /integertype eq {
exch pop cvi % xref start position
exch PDFfile exch setfileposition
prevline dup (startxref) linene {
% startxref not on a line by itself. We have found PDF from
% www.verypdf.com in which the startxref was on the same line as
% the end of trailer dictionary. Check for this. Note: This
% violates the spec.
dup (startxref) search {
% found startxref - print warning
pop pop pop % clear strings from search
( **** Warning: format of the startxref line in this file is invalid.\n)
pdfformaterror
} { % no startxref - we have a problem.
/findxref cvx /syntaxerror signalerror
} ifelse
} if
pop pop
} { % else, this file has 'startxref #####' format
(startxref) ne { /findxref cvx /syntaxerror signalerror } if
cvi % xref start position
( **** Warning: format of the startxref line in this file is invalid.\n)
pdfformaterror
exch PDFfile exch setfileposition
} ifelse
} bind def
/stderrfile (%stderr) (w) file def
/stderrprint { % <string> stderrprint -
//stderrfile dup 3 -1 roll writestring flushfile
} bind def
/pdfformaterror { % <string> pdfformaterror -
stderrprint
/Repaired true store
} bind def
/knownoget_safe
{ 2 copy knownoget { 3 1 roll pop pop //true } { pop pop //false } ifelse
} odef
/printProducer {
Trailer /Info { knownoget_safe } stopped { pop pop false } if {
/Producer knownoget not { null } if
} {
null
} ifelse
dup null eq {
pop
} {
( **** The file was produced by: \n **** >>>> ) stderrprint
% Handle a Unicode Producer.
(\376\377) anchorsearch {
pop dup length 2 idiv string 0 1 2 index length 1 sub {
% Stack: origstr newstr i
1 index exch 3 index 1 index 2 mul 1 add get put
} for exch pop
} if
stderrprint
( <<<<\n) stderrprint
} ifelse
} bind def
% The TTFWarnList is the list of all TrueType fonts that were not embedded.
% The UndefProcList collects noisy warnings.
% This gets rid of many multiple warnings from pdf_font.ps
/printCollectedWarnings {
TTFWarnList length 0 gt {
(\n **** Warning: Fonts with Subtype = /TrueType should be embedded.\n)
stderrprint
( The following fonts were not embedded:\n)
stderrprint
[ TTFWarnList { pop .namestring (\t\t\t) exch concatstrings (\n) concatstrings } forall ]
{ lt } .sort { stderrprint } forall
} if
UndefProcList length 0 gt {
(\n **** Embedded font uses undefined procedure\(s\): ) stderrprint
UndefProcList {
exch .namestring stderrprint ( ) stderrprint
=string cvs stderrprint ( times, ) stderrprint
} forall
(\n) stderrprint
} if
} bind def
/printrepaired {
printCollectedWarnings
(\n **** This file had errors that were repaired or ignored.\n)
stderrprint
printProducer
( **** Please notify the author of the software that produced this\n)
stderrprint
( **** file that it does not conform to Adobe's published PDF\n)
stderrprint
( **** specification.\n\n)
stderrprint
} bind def
% Write the outline structure for a file. Uses linkdest (below).
% omit links to pages that don't exist.
/writeoutline % <outlinedict> writeoutline -
{ mark
0 2 index /First knownoget
{ { exch 1 add exch /Next knownoget not { exit } if } loop }
if
% stack: dict mark count
dup 0 eq
{ pop 1 index }
{ 2 index /Count knownoget { 0 lt { neg } if } if
/Count exch 3 index
}
ifelse { linkdest } stopped
{
cleartomark % ignore this link
( **** Warning: Outline has invalid link that was discarded.\n)
pdfformaterror
} {
/Title oget /Title exch /OUT pdfmark
}
ifelse
/First knownoget
{ { dup writeoutline /Next knownoget not { exit } if } loop }
if
} bind def
% Close a PDF file.
/pdfclose % <dict> pdfclose -
{ begin
PDFfile closefile
end
} bind def
% ======================== Page accessing ======================== %
% Get a (possibly inherited) attribute of a page.
/pget % <pagedict> <key> pget <value> -true-
% <pagedict> <key> pget -false-
{ 2 copy knownoget
{ exch pop exch pop true
}
{ exch /Parent knownoget
{ exch pget }
{ pop false }
ifelse
}
ifelse
} bind def
% Get the value of a resource on a given page.
/rget { % <resname> <pagedict> <restype> rget <value> -true-
% <resname> <pagedict> <restype> rget -false-
LocalResources 1 index knownoget {
3 index knownoget
} {
false
} ifelse {
exch pop exch pop exch pop true
} {
exch /Resources pget {
exch knownoget { exch knownoget } { pop false } ifelse
} {
pop pop false
} ifelse
} ifelse
} bind def
% Get the total number of pages in the document.
/pdfpagecount % - pdfpagecount <int>
{ Trailer /Root oget /Pages oget /Count oget
} bind def
% Find the N'th page of the document by iterating through the Pages tree.
% The first page is numbered 1.
/pdffindpageref { % <int> pdffindpage <objref>
dup Trailer /Root oget /Pages get
{ % We should be able to tell when we reach a leaf
% by finding a Type unequal to /Pages. Unfortunately,
% some files distributed by Adobe lack the Type key
% in some of the Pages nodes! Instead, we check for Kids.
dup oforce /Kids knownoget not { exit } if
exch pop null
0 1 3 index length 1 sub {
2 index exch get
dup oforce dup /Kids known { /Count oget } { pop 1 } ifelse
% Stack: index kids null noderef count
dup 5 index ge { pop exch pop exit } if
5 -1 roll exch sub 4 1 roll pop
} for exch pop
% Stack: index null|noderef
dup null eq { pop pop 1 null exit } if
} loop
% Stack: index countleft noderef
1 index 1 ne { pop pop /pdffindpage cvx /rangecheck signalerror } if
exch pop
PageIndex 2 index 1 sub 65533 .min 2 index oforce put
PageNumbers 1 index oforce 3 index dup 65534 le
{ put }
{ pop pop pop } % don't store more than 65534 pagenumbers
ifelse
exch pop
} bind def
/pdffindpage { % <int> pdffindpage <pagedict>
pdffindpageref oforce
} bind def
% Find the N'th page of the document.
% The first page is numbered 1.
/pdfgetpage % <int> pdfgetpage <pagedict>
{ PageIndex 1 index 1 sub dup 65533 lt
{ get }
{ pop pop null }
ifelse
dup null ne
{ exch pop oforce }
{ pop pdffindpage }
ifelse
} bind def
% Find the page number of a page object (inverse of pdfgetpage).
/pdfpagenumber % <pagedict> pdfpagenumber <int>
{ % We use the simplest and stupidest of all possible algorithms....
PageNumbers 1 index .knownget
{ exch pop
}
{ 1 1 PageCount 1 add % will give a rangecheck if not found
{ dup pdfgetpage oforce 2 index eq { exit } if pop
}
for exch pop
}
ifelse
} bind def
% Arrange the four elements that define a rectangle into a 'normal' order.
/normrect_elems % <x1> <y1> <x2> <y2> normrect_elems <llx> <lly> <urx> <ury>
{
exch 4 1 roll % <x2> <x1> <y1> <y2>
2 copy gt { exch } if % <x2> <x1> <lly> <ury>
4 2 roll 2 copy lt { exch } if % <lly> <ury> <urx> <llx>
4 1 roll exch % <llx> <lly> <urx> <ury>
} bind def
% Arrange a rectangle into a 'normal' order. I.e the lower left corner
% followed by the upper right corner.
/normrect % <rect> normrect <rect>
{
aload pop normrect_elems 4 array astore
} bind def
/boxrect % <llx> <lly> <urx> <ury> boxrect <x> <y> <w> <h>
{ exch 3 index sub exch 2 index sub
} bind def
/resolvedest { % <name|string|other> resolvedest <other|null>
dup type /nametype eq {
Trailer /Root oget /Dests knownoget {
exch knownoget not { null } if
} {
pop null
} ifelse
} {
dup type /stringtype eq {
Trailer /Root oget /Names knownoget {
/Dests knownoget {
exch nameoget
} {
pop null
} ifelse
} {
pop null
} ifelse
} if
} ifelse
} bind def
/linkdest { % <link|outline> linkdest
% ([/Page <n>] /View <view> | ) <link|outline>
dup /Dest knownoget
{ resolvedest
dup type /dicttype eq { /D knownoget not { null } if } if
dup null eq
{ pop }
{ dup 0 oget
dup type /dicttype eq {
dup /Type knownoget {
/Page eq {
pdfpagenumber
} if
} if
} if
dup type /integertype ne
{ pop }
{ /Page exch 4 -2 roll }
ifelse
dup length 1 sub 1 exch getinterval /View exch 3 -1 roll
}
ifelse
}
if
} bind def
% <pagedict> mark ... -proc- -
/namedactions 8 dict dup begin
/FirstPage {
/Page 1 3 -1 roll
} def
/LastPage {
counttomark 2 add index pdfpagecount /Page exch 3 -1 roll
} def
/NextPage {
counttomark 2 add index pdfpagenumber 1 add /Page exch 3 -1 roll
} def
/PrevPage {
counttomark 2 add index pdfpagenumber 1 sub /Page exch 3 -1 roll
} def
end readonly def
% <pagedict> <annotdict> -proc- -
/annottypes 5 dict dup begin
/Text {
mark exch
{ /Rect /Open /Contents }
{ 2 copy knownoget { 3 -1 roll } { pop } ifelse }
forall pop /ANN pdfmark
} bind def
/Link {
mark exch
dup /C knownoget { /Color exch 3 -1 roll } if
{ /Rect /Border }
{ 2 copy knownoget { 3 -1 roll } { pop } ifelse }
forall dup /A knownoget {
dup /URI known {
/A mark 3 2 roll % <<>> /A [ <<action>>
{ oforce } forall
.dicttomark
3 2 roll
} {
dup /D knownoget {
exch pop exch dup length dict copy dup /Dest 4 -1 roll put
} {
/N knownoget { % Assume /S /Named
namedactions exch .knownget { exec } if
} if
} ifelse
} ifelse
} if
linkdest pop /LNK pdfmark
} bind def
end readonly def
% **** The following procedure should not be changed to allow clients
% **** to directly interface with the constituent procedures. GSview
% **** and some Artifex customers rely on the pdfshowpage_init,
% **** pdfshowpage_setpage, pdfshowpage_finish so all logic should be
% **** implemented in one of those three procedures.
/pdfshowpage % <pagedict> pdfshowpage -
{ dup /Page exch store
pdfshowpage_init
pdfshowpage_setpage
pdfshowpage_finish
} bind def
/pdfpagecontents % <pagedict> pdfpagecontents <contents>
{ } bind def
/pdfshowpage_init % <pagedict> pdfshowpage_init <pagedict>
{ /DSCPageCount DSCPageCount 1 add store
} bind def
/.pdfshowpage_Install { % <pagedict> [<prevproc>] .pdfshowpage_Install -
exch
% We would like to clip to the CropBox here, but the subsequent
% initgraphics would override it. Instead, we have to handle it
% in graphicsbeginpage.
dup /CropBox pget dup {exch pop} if systemdict /UseCropBox known and {
dup /CropBox pget pop
} {
dup /MediaBox pget pop % There has to be a MediaBox
} ifelse
% stack: [<prevproc>] <pagedict> <Crop|Media Box>
exch pop oforce_array normrect % done with the pagedict
systemdict /PDFFitPage known {
PDFDEBUG { (Fiting PDF to imageable area of the page.) = flush } if
currentpagedevice /.HWMargins get aload pop
currentpagedevice /PageSize get aload pop
3 -1 roll sub 3 1 roll exch sub exch
% stack: [<prevproc>] <pagedict> <Crop|Media Box> Xmin Ymin Xmax Ymax
PDFDEBUG { ( Translate up by [ ) print 3 index =print (, ) print 2 index =print ( ]) = flush } if
3 index 3 index translate % move origin up to imageable area
2 index sub exch 3 index sub exch 4 2 roll pop pop
% stack: [Box] XImageable YImageable
2 index aload pop 2 index sub exch 3 index sub exch 4 2 roll pop pop
% stack: [Box] XImageable YImageable XBox YBox
3 -1 roll exch div 3 1 roll div .min
PDFDEBUG { ( Scale by ) print dup = flush } if
dup scale
} if
% Now translate to the origin given in the Crop|Media Box
dup 0 get neg exch 1 get neg translate
0 get
exec
} bind def
/pdfshowpage_setpage { % <pagedict> pdfshowpage_setpage <pagedict>
5 dict begin % for setpagedevice
% Stack: pagedict
% UseCIEColor is always true for PDF; see the comment in runpdf above
/UseCIEColor true def
currentpagedevice /Orientation 2 index /Rotate pget not { 0 } if 90 idiv
% Rotate specifies *clockwise* rotation!
neg 3 and def
% Stack: pagedict currentpagedict
1 index /CropBox pget dup {exch pop} if systemdict /UseCropBox known and {
% Set the page size.
1 index /CropBox pget pop oforce_elems normrect_elems boxrect
2 array astore /PageSize exch def pop pop
} {
1 index /MediaBox pget {
% Set the page size.
oforce_elems normrect_elems boxrect
2 array astore /PageSize exch def pop pop
} if
} ifelse
% Don't change the page size if we are going to fit the PDF to the page
systemdict /PDFFitPage known { currentdict /PageSize undef } if
% Let the device know if we will be using PDF 1.4 transparency.
% The clist logic may need to adjust the size of bands.
1 index pageusestransparency /PageUsesTransparency exch def
dup /Install .knownget {
% Don't let the Install procedure get more deeply
% nested after every page.
dup type dup /arraytype eq exch /packedarraytype eq or {
dup length 4 eq {
dup 2 get /.pdfshowpage_Install load eq {
1 get 0 get % previous procedure
} if
} if
} if
} {
{ }
} ifelse 1 array astore
2 index exch /.pdfshowpage_Install load /exec load
4 packedarray cvx
% Stack: pagedict currentpagedict installproc
/Install exch def
% Stack: pagedict currentpagedict
pop currentdict end setpagedevice
} bind def
/pdfshowpage_finish { % <pagedict> pdfshowpage_finish -
save /PDFSave exch store
/PDFdictstackcount countdictstack store
(before exec) VMDEBUG
% set up color space substitution (this must be inside the page save)
pdfshowpage_setcspacesub
.writepdfmarks {
% Copy the crop box.
dup /CropBox knownoget {
oforce_array normrect
% .pdfshowpage_Install translates the origin -
% do same here with the CropBox.
1 index /CropBox pget dup {exch pop} if systemdict /UseCropBox known and {
1 index /CropBox pget pop
} {
1 index /MediaBox pget pop % There has to be a MediaBox
} ifelse
oforce_array normrect
dup 0 get exch 1 get % [] tx ty
2 index 0 get 2 index sub 3 index exch 0 exch put
2 index 2 get 2 index sub 3 index exch 2 exch put
2 index 1 get 1 index sub 3 index exch 1 exch put
2 index 3 get 1 index sub 3 index exch 3 exch put
pop pop
% If the page has been rotated, rotate the CropBox.
mark /CropBox 3 -1 roll
3 index /Rotate pget {
90 idiv 1 and 0 ne {
aload pop 4 -2 roll exch 4 2 roll exch 4 array astore
} if
} if
/PAGE pdfmark
} if
% Copy annotations and links.
dup /Annots knownoget {
0 1 2 index length 1 sub
{ 1 index exch oget
dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse
}
for pop
} if
} if % end .writepdfmarks
% Display the actual page contents.
6 dict begin
/BXlevel 0 def
/BGDefault currentblackgeneration def
/UCRDefault currentundercolorremoval def
%****** DOESN'T HANDLE COLOR TRANSFER YET ******
/TRDefault currenttransfer def
matrix currentmatrix 2 dict
2 index /CropBox knownoget {
oforce_elems normrect_elems boxrect
4 array astore 1 index /ClipRect 3 -1 roll put
} if
dictbeginpage setmatrix
/DefaultQstate qstate store
dup % for showing annotations below
count 1 sub /pdfemptycount exch store
% If the page uses any transparency features, show it within
% a transparency group.
dup pageusestransparency dup /PDFusingtransparency exch def {
% Show the page within a PDF 1.4 device filter.
0 .pushpdf14devicefilter {
/DefaultQstate qstate store % device has changed -- reset DefaultQstate
% If the page has a Group, enclose contents in transparency group.
% (Adobe Tech Note 5407, sec 9.2)
dup /Group knownoget {
1 index /CropBox knownoget not {
1 index /MediaBox pget pop
} if oforce_array normrect .beginformgroup {
showpagecontents
} stopped {
.discardtransparencygroup stop
} if .endtransparencygroup
} {
showpagecontents
} ifelse
} stopped {
% todo: discard
.poppdf14devicefilter
/DefaultQstate qstate store % device has changed -- reset DefaultQstate
stop
} if .poppdf14devicefilter
/DefaultQstate qstate store % device has changed -- reset DefaultQstate
} {
showpagecontents
} ifelse
% check for extra garbage on the ostack and clean it up
count pdfemptycount sub dup 0 ne {
( **** File did not complete the page properly and may be damaged.\n)
pdfformaterror
{ pop } repeat
} {
pop
} ifelse
% todo: mixing drawing ops outside the device filter could cause
% problems, for example with the pnga device.
/Annots knownoget { { oforce drawannot } forall } if
endpage
end % scratch dict
% Some PDF files don't have matching q/Q (gsave/grestore) so we need
% to clean up any left over dicts from the dictstack
countdictstack PDFdictstackcount sub dup 0 ne {
( **** Warning: File has imbalanced q/Q operators \(too many q's\)\n)
pdfformaterror
{ end } repeat
} {
pop
} ifelse
(after exec) VMDEBUG
Repaired % pass Repaired state around the restore
PDFSave restore
/Repaired exch def
} bind def
/showpagecontents { % <pagedict> showpagecontents -
gsave % preserve gstate for Annotations later
/Contents knownoget not { 0 array } if
dup type /arraytype ne { 1 array astore } if {
oforce false resolvestream pdfopdict .pdfrun
} forall
grestore
} bind def
/processcolorspace { % - processcolorspace <colorspace>
% The following is per the PLRM3.
currentdevice 1 dict dup /ProcessColorModel dup put .getdeviceparams
exch pop exch pop
dup type /nametype ne { cvn } if
dup { setcolorspace } .internalstopped { pop /DeviceRGB } if
} bind def
% ------ Transparency support ------ %
% Define minimum PDF version for checking for transparency features.
% Transparency is a 1.4 feature however we have seen files that claimed
% to be PDF 1.3 with transparency features.
/PDFtransparencyversion 1.3 def
% Determine whether a page might invoke any transparency features:
% - Non-default BM, ca, CA, or SMask in an ExtGState
% - Image XObject with SMask
% Note: we deliberately don't check to see whether a Group is defined,
% because Adobe Illustrator 10 (and possibly other applications) define
% a page-level group whether transparency is actually used or not.
% Ignoring the presence of Group is justified because, in the absence
% of any other transparency features, they have no effect.
/pageusestransparency { % <pagedict> pageusestransparency <bool>
PDFversion PDFtransparencyversion lt NOTRANSPARENCY or {
pop false
} {
false exch {
4 dict 1 index resourceusestransparency { pop not exit } if
/Parent knownoget not { exit } if
} loop
} ifelse
} bind def
% Check the Resources of a page or Form. Check for loops in the resource chain.
/resourceusestransparency { % <dict> <dict> resourceusestransparency <bool>
{ % Use loop to provide an exitable context.
/Resources knownoget not { 0 dict } if
2 copy known {
( **** File has circular references in resource dictionaries.\n)
pdfformaterror
pop false exit
} if
2 copy dup put
dup /ExtGState knownoget {
false exch {
exch pop oforce
dup /BM knownoget { dup /Normal ne exch /Compatible ne and
{ pop not exit } if
} if
dup /ca knownoget { 1 ne { pop not exit } if } if
dup /CA knownoget { 1 ne { pop not exit } if } if
dup /SMask knownoget { /None ne { pop not exit } if } if
pop
} forall { pop true exit } if
} if
dup /XObject knownoget {
false exch {
exch pop oforce dup /Subtype get
dup /Image eq { 1 index /SMask known { pop pop not exit } if } if
/Form eq {
3 index exch resourceusestransparency { not exit } if
} {
pop
} ifelse
} forall { pop true exit } if
} if
pop false exit
} loop
exch pop
} bind def
% ------ ColorSpace substitution support ------ %
%
% <pagedict> pdfshowpage_setcspacesub <pagedict>
%
% Set up color space substitution for a page. Invocations of this procedure
% must be bracketed by the save/restore operation for the page, to avoid
% unintended effects on other pages.
%
% If any color space substitution is used, and the current color space is a
% device dependent color space, make sure the current color space is updated.
% There is an optimization in the setcolorspace pseudo-operator that does
% nothing if both the current and operand color spaces are the same. For
% PostScript this optimization is disabled if the UseCIEColor page device
% parameter is true. This is not the case for PDF, as performance suffers
% significantly on some PDF files if color spaces are set repeatedly. Hence,
% if color space substitution is to be used, and the current color space
% is a device dependent color space, we must make sure to "transition" the
% current color space.
%
/pdfshowpage_setcspacesub
{
false
{ /DefaultGray /DefaultRGB /DefaultCMYK }
{
dup 3 index /ColorSpace //rget exec
{ resolvecolorspace /ColorSpace defineresource pop }
{ pop }
ifelse
}
forall
% if using color space substitution, "transition" the current color space
{
currentcolorspace dup length 1 eq % always an array
{
0 get
dup /DeviceGray eq 1 index /DeviceRGB eq or 1 index /DeviceCMYK or
{ /Pattern setcolorspace setcolorspace }
{ pop }
ifelse
}
{ pop }
if
}
if
}
bind def
end % pdfdict
.setglobal