ref: c138c7d8813dcd77cfe33e37f1416427819b5e93
dir: /sys/lib/ghostscript/pdf_main.ps/
% 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 /NoVerifyXref true 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