home *** CD-ROM | disk | FTP | other *** search
/ jppd.dyndns.org / jppd.dyndns.org.tar / jppd.dyndns.org / QUERYPRO / Impressora_PDF / converter.exe / GPLGS / pdf_rbld.ps < prev    next >
Text File  |  2004-08-28  |  14KB  |  304 lines

  1. %    Copyright (C) 2002 artofcode LLC.  All rights reserved.
  2. % This software is provided AS-IS with no warranty, either express or
  3. % implied.
  4. % This software is distributed under license and may not be copied,
  5. % modified or distributed except as expressly authorized under the terms
  6. % of the license contained in the file LICENSE in this distribution.
  7. % For more information about licensing, please refer to
  8. % http://www.ghostscript.com/licensing/. For information on
  9. % commercial licensing, go to http://www.artifex.com/licensing/ or
  10. % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  11. % San Rafael, CA  94903, U.S.A., +1(415)492-9861.
  12.  
  13. % $Id: pdf_rbld.ps,v 1.4.4.2 2004/08/27 21:36:36 dan Exp $
  14. % pdf_rbld.ps - Rebuilding of broken PDF files (xref errors)
  15.  
  16. % This module contains routines that are used if we detect an error
  17. % while reading the xref tables.  These routines will scan the file and
  18. % build an xref table by finding the objects.  We also need to find the
  19. % appropriate trailer dictionary.  Note:  One procedure is also used
  20. % even if we do not need to rebuild a PDF file.
  21. %
  22. % This module cannot rebuild a PDF file which has had errors created inside
  23. % of objects or binary data streams.  It often succeeds with files that
  24. % have had its end of lines converted between unix and dos versions.
  25.  
  26. % if true --> we have an object with duplicate object and generation numbers.
  27. /dup_obj_gen_num false def
  28.  
  29. % Note:  This routine is also used by non-rebuild code.
  30. % Store a line in the xref array (Actually Objects and Generations arrays)
  31. % <obj num> <obj loc> <gen num>  setxrefentry <obj num> <obj loc> <gen num>
  32. /setxrefentry
  33. {    % We store generation numbers as value + 1
  34.     % We reserve 0 to indicate an unknown generation (or xref entry)
  35.   1 add            % increment generation number
  36.     % To save space, generations numbers are stored in a lstring unless we
  37.     % find a generation number greater than 255.  If so then transfer to 
  38.     % an larray.
  39.   dup 255 gt {
  40.     Generations ltype /stringtype eq {    % Convert Generations to an larray.
  41.       larray Generations llength lgrowto dup    % Create new larray
  42.       0 1 2 index llength 1 sub {    % Copy from old lstring to new larray
  43.     Generations 1 index lget lput dup
  44.       } for
  45.       pop
  46.       /Generations exch store        % Save new Generations larray
  47.     } if
  48.   } if
  49.     % Verify that the new values are for a new object.  If the current
  50.     % entry is null then we have a new entry.
  51.   Objects 3 index lget null eq {
  52.     Objects 3 index 3 index cvx lput    % Save object location
  53.     Generations 3 index 2 index lput    % Save geenration number
  54.   } {
  55.     % Verify that the new entry has at least as high a generaton number
  56.     % We accept equal entry number because we have found PDF files in
  57.     % which there are multiple objects with the same object and entry
  58.     % numbers.  The normal xref logic only accepts the first such
  59.     % entry that it finds.  However the 'rebuild PDF' logic can find
  60.     % both such entries.  The correct one is usually the last one.
  61.     Generations 3 index lget 1 index le {
  62.       Objects 3 index 3 index cvx lput    % Save object location
  63.       Generations 3 index 2 index lput    % Save geenration number
  64.     } if
  65.     % Set error flag if we have equal object and generation numbers
  66.     Generations 3 index lget 1 index eq { /dup_obj_gen_num true def } if
  67.   } ifelse
  68. } bind def
  69.  
  70. % Print the contents of the xref array.  This actually consists of two
  71. % arrays (Objects and Generations).  Both are larrays.  larrays are a
  72. % special Ghostscript object which can be arrays with more than 64k
  73. % elements.
  74. /print_xref                % - print_xref -
  75. { 0 1 Objects llength 1 sub        % stack: 0 1 <number of objects - 1>
  76.   { dup =only                % print object number
  77.     (  ) print
  78.     dup Generations exch lget 1 sub =only % print Generation number
  79.     (  ) print
  80.     Objects exch lget ===        % print object location
  81.   } for
  82. } bind def
  83.  
  84. % This is the same as the postscript token operator except that
  85. % errors are ignored.
  86. /token_nofail
  87. {
  88.   { token } .internalstopped
  89.   { pop false } if
  90. } bind odef
  91.  
  92. % Get token from string and check its type
  93. %   <string> <type> typed_token <false>        % no token or not match
  94. %   <string> <type> typed_token <obj> <last> <true>    % matching token type
  95. % Where last is the string remainder
  96. /typed_token
  97. { exch
  98.   token_nofail            % get token
  99.   {
  100.     dup type            % stack:  type last token type
  101.     4 -1 roll eq {        % stack:  last token bool
  102.       exch true            % desired object found - set exit status
  103.     } {
  104.       pop pop false        % not type - clear stack, set exit status
  105.     } ifelse
  106.   } {
  107.     pop false            % no token - pop type, set exit status
  108.   } ifelse            % check if we got token
  109. } bind def
  110.  
  111. % Allocate space for post_eof_count to be bound into procedures below.
  112. /post_eof_count 0 def
  113.  
  114. % We want the location of the trailer dictionary at the start of file.
  115. % First we will find the xref.  Then we will skip over the xref entries
  116. % to the trailer.
  117. /search_start_trailer        % - search_start_trailer <trailer loc>
  118. { % Read the first 300 bytes and check for xref
  119.   PDFfile 0 setfileposition
  120.   300 string 0 1 299 { 2 copy PDFfile read pop put pop } for
  121.   (xref) search {
  122.     % found 'xref'
  123.     exch pop exch pop length 4 add PDFfile exch setfileposition
  124.     PDFfile token pop        % get starting entry - or 'trailer'
  125.     (trailer) ne {        % if we do not already have 'trailer'
  126.       PDFfile token pop        % get number of entries
  127.       PDFfile token pop pop    % this moves us into the middle of the first entry
  128.       25 string exch        % define working string for readline
  129.       { PDFfile 1 index readline pop pop
  130.       } repeat            % skip entries
  131.       pop            % pop working string
  132.       PDFfile token pop        % get 'trailer'
  133.       PDFfile fileposition    % get file position
  134.     } if
  135.   } {
  136.     pop 0            % no xref - should not happen
  137.   } ifelse
  138. } bind def
  139.  
  140. % We want the location of the trailer dictionary at the end of file.
  141. % We will read the last block of data and search for the final occurance
  142. % of the word 'trailer'
  143. /search_end_trailer        % - search_end_trailer <trailer loc>
  144. { % Position to read block of data from the end of the file.  Note:  We ignore
  145.   % anything past the last %%EOF since this is not PDF data.
  146.   PDFfile 0 setfileposition
  147.   PDFfile bytesavailable post_eof_count sub    % location of end of data
  148.   dup 4096 .min            % block size to read
  149.                 % stack: <file end pos> <block size>
  150.   % move file position to the start of the block
  151.   2 copy sub PDFfile exch setfileposition
  152.   % read block of data
  153.   dup string 0 1 4 -1 roll 1 sub { 2 copy PDFfile read pop put pop } for
  154.   % search for last occurance of 'trailer'
  155.   (trailer) { search not { exit } if pop } loop
  156.   % determine where the trailer is in the file
  157.   %   trailer loc = end loc - remaing string length
  158.   length sub 
  159. } bind def
  160.  
  161. % We want to find the trailer dictionary.  There is a trailer dictionary
  162. % for each xref object list.  We only want the trailer dictionary associated
  163. % with the first xref object list.  In theory this can be anywhere in the
  164. % file.  However since we are trying to repair a broken file, we cannot simply
  165. % follow the xref links.  So we are falling back to a simple strategy.  We
  166. % find the specified location of the first xref list.  If its location is in
  167. % the first half of the file then we search for the first trailer dictionary
  168. % at the start of the file.  Otherwise we search for the last trailer at the
  169. % end of the file.
  170. /search_trailer            % - search_trailer -
  171. { % Find the 'startxref' and associated position at the end of the file.
  172.   % Position to read block of data from the end of the file.  Note:  We
  173.   % actually end at the end of the last %%EOF since this is the end of the
  174.   % useful PDF data.  (Some files contain trailing garbage.)
  175.   PDFfile 0 setfileposition
  176.   PDFfile bytesavailable    % size of file
  177.   post_eof_count sub dup    % location of end of last %%EOF
  178.   dup 4096 .min            % block size to read
  179.   % stack: <useful file size> <useful file size file> <block size>
  180.   % move file position to the start of the block
  181.   2 copy sub PDFfile exch setfileposition
  182.   % read block of data
  183.   dup string 0 1 4 -1 roll 1 sub { 2 copy PDFfile read pop put pop } for
  184.   % search for last occurance of 'startxref'
  185.   (startxref) { search not { exit } if pop } loop
  186.   % determine where the trailer is in the file
  187.   %   trailer loc = end loc - remaing string length
  188.   length sub 9 sub
  189.   % move the file to this position and read startxref and position
  190.   PDFfile exch setfileposition
  191.   PDFfile token pop pop PDFfile token pop
  192.   % compare xref position to 1/2 the length of the file and search for trailer
  193.   exch 2 div lt { search_start_trailer } { search_end_trailer } ifelse
  194.   % get the trailer
  195.   PDFfile exch setfileposition        % set to the specified trailer location
  196.   PDFfile traileropdict .pdfrun        % read trailer info
  197.   /Trailer exch def
  198. } bind def
  199.  
  200. % This routine will determine if there is stuff after the %%EOF.  There is
  201. % supposed to be only a line termination.  However many real life files
  202. % contain some garbage.  This routine checks how much.  We then ignore this
  203. % stuff when we are scanning for objects.
  204. /determine_post_eof_count        % - determine_post_eof_count <count>
  205. { % Position to read block of data from the end of the file. 
  206.   PDFfile 0 setfileposition
  207.   PDFfile bytesavailable    % size of file
  208.   dup 4096 .min            % block size to read
  209.   % stack: <file size> <file size> <block size>
  210.   % move file position to the start of the block
  211.   2 copy sub PDFfile exch setfileposition
  212.   % read block of data
  213.   dup string 0 1 4 -1 roll 1 sub { 2 copy PDFfile read pop put pop } for
  214.   % search for last occurance of '%%EOF'
  215.   (%%EOF) { search not { exit } if pop } loop
  216.   % how much is left = remaining string length
  217.   length exch pop        % pop /%%EOF
  218. } bind def
  219.  
  220. % This routine will scan a file searaching for object locations to build
  221. % an alternate version of the data in the xref tables.
  222. % Its purpose is to provide a basis for an xref fixing facility.
  223. /search_objects                % - search_objects -
  224. { % Initialize the Objects, Generations, etc. larrays
  225.   initPDFobjects
  226.   % reset duplicate object and generation numbers error flag
  227.   /dup_obj_gen_num false def
  228.   % Determine how many bytes are in the file after the final %%EOF
  229.   /post_eof_count determine_post_eof_count def
  230.   % Start at the beginning of the file
  231.   PDFfile 0 setfileposition
  232.   % Create a working string (and also store its length on stack)
  233.   1024 dup string
  234.   { % Now loop through the entire file lloking for objects
  235.     PDFfile fileposition        % save current file position
  236.     % When we get near the end of the file, we use a smaller interval of
  237.     % our working string to prevent reading past the end.  (See comments on
  238.     % EOF testing below.)
  239.     PDFfile bytesavailable post_eof_count sub 10 sub dup 4 index lt {
  240.       2 index 0 3 -1 roll getinterval    % near EOF, use interval of string
  241.     } { pop 1 index            % not near end, use full working string
  242.     }ifelse
  243.     % Read a line from file.  If the line does not fit into our working string,
  244.     % or any other error, then we will discard it.
  245.     PDFfile exch { readline } .internalstopped
  246.     { pop pop false } if        % indicate no string if we stopped
  247.     { % stack: <length> <working_str> <loc> <string>
  248.       % Now that we have line, get obj num, ref num, and 'obj'.  Verify that each
  249.       % of these is correct type.
  250.       /integertype typed_token {    % get obj number
  251.         /integertype typed_token {    % get ref number
  252.           /nametype typed_token {    % get 'obj' text
  253.         pop                % pop remaining string
  254.         /obj eq {            % verify name is 'obj'
  255.           % make sure we have room in the arrays.  We work in increments
  256.           % of 20 each time we increase the size.
  257.           1 index 20 add 20 idiv 20 mul
  258.           growPDFobjects
  259.           % save xref parameters into Objects and Generations
  260.           1 index 3 index 2 index    % rearrange parms for setxrefentry
  261.           setxrefentry        % save parameters
  262.           pop pop pop        % clear parameters
  263.         } if            % check if name is 'obj'
  264.           } if                % check if we got 'obj" string
  265.           pop                % remove ref number
  266.         } if                % check if we got ref number
  267.         pop                % remove obj number
  268.       } if                % check if we got object number
  269.     } if                % check if got a string from readline
  270.     pop                    % remove location
  271.     % Check if we are approaching the end of the file.  We do not want to
  272.     % read past the end of the file since that closes it.  We actually stop
  273.     % 10-20 bytes early since there cannot be an object that close to the end.
  274.     % (There is a Trailer dictionary, etc. at the end of the file.)
  275.     PDFfile bytesavailable post_eof_count sub 20 lt { exit } if
  276.   } loop                % loop through the entire file
  277.   pop pop                % remove working string and its length
  278.   % Output warning if we have two objects with the same object and generation
  279.   % numbers.
  280.   dup_obj_gen_num {
  281.     (   **** Warning:  There are objects with matching object and generation\n)
  282.     pdfformaterror
  283.     (   **** numbers.  The accuracy of the resulting image is unknown.\n)
  284.     pdfformaterror
  285.   } if
  286. } bind def
  287.  
  288. % Print xref warning message
  289. /print_xref_warning
  290. {
  291.   (   **** Warning:  An error occurred while reading an XREF table.\n)
  292.   pdfformaterror
  293.   (   **** The file has been damaged.  This may have been caused\n)
  294.   pdfformaterror
  295.   (   **** by a problem while converting or transfering the file.\n)
  296.   pdfformaterror
  297.   (   **** Ghostscript will attempt to recover the data.\n)
  298.   pdfformaterror
  299. } bind def
  300.  
  301.