pdfwrite.ps 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. % Copyright (C) 1999, 2000, 2001 Aladdin Enterprises. All rights reserved.
  2. %
  3. % This file is part of AFPL Ghostscript.
  4. %
  5. % AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or
  6. % distributor accepts any responsibility for the consequences of using it, or
  7. % for whether it serves any particular purpose or works at all, unless he or
  8. % she says so in writing. Refer to the Aladdin Free Public License (the
  9. % "License") for full details.
  10. %
  11. % Every copy of AFPL Ghostscript must include a copy of the License, normally
  12. % in a plain ASCII text file named PUBLIC. The License grants you the right
  13. % to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14. % conditions described in the License. Among other things, the License
  15. % requires that the copyright notice and this notice be preserved on all
  16. % copies.
  17. % $Id: pdfwrite.ps,v 1.7 2001/07/30 07:16:32 lpd Exp $
  18. % Writer for transmuting PDF files.
  19. % NOTES:
  20. % We do editing by replacing objects (in the cache) and then doing a
  21. % simple recursive walk with object renumbering.
  22. % Free variables:
  23. % RMap [per input file] (dict): input_obj# => output_obj#
  24. % PDFfile (file): current input file
  25. % OFile (file): current output file
  26. % XRef (dict): output_obj# => output_file_pos
  27. % ToWrite: 0..N-1 => [obj# gen#]
  28. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  29. .currentglobal true .setglobal
  30. /PDEBUG where { pop } { /PDEBUG false def } ifelse
  31. % ================ Object mapping ================ %
  32. % Initialize the object number and location map.
  33. /omapinit { % - omapinit -
  34. /RMap 100 dict def
  35. /XRef 100 dict def
  36. PDEBUG { (omapinit) = } if
  37. } def
  38. % Map an object number.
  39. /omapnew { % <oldobj#> omap <newobj#> <isnew>
  40. RMap 1 index .knownget {
  41. exch pop false
  42. } {
  43. PDEBUG { (omap\() print dup =only } if
  44. RMap dup length 1 add 2 index exch dup 5 1 roll put pop true
  45. PDEBUG { (\) = ) print 1 index = } if
  46. } ifelse
  47. } def
  48. /omap { % <oldobj#> omap <newobj#>
  49. omapnew pop
  50. } bind def
  51. % Save and restore the object map.
  52. % Note that currentomap either returns a copy or calls omapinit.
  53. /currentomap { % <copy> currentomap <omap>
  54. {
  55. [RMap dup length dict copy XRef dup length dict copy]
  56. } {
  57. [RMap XRef] omapinit
  58. } ifelse
  59. } bind def
  60. /setomap { % <omap> setomap -
  61. aload pop /XRef exch def /RMap exch def
  62. PDEBUG {
  63. (setomap: #Xref = ) print XRef length =only
  64. (, #RMap = ) print RMap length =
  65. } if
  66. } bind def
  67. % ================ Writing ================ %
  68. % ---------------- Low-level output ---------------- %
  69. % Write a string on the output file.
  70. /ows { % <string> ows -
  71. OFile exch writestring
  72. } def
  73. % ---------------- Scalars ---------------- %
  74. % Note that the '#' character isn't legal in a name unless it is a prefix
  75. % for a hex encoded character (for PDF 1.2 and later). The following assumes
  76. % that the names are already valid PDF 1.2+ names so that we can treat the
  77. % '#' as a legal character. The next two hex characters are already in the
  78. % set of valid name characters. PDF 1.1 and earlier allowed spaces in names
  79. % which probably wouldn't make it past the tokenizer anyway.
  80. /pdfnamechars
  81. (!"#$&'*+,-.0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^_`abcdefghijklmnopqrstuvwxyz|~)
  82. readonly def
  83. /pdfwritename { % <name> pdfwritename -
  84. (/) ows .namestring {
  85. ( ) dup 0 4 -1 roll put
  86. //pdfnamechars 1 index search {
  87. pop pop pop
  88. } {
  89. pop 0 get 256 add 16 =string cvrs
  90. dup 0 (#) 0 get put
  91. } ifelse ows
  92. } forall
  93. } def
  94. % ---------------- Composite objects ---------------- %
  95. /pdfwriteprocs mark
  96. /resolveR { pdfwriteref }
  97. /O { pdfwritenewref }
  98. .dicttomark readonly def
  99. /pdfwritearray { % <array> pdfwritearray -
  100. dup xcheck {
  101. aload pop //pdfwriteprocs exch get exec
  102. } {
  103. % Because of a bug in Acrobat's parser for linearization parameters,
  104. % we have to include some whitespace after the opening [ (!).
  105. ([ ) ows { pdfwritevalue (\n) ows } forall (]) ows
  106. } ifelse
  107. } def
  108. /pdfwritedict { % <dict> pdfwritedict -
  109. dup xcheck {
  110. pdfwritestream
  111. } {
  112. (<<) ows {
  113. exch pdfwritevalue ( ) ows pdfwritevalue (\n) ows
  114. } forall (>>) ows
  115. } ifelse
  116. } def
  117. % ---------------- References ---------------- %
  118. /pdfwritenewref { % <newobj#> pdfwritenewref -
  119. OFile exch write=only ( 0 R) ows
  120. } def
  121. /pdfwriteref { % <obj#> <gen#> pdfwriteref -
  122. 1 index omapnew {
  123. ToWrite dup length 5 -2 roll 2 packedarray put
  124. } {
  125. exch pop exch pop
  126. } ifelse
  127. pdfwritenewref
  128. } def
  129. /pdfcopystring 200 string def
  130. /pdfwritestream { % <streamdict> pdfwritestream -
  131. % Remove File, FilePosition, and StreamKey;
  132. % optimize by replacing an indirect Length.
  133. dup dup length dict copy
  134. % Stack: origdict dict
  135. dup /File undef dup /FilePosition undef dup /StreamKey undef
  136. dup /Length get dup oforce ne {
  137. dup /Length 2 copy oget put
  138. } if
  139. exch dup /File get dup 3 -1 roll /FilePosition get setfileposition
  140. pdfcopystream
  141. } def
  142. % We put copying the stream contents in separate procedures so that we
  143. % can replace this function if desired.
  144. /pdfcopybytes { % <fromfile> <tofile> <length> pdfcopybytes -
  145. {
  146. dup 0 eq { exit } if
  147. //pdfcopystring 0 2 index 2 index length .min getinterval
  148. 3 index exch readstring 3 1 roll
  149. 3 index 1 index writestring length sub exch not { exit } if
  150. } loop pop pop pop
  151. } def
  152. /pdfcopystream { % <newstreamdict> <file> pdfcopystream -
  153. % (file has been positioned)
  154. 1 index pdfwritevalue (stream\n) ows
  155. exch /Length get OFile exch pdfcopybytes
  156. (endstream) ows
  157. } def
  158. % ---------------- General values/objects ---------------- %
  159. /pdfwritetypes mark
  160. % Scalars
  161. /nulltype { pop (null) ows }
  162. /integertype { =string cvs ows }
  163. /booleantype 1 index
  164. /realtype { OFile exch write===only }
  165. /stringtype 1 index
  166. /nametype { pdfwritename }
  167. % Composite/reference objects
  168. /arraytype { pdfwritearray }
  169. /packedarraytype 1 index
  170. /dicttype { pdfwritedict }
  171. .dicttomark readonly def
  172. /pdfwritevalue { % <obj> pdfwritevalue -
  173. PDEBUG { (****Writing: ) print dup === flush } if
  174. //pdfwritetypes 1 index type get exec
  175. } def
  176. % We make pdfwriteobjdef a separate procedure for external use.
  177. /pdfwriteobjheader { % <newobj#> pdfwriteobjheader -
  178. XRef 1 index OFile .fileposition put
  179. PDEBUG { (XRef\() print dup =only (\) = ) print XRef 1 index get = } if
  180. OFile exch write=only ( 0 obj\n) ows
  181. } def
  182. /pdfwriteobjdef { % <newobj#> <value> pdfwriteobjdef -
  183. exch pdfwriteobjheader
  184. pdfwritevalue (\nendobj\n) ows
  185. } def
  186. /pdfwriteobj { % <obj#> <gen#> pdfwriteobj -
  187. 1 index exch resolveR exch omap exch pdfwriteobjdef
  188. } def
  189. % ---------------- File-level entities ---------------- %
  190. % Write a PDF file header.
  191. % Free variables: OFile, PDFversion.
  192. /pdfwriteheader { % - pdfwriteheader -
  193. (%PDF-) ows OFile PDFversion write=
  194. (%\347\363\317\323\n) ows
  195. } bind def
  196. % Write a cross-reference table and trailer.
  197. /pdfwritexref { % <firstobj#> <#objs> pdfwritexref -
  198. (xref\n) ows
  199. OFile 2 index write=only ( ) ows OFile 1 index write=
  200. 1 index add 1 sub 1 exch {
  201. dup 0 eq {
  202. pop (0000000000 65535 f \n) ows
  203. } {
  204. XRef exch get 1000000000 add =string cvs
  205. dup 0 (0) 0 get put
  206. ows ( 00000 n \n) ows
  207. } ifelse
  208. } for
  209. } bind def
  210. /pdfwritetrailer { % <trailer> pdfwritetrailer -
  211. (trailer\n) ows pdfwritevalue (\n) ows
  212. } bind def
  213. /pdfwritestartxref { % <startpos> pdfwritestartxref -
  214. (startxref\n) ows OFile exch write=
  215. (%%EOF\n) ows
  216. } bind def
  217. % ================ Top-level control ================ %
  218. /pdfwrite { % <file> <trailer> pdfwrite -
  219. 10 dict begin
  220. /trailer exch def
  221. /OFile exch def
  222. /ToWrite 100 dict def
  223. omapinit
  224. % Write the PDF file header.
  225. pdfwriteheader
  226. % Write the objects.
  227. trailer {
  228. exch pop dup xcheck { % The only executable objects are references.
  229. aload pop pop pdfwriteobj
  230. } {
  231. pop
  232. } ifelse
  233. } forall
  234. % Walk the object graph.
  235. {
  236. ToWrite dup length dup 0 eq { pop pop exit } if
  237. 1 sub 2 copy get 3 1 roll undef aload pop pdfwriteobj
  238. } loop
  239. % Write the xref table and trailer.
  240. /xref OFile fileposition def
  241. 0 XRef length 1 add pdfwritexref
  242. trailer dup length 1 add dict copy
  243. dup /Size XRef length 1 add put pdfwritetrailer
  244. xref pdfwritestartxref
  245. end
  246. } def
  247. .setglobal