1 % Copyright (C) 1994, 2000 Aladdin Enterprises. All rights reserved.
3 % This software is provided AS-IS with no warranty, either express or
6 % This software is distributed under license and may not be copied,
7 % modified or distributed except as expressly authorized under the terms
8 % of the license contained in the file LICENSE in this distribution.
10 % For more information about licensing, please refer to
11 % http://www.ghostscript.com/licensing/. For information on
12 % commercial licensing, go to http://www.artifex.com/licensing/ or
13 % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 % San Rafael, CA 94903, U.S.A., +1(415)492-9861.
16 % $Id: pdf_main.ps,v 1.100 2005/09/23 18:21:23 ray Exp $
18 % PDF file- and page-level operations.
20 /.setlanguagelevel where { pop 2 .setlanguagelevel } if
21 .currentglobal true .setglobal
22 /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
25 % Patch in an obsolete variable used by some third-party software.
28 /NoVerifyXref true def
30 % Test whether the current output device handles pdfmark.
31 /.writepdfmarkdict 1 dict dup /pdfmark null put readonly def
32 /.writepdfmarks { % - .writepdfmarks <bool>
33 currentdevice //.writepdfmarkdict .getdeviceparams
34 mark eq { false } { pop pop true } ifelse
35 systemdict /DOPDFMARKS known or
38 % For simplicity, we use a single interpretation dictionary for all
39 % PDF graphics execution, even though this is too liberal.
43 /endstream { exit } bind
44 (%%EOF) cvn { exit } bind % for filters
46 /BX { /BXlevel BXlevel 1 add store } bind
47 /EX { /BXlevel BXlevel 1 sub store } bind
55 .dicttomark readonly def
57 % ======================== Main program ======================== %
62 /defaultfontname /Times-Roman def
64 % Make sure the registered encodings are loaded, so we don't run the risk
65 % that some of the indices for their names will overflow the packed
66 % representation. (Yes, this is a hack.)
70 % Redefine 'run' so it recognizes PDF files.
74 dup type /filetype ne { (r) file } if
75 % skip leading whitespace characters (actually anything less than or equal to <sp>)
76 { dup ( ) .peekstring not { false exit } if
77 dup 0 get 32 le { pop dup read pop pop } { true exit } ifelse
83 dup (%stdin) (r) file eq {
84 % Copy PDF from stdin to temporary file then run it.
85 null (w+) //systemdict /.tempfile get exec exch 3 1 roll
86 % stack: tempname stdin tempfile
89 % stack: tempname stdin tempfile string
90 2 index 1 index readstring
91 exch 3 index exch writestring
96 % stack: tempname tempfile
104 cvx .runps % doesn't start with %PDF-
107 pop cvx .runps % didn't read 5 characters
110 cvx .runps % didn't start with %
113 pop closefile % file was empty
116 currentdict /runpdfstring .undef
119 /runpdfbegin { % <file> runpdf -
121 % It turns out that the PDF interpreter uses memory more
122 % effectively if it is run under at least one level of save.
123 % This is counter-intuitive, and we don't understand why it happens,
124 % but the improvement is significant.
134 Trailer /Root oget /Pages oget /CropBox knownoget
135 { oforce_array normrect mark /CropBox 3 -1 roll /PAGES pdfmark
139 { pop FirstPage dup pdfpagecount gt
140 { (\nRequested FirstPage is greater than the number of pages in the file: ) print
146 /LastPage where { pop LastPage pdfpagecount .min } { pdfpagecount } ifelse
148 { ( No pages will be processed \(FirstPage > LastPage\).) = flush }
150 { (Processing pages ) print 1 index =only ( through ) print dup =only
158 /dopdfpages { % firstpage# lastpage# dopdfpages -
159 << /PDFScanRules true >> setuserparams % set scanning rules for PDF vs. PS
161 { dup /Page# exch store
162 QUIET not { (Page ) print dup == flush } if
163 pdfgetpage pdfshowpage
165 << /PDFScanRules null >> setuserparams % restore scanning rules for PS
169 Repaired { printrepaired } if
178 /runpdf { % <file> runpdf -
185 % Redefine the procedure that the C code uses for running piped input.
186 % It is OK to use { (%stdin) run } here, because a startjob cannot occur.
188 { (%stdin) run } execute0
194 % ======================== File parsing ======================== %
196 % Read the cross-reference and trailer sections.
199 (<<) cvn { mark } bind
200 (>>) cvn { { .dicttomark } stopped {
201 ( **** File has unbalanced >> in trailer.\n) pdfformaterror
203 ([) cvn { mark } bind % ditto
205 % /true true % see .pdfexectoken in pdf_base.ps
206 % /false false % ibid.
208 /R { /resolveR cvx 3 packedarray cvx } bind % see Objects below
209 /startxref /exit load
210 .dicttomark readonly def
212 % Because of EOL conversion, lines with fixed contents might be followed
213 % by one or more blanks.
214 /lineeq % <filestr> <conststr> lineeq <bool>
216 { pop { ( ) anchorsearch not { () eq exit } if pop } loop }
220 /linene { lineeq not } bind def
222 % Read original version (pre PDF 1.5) of the xref table.
223 % Note: The position is the location of 'xref'. The current PDFfile
224 % position is just after the 'XREF'.
225 /readorigxref % <pos> readorigxref <trailerdict>
227 pop % We do not need the position.
228 0 % Initialize xref table error counter
229 { PDFfile token pop % first object # or trailer
230 dup /trailer eq { pop exit } if
231 PDFfile pdfstring readline pop
232 token pop % entry count
233 % remaining must be whitespace only (otherwise this xref Size was invalid.
234 exch dup length 0 ne {
235 false 1 index { 32 gt { pop true exit } if } forall {
236 ( **** Warning: xref subsection header has extra characters.\n)
238 /setxrefentry cvx /syntaxerror signalerror
242 % This section might be adding new objects:
243 % ensure that Objects and Generations are big enough.
244 % stack: <err count> <first obj> <entry count>
245 2 copy add growPDFobjects
246 { % stack: <err count> <obj num>
248 PDFfile 20 string readstring pop % always read 20 chars.
249 token pop % object position
250 exch token pop % generation #
251 exch token pop % n or f
252 exch % stack: <err count> <obj#> <loc> <gen#> <tag> <remainder of line>
254 % check to make sure trailing garbage is just white space
255 dup { 32 gt { 5 -1 roll 1 add 5 1 roll } if } forall % bump error count on garbage
257 pop % Stack: <err count> <obj#> <loc> <gen#> <tag>
258 dup /n eq { % xref line tag is /n
259 pop % pop dup of line tag
260 Objects 3 index lget null eq { % later update might have set it
261 0 3 1 roll % Set ObjectStream object number = 0
262 setxrefentry % Save xref entry
263 3 -1 roll pop % Remove ObjectStream object onumber
266 { % xref line tag was not /n
267 /f ne % verify that the tag was /f
268 { /setxrefentry cvx /syntaxerror signalerror
271 pop pop % pop <obj location> and <gen num>
272 % stack: <err count> <obj num>
273 1 add % increment object number
278 ( **** Warning: length of some xref entries is not equal to 20 bytes.\n)
281 PDFfile traileropdict .pdfrun
284 % This dicitonary is used to read the xref dictionary. It should work for
285 % reading any dictionary. dictlevelcount must contain 0.
287 (<<) cvn { /dictlevelcount dictlevelcount 1 add def mark } bind
288 (>>) cvn { .dicttomark /dictlevelcount dictlevelcount 1 sub def
289 dictlevelcount 0 eq { exit} if } bind
290 ([) cvn { mark } bind % ditto
292 % /true true % see .pdfexectoken in pdf_base.ps
293 % /false false % ibid.
295 /R { /resolveR cvx 3 packedarray cvx } bind % see Objects below
296 .dicttomark readonly def
298 % Get a variable length positive integer value from a stream. A value
299 % of zero is returned if the count is zero.
300 /getintn { % <stream> <count> getintn int
301 0 exch { 256 mul 1 index read pop add } repeat
302 exch pop % Discard stream
305 % This array contains handlers for processing the different types of
306 % entries in the XRef stream.
307 % Stack: <Xrefdict> <xref stream> <Index array> <pair loc> <obj num>
308 % <field 2> <field 3>
309 % The handlers leave the stack unchanged.
310 /xref15entryhandlers [
311 { % XRef entry type 0 - free or f type xref entry
313 % (obj num: ) print 2 index pdfstring cvs print ( ) print
314 % (loc: ) print 1 index pdfstring cvs print ( ) print
315 % (gen: ) print dup === flush
316 } bind % Do nothing for free xref entries
317 % XRef entry type 1 - normal or n type xref entry
318 { % field 2 = obj loc, field 3 = gen num
320 % (obj num: ) print 2 index pdfstring cvs print ( ) print
321 % (loc: ) print 1 index pdfstring cvs print ( ) print
322 % (gen: ) print dup === flush
323 0 3 1 roll % set stream number = 0
325 3 -1 roll pop % remove stream number
327 % XRef entry type 2 - compressed object type xref entry
328 { % field 2 = object stream num, field 3 = index into object stream
329 % (Compressed objects: ) print
330 % (obj num: ) print 2 index pdfstring cvs print ( ) print
331 % (field 2: ) print 1 index pdfstring cvs print ( ) print
332 % (field 3: ) print dup === flush
333 0 setxrefentry pop % set generation number = 0
337 % Read the PDF 1.5 version of the xref table.
338 % Note: The position is the location of the start of the dictionary object
339 % In PDF 1.5, the XRef dictionary also serves as the trailer dictionary
340 /readpdf15xref % <pos> readpdf15xref <trailerdict>
342 PDFfile exch setfileposition % move to start of object
343 % Get object number, revision, and 'obj' and discard
344 PDFfile token pop pop
345 PDFfile token pop pop
346 PDFfile token pop pop
347 % Get the XRef dicitionary
348 /dictlevelcount 0 def PDFfile xrefopdict .pdfrun
349 % Verify that we have an XRef dictionary
350 dup /Type get /XRef ne {
351 /readpdf15xref cvx /syntaxerror signalerror
353 % Ensure that we we have room in the objects array, etc.
354 dup /Size get growPDFobjects
355 % Create a stream for the XRef data
356 PDFfile token pop pop % Skip over 'stream'
357 dup stream false resolvestream
358 % Stack: <XRefdict> <xref stream>
359 % The Index array defines the ranges of object numbers in the
360 % XRef stream. Each value pair is consists of starting object
361 % number and the count of consecutive objects.
362 % Get the Index array, if present
363 1 index /Index .knownget not { % If no Index array ...
364 [ 0 3 index /Size get ] % Default = [ 0 Size ]
366 % Loop through the Index array
367 0 2 2 index length 1 sub {
368 % Get start and end of object range
369 2 copy get % Start of the range
370 dup 3 index 3 index 1 add get % Number of entries in range
371 % Loop through the range of object numbers
372 add 1 sub 1 exch { % Form end of range, set increment = 1
373 % Stack: <Xrefdict> <xref stream> <Index array> <pair loc> <obj num>
374 % Get xref parameters. Note: The number of bytes for each parameter
375 % is defined by the entries in the W array.
376 4 index /W get aload pop % Get W array values
377 % The first field indicates type of entry. Get first field value.
378 % If the num. of bytes for field 1 is 0 then default field value is 1
379 3 -1 roll dup 0 eq { pop 1 } { 6 index exch getintn } ifelse
380 % Get the handler for the xref entry type. We will execute the
381 % handler after we get the other two field values.
382 xref15entryhandlers exch get
383 3 -1 roll 6 index exch getintn % Get second field
384 3 -1 roll 6 index exch getintn % Get third field
385 3 -1 roll exec % Execute Xref entry handler
386 pop pop pop % Remove field values and obj num
387 } for % Loop through Xref entries
388 pop % Remove Index array pair loc
389 } for % Loop through Index array entries
390 pop pop % Remove Index array and xref stream
393 % Read the cross-reference table.
394 % <pos> is the position either from the startxref statement or the /Prev
395 % entry in the prior trailer dictionary.
396 /readxref % <pos> readxref <trailerdict>
398 PDFoffset add PDFfile exch setfileposition
399 % In some PDF files, this position actually points to
400 % white space before the xref line. Skip over this here.
402 PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
404 dup % Make copy of the file position (before last char was read).
405 PDFfile exch setfileposition
406 % The PDF specification says that the 'xref' must be on a line
407 % by itself. The code here formerly used readline and linene to
408 % check this. However, Acrobat Reader only requires the line to
409 % begin with 'xref', and there are enough applications producing
410 % non-compliant PDF files that we have to do this too.
411 PDFfile pdfstring 0 4 getinterval readstring pop
413 { readorigxref } % 'xref' -> original xref table
414 { readpdf15xref } % otherwise assume PDF 1.5 xref stream
418 % Open a PDF file and read the header, trailer, and cross-reference.
419 /pdfopen { % <file> pdfopen <dict>
420 % Color space substitution in PDF is handled somewhat differently
421 % than in PostScript. A given device color space will be substituted
422 % if the corresponding "Default..." entry exists in the Page's
423 % Resource dictionary (which might be inhereted); there is no
424 % UseCIEColor to enable/disable color mapping.
426 % This behavior is achieved by always setting UseCIEColor to true
427 % in the page device dictionary. If the value of this parameter was
428 % originally false (i.e.: the output device does not perform color
429 % space substitution by default), the instances DefaultGray,
430 % DefaultRGB, and DefaultCMYK of the (local) ColorSpace category
431 % are redefined to be DeviceGray, DeviceRGB, and DeviceCMYK,
432 % respectively. This is not done if UseCIEColor is true by default,
433 % as in that case color substitution is presumably desired even
434 % if the file does not request it.
435 currentpagedevice /UseCIEColor .knownget dup { pop } if not
436 { .currentglobal false .setglobal
437 /DefaultGray { /DeviceGray } cvlit /ColorSpace defineresource pop
438 /DefaultRGB { /DeviceRGB } cvlit /ColorSpace defineresource pop
439 /DefaultCMYK { /DeviceCMYK } cvlit /ColorSpace defineresource pop
446 % Copy bookmarks (outline) to the output.
447 Trailer /Root oget /Outlines knownoget {
449 { dup writeoutline /Next knownoget not { exit } if } loop
452 } if % end .writepdfmarks
456 % Verify that each entry in the xref table is pointing at an object with
457 % the correct object number and generation number.
458 /verify_xref % - verify_xref -
459 { 1 1 Objects llength 1 sub % stack: 1 1 <number of objects - 1>
460 { % Check if the object is free (i.e. not used). The values in
461 % Generations is the generation number plus 1. If the value in
462 % Generations is zero then the object is free.
463 Generations 1 index lget % Get the genration number
464 0 ne { % Skip if object number is free
465 ObjectStream 1 index lget % Check if object is in objectstream
466 0 eq { % We only check objects not in an objectstream
467 { % Use stop context since we may get an error if object is invalid
468 dup Objects exch lget % Get the object location
469 PDFoffset add PDFfile exch setfileposition
470 true % Stack: <obj num> <true>
471 PDFfile token pop % Read object number from file
472 2 index eq { % Verify object number
473 PDFfile token pop % Read generation number from file
474 Generations 3 index % Get specified generaton number
475 lget 1 sub % Gen numbs are stored with 1 added.
476 eq { % Verify generation number
477 PDFfile token pop /obj eq { % Verify 'obj' text
478 pop false % We have valid object, do not rebuild
483 { true } if % If we stop then we need to rebuild
485 ( **** Warning: File has an invalid xref entry: )
487 pdfstring cvs pdfformaterror
488 (. Rebuilding xref table.\n) pdfformaterror
491 } if % If the entry is invalid
492 } if % If not in an object stream
493 } if % If object entry is not free
494 pop % Remove object number
498 /pdfopencache { % - pdfopencache -
499 % Create and initialize some caches.
500 /PageCount pdfpagecount def
501 /PageNumbers PageCount 65534 .min dict def
502 /PageIndex PageCount 65534 .min array def
505 /pdfopenfile { % <file> pdfopenfile <dict>
506 pdfdict readonly pop % can't do it any earlier than this
508 /LocalResources 0 dict def
509 /DefaultQstate //null def % establish binding
510 /Printed where { pop } {
511 % Guess whether the output device is a printer.
512 /Printed currentpagedevice /OutputFile known def
514 /PSLevel1 where { pop } { /PSLevel1 false def } ifelse
515 % NB: PDFfile is used outside of the PDF code to determine that a
516 % PDF job is being processed; to not change or hide this key.
517 cvlit /PDFfile exch def
518 /PDFsource PDFfile def
520 currentglobal true .setglobal globaldict begin
521 /TTFWarnList 0 dict def /UndefProcList 0 dict def
523 PDFfile dup 0 setfileposition pdfstring readstring
524 not {/pdfopen cvx /syntaxerror signalerror} if
525 (%PDF-) search not {/pdfopen cvx /syntaxerror signalerror} if
526 length /PDFoffset exch def pop
527 % some badly formed PDF's (Visioneer) have something other than EOL
528 % after the version number. If we get an error, shorten the string
530 false exch % error encountered
532 { exch pop true exch 0 1 index length 1 sub dup 0 eq
533 { pop 0 exit } if % exit if string now empty
534 getinterval % trim character from right end and retry
537 ( **** Warning: PDF version number not followed by EOL.\n)
546 % Read the last cross-reference table.
547 count /pdfemptycount exch def
548 /Trailer << >> def % Initialize to an emptry dict.
549 { initPDFobjects findxref readxref } .internalstopped {
550 recover_xref_data % Read failed. Attempt to recover xref data.
551 search_trailer % Search for the primary trailer
553 /Trailer exch def % Save trailer dict after first xref table
554 % Read any previous cross-reference tables. When we are done,
555 % verify that the entries in the xref tables are valid if NoVerifyXref
558 { /Prev knownoget not { % If no previous xref table then ...
559 /NoVerifyXref where { pop } { verify_xref } ifelse exit
561 { readxref } .internalstopped {
562 recover_xref_data % Read failed. Attempt to recover xref data.
563 exit % Exit loop since recover gets all obj data.
564 } if % If readxref stopped
565 % The PDF spec. says that each trailer dict should contain the required
566 % entries. However we have seen a PDF file that only has a Prev entry in
567 % the initial trailer dict. Acrobat complains but it accepts these files.
568 % To work with these files, we are copying any entries which we find in
569 % a previous trailer dict which are not present in the initial dict.
571 Trailer 2 index known {
572 pop pop % discard if key already present
574 Trailer 3 1 roll put % add key if not present
577 } loop % Loop to previous trailer
578 } ifelse % Ifelse readxref stopped
579 Trailer /Encrypt knownoget {
581 pdf_process_Encrypt % signal error
586 % Look for [\r\n]%%EO from the current position of the file.
587 % Return the position of %%EO if found or -1 .
588 /findeof { % <file> find_eof <file> <position>
591 dup bytesavailable 4 lt { exit } if
592 dup 0 (%%EO) /SubFileDecode filter flushfile
593 dup dup fileposition 5 sub setfileposition
594 dup 5 string readstring not { pop exit } if
595 dup (\r%%EO) eq exch (\n%%EO) eq or {
596 dup fileposition 4 sub
603 % Skip backward over the %%EOF at the end of the PDF file, and read
604 % the preceding startxref line. The PDF specification unambiguously
605 % requires that the %%EOF appear on a line by itself, and that the
606 % startxref and the following position value appear on separate lines;
607 % however, some applications truncate the %%EOF to %%EO, and/or put the
608 % startxref and the following value on the same line.
609 % There seems to be no limit on the amount of garbage that can be
610 % appended to the PDF file. Current record (60K) belongs to
611 % PDF-Out (v 2.0 - 35). We start the search for %%EO from the last 1024
612 % bytes and continue from the beginning of the file.
613 /findxref { % - findxref <xrefpos>
614 PDFfile dup dup dup 0 setfileposition bytesavailable
615 dup /PDFfilelen exch def
616 % Find the last %%EOF string (within 1024 bytes)
617 1024 sub PDFoffset .max
618 setfileposition findeof % search the last 1024 bytes
621 dup PDFoffset setfileposition findeof % search from the beginnibg
623 ( **** Error: Cannot find a %%EOF marker anywhere in the file.\n)
625 /findxref cvx /syntaxerror signalerror
628 dup 3 1 roll setfileposition
630 % Check for whether this is, in fact, a valid PDF file.
631 dup PDFfilelen exch sub dup dup 7 gt exch 5 lt or {
634 string PDFfile exch readstring pop
635 dup (%%EOF\n) eq exch dup (%%EOF\r) eq
636 exch dup (%%EOF\r\n) eq exch (%%EOF) eq or or or not
638 ( **** Warning: File has a corrupted %%EOF marker, or garbage after %%EOF.\n)
641 PDFfile exch setfileposition
642 % Now read the startxref and xref start position.
643 prevline token not { null } if dup type /integertype eq {
644 exch pop cvi % xref start position
645 exch PDFfile exch setfileposition
646 prevline dup (startxref) linene {
647 % startxref not on a line by itself. We have found PDF from
648 % www.verypdf.com in which the startxref was on the same line as
649 % the end of trailer dictionary. Check for this. Note: This
651 dup (startxref) search {
652 % found startxref - print warning
653 pop pop pop % clear strings from search
654 ( **** Warning: format of the startxref line in this file is invalid.\n)
656 } { % no startxref - we have a problem.
657 /findxref cvx /syntaxerror signalerror
661 } { % else, this file has 'startxref #####' format
662 (startxref) ne { /findxref cvx /syntaxerror signalerror } if
663 cvi % xref start position
664 ( **** Warning: format of the startxref line in this file is invalid.\n)
666 exch PDFfile exch setfileposition
669 /stderrfile (%stderr) (w) file def
670 /stderrprint { % <string> stderrprint -
671 //stderrfile dup 3 -1 roll writestring flushfile
673 /pdfformaterror { % <string> pdfformaterror -
679 { 2 copy knownoget { 3 1 roll pop pop //true } { pop pop //false } ifelse
683 Trailer /Info { knownoget_safe } stopped { pop pop false } if {
684 /Producer knownoget not { null } if
691 ( **** The file was produced by: \n **** >>>> ) stderrprint
692 % Handle a Unicode Producer.
693 (\376\377) anchorsearch {
694 pop dup length 2 idiv string 0 1 2 index length 1 sub {
695 % Stack: origstr newstr i
696 1 index exch 3 index 1 index 2 mul 1 add get put
700 ( <<<<\n) stderrprint
703 % The TTFWarnList is the list of all TrueType fonts that were not embedded.
704 % The UndefProcList collects noisy warnings.
705 % This gets rid of many multiple warnings from pdf_font.ps
706 /printCollectedWarnings {
707 TTFWarnList length 0 gt {
708 (\n **** Warning: Fonts with Subtype = /TrueType should be embedded.\n)
710 ( The following fonts were not embedded:\n)
712 [ TTFWarnList { pop .namestring (\t\t\t) exch concatstrings (\n) concatstrings } forall ]
713 { lt } .sort { stderrprint } forall
715 UndefProcList length 0 gt {
716 (\n **** Embedded font uses undefined procedure\(s\): ) stderrprint
718 exch .namestring stderrprint ( ) stderrprint
719 =string cvs stderrprint ( times, ) stderrprint
725 printCollectedWarnings
726 (\n **** This file had errors that were repaired or ignored.\n)
729 ( **** Please notify the author of the software that produced this\n)
731 ( **** file that it does not conform to Adobe's published PDF\n)
733 ( **** specification.\n\n)
737 % Write the outline structure for a file. Uses linkdest (below).
738 % omit links to pages that don't exist.
739 /writeoutline % <outlinedict> writeoutline -
741 0 2 index /First knownoget
742 { { exch 1 add exch /Next knownoget not { exit } if } loop }
744 % stack: dict mark count
747 { 2 index /Count knownoget { 0 lt { neg } if } if
750 ifelse { linkdest } stopped
752 cleartomark % ignore this link
753 ( **** Warning: Outline has invalid link that was discarded.\n)
756 /Title oget /Title exch /OUT pdfmark
760 { { dup writeoutline /Next knownoget not { exit } if } loop }
765 /pdfclose % <dict> pdfclose -
771 % ======================== Page accessing ======================== %
773 % Get a (possibly inherited) attribute of a page.
774 /pget % <pagedict> <key> pget <value> -true-
775 % <pagedict> <key> pget -false-
777 { exch pop exch pop true
779 { exch /Parent knownoget
787 % Get the value of a resource on a given page.
788 /rget { % <resname> <pagedict> <restype> rget <value> -true-
789 % <resname> <pagedict> <restype> rget -false-
790 LocalResources 1 index knownoget {
795 exch pop exch pop exch pop true
797 exch /Resources pget {
798 exch knownoget { exch knownoget } { pop false } ifelse
805 % Get the total number of pages in the document.
806 /pdfpagecount % - pdfpagecount <int>
807 { Trailer /Root oget /Pages oget /Count oget
810 % Find the N'th page of the document by iterating through the Pages tree.
811 % The first page is numbered 1.
812 /pdffindpageref { % <int> pdffindpage <objref>
813 dup Trailer /Root oget /Pages get
814 { % We should be able to tell when we reach a leaf
815 % by finding a Type unequal to /Pages. Unfortunately,
816 % some files distributed by Adobe lack the Type key
817 % in some of the Pages nodes! Instead, we check for Kids.
818 dup oforce /Kids knownoget not { exit } if
820 0 1 3 index length 1 sub {
822 dup oforce dup /Kids known { /Count oget } { pop 1 } ifelse
823 % Stack: index kids null noderef count
824 dup 5 index ge { pop exch pop exit } if
825 5 -1 roll exch sub 4 1 roll pop
827 % Stack: index null|noderef
828 dup null eq { pop pop 1 null exit } if
830 % Stack: index countleft noderef
831 1 index 1 ne { pop pop /pdffindpage cvx /rangecheck signalerror } if
833 PageIndex 2 index 1 sub 65533 .min 2 index oforce put
834 PageNumbers 1 index oforce 3 index dup 65534 le
836 { pop pop pop } % don't store more than 65534 pagenumbers
840 /pdffindpage { % <int> pdffindpage <pagedict>
841 pdffindpageref oforce
844 % Find the N'th page of the document.
845 % The first page is numbered 1.
846 /pdfgetpage % <int> pdfgetpage <pagedict>
847 { PageIndex 1 index 1 sub dup 65533 lt
857 % Find the page number of a page object (inverse of pdfgetpage).
858 /pdfpagenumber % <pagedict> pdfpagenumber <int>
859 { % We use the simplest and stupidest of all possible algorithms....
860 PageNumbers 1 index .knownget
863 { 1 1 PageCount 1 add % will give a rangecheck if not found
864 { dup pdfgetpage oforce 2 index eq { exit } if pop
871 % Arrange the four elements that define a rectangle into a 'normal' order.
872 /normrect_elems % <x1> <y1> <x2> <y2> normrect_elems <llx> <lly> <urx> <ury>
874 exch 4 1 roll % <x2> <x1> <y1> <y2>
875 2 copy gt { exch } if % <x2> <x1> <lly> <ury>
876 4 2 roll 2 copy lt { exch } if % <lly> <ury> <urx> <llx>
877 4 1 roll exch % <llx> <lly> <urx> <ury>
880 % Arrange a rectangle into a 'normal' order. I.e the lower left corner
881 % followed by the upper right corner.
882 /normrect % <rect> normrect <rect>
884 aload pop normrect_elems 4 array astore
887 /boxrect % <llx> <lly> <urx> <ury> boxrect <x> <y> <w> <h>
888 { exch 3 index sub exch 2 index sub
890 /resolvedest { % <name|string|other> resolvedest <other|null>
891 dup type /nametype eq {
892 Trailer /Root oget /Dests knownoget {
893 exch knownoget not { null } if
898 dup type /stringtype eq {
899 Trailer /Root oget /Names knownoget {
911 /linkdest { % <link|outline> linkdest
912 % ([/Page <n>] /View <view> | ) <link|outline>
915 dup type /dicttype eq { /D knownoget not { null } if } if
919 dup type /dicttype eq {
920 dup /Type knownoget {
926 dup type /integertype ne
928 { /Page exch 4 -2 roll }
930 dup length 1 sub 1 exch getinterval /View exch 3 -1 roll
936 % <pagedict> mark ... -proc- -
937 /namedactions 8 dict dup begin
942 counttomark 2 add index pdfpagecount /Page exch 3 -1 roll
945 counttomark 2 add index pdfpagenumber 1 add /Page exch 3 -1 roll
948 counttomark 2 add index pdfpagenumber 1 sub /Page exch 3 -1 roll
951 % <pagedict> <annotdict> -proc- -
952 /annottypes 5 dict dup begin
955 { /Rect /Open /Contents }
956 { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
957 forall pop /ANN pdfmark
961 dup /C knownoget { /Color exch 3 -1 roll } if
963 { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
964 forall dup /A knownoget {
966 /A mark 3 2 roll % <<>> /A [ <<action>>
972 exch pop exch dup length dict copy dup /Dest 4 -1 roll put
974 /N knownoget { % Assume /S /Named
975 namedactions exch .knownget { exec } if
980 linkdest pop /LNK pdfmark
984 % **** The following procedure should not be changed to allow clients
985 % **** to directly interface with the constituent procedures. GSview
986 % **** and some Artifex customers rely on the pdfshowpage_init,
987 % **** pdfshowpage_setpage, pdfshowpage_finish so all logic should be
988 % **** implemented in one of those three procedures.
989 /pdfshowpage % <pagedict> pdfshowpage -
990 { dup /Page exch store
996 /pdfpagecontents % <pagedict> pdfpagecontents <contents>
999 /pdfshowpage_init % <pagedict> pdfshowpage_init <pagedict>
1000 { /DSCPageCount DSCPageCount 1 add store
1003 /.pdfshowpage_Install { % <pagedict> [<prevproc>] .pdfshowpage_Install -
1005 % We would like to clip to the CropBox here, but the subsequent
1006 % initgraphics would override it. Instead, we have to handle it
1007 % in graphicsbeginpage.
1008 dup /CropBox pget dup {exch pop} if systemdict /UseCropBox known and {
1009 dup /CropBox pget pop
1011 dup /MediaBox pget pop % There has to be a MediaBox
1013 % stack: [<prevproc>] <pagedict> <Crop|Media Box>
1014 exch pop oforce_array normrect % done with the pagedict
1015 systemdict /PDFFitPage known {
1016 PDFDEBUG { (Fiting PDF to imageable area of the page.) = flush } if
1017 currentpagedevice /.HWMargins get aload pop
1018 currentpagedevice /PageSize get aload pop
1019 3 -1 roll sub 3 1 roll exch sub exch
1020 % stack: [<prevproc>] <pagedict> <Crop|Media Box> Xmin Ymin Xmax Ymax
1021 PDFDEBUG { ( Translate up by [ ) print 3 index =print (, ) print 2 index =print ( ]) = flush } if
1022 3 index 3 index translate % move origin up to imageable area
1023 2 index sub exch 3 index sub exch 4 2 roll pop pop
1024 % stack: [Box] XImageable YImageable
1025 2 index aload pop 2 index sub exch 3 index sub exch 4 2 roll pop pop
1026 % stack: [Box] XImageable YImageable XBox YBox
1027 3 -1 roll exch div 3 1 roll div .min
1028 PDFDEBUG { ( Scale by ) print dup = flush } if
1031 % Now translate to the origin given in the Crop|Media Box
1032 dup 0 get neg exch 1 get neg translate
1037 /pdfshowpage_setpage { % <pagedict> pdfshowpage_setpage <pagedict>
1038 5 dict begin % for setpagedevice
1040 % UseCIEColor is always true for PDF; see the comment in runpdf above
1041 /UseCIEColor true def
1042 currentpagedevice /Orientation 2 index /Rotate pget not { 0 } if 90 idiv
1043 % Rotate specifies *clockwise* rotation!
1045 % Stack: pagedict currentpagedict
1046 1 index /CropBox pget dup {exch pop} if systemdict /UseCropBox known and {
1047 % Set the page size.
1048 1 index /CropBox pget pop oforce_elems normrect_elems boxrect
1049 2 array astore /PageSize exch def pop pop
1051 1 index /MediaBox pget {
1052 % Set the page size.
1053 oforce_elems normrect_elems boxrect
1054 2 array astore /PageSize exch def pop pop
1057 % Don't change the page size if we are going to fit the PDF to the page
1058 systemdict /PDFFitPage known { currentdict /PageSize undef } if
1059 % Let the device know if we will be using PDF 1.4 transparency.
1060 % The clist logic may need to adjust the size of bands.
1061 1 index pageusestransparency /PageUsesTransparency exch def
1062 dup /Install .knownget {
1063 % Don't let the Install procedure get more deeply
1064 % nested after every page.
1065 dup type dup /arraytype eq exch /packedarraytype eq or {
1067 dup 2 get /.pdfshowpage_Install load eq {
1068 1 get 0 get % previous procedure
1074 } ifelse 1 array astore
1075 2 index exch /.pdfshowpage_Install load /exec load
1077 % Stack: pagedict currentpagedict installproc
1079 % Stack: pagedict currentpagedict
1080 pop currentdict end setpagedevice
1083 /pdfshowpage_finish { % <pagedict> pdfshowpage_finish -
1084 save /PDFSave exch store
1085 /PDFdictstackcount countdictstack store
1086 (before exec) VMDEBUG
1088 % set up color space substitution (this must be inside the page save)
1089 pdfshowpage_setcspacesub
1093 % Copy the crop box.
1094 dup /CropBox knownoget {
1095 oforce_array normrect
1097 % .pdfshowpage_Install translates the origin -
1098 % do same here with the CropBox.
1100 1 index /CropBox pget dup {exch pop} if systemdict /UseCropBox known and {
1101 1 index /CropBox pget pop
1103 1 index /MediaBox pget pop % There has to be a MediaBox
1105 oforce_array normrect
1106 dup 0 get exch 1 get % [] tx ty
1107 2 index 0 get 2 index sub 3 index exch 0 exch put
1108 2 index 2 get 2 index sub 3 index exch 2 exch put
1109 2 index 1 get 1 index sub 3 index exch 1 exch put
1110 2 index 3 get 1 index sub 3 index exch 3 exch put
1113 % If the page has been rotated, rotate the CropBox.
1114 mark /CropBox 3 -1 roll
1115 3 index /Rotate pget {
1116 90 idiv 1 and 0 ne {
1117 aload pop 4 -2 roll exch 4 2 roll exch 4 array astore
1123 % Copy annotations and links.
1124 dup /Annots knownoget {
1125 0 1 2 index length 1 sub
1127 dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse
1132 } if % end .writepdfmarks
1134 % Display the actual page contents.
1137 /BGDefault currentblackgeneration def
1138 /UCRDefault currentundercolorremoval def
1139 %****** DOESN'T HANDLE COLOR TRANSFER YET ******
1140 /TRDefault currenttransfer def
1141 matrix currentmatrix 2 dict
1142 2 index /CropBox knownoget {
1143 oforce_elems normrect_elems boxrect
1144 4 array astore 1 index /ClipRect 3 -1 roll put
1146 dictbeginpage setmatrix
1147 /DefaultQstate qstate store
1149 dup % for showing annotations below
1150 count 1 sub /pdfemptycount exch store
1151 % If the page uses any transparency features, show it within
1152 % a transparency group.
1153 dup pageusestransparency dup /PDFusingtransparency exch def {
1154 % Show the page within a PDF 1.4 device filter.
1155 0 .pushpdf14devicefilter {
1156 /DefaultQstate qstate store % device has changed -- reset DefaultQstate
1157 % If the page has a Group, enclose contents in transparency group.
1158 % (Adobe Tech Note 5407, sec 9.2)
1159 dup /Group knownoget {
1160 1 index /CropBox knownoget not {
1161 1 index /MediaBox pget pop
1162 } if oforce_array normrect .beginformgroup {
1165 .discardtransparencygroup stop
1166 } if .endtransparencygroup
1172 .poppdf14devicefilter
1173 /DefaultQstate qstate store % device has changed -- reset DefaultQstate
1175 } if .poppdf14devicefilter
1176 /DefaultQstate qstate store % device has changed -- reset DefaultQstate
1180 % check for extra garbage on the ostack and clean it up
1181 count pdfemptycount sub dup 0 ne {
1182 ( **** File did not complete the page properly and may be damaged.\n)
1188 % todo: mixing drawing ops outside the device filter could cause
1189 % problems, for example with the pnga device.
1190 /Annots knownoget { { oforce drawannot } forall } if
1193 % Some PDF files don't have matching q/Q (gsave/grestore) so we need
1194 % to clean up any left over dicts from the dictstack
1195 countdictstack PDFdictstackcount sub dup 0 ne {
1196 ( **** Warning: File has imbalanced q/Q operators \(too many q's\)\n)
1202 (after exec) VMDEBUG
1203 Repaired % pass Repaired state around the restore
1207 /showpagecontents { % <pagedict> showpagecontents -
1208 gsave % preserve gstate for Annotations later
1209 /Contents knownoget not { 0 array } if
1210 dup type /arraytype ne { 1 array astore } if {
1211 oforce false resolvestream pdfopdict .pdfrun
1215 /processcolorspace { % - processcolorspace <colorspace>
1216 % The following is per the PLRM3.
1217 currentdevice 1 dict dup /ProcessColorModel dup put .getdeviceparams
1219 dup type /nametype ne { cvn } if
1220 dup { setcolorspace } .internalstopped { pop /DeviceRGB } if
1223 % ------ Transparency support ------ %
1225 % Define minimum PDF version for checking for transparency features.
1226 % Transparency is a 1.4 feature however we have seen files that claimed
1227 % to be PDF 1.3 with transparency features.
1228 /PDFtransparencyversion 1.3 def
1230 % Determine whether a page might invoke any transparency features:
1231 % - Non-default BM, ca, CA, or SMask in an ExtGState
1232 % - Image XObject with SMask
1233 % Note: we deliberately don't check to see whether a Group is defined,
1234 % because Adobe Illustrator 10 (and possibly other applications) define
1235 % a page-level group whether transparency is actually used or not.
1236 % Ignoring the presence of Group is justified because, in the absence
1237 % of any other transparency features, they have no effect.
1238 /pageusestransparency { % <pagedict> pageusestransparency <bool>
1239 PDFversion PDFtransparencyversion lt NOTRANSPARENCY or {
1243 4 dict 1 index resourceusestransparency { pop not exit } if
1244 /Parent knownoget not { exit } if
1249 % Check the Resources of a page or Form. Check for loops in the resource chain.
1250 /resourceusestransparency { % <dict> <dict> resourceusestransparency <bool>
1251 { % Use loop to provide an exitable context.
1252 /Resources knownoget not { 0 dict } if
1254 ( **** File has circular references in resource dictionaries.\n)
1259 dup /ExtGState knownoget {
1262 dup /BM knownoget { dup /Normal ne exch /Compatible ne and
1265 dup /ca knownoget { 1 ne { pop not exit } if } if
1266 dup /CA knownoget { 1 ne { pop not exit } if } if
1267 dup /SMask knownoget { /None ne { pop not exit } if } if
1269 } forall { pop true exit } if
1271 dup /XObject knownoget {
1273 exch pop oforce dup /Subtype get
1274 dup /Image eq { 1 index /SMask known { pop pop not exit } if } if
1276 3 index exch resourceusestransparency { not exit } if
1280 } forall { pop true exit } if
1287 % ------ ColorSpace substitution support ------ %
1290 % <pagedict> pdfshowpage_setcspacesub <pagedict>
1292 % Set up color space substitution for a page. Invocations of this procedure
1293 % must be bracketed by the save/restore operation for the page, to avoid
1294 % unintended effects on other pages.
1296 % If any color space substitution is used, and the current color space is a
1297 % device dependent color space, make sure the current color space is updated.
1298 % There is an optimization in the setcolorspace pseudo-operator that does
1299 % nothing if both the current and operand color spaces are the same. For
1300 % PostScript this optimization is disabled if the UseCIEColor page device
1301 % parameter is true. This is not the case for PDF, as performance suffers
1302 % significantly on some PDF files if color spaces are set repeatedly. Hence,
1303 % if color space substitution is to be used, and the current color space
1304 % is a device dependent color space, we must make sure to "transition" the
1305 % current color space.
1307 /pdfshowpage_setcspacesub
1310 { /DefaultGray /DefaultRGB /DefaultCMYK }
1312 dup 3 index /ColorSpace //rget exec
1313 { resolvecolorspace /ColorSpace defineresource pop }
1319 % if using color space substitution, "transition" the current color space
1321 currentcolorspace dup length 1 eq % always an array
1324 dup /DeviceGray eq 1 index /DeviceRGB eq or 1 index /DeviceCMYK or
1325 { /Pattern setcolorspace setcolorspace }