home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 10 / AU_CD10.iso / Updates / GhostScript / !GhostScr / 6_01 / lib / pdf_main.ps < prev    next >
Text File  |  2000-03-29  |  18KB  |  587 lines

  1. %    Copyright (C) 1994, 2000 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of Aladdin Ghostscript.
  3. % Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  4. % or distributor accepts any responsibility for the consequences of using it,
  5. % or for whether it serves any particular purpose or works at all, unless he
  6. % or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  7. % License (the "License") for full details.
  8. % Every copy of Aladdin Ghostscript must include a copy of the License,
  9. % normally in a plain ASCII text file named PUBLIC.  The License grants you
  10. % the right to copy, modify and redistribute Aladdin Ghostscript, but only
  11. % under certain conditions described in the License.  Among other things, the
  12. % License requires that the copyright notice and this notice be preserved on
  13. % all copies.
  14.  
  15. % $Id: pdf_main.ps,v 1.2 2000/03/10 03:56:11 lpd Exp $
  16. % pdf_main.ps
  17. % PDF file- and page-level operations.
  18.  
  19. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  20. .currentglobal true .setglobal
  21. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  22. pdfdict begin
  23.  
  24. % Patch in an obsolete variable used by some third-party software.
  25. /#? false def
  26.  
  27. % For simplicity, we use a single interpretation dictionary for all
  28. % PDF graphics execution, even though this is too liberal.
  29. /pdfopdict mark
  30.   objopdict { } forall
  31.   drawopdict { } forall
  32.   /endstream { exit } bind
  33.   (%%EOF) cvn { exit } bind        % for filters
  34.     % PDF 1.1 operators
  35.   /BX { /BXlevel BXlevel 1 add store } bind
  36.   /EX { /BXlevel BXlevel 1 sub store } bind
  37.   /PS { cvx exec } bind
  38.     % PDF 1.2 operators
  39.   /BMC { pop } bind
  40.   /BDC { pop pop } bind
  41.   /EMC { }
  42.   /MP { pop } bind
  43.   /DP { pop pop } bind
  44. .dicttomark readonly def
  45.  
  46. % ======================== Main program ======================== %
  47.  
  48. end            % pdfdict
  49. userdict begin
  50.  
  51. /defaultfontname /Times-Roman def
  52.  
  53. % Make sure the registered encodings are loaded, so we don't run the risk
  54. % that some of the indices for their names will overflow the packed
  55. % representation.  (Yes, this is a hack.)
  56. SymbolEncoding pop
  57. DingbatsEncoding pop
  58.  
  59. % Redefine 'run' so it recognizes PDF files.
  60. systemdict begin
  61. /.runps /run load def
  62. /runpdfstring 50 string def        % length is arbitrary
  63. /run
  64.  { dup type /filetype ne { (r) file } if
  65.    dup read
  66.     { dup (%) 0 get eq
  67.        { pop dup //runpdfstring
  68.         % Some invalid files might have extra-long first lines....
  69.       {  { readline } .internalstopped not { pop pop exit } if
  70.         pop =string
  71.       }
  72.      loop
  73.      //runpdfstring (PDF-) anchorsearch
  74.       { pop pop runpdf }
  75.       { pop cvx .runexec }
  76.      ifelse
  77.        }
  78.        { 2 copy unread pop .runps
  79.        }
  80.       ifelse
  81.     }
  82.     { closefile
  83.     }
  84.    ifelse
  85.  } bind odef
  86. /runpdf            % <file> runpdf -
  87.  { userdict begin
  88.    /Page# null def
  89.    /Page null def
  90.    /DSCPageCount 0 def
  91.    /PDFSave null def
  92.    GS_PDF_ProcSet begin
  93.    pdfdict begin
  94.    pdfopen begin
  95.    Trailer /Root oget /Pages oget /CropBox knownoget
  96.     { mark /CropBox 3 -1 roll /PAGES pdfmark
  97.     }
  98.    if
  99.    /FirstPage where { pop FirstPage } { 1 } ifelse
  100.    1
  101.    /LastPage where { pop LastPage } { pdfpagecount } ifelse
  102.    QUIET not
  103.     { (Processing pages ) print 2 index =only ( through ) print dup =only
  104.       (.) = flush
  105.     }
  106.    if
  107.     { dup /Page# exch store
  108.       QUIET not { (Page ) print dup == flush } if
  109.       pdfgetpage pdfshowpage
  110.     } for
  111.    currentdict pdfclose
  112.    end            % temporary dict
  113.    end            % pdfdict
  114.    end            % userdict
  115.  } bind def
  116. end            % systemdict
  117. % Redefine the procedure that the C code uses for running piped input.
  118. % It is OK to use { (%stdin) run } here, because a startjob cannot occur.
  119. /.runstdin {
  120.   { (%stdin) run } execute0
  121. } bind def
  122.  
  123. end            % userdict
  124. pdfdict begin
  125.  
  126. % ======================== File parsing ======================== %
  127.  
  128. % Read the cross-reference and trailer sections.
  129.  
  130. /traileropdict mark
  131.   (<<) cvn { mark } bind
  132.   (>>) cvn /.dicttomark load
  133.   ([) cvn { mark } bind        % ditto
  134.   (]) cvn dup load
  135.   /true true
  136.   /false false
  137.   /null null
  138.   /R { /resolveR cvx 3 packedarray cvx } bind    % see Objects below
  139.   /startxref /exit load
  140. .dicttomark readonly def
  141.  
  142. % Because of EOL conversion, lines with fixed contents might be followed
  143. % by one or more blanks.
  144. /lineeq            % <filestr> <conststr> lineeq <bool>
  145.  { anchorsearch
  146.     { pop { ( ) anchorsearch not { () eq exit } if pop } loop }
  147.     { pop false }
  148.    ifelse
  149.  } bind def
  150. /linene { lineeq not } bind def
  151.  
  152. % Read (mostly scan) the cross-reference table.
  153. /readxref        % <pos> readxref <trailerdict>
  154.  { PDFoffset add PDFfile exch setfileposition
  155.         % In some PDF files, this position actually points to
  156.         % white space before the xref line.  Skip over this here.
  157.    { PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
  158.    } loop
  159.    PDFfile exch setfileposition
  160.    PDFfile pdfstring readline pop
  161.    (xref) linene { /readxref cvx /syntaxerror signalerror } if
  162.         % Store the xref table entry position for each object.
  163.         % We only need to read the run headers, not every entry.
  164.     { PDFfile token pop        % first object # or trailer
  165.       dup /trailer eq { pop exit } if
  166.       PDFfile pdfstring readline pop
  167.       token pop            % entry count
  168.       exch pop exch
  169.         % This section might be adding new objects:
  170.         % ensure that Objects and Generations are big enough.
  171.         % Stack: count obj#
  172.       2 copy add growPDFobjects
  173.       PDFfile fileposition 3 -1 roll
  174.        { Objects 2 index lget null eq    % later update might have set it
  175.       { Objects 2 index 2 index cvx lput }
  176.          if exch 1 add exch 20 add
  177.        }
  178.       repeat PDFfile exch setfileposition pop
  179.     } loop
  180.    count /pdfemptycount exch def
  181.    PDFfile traileropdict .pdfrun
  182.  } bind def
  183.  
  184. % Open a PDF file and read the header, trailer, and cross-reference.
  185. /pdfopen        % <file> pdfopen <dict>
  186.  { pdfdict readonly pop        % can't do it any earlier than this
  187.    15 dict begin
  188.    /LocalResources 0 dict def
  189.    /PSLevel1 where { pop } { /PSLevel1 false def } ifelse
  190.    cvlit /PDFfile exch def
  191.    /PDFsource PDFfile def
  192.    PDFfile dup 0 setfileposition pdfstring readstring 
  193.    not {/pdfopen cvx /syntaxerror signalerror} if
  194.    (%PDF-) search not {/pdfopen cvx /syntaxerror signalerror} if
  195.    length /PDFoffset exch def pop cvr /PDFversion exch def
  196.    findxref
  197.    initPDFobjects
  198.     % Read the last cross-reference table.
  199.    readxref /Trailer exch def
  200.    Trailer /Encrypt known
  201.     { pdf_process_Encrypt    % signal error
  202.     }
  203.    if
  204.     % Read any previous cross-reference tables.
  205.    Trailer { /Prev .knownget not { exit } if readxref } loop
  206.     % Create and initialize some caches.
  207.    /PageCount pdfpagecount def
  208.    /PageNumbers PageCount dict def
  209.    /PageIndex PageCount array def
  210.     % Copy bookmarks (outline) to the output.
  211.    Trailer /Root oget /Outlines knownoget
  212.     { /First knownoget
  213.       { { dup writeoutline /Next knownoget not { exit } if } loop } if
  214.     }
  215.    if
  216.    currentdict end
  217.  } bind def
  218.  
  219. % Skip backward over the %%EOF at the end of the PDF file, and read
  220. % the preceding startxref line.  We put this in a separate procedure so
  221. % we can offer the option of accepting certain invalid files that Acrobat
  222. % Reader also accepts.
  223. % ****** NOTE: this will be changed in a future release so that %%EO
  224. % ****** (missing the F), and startxref followed by a blank rather than
  225. % ****** an EOL, will no longer be accepted.
  226. /findxref {        % - findxref <xrefpos>
  227.   PDFfile dup dup 0 setfileposition bytesavailable {
  228.     % Scan backwards over trailing control-character garbage
  229.     % (nulls, ^Zs, EOLs, blanks).
  230.     1 sub 2 copy setfileposition 1 index read pop
  231.     32 gt {exit} if
  232.   } loop
  233.     % Stack: file endpos
  234.     % The top of the stack is now the file position of the last
  235.     % non-garbage character.
  236.     % We can't use prevline to check for the %%EOF, because if the
  237.     % %%EOF isn't followed by an EOL, prevline will read past the
  238.     % end of the file, and the file will get closed.
  239.     % Acrobat apparently accepts files in which the %%EOF was
  240.     % truncated to %%EO (!); we do the same.
  241.   5 sub 2 copy setfileposition
  242.   1 index (xxxxxx) readstring pop
  243.   (\015%%EO) anchorsearch { pop pop } {
  244.     (\012%%EO) anchorsearch { pop pop } {
  245.       pop findxreferror
  246.     } ifelse
  247.   } ifelse
  248.   1 add setfileposition
  249.     % Now read the startxref and xref start position.
  250.   prevline token pop dup type /integertype eq {
  251.     exch pop cvi        % xref start position
  252.     exch PDFfile exch setfileposition
  253.     prevline (startxref) linene { findxreferror } if
  254.     pop
  255.   } {    % else, this file has 'startxref #####' format
  256.     (startxref) ne { findxreferror } if
  257.     cvi        % xref start position
  258.     exch PDFfile exch setfileposition
  259.   } ifelse
  260. } bind def
  261. /findxreferror {
  262.   (\n) =
  263.   (    **** The PDF input file appears to be corrupted, or not a PDF file.) =
  264.   (    **** Try preceding the input file with lib/pdfeof.ps.) =
  265.   (    **** If the file then is processed without error, please notify the) =
  266.   (    **** author of the software that produced the PDF file that the file) =
  267.   (    **** does not conform to Adobe's published PDF specification.) =
  268.   (\n) = flush
  269.   /pdfopen cvx /syntaxerror signalerror
  270. } bind def
  271.  
  272. % Write the outline structure for a file.  Uses linkdest (below).
  273. /writeoutline        % <outlinedict> writeoutline -
  274.  { mark
  275.    0 2 index /First knownoget
  276.     { { exch 1 add exch /Next knownoget not { exit } if } loop }
  277.    if
  278.         % stack: dict mark count
  279.    dup 0 eq
  280.     { pop 1 index
  281.     }
  282.     { 2 index /Count knownoget { 0 lt { neg } if } if
  283.       /Count exch 3 index
  284.     }
  285.    ifelse linkdest /Title oget /Title exch /OUT pdfmark
  286.    /First knownoget
  287.     { { dup writeoutline /Next knownoget not { exit } if } loop }
  288.    if
  289.  } bind def
  290.  
  291. % Close a PDF file.
  292. /pdfclose        % <dict> pdfclose -
  293.  { begin
  294.    PDFfile closefile
  295.    end
  296.  } bind def
  297.  
  298. % ======================== Page accessing ======================== %
  299.  
  300. % Get a (possibly inherited) attribute of a page.
  301. /pget            % <pagedict> <key> pget <value> -true-
  302.             % <pagedict> <key> pget -false-
  303.  { 2 copy knownoget
  304.     { exch pop exch pop true
  305.     }
  306.     { exch /Parent knownoget
  307.        { exch pget }
  308.        { pop false }
  309.       ifelse
  310.     }
  311.    ifelse
  312.  } bind def
  313.  
  314. % Get the value of a resource on a given page.
  315. /rget {            % <resname> <pagedict> <restype> rget <value> -true-
  316.             % <resname> <pagedict> <restype> rget -false-
  317.   LocalResources 1 index knownoget {
  318.      3 index knownoget
  319.   } {
  320.     false
  321.   } ifelse {
  322.     exch pop exch pop exch pop true
  323.   } {
  324.     exch /Resources pget {
  325.       exch knownoget { exch knownoget } { pop false } ifelse
  326.     } {
  327.       pop pop false
  328.     } ifelse
  329.   } ifelse
  330. } bind def
  331.  
  332. % Get the total number of pages in the document.
  333. /pdfpagecount        % - pdfpagecount <int>
  334.  { Trailer /Root oget /Pages oget /Count oget
  335.  } bind def
  336.  
  337. % Find the N'th page of the document by iterating through the Pages tree.
  338. % The first page is numbered 1.
  339. /pdffindpage        % <int> pdffindpage <pagedict>
  340.  { dup Trailer /Root oget /Pages oget
  341.     {        % We should be able to tell when we reach a leaf
  342.         % by finding a Type unequal to /Pages.  Unfortunately,
  343.         % some files distributed by Adobe lack the Type key
  344.         % in some of the Pages nodes!  Instead, we check for Kids.
  345.       dup /Kids knownoget not { exit } if
  346.       exch pop null
  347.       0 1 3 index length 1 sub
  348.        { 2 index exch oget
  349.      dup /Kids known { dup /Count oget } { 1 } ifelse
  350.         % Stack: index kids null node count
  351.      dup 5 index ge { pop exch pop exit } if
  352.      5 -1 roll exch sub 4 1 roll pop
  353.        }
  354.       for exch pop
  355.       dup null eq { pop pop 1 null exit } if
  356.     }
  357.    loop
  358.         % Stack: index countleft node
  359.    1 index 1 ne { pop pop /pdffindpage cvx /rangecheck signalerror } if
  360.    exch pop
  361.    PageIndex 2 index 1 sub 2 index put
  362.    PageNumbers 1 index 3 index put
  363.    exch pop
  364.  } bind def
  365.  
  366. % Find the N'th page of the document.
  367. % The first page is numbered 1.
  368. /pdfgetpage        % <int> pdfgetpage <pagedict>
  369.  { PageIndex 1 index 1 sub get dup null ne
  370.     { exch pop }
  371.     { pop pdffindpage }
  372.    ifelse
  373.  } bind def
  374.  
  375. % Find the page number of a page object (inverse of pdfgetpage).
  376. /pdfpagenumber        % <pagedict> pdfpagenumber <int>
  377.  {    % We use the simplest and stupidest of all possible algorithms....
  378.    PageNumbers 1 index .knownget
  379.     { exch pop
  380.     }
  381.     { 1 1 PageCount 1 add    % will give a rangecheck if not found
  382.        { dup pdfgetpage oforce 2 index eq { exit } if pop
  383.        }
  384.       for exch pop
  385.     }
  386.    ifelse
  387.  } bind def
  388.  
  389. % Display a given page.
  390. /boxrect        % [<llx> <lly> <urx> <ury>] boxrect <x> <y> <w> <h>
  391.  { aload pop exch 3 index sub exch 2 index sub
  392.  } bind def
  393. /resolvedest {        % <name|string|other> resolvedest <other|null>
  394.   dup type /nametype eq {
  395.     Trailer /Root oget /Dests knownoget {
  396.       exch knownoget not { null } if
  397.     } {
  398.       null
  399.     } ifelse
  400.   } {
  401.     dup type /stringtype eq {
  402.       Trailer /Root oget /Names knownoget {
  403.     /Dests knownoget {
  404.       exch nameoget
  405.     } {
  406.       pop null
  407.     } ifelse
  408.       } {
  409.     pop null
  410.       } ifelse
  411.     } if
  412.   } ifelse
  413. } bind def
  414. /linkdest {        % <link|outline> linkdest
  415.             %   ([/Page <n>] /View <view> | ) <link|outline>
  416.   dup /Dest knownoget
  417.     { resolvedest
  418.       dup type /dicttype eq { /D knownoget not { null } if } if
  419.       dup null eq
  420.        { pop }
  421.        { dup 0 oget
  422.      dup null eq
  423.       { pop }
  424.       { dup type /integertype ne { pdfpagenumber } if
  425.         /Page exch 4 -2 roll
  426.       }
  427.      ifelse
  428.      dup length 1 sub 1 exch getinterval /View exch 3 -1 roll
  429.        }
  430.       ifelse
  431.     }
  432.    if
  433. } bind def
  434. % <pagedict> mark ... -proc- -
  435. /namedactions 8 dict dup begin
  436.   /FirstPage {
  437.     /Page 1 3 -1 roll
  438.   } def
  439.   /LastPage {
  440.     counttomark 2 add index pdfpagecount /Page exch 3 -1 roll
  441.   } def
  442.   /NextPage {
  443.     counttomark 2 add index pdfpagenumber 1 add /Page exch 3 -1 roll
  444.   } def
  445.   /PrevPage {
  446.     counttomark 2 add index pdfpagenumber 1 sub /Page exch 3 -1 roll
  447.   } def
  448. end readonly def
  449. % <pagedict> <annotdict> -proc- -
  450. /annottypes 5 dict dup begin
  451.   /Text {
  452.     mark exch
  453.      { /Rect /Open /Contents }
  454.      { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
  455.     forall pop /ANN pdfmark
  456.   } bind def
  457.   /Link {
  458.     mark exch
  459.      { /Rect /Border }
  460.      { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
  461.     forall dup /A knownoget {
  462.       dup /D knownoget {
  463.     exch pop exch dup length dict copy dup /Dest 4 -1 roll put
  464.       } {
  465.     /N knownoget {        % Assume /S /Named
  466.       namedactions exch .knownget { exec } if
  467.     } if
  468.       } ifelse
  469.     } if
  470.     linkdest pop /LNK pdfmark
  471.   } bind def
  472. end readonly def
  473.  
  474. /pdfshowpage        % <pagedict> pdfshowpage -
  475.  { dup /Page exch store
  476.    pdfshowpage_init 
  477.    pdfshowpage_setpage 
  478.    save /PDFSave exch store
  479.    (before exec) VMDEBUG
  480.      pdfshowpage_finish
  481.    (after exec) VMDEBUG
  482.    PDFSave restore
  483.  } bind def
  484.  
  485. /pdfpagecontents    % <pagedict> pdfpagecontents <contents>
  486.  { } bind def
  487.  
  488. /pdfshowpage_init     % <pagedict> pdfshowpage_init <pagedict>
  489.  { /DSCPageCount DSCPageCount 1 add store
  490.  } bind def
  491.  
  492. /.pdfshowpage_Install {    % <tx> <ty> [<prevproc>] .pdfshowpage_Install -
  493.   3 1 roll translate 0 get exec
  494. } bind def
  495.  
  496. /pdfshowpage_setpage {    % <pagedict> pdfshowpage_setpage <pagedict>
  497.   4 dict begin        % for setpagedevice
  498.     % Stack: pagedict
  499.     % We want to look at Rotate for displays, but not for printers.
  500.     % The following is a hack, but we don't know a better way to do this.
  501.   currentpagedevice dup /OutputFile known not {
  502.     /Orientation 2 index /Rotate pget not { 0 } if 90 idiv
  503.     % Rotate specifies *clockwise* rotation!
  504.     neg 3 and def
  505.   } if
  506.     % Stack: pagedict currentpagedict
  507.   1 index /MediaBox pget {
  508.             % Set the page size.
  509.     boxrect [ 2 index 2 index ]
  510.     % Stack: pagedict currentpagedict llx lly width height pagesize
  511.     /PageSize exch def
  512.             % Set the offset on the page.
  513.     % Stack: pagedict currentpagedict llx lly width height
  514.             % Make the page offset values reals to simplify the
  515.             % testing of the Install procedure below.
  516.     pop pop exch 0.0 exch sub exch 0.0 exch sub
  517.     % Stack: pagedict currentpagedict -llx -lly
  518.     [ 3 1 roll 3 index /Install .knownget {
  519.             % Don't let the Install procedure get more deeply
  520.             % nested after every page.
  521.       dup type /arraytype eq {
  522.     dup length 5 eq {
  523.       dup 3 get /.pdfshowpage_Install load eq {
  524.     % Stack: mark tx ty [oldtx oldty [oldproc] .pdfshowpage_Install exec]
  525.         aload pop pop pop
  526.         5 2 roll 3 -1 roll add
  527.     % Stack: mark oldty [oldproc] ty newtx
  528.         exch 4 -1 roll add
  529.     % Stack: mark [oldproc] newtx newty
  530.         3 -1 roll 0 get
  531.       } if
  532.     } if
  533.       } if
  534.       1 array astore /.pdfshowpage_Install load /exec load
  535.     } {
  536.       /translate load
  537.     } ifelse ] cvx
  538.     % Stack: pagedict currentpagedict installproc
  539.     /Install exch def
  540.   } if
  541.     % Stack: pagedict currentpagedict
  542.   pop currentdict end setpagedevice
  543. } bind def
  544.  
  545. /pdfshowpage_finish    % <pagedict> pdfshowpage_finish -
  546.  {
  547.     % Copy crop box.
  548.    dup /CropBox pget
  549.     { boxrect rectclip
  550.       dup /CropBox knownoget { mark /CropBox 3 -1 roll /PAGE pdfmark } if
  551.     }
  552.    if
  553.  
  554.     % Copy annotations and links.
  555.    dup /Annots knownoget
  556.     { 0 1 2 index length 1 sub
  557.        { 1 index exch oget
  558.          dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse
  559.        }
  560.       for pop
  561.     }
  562.    if
  563.  
  564.     % Display the actual page contents.
  565.    6 dict begin
  566.    /BXlevel 0 def
  567.    /BGDefault currentblackgeneration def
  568.    /UCRDefault currentundercolorremoval def
  569.     %****** DOESN'T HANDLE COLOR TRANSFER YET ******
  570.    /TRDefault currenttransfer def
  571.    matrix currentmatrix beginpage setmatrix
  572.    dup /Contents knownoget not { 0 array } if
  573.    dup type /arraytype ne { 1 array astore } if
  574.    count 1 sub /pdfemptycount exch store
  575.     { oforce false resolvestream pdfopdict .pdfrun
  576.     } forall
  577.    /Annots knownoget { { oforce drawannot } forall } if
  578.    endpage
  579.    end            % scratch dict
  580.  } bind def
  581.  
  582. end            % pdfdict
  583. .setglobal
  584.