]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/ghostscript/pdfwrite.ps
acid leak: remove arena pointer a < 0xff000000 check
[plan9front.git] / sys / lib / ghostscript / pdfwrite.ps
1 %    Copyright (C) 1999, 2000, 2001 Aladdin Enterprises.  All rights reserved.
2
3 % This software is provided AS-IS with no warranty, either express or
4 % implied.
5
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.
9
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.
15
16 % $Id: pdfwrite.ps,v 1.11 2003/05/20 13:46:22 alexcher Exp $
17 % Writer for transmuting PDF files.
18
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
29 /.setlanguagelevel where { pop 2 .setlanguagelevel } if
30 .currentglobal true .setglobal
31
32 /PDFWRDEBUG where { pop } { /PDFWRDEBUG false def } ifelse
33
34 % ======== Long dictionary support =============== %
35
36 % The key must be a non-negative iteger.
37
38 /ld_dict {          % <len> ld_dict <ldict>
39   pop << 0 <<>> >>
40 } bind def
41
42 /ld_length {        % <ldict> ld_length <length>
43   0 exch { exch pop length add } forall
44 } bind def
45
46 /ld_get {           % <ldict> <key> ld_get <any>
47   dup 3 1 roll -15 bitshift get exch get
48 } bind def
49
50 /ld_put {           % <ldict> <key> <any> ld_put -
51   3 1 roll dup               % any ldict key key
52   4 1 roll -15 bitshift      % key any ldict key>>15
53   2 copy known {
54     get                      % key any subdict
55     3 1 roll put             % -
56   } {
57     64 dict dup 6 1 roll     % <<>> key any ldict key>>15 <<>>
58     put put
59   } ifelse                   % -
60 } bind def
61                     
62 /ld_known {         % <ldict> <key> ld_known <bool>
63   dup 3 1 roll -15 bitshift  % key <<>> key<<15
64   2 copy known {
65     get exch known
66   } {
67     pop pop pop //false
68   } ifelse
69 } bind def
70                              
71 /ld_knownget {      % <ldict> <key> ld_known false | <any> true
72   dup 3 1 roll -15 bitshift  % key <<>> key<<15
73   2 copy known {
74     get exch .knownget
75   } {
76     pop pop pop //false
77   } ifelse
78 } bind def
79
80 /ld_def {           % <key> <any> ld_def -
81   currentdict 3 1 roll ld_put
82 } bind def
83
84 /ld_forall {        % <ldict> <proc} ld_forall -
85   { forall exch pop } aload pop
86   4 2 roll 4 packedarray cvx forall
87 } bind def
88
89 /ld_clone {         % <ldict> ld_clone <ldict copy>
90   << exch { dup length dict copy } forall >>
91 } bind def
92
93 % ================ Object mapping ================ %
94
95 % Initialize the object number and location map.
96 /omapinit {             % - omapinit -
97   /RMap 100 ld_dict def
98   /XRef 100 ld_dict def
99   PDFWRDEBUG { (omapinit) = } if
100 } bind def
101
102 % Map an object number.
103 /omapnew {              % <oldobj#> omap <newobj#> <isnew>
104   RMap 1 index ld_knownget {
105     exch pop //false
106   } {
107     PDFWRDEBUG { (omap\() print dup =only } if
108     RMap dup ld_length 1 add   % old# <<>> len+1
109     2 index exch dup           % old# <<>> old# len+1 len+1
110     5 1 roll                   % len+1 old# <<>> old# len+1
111     ld_put pop //true          % len+1 true
112     PDFWRDEBUG { (\) = ) print 1 index = } if
113   } ifelse
114 } bind def
115 /omap {                 % <oldobj#> omap <newobj#>
116   omapnew pop
117 } bind def
118
119 % Save and restore the object map.
120 % Note that currentomap either returns a copy or calls omapinit.
121 /currentomap {          % <copy> currentomap <omap>
122   {
123     [RMap ld_clone XRef ld_clone]
124   } {
125     [RMap XRef] omapinit
126   } ifelse
127 } bind def
128 /setomap {              % <omap> setomap -
129   aload pop /XRef exch def /RMap exch def
130   PDFWRDEBUG {
131     (setomap: #Xref = ) print XRef ld_length =only
132     (, #RMap = ) print RMap ld_length =
133   } if
134 } bind def
135
136 % ================ Writing ================ %
137
138 % ---------------- Low-level output ---------------- %
139
140 % Write a string on the output file.
141 /ows {                  % <string> ows -
142   OFile exch writestring
143 } bind def
144
145 % ---------------- Scalars ---------------- %
146
147 % Note that the '#' character isn't legal in a name unless it is a prefix
148 % for a hex encoded character (for PDF 1.2 and later). The following assumes
149 % that the names are already valid PDF 1.2+ names so that  we can treat the
150 % '#' as a legal character. The next two hex characters are already in the
151 % set of valid name characters. PDF 1.1 and earlier allowed spaces in names
152 % which probably wouldn't make it past the tokenizer anyway.
153 /pdfnamechars
154   (!"#$&'*+,-.0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^_`abcdefghijklmnopqrstuvwxyz|~)
155 readonly def
156 /pdfwritename {         % <name> pdfwritename -
157   (/) ows .namestring {
158     ( ) dup 0 4 -1 roll put
159     //pdfnamechars 1 index search {
160       pop pop pop
161     } {
162       pop 0 get 256 add 16 =string cvrs
163       dup 0 (#) 0 get put
164     } ifelse ows
165   } forall
166 } bind def
167
168 % ---------------- Composite objects ---------------- %
169
170 /pdfwriteprocs mark
171   /resolveR { pdfwriteref }
172   /O { pdfwritenewref }
173 .dicttomark readonly def
174 /pdfwritearray {        % <array> pdfwritearray -
175   dup xcheck {
176     aload pop //pdfwriteprocs exch get exec
177   } {
178         % Because of a bug in Acrobat's parser for linearization parameters,
179         % we have to include some whitespace after the opening [ (!).
180     ([ ) ows { pdfwritevalue (\n) ows } forall (]) ows
181   } ifelse
182 } bind def
183
184 /pdfwritedict {         % <dict> pdfwritedict -
185   dup xcheck {
186     pdfwritestream
187   } {
188     (<<) ows {
189       exch pdfwritevalue ( ) ows pdfwritevalue (\n) ows
190     } forall (>>) ows
191   } ifelse
192 } bind def
193
194 % ---------------- References ---------------- %
195
196 /pdfwritenewref {       % <newobj#> pdfwritenewref -
197   OFile exch write=only ( 0 R) ows
198 } bind def
199
200 /pdfwriteref {          % <obj#> <gen#> pdfwriteref -
201   1 index omapnew {
202     ToWrite dup length 5 -2 roll 2 packedarray put
203   } {
204     exch pop exch pop
205   } ifelse
206   pdfwritenewref
207 } bind def
208
209 /pdfcopystring 200 string def
210 /pdfwritestream {       % <streamdict> pdfwritestream -
211         % Remove File, FilePosition, and StreamKey;
212         % optimize by replacing an indirect Length.
213   dup dup length dict copy
214         % Stack: origdict dict
215   dup /File undef dup /FilePosition undef dup /StreamKey undef
216   dup /Length get dup oforce ne {
217     dup /Length 2 copy oget put
218   } if
219   exch dup /File get dup 3 -1 roll /FilePosition get setfileposition
220   pdfcopystream
221 } bind def
222
223 % We put copying the stream contents in separate procedures so that we
224 % can replace this function if desired.
225 /pdfcopybytes {         % <fromfile> <tofile> <length> pdfcopybytes -
226   {
227     dup 0 eq { exit } if
228     //pdfcopystring 0 2 index 2 index length .min getinterval
229     3 index exch readstring 3 1 roll
230     3 index 1 index writestring length sub exch not { exit } if
231   } loop pop pop pop
232 } bind def
233 /pdfcopystream {        % <newstreamdict> <file> pdfcopystream -
234                         %   (file has been positioned)
235   1 index pdfwritevalue (stream\n) ows
236   exch /Length get OFile exch pdfcopybytes
237   (endstream) ows
238 } bind def
239
240 % ---------------- General values/objects ---------------- %
241
242 /pdfwritetypes mark
243         % Scalars
244   /nulltype { pop (null) ows } bind
245   /integertype { =string cvs ows } bind
246   /booleantype 1 index
247   /realtype { OFile exch write===only } bind
248   /stringtype 1 index
249   /nametype { pdfwritename } bind
250         % Composite/reference objects
251   /arraytype { pdfwritearray } bind
252   /packedarraytype 1 index
253   /dicttype { pdfwritedict } bind
254 .dicttomark readonly def
255
256 /pdfwritevalue {        % <obj> pdfwritevalue -
257   PDFWRDEBUG { (****Writing: ) print dup === flush } if
258   //pdfwritetypes 1 index type get exec
259 } bind def
260
261 % We make pdfwriteobjdef a separate procedure for external use.
262 /pdfwriteobjheader {    % <newobj#> pdfwriteobjheader -
263   XRef 1 index OFile .fileposition ld_put
264   PDFWRDEBUG { (XRef\() print dup =only (\) = ) print XRef 1 index ld_get = } if
265   OFile exch write=only ( 0 obj\n) ows
266 } bind def
267 /pdfwriteobjdef {       % <newobj#> <value> pdfwriteobjdef -
268   exch pdfwriteobjheader
269   pdfwritevalue (\nendobj\n) ows
270 } bind def
271 /pdfwriteobj {          % <obj#> <gen#> pdfwriteobj -
272   1 index exch resolveR exch omap exch pdfwriteobjdef
273 } bind def
274
275 % ---------------- File-level entities ---------------- %
276
277 % Write a PDF file header.
278 % Free variables: OFile, PDFversion.
279 /pdfwriteheader {       % - pdfwriteheader -
280   (%PDF-) ows OFile PDFversion write=
281   (%\347\363\317\323\n) ows
282 } bind def
283
284 % Write a cross-reference table and trailer.
285 /pdfwritexref {         % <firstobj#> <#objs> pdfwritexref -
286   (xref\n) ows
287   OFile 2 index write=only ( ) ows OFile 1 index write=
288   1 index add 1 sub 1 exch {
289     dup 0 eq {
290       pop (0000000000 65535 f \n) ows
291     } {
292       XRef exch ld_get 1000000000 add =string cvs
293       dup 0 (0) 0 get put
294       ows ( 00000 n \n) ows
295     } ifelse
296   } for
297 } bind def
298 /pdfwritetrailer {      % <trailer> pdfwritetrailer -
299   (trailer\n) ows pdfwritevalue (\n) ows
300 } bind def
301 /pdfwritestartxref {    % <startpos> pdfwritestartxref -
302   (startxref\n) ows OFile exch write=
303   (%%EOF\n) ows
304 } bind def
305
306 % ================ Top-level control ================ %
307
308 /pdfwrite {             % <file> <trailer> pdfwrite -
309   10 dict begin
310   /trailer exch def
311   /OFile exch def
312   /ToWrite 100 dict def
313   omapinit
314
315         % Write the PDF file header.
316
317   pdfwriteheader
318
319         % Write the objects.
320
321   trailer {
322     exch pop dup xcheck {       % The only executable objects are references.
323       aload pop pop pdfwriteobj
324     } {
325       pop
326     } ifelse
327   } forall
328         % Walk the object graph.
329   {
330     ToWrite dup length dup 0 eq { pop pop exit } if
331     1 sub 2 copy get 3 1 roll undef aload pop pdfwriteobj
332   } loop
333
334         % Write the xref table and trailer.
335
336   /xref OFile fileposition def
337   0 XRef ld_length 1 add pdfwritexref
338   trailer dup length 1 add dict copy
339   dup /Size XRef ld_length 1 add put pdfwritetrailer
340   xref pdfwritestartxref
341
342   end
343 } bind def
344
345 .setglobal