% 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 10687 2010-02-02 07:23:57Z alexcher $ % 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 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 /obj { ( **** Warning: Content stream is not terminated by 'endstream'.\n) pdfformaterror pop pop exit } bind % 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 { /BMClevel BMClevel 1 add store pop } bind /BDC { /BMClevel BMClevel 1 add store exch /OC eq { dup type /nametype eq { PDFfile fileposition exch % pos /Name Page /Properties rget { ocg-is-visible not { OFFlevels BMClevel dup put } if } if PDFfile exch setfileposition } { pop } ifelse } { pop } ifelse } bind /EMC { OFFlevels BMClevel 2 copy known { 2 copy undef } if 1 sub /BMClevel exch store pop } bind /MP { pop } bind /DP { pop pop } bind /- { 0 % Bug 690016 ( **** Warning: Invalid operator '-' is assumed to be a number 0.\n) pdfformaterror } 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 ) { dup ( ) .peekstring not { false exit } if dup 0 get 32 le { pop dup read pop pop } { true exit } ifelse } loop exch pop { % Appletalk PAP sends short strings with %! header expecting a response. % 'gv' swallows the %!PS line, then sends DSC comments beginning with %% % and also waits for a response. The following avoids those hangs. dup 2 string .peekstring pop dup (%!) eq exch (%%) eq or { cvx .runps } { dup 1023 string .peekstring pop dup length 400 ge { % "1024 string" exceeds current %stdin buffer % Valid PDF file cannot be smaller than 400 bytes. (%PDF-) search { 3 1 roll pop pop dup (%!PS) search not { length 0 ne { 1 index exch readstring pop pop (%stderr) (w) file dup ( **** Warning: File has some garbage before %PDF- .\n) writestring flushfile } { pop } ifelse dup (%stdin) (r) file eq { % Copy PDF from stdin to temporary file then run it. null (w+) /.tempfile .systemvar 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 } { pop pop pop pop cvx .runps % (%!PS) found first } ifelse } { pop cvx .runps % (%PDF-) not found } ifelse } { pop cvx .runps % too short for PDF } ifelse } ifelse } { closefile % file was empty } ifelse } bind odef currentdict /runpdfstring .undef /runpdfbegin { % runpdfbegin - 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 } bind def /runpdfpagerange { % - runpdfpagerange /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 << /RenderTTNotdef systemdict /RENDERTTNOTDEF get >> setuserparams % Should we render TT /.notdef 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 2 vmreclaim % couldn't hurt } bind def % Copy stream to an external temporary file and % return the file name as PS name. /copy_embedded_file { //true resolvestream % strm dup 1023 string .peekstring pop % "1024 string" exceeds current %stdin buffer dup length 400 ge { % Valid PDF file cannot be smaller than 400 bytes. (%PDF-) search { pop pop pop //true } { pop //false } ifelse } { pop //false } ifelse { //null (w) /.tempfile % strm (name) null (w) /.tempfile .systemvar exec % strm (name) file 3 -1 roll % (name) file strm 32768 string % (name) file strm (buf) { 3 copy readstring % (name) file strm (buf) file (data) bool 3 1 roll % (name) file strm (buf) bool file (data) writestring % (name) file strm (buf) bool not { exit } if } loop pop closefile % (name) file closefile % (name) cvn % /name } { closefile } ifelse } bind def % Copy selected subfiles to temporary files and return the file names % as a PostScript names to protect them from restore. % Currently, all PDF files in the Portfolio are extracted and returned. % % - pdf_collection_files [ /temp_file_name ... /temp_file_name /pdf_collection_files { mark Trailer /Root oget dup /Collection oknown { /Names knownoget { /EmbeddedFiles knownoget { /Names knownoget { { oforce dup type /dicttype eq { /EF knownoget { /F knownoget { copy_embedded_file } if } if } { pop } ifelse } forall } if } if } if } { pop } ifelse } bind def /runpdf { % runpdf - //runpdfbegin exec //pdf_collection_files exec dup mark eq { pop process_trailer_attrs //runpdfpagerange exec //dopdfpages exec //runpdfend exec } { //runpdfend exec ] { dup type /filetype eq { //runpdfbegin exec process_trailer_attrs //runpdfpagerange exec //dopdfpages exec //runpdfend exec closefile } { .namestring dup (r) file //runpdfbegin exec process_trailer_attrs //runpdfpagerange exec //dopdfpages exec //runpdfend exec deletefile } ifelse } forall } ifelse } bind def currentdict /pdf_collection_files .undef 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 { /dictlevelcount dictlevelcount 1 add store mark } bind (>>) cvn { { .dicttomark } stopped { ( **** File has unbalanced >> in trailer.\n) pdfformaterror } if /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 % Because of EOL conversion, lines with fixed contents might be followed % by one or more blanks. /lineeq % lineeq { 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 % readorigxref { 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: 2 copy add growPDFobjects { % stack: % 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: 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: dup /n eq { % xref line tag is /n pop % pop dup of line tag 2 copy or 0 eq { ( **** Warning: considering '0000000000 00000 n' as a free entry.\n) pdfformaterror } { 0 3 1 roll % Set ObjectStream object number = 0 //false setxrefentry % Save xref entry, don't change existing entries 3 -1 roll pop % Remove ObjectStream object onumber } ifelse } { % xref line tag was not /n /f ne % verify that the tag was /f { /setxrefentry cvx /syntaxerror signalerror } if } ifelse pop pop % pop and % stack: 1 add % increment object number } repeat pop % pop } loop 0 ne { ( **** Warning: length of some xref entries is not equal to 20 bytes.\n) pdfformaterror } if /dictlevelcount 0 def 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 { % 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: % % 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 //false 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 //false 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 % readpdf15xref { 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: % 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: % 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. % is the position either from the startxref statement or the /Prev % entry in the prior trailer dictionary. /readxref % readxref { PDFoffset add PDFfile exch % Check that the given location is within the file. dup PDFfilelen gt { ( **** Warning: Specified xref location is beyond end of file.\n) pdfformaterror /readxref cvx /invalidaccess signalerror } if 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 % if hybrid-reference PDF, also fetch the entries % found in the XRef stream pointed by /XRefStm dup /XRefStm knownoget { readpdf15xref pop } if } { readpdf15xref } % otherwise assume PDF 1.5 xref stream ifelse } bind def % Open a PDF file and read the header, trailer, and cross-reference. /pdfopen { % pdfopen % 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 currentdict end } bind def /process_trailer_attrs { % - process_trailer_attrs - writeoutputintents .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 % Initialize OC groups Trailer /Root oget /OCProperties knownoget { % By default, OCGs are 'on'; mark only 'off' OCGs. /D knownoget { /OFF knownoget { { oforce dup type /dicttype eq { /OFF 0 put } { pop } ifelse } forall } if } if } if } 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 - { PDFfilelen 1 1 Objects llength 1 sub % stack: filesize 1 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. % Stack: 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 dup 3 index ge % Compare object location to file size { pop true } % Rebuild if location not in file { PDFfile exch setfileposition % Go to the object location true % Stack: 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 } ifelse } .internalstopped { true } if % If we stop then we need to rebuild % Stack: { ( **** 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 } { % The object is in an object stream. We currently do not rebuild % objects in an object stream. So If we find one, then abort the % verification of the xref table entries. pop exit % Pop object number and then exit loop } ifelse % If not in an object stream } if % If object entry is not free pop % Remove object number } for pop % Remove the size of the file } 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 { % pdfopenfile 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 /UndefProcList 0 dict def end .setglobal PDFfile dup 0 setfileposition 0 () /SubFileDecode filter % to avoid file closure pdfstring readstring pop (%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 % Scan numbers in the range 2147483648..4294967295 in Encrypt dictionary % as unsigned integers for compatibility with Acrobat Reader. Bug 689010. << /PDFScanUnsigned //true >> setuserparams { Trailer /Encrypt knownoget { pop pdf_process_Encrypt % signal error } if } stopped << /PDFScanUnsigned //false >> setuserparams { stop } if % Check for recursion in the page tree. Bug 689954, MOAB-06-01-2007 verify_page_tree 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 { % find_eof -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 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 { % stderrprint - //stderrfile dup 3 -1 roll writestring flushfile } bind def /pdfformaterror { % 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 UndefProcList collects noisy warnings. % This gets rid of many multiple warnings from pdf_font.ps /printCollectedWarnings { 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 % 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 { dup /A knownoget { dup /URI known { /A mark 3 2 roll % <<>> /A [ <> { 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 } 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 % pdfclose - { begin PDFfile closefile end } bind def % ======================== Page accessing ======================== % % Get a (possibly inherited) attribute of a page. /pget % pget -true- % pget -false- { 2 copy knownoget { exch pop exch pop true } { exch /Parent knownoget { exch pget } % finally see if the key is (misplaced) in the Root Catalog dict { dup Trailer /Root oget exch knownoget dup { 3 -1 roll ( **** Warning: The /) pdfformaterror 50 string cvs pdfformaterror ( key is missing from the Page tree.\n) pdfformaterror } { exch pop } ifelse } ifelse } ifelse } bind def % Get the value of a resource on a given page. /rget { % rget -true- % rget -false- LocalResources 1 index knownoget { dup type /dicttype eq { 3 index knownoget } { //false exch { 4 index knownoget { exch not exit } if } forall } ifelse } { //false } ifelse { exch pop exch pop exch pop //true } { exch /Resources pget { exch knownoget { % /Name [<<>> ...] dup type /dicttype eq { exch knownoget } { % GS uses array of dicts if resources don't fit in one dict. //false 3 1 roll { % false /Name <<>> 1 index knownoget { % false /Name val exch pop exch not % i.e. true 0 exit } if } forall pop } ifelse } { pop //false } ifelse } { pop pop //false } ifelse } ifelse } bind def % Get the total number of pages in the document. /pdfpagecount % - pdfpagecount { Trailer /Root oget /Pages oget dup /Count knownoget { dup 0 le { pop dup /Kids knownoget { pop ( **** Warning: Invalid Page count.\n) pdfformaterror % find the last page and use that as the Count 1 1 999999999 { dup pdffindpage? exch pop //null eq { exit } { pop } ifelse } for 1 sub % decrement to last page that we were able to find 2 copy /Count exch put } { 0 % return 0 and keep 0 page count. ( **** Warning: PDF document has no pages.\n) pdfformaterror } ifelse } if exch pop } { dup /Type oget /Page eq { << exch 1 array astore /Kids exch /Count 1 /Type /Pages >> Trailer /Root oget /Pages 3 -1 roll put 1 ( **** Warning: No /Pages node. The document root directly point a page.\n) pdfformaterror } { ( **** Warning: Page count not found; assuming 1.\n) pdfformaterror pop 1 } ifelse } ifelse } bind def % Check for loops in the 'page tree' but accept an acyclic graph. % - verify_page_tree - /verify_page_tree { Trailer /Root oget /Pages oget 10 dict begin /verify_page_tree_recursive { dup 1 def dup /Kids knownoget { { oforce currentdict 1 index known { ( **** Error: there's a loop in the page tree. Giving up.\n) pdfformaterror /verify_page_tree cvx /syntaxerror signalerror } if verify_page_tree_recursive } forall } if currentdict exch undef } def verify_page_tree_recursive end } bind def /pdffindpage? { % pdffindpage? 1 null (page not found) % pdffindpage? 1 noderef (page found) % pdffindpage? 0 null (Error: page not found) 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 } bind def % Find the N'th page of the document by iterating through the Pages tree. % The first page is numbered 1. /pdffindpageref { % pdffindpage dup pdffindpage? % 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 { % pdffindpage pdffindpageref oforce } bind def % Find the N'th page of the document. % The first page is numbered 1. /pdfgetpage % pdfgetpage { 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 % pdfpagenumber { % 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 % normrect_elems { exch 4 1 roll % 2 copy gt { exch } if % 4 2 roll 2 copy lt { exch } if % 4 1 roll exch % } bind def % Arrange a rectangle into a 'normal' order. I.e the lower left corner % followed by the upper right corner. /normrect % normrect { aload pop normrect_elems 4 array astore } bind def /fix_empty_rect_elems % fix_empty_rect_elems { dup 3 index eq { //true } { 1 index 4 index eq } ifelse { pop pop pop pop ( **** Warning: File has an empty ) pdfformaterror pdfstring cvs pdfformaterror (. Using the current page size instead.\n) pdfformaterror 0 0 currentpagedevice /PageSize get aload pop } { 5 -1 roll pop } ifelse } bind def /boxrect % boxrect { exch 3 index sub exch 2 index sub } bind def /resolvedest { % resolvedest 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 % Procedures to do the necessary transformations of view destinations % -- /viewdestprocs 8 dict dup begin /Fit { exch pop exch pop } bind def /FitH { aload pop 0 4 -1 roll 1 and 0 eq { exch } if 4 -1 roll transform exch pop 2 array astore } bind def /FitV { aload pop 0 4 -1 roll 1 and 0 ne { exch } if 4 -1 roll transform pop 2 array astore } bind def /FitB /Fit load def /FitBH /FitH load def /FitBV /FitV load def /XYZ { aload pop 3 1 roll 2 copy 7 -1 roll 1 and 0 ne { exch } if 4 2 roll % odd rotation switches x<->y 2 { dup null eq { pop 0 } if exch } repeat % replace nulls with 0 7 -1 roll transform % transform coordinates 2 { 3 -1 roll null eq { pop null } if exch } repeat % put the nulls back 3 -1 roll 4 array astore } bind def /FitR { exch pop aload pop 2 { 5 index transform 4 2 roll } repeat normrect_elems 5 array astore exch pop } bind def end readonly def /linkdest { % linkdest % ([/Page ] /View | ) dup /Dest knownoget { resolvedest dup type /dicttype eq { /D knownoget not { null } if } if dup null eq { pop } { dup 0 oget false % don't have a page# and transformation matrix (yet) 1 index type /dicttype eq { 1 index /Type knownoget { /Page eq { pop % the "false" flag dup pdf_cached_PDF2PS_matrix exch dup /Rotate pget not { 0 } if 90 idiv exch pdfpagenumber true % now we have a page# and a transformation matrix } if } if } if % stack: ( true | false ) { /Page exch 6 2 roll % stack: [/Page ] 3 -1 roll dup length 1 sub 1 exch getinterval /View 4 1 roll % stack: [/Page ] /View //viewdestprocs 1 index 0 get get exec 3 -1 roll } { pop dup length 1 sub 1 exch getinterval /View exch 3 -1 roll } ifelse } ifelse } if } bind def % mark ... -proc- /namedactions 8 dict dup begin /FirstPage { 1 //false } def /LastPage { pdfpagecount //false } def /NextPage { counttomark 2 add index pdfpagenumber 1 add dup pdfpagecount gt } bind def /PrevPage { counttomark 2 add index pdfpagenumber 1 sub dup 1 lt } bind def end readonly def % -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 /BS knownoget { << exch { oforce } forall >> /BS exch 3 -1 roll } if dup /F knownoget { /F exch 3 -1 roll } if dup /C knownoget { /Color exch 3 -1 roll } if dup /Rect knownoget { /Rect exch 3 -1 roll } if dup /Border knownoget { dup type /arraytype eq { dup length 3 lt } { //true } ifelse { pop [ 0 0 0 ] % Following AR5 use invisible border. } if /Border exch 3 -1 roll } if dup /A knownoget { dup /URI known { /A mark 3 2 roll % <<>> /A [ <> { 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 { pop ( **** Warning: Ignoring a named action pointing out of the document page range.\n) pdfformaterror } { /Page exch 3 -1 roll } ifelse } 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 % pdfshowpage - { dup /Page exch store pdfshowpage_init pdfshowpage_setpage pdfshowpage_finish } bind def /pdfpagecontents % pdfpagecontents { } bind def /pdfshowpage_init % pdfshowpage_init { /DSCPageCount DSCPageCount 1 add store } bind def /get_media_box { % get_media_box /MediaBox pget not { ( **** Page has no /MediaBox attribute. Using the current page size.\n) pdfformaterror [ 0 0 currentpagedevice /PageSize get aload pop ] } if } bind def /get_any_box { % get_any_box //systemdict /UseTrimBox .knownget dup { and } if { dup /TrimBox pget { exch pop /TrimBox exch } if } if dup type /arraytype ne { //systemdict /UseCropBox .knownget dup { and } if { dup /CropBox pget { exch pop /CropBox exch } if } if } if dup type /arraytype ne { /MediaBox exch get_media_box } if } bind def % Compute the matrix that transforms the PDF->PS "default" user space /pdf_PDF2PS_matrix { % -- matrix matrix currentmatrix matrix setmatrix exch % stack: savedCTM dup get_any_box % stack: savedCTM /Trim|Crop|MediaBox oforce_elems normrect_elems fix_empty_rect_elems 4 array astore //systemdict /PDFFitPage known { PDFDEBUG { (Fiting PDF to imageable area of the page.) = flush } if currentpagedevice /.HWMargins get aload pop currentpagedevice /PageSize get aload pop % Adjust PageSize and .HWMargins for the page portrait/landscape orientation 2 copy gt 7 index aload pop 3 -1 roll sub 3 1 roll exch sub exch 10 index /Rotate pget not { 0 } if 90 idiv 1 and 0 ne { exch } if gt ne { 2 copy ne { % rotate the .HWMargins 2 copy lt { 6 2 roll 4 -1 roll 6 -2 roll } { 6 2 roll 4 1 roll 6 -2 roll } ifelse % rotate the page dimensions exch } if } if 3 -1 roll sub 3 1 roll exch sub exch % stack: savedCTM 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: savedCTM [Box] XImageable YImageable 2 index aload pop 2 index sub exch 3 index sub exch 4 2 roll pop pop 5 index /Rotate pget not { 0 } if 90 idiv 1 and 0 ne { exch } if % stack: savedCTM [Box] XImageable YImageable XBox YBox 3 -1 roll exch div 3 1 roll div .min PDFDEBUG { ( Scale by ) print dup = flush } if } { //systemdict /NoUserUnit .knownget not { false } if { 1 } { 1 index /UserUnit knownoget { PDFDEBUG { (Scaling due to UserUnit by ) print dup = flush } if } { 1 } ifelse } ifelse } ifelse % stack: savedCTM [Box] scale dup scale % Rotate according to /Rotate aload pop boxrect { { pop pop } { -90 rotate pop neg 0 translate } { 180 rotate neg exch neg exch translate } { 90 rotate neg 0 exch translate pop } } 5 index /Rotate pget not { 0 } if PDFDEBUG { dup 0 ne { (Rotating by ) print dup =print ( degrees.) = flush } if } if 90 idiv 3 and get exec % Now translate to the origin given in the Crop|Media Box exch neg exch neg translate % stack: savedCTM pop matrix currentmatrix exch setmatrix } bind def % Cache the matrix that transforms the PDF->PS "default" user space % into under the key //PDF2PS_matrix_key, then return it /PDF2PS_matrix_key (PDF->PS matrix) cvn def /pdf_cached_PDF2PS_matrix { % -- dup //PDF2PS_matrix_key .knownget { exch pop } { dup dup pdf_PDF2PS_matrix //PDF2PS_matrix_key exch put //PDF2PS_matrix_key get } ifelse } bind def currentdict /PDF2PS_matrix_key undef /.pdfshowpage_Install { % [] .pdfshowpage_Install - 0 get exec pdf_cached_PDF2PS_matrix concat } bind def /pdfshowpage_setpage { % pdfshowpage_setpage 6 dict begin % for setpagedevice % Stack: pdfpagedict % UseCIEColor is always true for PDF; see the comment in runpdf above /UseCIEColor true def /Orientation 0 def currentpagedevice % Stack: pdfpagedict currentpagedevicedict 1 index get_any_box % Stack: pdfpagedict currentpagedevicedict /BoxName [box] oforce_elems normrect_elems fix_empty_rect_elems boxrect 4 2 roll pop pop 3 index /Rotate pget not { 0 } if 90 idiv 1 and 0 ne { exch } if % stack: pdfpagedict currentpagedevicedict boxwidth boxheight //systemdict /PDFFitPage known { % Preserve page size, % but choose portrait/landscape depending on box width:height ratio % (if box width == height, select portrait orientation) gt 1 index /PageSize get aload pop 2 copy gt 4 -1 roll ne { exch } if } { % Set the page size. //systemdict /NoUserUnit .knownget not { false } if not { 3 index /UserUnit knownoget { dup 4 -1 roll mul 3 1 roll mul } if } if } ifelse 2 array astore /PageSize exch def % Determine the number of spot colors used on the page. Note: This searches % the pages resources. It may be high if a spot color is in a resource but % is not actually used on the page. /PageSpotColors 2 index countspotcolors def % 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 /.free_page_resources { % - .free_page_resources - Page /Resources pget { /Shading knownoget { { { dup type /dicttype eq { dup /.shading_dict known { dup /.shading_dict undef } if } if pop pop } forall } big-res-forall } if } if } bind def /pdfshowpage_finish { % 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 pget { % .pdfshowpage_Install transforms the user space - % do same here with the CropBox. oforce_elems 2 { Page pdf_cached_PDF2PS_matrix transform 4 2 roll } repeat normrect_elems /CropBox 5 1 roll fix_empty_rect_elems 4 array astore mark /CropBox 3 -1 roll /PAGE pdfmark } if % Copy annotations and links. dup /Annots knownoget { 0 1 2 index length 1 sub { 1 index exch oget dup type /dicttype eq { dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse } { pop } ifelse } for pop } if } if % end .writepdfmarks % Display the actual page contents. 8 dict begin /BXlevel 0 def /BMClevel 0 def /OFFlevels 0 dict def /BGDefault currentblackgeneration def /UCRDefault currentundercolorremoval def %****** DOESN'T HANDLE COLOR TRANSFER YET ****** /TRDefault currenttransfer def matrix currentmatrix 2 dict 2 index /CropBox pget { oforce_elems normrect_elems boxrect 4 array astore 1 index /ClipRect 3 -1 roll put } if dictbeginpage setmatrix /DefaultQstate qstate store 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 pget { /CropBox exch } { 1 index get_media_box /MediaBox exch } ifelse oforce_elems normrect_elems fix_empty_rect_elems 4 array astore .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 .free_page_resources % todo: mixing drawing ops outside the device filter could cause % problems, for example with the pnga device. endpage end % scratch dict % Indicate that the number of spot colors is unknown in case the next page % imaged is a PS file. << /PageSpotColors -1 >> .setpagedevice % 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 unbalanced 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 % Display the contents of a page (including annotations). /showpagecontents { % showpagecontents - dup % Save the pagedict for the Annotations count 1 sub /pdfemptycount exch store 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 % 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 grestore % Draw the annotations //systemdict /ShowAnnots .knownget not { //true } if { /Annots knownoget { { oforce dup //null ne { drawannot } { pop } ifelse } forall } if } if //systemdict /ShowAcroForm .knownget { //true eq } { //false } ifelse { Trailer /Root oget /AcroForm knownoget { draw_acro_form } if } if } bind def /processcolorspace { % - processcolorspace % 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.2 with transparency features. Bug 689288. /PDFtransparencyversion 1.2 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 { % pageusestransparency PDFversion PDFtransparencyversion lt NOTRANSPARENCY or { pop //false } { dup //false exch { 4 dict 1 index resourceusestransparency { pop not exit } if /Parent knownoget not { exit } if } loop % Also check for transparency in the annotation (if not in resources). { pop //true } { annotsusetransparency } ifelse } ifelse } bind def % Check if transparency is specified in an ExtGState dict /extgstateusestransparency { % extgstateusestransparency //false exch % Assume no transparency { % establish loop context 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 exit } loop } bind def % Check if transparency is used in a Pattern /patternusestransparency { % patternusestransparency NOTRANSPARENCY { pop //false } { //false exch % Assume no transparency { 4 dict 1 index resourceusestransparency { pop not exit } if dup /ExtGState knownoget { extgstateusestransparency { pop not exit } if } if pop exit } loop } ifelse } bind def % Check the Resources of a page or Form. Check for loops in the resource chain. /resourceusestransparency { % resourceusestransparency { % Use loop to provide an exitable context. /Resources knownoget not { 0 dict } if 2 copy .knownget { { % Some circular references may be missed because scanning stops % when the 1st transparency is found. ( **** File has circular references in resource dictionaries.\n) pdfformaterror } if pop //false exit } if 2 copy //true put % In the current chain. dup /ExtGState knownoget { //false exch { { exch pop oforce extgstateusestransparency { pop //true exit } if } forall dup { exit } if } big-res-forall { pop //true exit } if } if dup /Pattern knownoget { //false exch { { exch pop oforce patternusestransparency { pop //true exit } if } forall dup { exit } if } big-res-forall { pop //true exit } if } if dup /XObject knownoget { dup type dup /dicttype eq exch /arraytype eq or { //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 dup { exit } if } big-res-forall { pop //true exit } if } { ( **** Ignoring non-dictionary /XObject attribute.\n) pdfformaterror pop } ifelse } if 2 copy //false put % Visited but not in the current chain. pop //false exit } loop exch pop } bind def % Check if the annotations on a page use transparency /annotsusetransparency { % annotsusetransparency //false exch % Assume no transparency /Annots knownoget { % Get Annots array { oforce dup //null ne { /AP knownoget { % Get appearance dict for the annoation /N knownogetdict { % Get the /N (i.e. normal) appearance stream 4 dict exch resourceusestransparency { pop //true exit } if } if } if % If AP dict known } { pop } ifelse } forall % For all annots on the page } if } bind def % Add a color name to our spot color list. Ignore /All and /None /putspotcolor { % putspotcolor - % The 'name' could be a string. If so then convert to a name. exch dup type /stringtype eq { cvn } if dup dup /None eq exch /All eq or { pop pop } { 0 put } ifelse } bind def % Determine which spot colors are used within a color space Note: This % dict will include all colors used in Separation or DeviceN color spaces. % Thus it may include Cyan, Magenta, Yellow, and Black. % colorspacespotcolors - /colorspacespotcolors { exch dup type /arraytype eq { % If we have an Indexed color space then get the base space. dup 0 oget dup /Indexed eq { pop 1 oget 2 copy colorspacespotcolors } { % Stack: dup /Separation eq exch /DeviceN eq or { dup 1 oget dup type /arraytype eq { { oforce 2 index putspotcolor } forall } { 2 index putspotcolor } ifelse } if } ifelse } if pop pop } bind def % Enumerate resource dictionary or an array of dictionaries % big-res-forall - /big-res-forall { 1 index type /dicttype eq { 0 get } if forall } bind def % Check the Resources of a page, form, or annotation. Determine which spot % colors are used within the resource Note: The spot color dict will include % all colors used in Separation or DeviceN color spaces. Thus it may include % Cyan, Magenta, Yellow, and Black. We also pass a dict that is used to check % for loops in the resource list. % % resourcespotcolors /resourcespotcolors { { % Use loop to provide an exitable context. % Exit if no Resources entry /Resources knownoget not { exit } if % Exit if we have already seen this dict 2 copy known { pop exit } if % Save the Resources dict into our loop checking dict. 2 copy 0 put % Scan resources that might contain a color space. dup /ColorSpace knownoget { { { exch pop oforce 3 index colorspacespotcolors } forall } big-res-forall } if dup /Pattern knownoget { { { exch pop oforce 4 copy exch pop resourcespotcolors pop pop pop } forall } big-res-forall } if dup /Shading knownoget { { { exch pop oforce /ColorSpace oget 3 index colorspacespotcolors } forall } big-res-forall } if /XObject knownoget { dup type dup /dicttype eq exch /arraytype eq or { { { exch pop oforce dup /Subtype get /Form eq { resourcespotcolors } { pop } ifelse } forall } big-res-forall } { pop % Just ignore here, already reported by resourceusestransparency. } ifelse } if exit } loop } bind def % Determine which spot colors are used within the annotations. Note: This % dict will include all colors used in Separation or DeviceN color spaces. % Thus it may include Cyan, Magenta, Yellow, and Black. % % annotsspotcolors /annotsspotcolors { { oforce dup //null ne { /AP knownoget { % Get appearance dict for the annoation /N knownogetdict { % Get the /N (i.e. normal) appearance stream resourcespotcolors } if % If normal appearance streamknown } if % If AP dict known } { pop } ifelse } forall } bind def % Determine spot colors are used within a page. We are creating a dict to % hold the spot color names as keys. Using a dict avoids having to worry % about duplicate entries. The keys in the dict contain the spot color % names. However the values have no meaning. Note: This dict will include % all colors used in Separation or DeviceN color spaces specified in the % page's resources. Thus it may include Cyan, Magenta, Yellow, and Black. % There is no attempt to verify that these color spaces are actually used % within the object streams for the page. /pagespotcolors { % pagespotcolors dup % Create a dict to hold spot color names. 0 dict exch % Create a dict to be used to check for reference loops. 4 dict exch % Check for color spaces in the Resources resourcespotcolors % Also check for color spaces in the annotations. 3 -1 roll /Annots knownoget { annotsspotcolors } if pop % Discard reference loop dict } bind def % Determine how many (if any) spot colors are used by a page. % Note: This count does not include Cyan, Magenta, Yellow, or Black /countspotcolors { % countspotcolors pagespotcolors % Get dict with all spot colors dup length % spot color dict length % Remove CMYK from the spot color count. [ /Cyan /Magenta /Yellow /Black ] { 2 index exch known { 1 sub } if } forall exch pop % Remove spot color dict } bind def % ------ ColorSpace substitution support ------ % % % pdfshowpage_setcspacesub % % 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 % Write OutputIntents to device if the device handles it /writeoutputintents { currentdevice 1 dict dup /OutputIntent //null put readonly .getdeviceparams mark ne { pop pop % device supports OutputIntent parameter Trailer /Root oget /OutputIntents knownoget { dup type /arraytype eq { { % process all output profiles present oforce dup length dict .copydict dup /DestOutputProfile knownoget { PDFfile fileposition exch mark exch { oforce } forall .dicttomark //true resolvestream [ { counttomark 1 add index 64000 string readstring not { exit } if } loop ] exch closefile 0 1 index { length add } forall .bytestring 0 3 2 roll { 3 copy putinterval length add } forall pop exch PDFfile exch setfileposition 1 index /DestOutputProfile 3 2 roll put } if % Convert to string array because it's easier for the device [ 1 index /OutputCondition knownoget not { () } if 2 index /OutputConditionIdentifier knownoget not { () } if 3 index /RegistryName knownoget not { () } if 4 index /Info knownoget not { () } if 5 index /DestOutputProfile knownoget not { () } if ] [ /OutputIntent 3 2 roll .pdfputparams pop pop pop % done with this OutputIntent dictionary } forall } { pop ( **** Warning: OutputIntent attribute of a wrong type is ignored.\n) pdfformaterror } ifelse } if % OutputIntents known % tell device there are no more OutputIntents [ /OutputIntent [ ] .pdfputparams pop pop } if } bind def end % pdfdict .setglobal