home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / gs403osk.tgz / gs403osk.tar / pdf_base.ps < prev    next >
Text File  |  1996-10-12  |  13KB  |  388 lines

  1. %    Copyright (C) 1994, 1996 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. % pdf_base.ps
  16. % Basic parser for PDF reader.
  17.  
  18. % This handles basic parsing of the file (including the trailer
  19. % and cross-reference table), as well as objects, object references,
  20. % and streams; it doesn't include any facilities for making marks on
  21. % the page.
  22.  
  23. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  24. .currentglobal true .setglobal
  25. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  26. pdfdict begin
  27.  
  28. % Since PDF files can include extremely large dictionaries,
  29. % we increase the operand stack size here.
  30. /setuserparams where
  31.  { pop mark /MaxOpStack 10000 .dicttomark setuserparams
  32.  } if
  33.  
  34. % We rebind #, #?, #dsc, and #dscfile later if we're writing out PostScript.
  35. /#          % <arg1> ... <argN> <opname> <N> # -
  36.  { pop cvx exec
  37.  } bind def
  38. /#?
  39.  { false
  40.  } bind def
  41. /#dsc           % mark <obj1> ... #dsc -
  42.  { cleartomark
  43.  } bind def
  44. /#dscfile       % <filename> #dscfile -
  45.  { pop
  46.  } bind def
  47.  
  48. % Define the name interpretation dictionary for reading values.
  49. /valueopdict mark
  50.   (<<) cvn { mark } bind    % don't push an actual mark!
  51.   (>>) cvn /.dicttomark load
  52.   ([) cvn { mark } bind     % ditto
  53.   (]) cvn dup load
  54.   /true true
  55.   /false false
  56.   /null null
  57.   /F dup cvx        % see Objects section below
  58.   /R dup cvx        % see Objects section below
  59.   /stream dup cvx   % see Streams section below
  60. .dicttomark readonly def
  61.  
  62. % ------ Utilities ------ %
  63.  
  64. % Define a scratch string.  The PDF language definition says that
  65. % no line in a PDF file can exceed 255 characters.
  66. /pdfstring 255 string def
  67.  
  68. % Read the previous line of a file.  If we aren't at a line boundary,
  69. % read the line containing the current position.
  70. % Skip any blank lines.
  71. /prevline       % - prevline <startpos> <substring>
  72.  { PDFfile fileposition dup () pdfstring
  73.    2 index 257 sub 0 .max PDFfile exch setfileposition
  74.     {       % Stack: initpos linepos line string
  75.       PDFfile fileposition
  76.       PDFfile 2 index readline pop
  77.       dup length 0 gt
  78.        { 3 2 roll 5 -2 roll pop pop 2 index }
  79.        { pop }
  80.       ifelse
  81.         % Stack: initpos linepos line string startpos
  82.       PDFfile fileposition 5 index ge { exit } if
  83.       pop
  84.     }
  85.    loop pop pop 3 -1 roll pop
  86.  } bind def
  87.  
  88. % Execute a file, interpreting its executable names in a given
  89. % dictionary.  The name procedures may do whatever they want
  90. % to the operand stack.
  91. /.pdfrun            % <file> <opdict> .pdfrun -
  92.  {  % Construct a procedure with the file and opdict bound into it.
  93.    1 index cvlit mark mark 4 2 roll
  94.     { token not { (%%EOF) cvn cvx } if
  95.       dup xcheck
  96.        { DEBUG { dup == flush } if
  97.      2 copy .knownget
  98.       { exch pop exch pop exec }
  99.       { (%stderr) (w) file
  100.         dup (****************Unknown operator: ) writestring
  101.         dup 3 -1 roll .writecvs dup (\n) writestring flushfile
  102.         pop
  103.       }
  104.      ifelse
  105.        }
  106.        { exch pop DEBUG { dup ==only ( ) print flush } if
  107.        }
  108.       ifelse
  109.     }
  110.    aload pop .packtomark cvx
  111.    /loop cvx 2 packedarray cvx
  112.     { stopped /PDFsource } aload pop
  113.    PDFsource
  114.     { store { stop } if } aload pop .packtomark cvx
  115.    /PDFsource 3 -1 roll store exec
  116.  } bind def
  117.  
  118. % ------ File reading ------ %
  119.  
  120. % Read the cross-reference entry for an (unresolved) object.
  121. % The caller must save and restore the PDFfile position if desired.
  122. % For invalid (free) objects, we return 0.
  123. /readxrefentry      % <object#> readxrefentry <objpos>
  124.  { dup Objects exch get
  125.    PDFfile exch setfileposition
  126.    PDFfile token pop        % object position
  127.    PDFfile token pop        % generation #
  128.    PDFfile token pop        % n or f
  129.    dup /n eq
  130.     { pop 1 add dup 255 gt
  131.        { Generations type /stringtype eq
  132.       {     % Convert Generations from a string to an array.
  133.         Generations length array dup
  134.         0 1 2 index length 1 sub
  135.          { Generations 1 index get put dup
  136.          }
  137.         for pop /Generations exch store
  138.       }
  139.      if
  140.        }
  141.       if
  142.     }
  143.     { /f eq
  144.        { pop 0 }
  145.        { /readxrefentry cvx /syntaxerror signalerror }
  146.       ifelse
  147.     }
  148.    ifelse
  149.         % Stack: obj# objpos 1+gen#
  150.    Generations 4 -1 roll 3 -1 roll put
  151.  } bind def
  152.  
  153. % ================================ Objects ================================ %
  154.  
  155. % We represent an unresolved object reference by a procedure of the form
  156. % {obj# gen# resolveR}.  This is not a possible PDF object, because PDF has
  157. % no way to represent procedures.  Since PDF in fact has no way to represent
  158. % any PostScript object that doesn't evaluate to itself, we can 'force'
  159. % a possibly indirect object painlessly with 'exec'.
  160. % Note that since we represent streams by executable dictionaries
  161. % (see below), we need both an xcheck and a type check to determine
  162. % whether an object has been resolved.
  163. /unresolved?        % <object#> unresolved? <bool>
  164.  { Objects exch get dup xcheck exch type /integertype eq and
  165.  } bind def
  166. /oforce /exec load def
  167. /oget       % <array> <index> oget <object>
  168.         % <dict> <key> oget <object>
  169.  { 2 copy get dup xcheck
  170.     { exec dup 4 1 roll put }
  171.     { exch pop exch pop }
  172.    ifelse
  173.  } bind def
  174. % A null value in a dictionary is equivalent to an omitted key;
  175. % we must check for this specially.
  176. /knownoget
  177.  { 2 copy known
  178.     { oget dup null eq { pop false } { true } ifelse }
  179.     { pop pop false }
  180.    ifelse
  181.  } bind def
  182.  
  183. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  184. % Per the specification, we convert these to nulls.
  185. /F      % <file#> <object#> <generation#> F <object>
  186.  {      % Some PDF 1.1 files use F as a synonym for f!
  187.    count 3 lt { setfillcolor /fill fsexec } { pop pop pop null } ifelse
  188.  } bind def
  189.  
  190. % We keep track of objects in a pair of arrays, Objects and Generations.
  191. % Generations[N] is 1+ the current generation number for object number N.
  192. % (As far as we can tell, this is needed only for error checking.)
  193. % If object N is loaded, Objects[N] is the actual object;
  194. % otherwise, Objects[N] is an executable integer giving the file offset
  195. % of the object's entry in the cross-reference table.
  196. % For free objects, Generations[N] is 0.
  197. /checkgeneration  % <object#> <generation#> checkgeneration <object#> <OK>
  198.  { Generations 2 index get 1 sub 1 index eq
  199.     { pop true
  200.     }
  201.     { (Warning: wrong generation: ) print 1 index =only ( ) print = false
  202.     }
  203.    ifelse
  204.  } bind def
  205. /R      % <object#> <generation#> R <object>
  206.  { 1 index unresolved?
  207.     { /resolveR cvx 3 packedarray cvx }
  208.     { checkgeneration { Objects exch get } { pop null } ifelse }
  209.    ifelse
  210.  } bind def
  211.  
  212. % If we encounter an object definition while reading sequentially,
  213. % we just store it away and keep going.
  214. /objopdict mark
  215.   valueopdict { } forall
  216.   /endobj dup cvx
  217. .dicttomark readonly def
  218. /obj            % <object#> <generation#> obj <object>
  219.  { PDFfile objopdict .pdfrun
  220.  } bind def
  221. /endobj         % <object#> <generation#> <object> endobj <object>
  222.  { 3 1 roll
  223.         % Read the xref entry if we haven't yet done so.
  224.         % This is only needed for generation # checking.
  225.    1 index unresolved?
  226.     { PDFfile fileposition
  227.       2 index readxrefentry pop
  228.       PDFoffset add PDFfile exch setfileposition
  229.     } if
  230.    checkgeneration { Objects exch 2 index put } { pop pop null } ifelse
  231.  } bind def
  232.  
  233. % When resolving an object reference, we stop at the endobj.
  234. /resolveopdict mark
  235.   valueopdict { } forall
  236.   /endobj { endobj exit } bind
  237. .dicttomark readonly def
  238. /resolveR       % <object#> <generation#> resolveR <object>
  239.  { DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
  240.    1 index unresolved?
  241.     { PDFfile fileposition 3 1 roll
  242.       1 index readxrefentry
  243.       3 1 roll checkgeneration
  244.        {        % Stack: savepos objpos obj#
  245.      exch PDFoffset add PDFfile exch setfileposition
  246.      PDFfile token pop 2 copy ne
  247.       { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  248.       }
  249.      if pop PDFfile token pop
  250.      PDFfile token pop /obj ne
  251.       { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  252.       }
  253.      if
  254.      pdf_run_resolve    % PDFfile resolveopdict .pdfrun
  255.        }
  256.        { Objects exch null put pop null
  257.        }
  258.       ifelse exch PDFfile exch setfileposition
  259.     }
  260.     { pop Objects exch get
  261.     }
  262.    ifelse
  263.  } bind def      
  264.  
  265. %================================ Streams ================================ %
  266.  
  267. % We represent a stream by an executable dictionary that contains,
  268. % in addition to the contents of the original stream dictionary:
  269. %   /File - the file or string where the stream contents are stored;
  270. %   /FilePosition - iff File is a file, the position in the file
  271. %     where the contents start.
  272. %   /StreamKey - the key used to decrypt this stream if any
  273. % We do the real work of constructing the data stream only when the
  274. % contents are needed.
  275.  
  276. % Construct a stream.  The length is not reliable in the face of
  277. % different end-of-line conventions, but it's all we've got.
  278. %
  279. % PDF files are inconsistent about what may fall between the 'stream' keyword
  280. % and the actual stream data, and it appears that no one algorithm can
  281. % detect this reliably.  Furthermore, if we are already reading from a stream,
  282. % we can't back it up to determine what terminated the keyword.
  283. % We think the following is the best we can do:
  284. %   If we are already reading from a stream, skip the next character
  285. %   after the keyword terminator iff it is a \n.
  286. % This is chancy if the data are in binary form, but such files are
  287. % questionable to begin with.
  288. /streamskipeols
  289.  {      % We used to use the following algorithm:
  290. %    { PDFsource read not { /stream cvx /syntaxerror signalerror } if
  291. %      dup 10 eq 1 index 13 eq or not { PDFsource exch unread exit } if pop
  292. %    }
  293. %   loop
  294.         % But the one described above seems to work more often:
  295.     PDFsource read not { /stream cvx /syntaxerror signalerror } if
  296.     dup 10 ne { PDFsource exch unread } { pop } ifelse
  297.  } bind def
  298. /stream
  299.  { PDFsource PDFfile eq
  300.     { dup /File PDFfile put
  301.         % We used to do the following:
  302. %      prevline pop pop
  303.       streamskipeols
  304.       dup /FilePosition PDFfile fileposition put
  305.       DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
  306.       PDFfile fileposition 1 index /Length oget add
  307.         PDFfile exch setfileposition
  308.     }
  309.     {   % We're already reading from a stream, which we can't reposition.
  310.     % Capture the sub-stream contents in a string.
  311.       streamskipeols
  312.       dup /Length oget string PDFsource exch readstring
  313.       not
  314.        { (Unexpected EOF in stream!\n) print
  315.      /stream cvx /rangecheck signalerror
  316.        }
  317.       if
  318.       1 index exch /File exch put
  319.     }
  320.    ifelse
  321.    PDFsource token pop
  322.      /endstream ne { /stream cvx /syntaxerror signalerror } if
  323.    cvx
  324.  } bind def
  325.  
  326. % Resolve a stream dictionary to a PostScript stream.
  327. % Streams with no filters require special handling:
  328. %   - If we are going to interpret their contents, we let endstream
  329. %     terminate the interpretation loop;
  330. %   - If we are just going to read data from them, we impose
  331. %     a SubFileDecode filter that reads just the requisite amount of data.
  332. % Note that, in general, resolving a stream repositions PDFfile.
  333. % Clients must save and restore the position of PDFfile themselves.
  334. /resolvestream      % <streamdict> <readdata?> resolvestream <stream>
  335.  { exch dup /FilePosition .knownget
  336.     { 1 index /File get exch setfileposition }
  337.    if
  338.         % Stack: readdata? dict
  339.    dup /DecodeParms .knownget not { null } if
  340.    1 index /Filter .knownget not { {} } if
  341.    dup type /nametype eq
  342.     { 1 array astore
  343.       1 index null ne { exch 1 array astore exch } if
  344.     }
  345.    if
  346.         % Stack: readdata? dict parms filternames
  347.    2 index /File get exch
  348.         % Stack: readdata? dict parms file/string filternames
  349.    pdf_decrypt_stream       % add decryption if needed
  350.    dup length 0 eq
  351.     {       % All the PDF filters have EOD markers, but in this case
  352.         % there is no specified filter.
  353.       pop exch pop
  354.         % Stack: readdata? dict file/string
  355.       2 index
  356.        {    % We're going to read data; use a SubFileDecode filter.
  357.      1 index /Length oget () /SubFileDecode filter
  358.        }
  359.        { dup type /filetype ne
  360.       { % Use a SubFileDecode filter to read from a string.
  361.         0 () SubFileDecode filter
  362.       }
  363.      if
  364.        }
  365.       ifelse
  366.     }
  367.     { 2 index null eq
  368.        { { filter }
  369.        }
  370.        {    % Stack: readdata? dict parms file/string filtername
  371.          { 2 index 0 get dup null eq { pop } { exch } ifelse filter
  372.        exch dup length 1 sub 1 exch getinterval exch
  373.      }
  374.        }
  375.       ifelse forall exch pop
  376.     }
  377.    ifelse
  378.         % Stack: readdata? dict file
  379.    exch pop exch pop
  380.  } bind def
  381. /endstream { exit } def
  382.  
  383. end         % pdfdict
  384. .setglobal
  385.