Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
%    Copyright (C) 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: pdfopt.ps,v 1.20 2003/06/02 19:52:58 alexcher Exp $
17
% PDF linearizer ("optimizer").
18
 
19
.currentglobal true .setglobal
20
/pdfoptdict 200 dict def
21
pdfoptdict begin
22
 
23
% This linearizer is designed for simplicity, not for performance.
24
% See the main program (the last procedure in the file) for comments
25
% describing the main processing sequence.
26
 
27
% ---------------- Utilities ---------------- %
28
 
29
% ------ Data structures ------ %
30
 
31
% Distinguish dictionaries, arrays, and everything else.
32
/ifdaelse {		% <obj> <dictproc> <arrayproc> <otherproc> ifdaelse -
33
  3 index type dup /dicttype eq {
34
    pop pop pop
35
  } {
36
    dup /arraytype ne exch /packedarraytype ne and {
37
      exch
38
    } if pop exch pop
39
  } ifelse exec
40
} bind def
41
 
42
% Implement dynamically growable arrays using a dictionary.
43
/darray {		% <size> darray <darray>
44
  dict
45
} bind def
46
/dadd {			% <darray> <value> dadd -
47
  1 index length exch put
48
} bind def
49
/daforall {		% <darray> <proc> daforall -
50
  /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx
51
 
52
} bind def
53
/dacontents {		% <darray> dacontents <array>
54
  [ exch { } daforall ]
55
} bind def
56
/dacontstring {		% <darray> dacontstring <string>
57
 
58
  dup /NullEncode filter
59
			% Stack: darray str filter
60
  3 -1 roll { 1 index exch writestring } daforall
61
  closefile
62
} bind def
63
 
64
% Force an object, mapping it if it is a reference.
65
/omforcenew {		% <obj> omforce <obj'> <notseen>
66
  dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse
67
} bind def
68
/omforce {		% <obj> omforce <obj'>
69
  omforcenew pop
70
} bind def
71
/omget {		% <dict|array> <key> omget <obj>
72
  get omforce
73
} bind def
74
% Visit an entire tree.
75
/omvisit {		% <obj> omvisit -
76
  omforcenew {
77
    { { omvisit omvisit } forall }
78
    { { omvisit } forall }
79
    { pop }
80
    ifdaelse
81
  } {
82
    pop
83
  } ifelse
84
} bind def
85
% Visit a tree, stopping at references to Page objects.
86
% (This is only needed for the OpenAction in the Catalog.)
87
/omvisitnopage {	% <obj> omvisitnopage -
88
  dup oforce dup type /dicttype eq {
89
    /Type .knownget { /Page eq } { false } ifelse
90
  } {
91
    pop false
92
  } ifelse {
93
    pop		% Page reference
94
  } {
95
    omforcenew {
96
      { { omvisitnopage omvisitnopage } forall }
97
      { { omvisitnopage } forall }
98
      { pop }
99
      ifdaelse
100
    } {
101
      pop
102
    } ifelse
103
  } ifelse
104
} bind def
105
 
106
% Collect the list of currently mapped object numbers, in order.
107
/omapped {		% - omapped <obj#s>
108
  RMap ld_length larray exch lgrowto
109
  RMap {
110
    2 index 3 1 roll 1 sub exch lput
111
  } ld_forall
112
} bind def
113
 
114
% Collect the list of object numbers passed to omap by a procedure.
115
/visited {		% <proc> visited <obj#s>
116
  false currentomap 2 .execn
117
  omapped exch setomap
118
} bind def
119
 
120
% ------ Output ------ %
121
 
122
% Provide a framework for closure-based streams.
123
.currentglobal false .setglobal
124
userdict /clostreams 20 dict put	% stream -> [data endproc]
125
.setglobal
126
% Create a closure-based stream.
127
/clostream {		% <data> <proc> <endproc> clostream <stream>
128
  2 index 3 -1 roll /exec load 3 packedarray cvx
129
  /NullEncode filter
130
		% Stack: data endproc stream
131
  clostreams 1 index 5 -2 roll 2 array astore put
132
} bind def
133
% Close a closure-based stream.
134
/closend {		% <stream> closend <result>
135
  dup closefile clostreams exch
136
  2 copy get 3 1 roll undef aload pop exec
137
} bind def
138
 
139
% Implement in-memory output streams.
140
/msproc {		% <data> <more> <accum> msproc <scratch>
141
  3 -1 roll dadd { 100 string } { () } ifelse
142
} bind def
143
/mstream {		% - mstream <mstream>
144
  10 darray {msproc} {dacontstring} clostream
145
} bind def
146
/mcontents {		% <mstream> mcontents <string>
147
  closend
148
} bind def
149
 
150
% Implement a stream that only keeps track of its position.
151
% (All streams should do this, but the PLRM doesn't require it.)
152
/posbuf 100 string def
153
/posproc {		% <data> <more> <accum> posproc <scratch>
154
 
155
  pop //posbuf
156
} bind def
157
/postream {		% - postream <postream>
158
  [0] {posproc} {0 get} clostream
159
} bind def
160
/poslength {		% <postream> poslength <pos>
161
  closend
162
} bind def
163
 
164
% Implement streams with variable-bit-width data.
165
% Note that these are dictionary objects, not stream objects.
166
/bitstream {		% <stream> bitstream <bstream>
167
  4 dict begin /S exch def /N 8 def /B 0 def
168
  currentdict end
169
} bind def
170
/bitwrite {		% <bstream> <value> <width> bitwrite -
171
  PDFOPTDEBUG { ( ) print 1 index =only (:) print dup = } if
172
  3 -1 roll begin
173
  N exch sub dup 0 ge {
174
    /N exch def N bitshift B add
175
  } {
176
    2 copy bitshift B add S exch write
177
			% Stack: value -left
178
    { 8 add dup 0 ge { exit } if
179
      2 copy bitshift 255 and S exch write
180
    } loop
181
    /N 1 index def bitshift 255 and
182
  } ifelse /B exch def
183
  end
184
} bind def
185
/bitflush {		% <bstream> bitflush -
186
  begin N 8 ne { S B write /B 0 def /N 8 def } if end
187
} bind def
188
 
189
/bwn {                  % <value> <width> bwn -
190
  2 copy                % v w v w
191
  2 exch exp ge {       % v w v>=2**w
192
    /bwn cvx /rangecheck signalerror
193
  } if
194
  bits 3 1 roll bitwrite
195
} def
196
 
197
% Capture OFile output on the temporary file, in memory, or just as a length.
198
/totemp {		% <proc> totemp <start> <end>
199
  TFile fileposition OFile
200
  /OFile TFile def 3 .execn
201
  /OFile exch def
202
  TFile fileposition
203
} bind def
204
/tomemory {		% <proc> tomemory <string>
205
  OFile /OFile mstream def 2 .execn
206
  OFile mcontents exch /OFile exch def
207
} bind def
208
/tolength {		% <proc> tolength <string>
209
  OFile /OFile postream def 2 .execn
210
  OFile poslength exch /OFile exch def
211
} bind def
212
 
213
% Copy a range of bytes from TFile to OFile.
214
/copyrange {		% <start> <end> copybytes -
215
  TFile 2 index setfileposition
216
  exch sub 1024 string exch {
217
		% Stack: buf left
218
    2 copy 1 index length .min 0 exch getinterval
219
    TFile exch readstring pop OFile exch writestring
220
    1 index length sub dup 0 le { exit } if
221
  } loop pop pop
222
} bind def
223
 
224
% Pad with blanks to a specified position.
225
/padto {		% <pos> padto -
226
  OFile fileposition sub
227
  dup 0 lt {
228
    (ERROR: file position incorrect by ) print =
229
    /padto cvx /rangecheck signalerror
230
  } {
231
    { ( ) ows } repeat
232
  } ifelse
233
} bind def
234
 
235
% ---------------- Read objects into memory ---------------- %
236
 
237
/touch {		% <object> touch -
238
  {
239
    { touch touch } forall
240
  } {
241
    dup xcheck {
242
		% Executable array, must be an indirect object.
243
      dup 0 get resolved? { pop pop } { oforce touch } ifelse
244
    } {
245
      { touch } forall
246
    } ifelse
247
  } {
248
    pop
249
  } ifdaelse
250
} bind def
251
 
252
% ---------------- Replace references with referents ---------------- %
253
 
254
/replaceable? {		% <value> replaceable? <bool>
255
  dup type /integertype eq exch xcheck not and
256
} bind def
257
/replacement {		% <obj|ref> replacement <obj'>
258
  dup oforce dup replaceable? { exch } if pop
259
} bind def
260
 
261
/replacerefs {		% <object> replacerefs <object>
262
  {
263
    dup {
264
      2 index 2 index undef
265
      exch replacement exch replacement
266
      2 index 3 1 roll put
267
    } forall
268
  } {
269
 
270
      1 index exch 2 copy get replacement put
271
    } for
272
  } {
273
  } ifdaelse
274
} bind def
275
 
276
/replaceReferences {	% - replaceReferences -
277
  Objects { replacerefs pop } lforall
278
		% Delete replaced objects.
279
 
280
    Objects 1 index lget replaceable? {
281
      PDFOPTDEBUG { (Deleting ) print dup = } if
282
      Generations 1 index 0 lput
283
    } if pop
284
  } for
285
} bind def
286
 
287
% ---------------- Create new objects ---------------- %
288
 
289
/createObjects {	% [<obj>...] createObjects <firstobj#>
290
  Objects llength dup
291
  dup 3 index length add growPDFobjects
292
		% Stack: objects objn objn
293
  3 1 roll exch {
294
    Objects 2 index 3 -1 roll lput
295
    Generations 1 index 1 lput
296
    1 add
297
  } forall pop
298
} bind def
299
 
300
% ---------------- Propagate attributes ---------------- %
301
 
302
/nopropattrs <<
303
	% Never propagate these.
304
  /Type dup /Kids dup /Count dup /Parent dup
305
	% Handle Resources specially.
306
  /Resources dup
307
>> def
308
 
309
% Merge Resources.
310
/mergeres {		% <fromdict> <todict> mergeres -
311
		% Values in todict take priority over fromdict.
312
  1 index /Resources .knownget {
313
    1 index /Resources .knownget {
314
		% Stack: fromdict todict fromres tores
315
      exch oforce exch oforce
316
		% todict's Resources may be shared, so make a copy.
317
      dup length dict .copydict
318
      exch {
319
		% Stack: fromdict todict tores' fromkey fromvalue
320
	2 index 2 index knownoget {
321
		% Stack: fromdict todict tores' fromkey fromvalue tovalue
322
	  exch oforce exch
323
		% ProcSet is an array, other types are dictionaries.
324
	  dup type /dicttype eq {
325
		% Dictionary, not ProcSet.
326
	    exch dup length 2 index length add dict .copydict .copydict
327
	  } {
328
		% Array or packed array, ProcSet.
329
		% Use dictionaries to do the merge.
330
	    dup length 2 index length add dict begin
331
	    exch { dup def } forall { dup def } forall
332
	    mark currentdict end { pop } forall .packtomark
333
	  } ifelse
334
	} if
335
	2 index 3 1 roll put
336
      } forall
337
    } if /Resources exch put pop
338
  } {
339
    pop pop
340
  } ifelse
341
} bind def
342
 
343
% Merge attributes other than Resources.
344
/mergeattrs {		% <fromdict> <todict> mergeattrs <fromdict> <todict>
345
		% Values in todict take priority over fromdict.
346
  1 index {
347
		% Stack: fromdict todict fromkey fromvalue
348
    //nopropattrs 2 index known {
349
      pop pop
350
    } {
351
      2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse
352
    } ifelse
353
  } forall
354
} bind def
355
 
356
% Propagate attributes to a subtree.
357
/proppage {		% <attrs> <subtree> proppage -
358
		% We should be able to tell when we reach a leaf
359
		% by finding a Type unequal to /Pages.  Unfortunately,
360
		% some files distributed by Adobe lack the Type key
361
		% in some of the Pages nodes!  Instead, we check for Kids.
362
  dup /Kids knownoget {
363
		% Accumulate inherited values.
364
    3 1 roll
365
		% Stack: kids attrs pagesnode
366
    dup length dict .copydict mergeattrs
367
    dup 3 1 roll mergeres
368
    exch { oforce 1 index exch proppage } forall pop
369
  } {
370
		% Merge inherited values into the leaf.
371
    mergeattrs mergeres
372
  } ifelse
373
} bind def
374
 
375
% Propagate attributes to all pages.
376
/propagateAttributes {	% - propagateAttributes -
377
 
378
} bind def
379
 
380
% ---------------- Identify document-level objects ---------------- %
381
 
382
/identifyDocumentObjects {	% - identifyDocumentObjects <obj#s>
383
  {
384
    Trailer /Root omget
385
    dup /PageMode .knownget { omvisit } if
386
	% Don't allow omvisit to trace references to Page objects.
387
    dup /OpenAction .knownget { omvisitnopage } if
388
    Trailer /Encrypt .knownget { omvisit } if
389
    dup /Threads .knownget {
390
      omforce { omvisit } forall
391
    } if
392
    dup /AcroForm .knownget { omvisit } if
393
    pop
394
  } visited
395
} bind def
396
 
397
% ---------------- Identify the objects of each page ---------------- %
398
 
399
/identifyfont {		% <fontref> identifyfont -
400
  omforce {
401
    exch /FontDescriptor eq {
402
      omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse
403
      exch {
404
	exch dup dup /FontFile eq exch /FontFile2 eq or
405
	exch /FontFile3 eq or 2 index and {
406
	  fontfiles exch dadd
407
	} {
408
	  omvisit
409
	} ifelse
410
      } forall pop
411
    } {
412
      omvisit
413
    } ifelse
414
  } forall
415
} bind def
416
 
417
% Collect all the objects referenced from a page.  The first object number
418
% (which may not be the smallest one) is that of the page object itself.
419
/identifyPageObjects {	% <extra> <page#> identifyPageObjects <obj#s>
420
  PDFOPTDEBUG {
421
    (%Objects for page: ) print dup =
422
  } if
423
  pdffindpageref
424
  dup 0 get 3 1 roll
425
  4 dict begin
426
  /images 10 darray def
427
  /fontfiles 10 darray def
428
  {
429
    omforce
430
		% Stack: pageobj# extra page
431
		% Visit any extra objects if applicable.
432
    exch omvisit
433
		% Visit Annots, if any.
434
		% We don't try to defer the drawing information.
435
    dup /Annots .knownget { omvisit } if
436
		% Visit beads.
437
    dup /B .knownget { omvisit } if
438
		% Visit resources dictionaries.
439
    dup /Resources .knownget {
440
      omforce dup {
441
		% Visit the first-level Resource dictionaries.
442
	omforce pop pop
443
      } forall {
444
		% Visit the resources themselves.
445
		% Skip Image XObjects, and FontFile streams if the
446
		% FontDescriptor Flags have bit 6 set.
447
		% We don't try to visit the resources in the order in which
448
		% the Contents stream(s) reference(s) them.
449
	exch dup /XObject eq {
450
	  pop oforce {
451
	    dup oforce /Subtype get /Image eq {
452
	      images exch dadd
453
	    } {
454
	      omvisit
455
	    } ifelse pop
456
	  } forall
457
	} {
458
	  /Font eq {
459
	    oforce { identifyfont pop } forall
460
	  } {
461
	    oforce omvisit
462
	  } ifelse
463
	} ifelse
464
      } forall
465
    } if
466
		% Visit the Contents stream(s).
467
    dup /Contents .knownget { omvisit } if
468
		% Visit Image XObjects.  We don't try to visit them in
469
		% reference order.
470
    images { omvisit } daforall
471
		% Visit FontFile streams.  We don't try to visit them in
472
		% reference order.
473
    fontfiles { omvisit } daforall
474
    pop
475
  } visited end
476
		% Stack: pageobj# obj#s_larray
477
  [ 3 1 roll {
478
    2 copy eq { pop } { exch } ifelse
479
  } lforall counttomark 1 roll ]
480
  PDFOPTDEBUG {
481
    (%Objects = ) print dup === flush
482
  } if
483
} bind def
484
 
485
% Identify the objects of the first page.
486
/identifyFirstPageObjects {	% - identifyFirstPageObjects <obj#s>
487
  Trailer /Root oget null
488
  1 index /PageMode knownoget {
489
    /UseOutlines eq {
490
      1 index /Outlines knownoget { exch pop } if
491
    } if
492
  } if exch pop
493
  1 identifyPageObjects
494
} bind def
495
 
496
% Identify the non-shared objects of the other pages, and the shared objects.
497
% Note that the page objects themselves may appear to be shared, because of
498
% references from Dest entries in annotations, but they must be treated as
499
% non-shared.  Note also that some objects referenced on the first page may
500
% also be referenced from other pages.
501
/identifyOtherPageObjects {	% - identifyOtherPageObjects [<pageobj#s> ...]
502
				%   <sharedobj#s>
503
  4 dict begin
504
  /marks lstring Objects llength lgrowto def
505
		% Collect objects of other pages and identify sharing.
506
  [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
507
  dup {
508
    { marks exch 2 copy lget 1 add 254 .min lput } forall
509
  } forall
510
		% Mark document-level and first page objects.
511
  CatalogNs { marks exch 255 lput } lforall
512
  FirstPageNs { marks exch 255 lput } forall
513
		% Mark the page objects themselves as non-shared.
514
  dup {
515
 
516
  } forall
517
		% Collect the non-shared objects of each page.
518
  dup
519
  [ exch {
520
    [ exch {
521
      marks 1 index lget 1 ne { pop } if
522
    } forall ]
523
  } forall ]
524
                % Collect the shared objects of each page.
525
  exch
526
  [ exch {
527
    [ exch {
528
      marks 1 index lget dup 1 le exch 255 eq or { pop } if
529
    } forall ]
530
  } forall ]
531
 
532
                % Collect the shared objects.
533
  [ 1 1 marks llength 1 sub {
534
    marks 1 index lget dup 1 le exch 255 eq or { pop } if
535
  } for ]
536
 
537
  end
538
} bind def
539
 
540
% Identify objects not associated with any page.
541
/identifyNonPageObjects {	% - identifyNonPageObjects <obj#s>
542
  4 dict begin
543
  /marks lstring Objects llength lgrowto def
544
 
545
  LPDictN     marks exch 1 lput
546
  PHSN        marks exch 1 lput
547
  CatalogNs   { marks exch 1 lput } lforall
548
  FirstPageNs { marks exch 1 lput } forall
549
  SharedNs    { marks exch 1 lput } forall
550
  OtherPageNs { { marks exch 1 lput } forall } forall
551
 
552
	%****** PUT THESE IN A REASONABLE ORDER ******
553
  /npobj larray
554
 
555
  1 1 Objects llength 1 sub {
556
    marks 1 index lget 0 eq {
557
      Generations exch lget 0 ne { 1 add } if
558
    } {
559
      pop
560
    } ifelse
561
  } for 
562
  lgrowto def
563
 
564
 
565
  1 1 Objects llength 1 sub {
566
    marks 1 index lget 0 eq {
567
                                          % i
568
      Generations 1 index lget 0 ne {
569
                                          % i
570
        npobj 2 index                     % i nobj 0
571
        3 -1 roll                         % nobj 0 i
572
        lput 1 add
573
      } {
574
        pop
575
      } ifelse
576
    } {
577
      pop
578
    } ifelse
579
  } for 
580
  pop
581
 
582
  npobj
583
  end
584
} bind def
585
 
586
% ---------------- Assign object numbers ---------------- %
587
 
588
% Assign object numbers to all objects that will be copied.
589
% Return the first (translated) object number in the First Page xref table.
590
/assignObjectNumbers {		% - assignObjectNumbers -
591
  OtherPageNs { { omap pop } forall } forall
592
  SharedNs { omap pop } forall
593
  NonPageNs { omap pop } lforall
594
		% Assign object numbers for the First Page xref table last.
595
  LPDictN omap	% don't pop, this is the return value
596
  CatalogNs { omap pop } lforall
597
  FirstPageNs { omap pop } forall
598
  PHSN omap pop
599
} bind def
600
 
601
% ---------------- Create the LPDict ---------------- %
602
 
603
% Create the contents of the LPDict.
604
/createLPDict {			% <phsstart> <phsend> <firstpageend>
605
				%   <xref0start> <filelength> createLPDict -
606
  LPDict
607
  dup /Linearized 1 put
608
  dup /L 4 -1 roll put		% filelength
609
  dup /T 4 -1 roll put		% xref0start
610
  dup /E 4 -1 roll put		% firstpageend
611
  dup /H 5 -2 roll 1 index sub 2 array astore put	% phsstart, end-start
612
  dup /O 1 pdffindpageref 0 get omap put
613
  /N pdfpagecount put
614
} bind def
615
 
616
% ---------------- Adjust object positions ---------------- %
617
 
618
/adjustObjectPositions {	% <boundary> <deltabelow> <deltaabove>
619
				%   adjustObjectPositions -
620
	% Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
621
	% We handle the first two as special cases.
622
  XRef {
623
		% Stack: bdy below above key loc
624
    dup 5 index ge { 2 } { 3 } ifelse index add
625
    XRef 3 1 roll ld_put
626
  } ld_forall pop pop pop
627
  XRef LPDictN omap HeaderLength ld_put
628
  XRef PHSN omap PHSStart ld_put
629
} bind def
630
 
631
% ---------------- Write the output file ---------------- %
632
 
633
% Write objects identified by object number.
634
/writeobjn {		% <obj#> writeobjn -
635
  Generations 1 index lget pdfwriteobj
636
} bind def
637
/writeobjns {		% <obj#s> writeobjns -
638
  { writeobjn } forall
639
} bind def
640
/lwriteobjns {		% <obj#s> writeobjns -
641
  { writeobjn } lforall
642
} bind def
643
 
644
% Write a part of the output file.
645
/writePart {		% <proc> <label> writePart -
646
  PDFOPTDEBUG {
647
    dup print ( count=) print count =only ( start=) print
648
    OFile { .fileposition } stopped { pop (???) } if =
649
    2 .execn
650
    print ( end=) print
651
    OFile { .fileposition } stopped { pop (???) } if =
652
  } {
653
    pop exec
654
  } ifelse
655
} bind def
656
 
657
% Write the header.
658
/writePart1 {		% - writePart1 -
659
  {
660
    pdfwriteheader
661
  } (part1) writePart
662
} bind def
663
 
664
% Write the linearization parameters dictionary.
665
/writePart2 {		% - writePart2 -
666
  {
667
    LPDictN writeobjn
668
  } (part2) writePart
669
} bind def
670
 
671
% Write the First Page xref table and trailer.
672
% Free variables: FirstPageXN.
673
/writePart3 {		% <xrefstart> writePart3 -
674
  {
675
    FirstPageXN NObjects 1 add 1 index sub pdfwritexref
676
    Trailer dup length 1 add dict copy
677
    dup /Size NObjects 1 add put
678
    dup /Prev 4 -1 roll put
679
    pdfwritetrailer
680
 
681
  } (part3) writePart
682
} bind def
683
 
684
% Write the Catalog and other required document-level objects.
685
% Free variables: CatalogNs.
686
/writePart4 {		% - writePart4 -
687
  {
688
    CatalogNs lwriteobjns
689
  } (part4) writePart
690
} bind def
691
 
692
% Write the Primary Hint Stream.
693
/writePart5 {		% - writePart5 -
694
  {
695
    PHSN writeobjn
696
  } (part5) writePart
697
} bind def
698
 
699
% Write the First Page's objects.
700
% Free variables: FirstPageNs.
701
/writePart6 {		% - writePart6 -
702
  {
703
    FirstPageNs writeobjns
704
  } (part6) writePart
705
} bind def
706
 
707
% Write the objects of other pages (Page + non-shared objects).
708
% Free variables: OtherPageNs.
709
/writePart7 {		% - writePart7 <lengths>
710
  {
711
    [ OtherPageNs {
712
      OFile fileposition exch
713
      writeobjns OFile fileposition exch sub
714
    } forall ]
715
  } (part7) writePart
716
} bind def
717
 
718
% Write the shared objects of other pages.
719
% Free variables: SharedNs.
720
/writePart8 {		% - writePart8 -
721
  {
722
    SharedNs writeobjns
723
  } (part8) writePart
724
} bind def
725
 
726
% Write the other objects not associated with pages.
727
% Free variables: NonPageNs.
728
/writePart9 {		% - writePart9 -
729
  {
730
    NonPageNs { writeobjn } lforall
731
  } (part9) writePart
732
} bind def
733
 
734
% Write the main xref table and trailer.
735
% Free variables: FirstPageXN.
736
/writePart11xref {	% writePart11 -
737
  {
738
 
739
  } (part11xref) writePart
740
} bind def
741
/writePart11rest {	% <part3start> writePart11rest -
742
  {
743
    << /Size FirstPageXN >> pdfwritetrailer
744
    pdfwritestartxref
745
  } (part11rest) writePart
746
} bind def
747
 
748
% ---------------- Write hint tables ---------------- %
749
 
750
/bitsneeded {		% <maxvalue> bitsneeded <#bits>
751
 
752
} bind def
753
 
754
% Find the start and end of objects in the output.
755
/omstart {		% <obj#> omstart <pos>
756
  PDFOPTDEBUG { (start\() print dup =only } if
757
  omap
758
  PDFOPTDEBUG { (=>) print dup =only } if
759
  XRef exch ld_get
760
  PDFOPTDEBUG { (\) = ) print dup = } if
761
} bind def
762
/omend {		% <obj#> omend <pos>
763
	% The end of an object is the start of the next object.
764
	% The caller must be sure that this object is not the last one
765
	% in part 9.
766
  PDFOPTDEBUG { (end\() print dup =only } if
767
  omap
768
  PDFOPTDEBUG { (=>) print dup =only } if
769
  1 add
770
	% Check that the requested object wasn't the last one in part 6:
771
	% the next object in the output file is the first in part 7.
772
  PHSN omap 1 index eq { pop 1 } if
773
  XRef exch ld_get
774
  PDFOPTDEBUG { (\) = ) print dup = } if
775
} bind def
776
/omlength {		% <obj#> omlength <length>
777
  dup omend exch omstart sub
778
} bind def
779
 
780
% Find the Contents of a page.
781
/contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true
782
		   % <pagedict> contentsobjects false
783
  /Contents .knownget {
784
    dup oforce                   % ref []
785
    dup type /dicttype eq {
786
      pop 0 get dup true         % ref ref
787
    } {
788
      exch pop                   % []
789
      dup length 0 ne {
790
        dup 0 get 0 get          % [] 1st
791
        exch dup                 % 1st [] [] 
792
        length 1 sub get 0 get   % 1st last 
793
        true
794
      } {
795
        pop false
796
      } ifelse
797
    } ifelse
798
  } {
799
    false
800
  } ifelse
801
} bind def
802
 
803
/contentsstart {	% <pagedict> contentsstart <pos> true
804
			% <pagedict> contentsstart false
805
  contentsobjects { pop omstart true } { false } ifelse
806
} bind def
807
 
808
/contentslength {	% <pagedict> contentslength <length>
809
  contentsobjects { omend exch omstart sub } { 0 } ifelse
810
} bind def
811
 
812
 
813
/writePageOffsetHints {
814
  PDFOPTDEBUG { /writePageOffsetHints == } if
815
  20 dict begin
816
  /bits OFile bitstream def
817
 
818
	% Calculate least length of a page.
819
  FirstPageLength OtherPageLengths { .min } forall
820
  /minpl exch def
821
 
822
        % Calculate least contents length.
823
  FirstPageNs 0 get Objects exch lget contentslength
824
  OtherPageNs { 0 get Objects exch lget contentslength .min } forall
825
  /mincl exch def
826
 
827
	% The Adobe documentation says that all versions of Acrobat
828
	% require item 8 (mincl) to be zero.  Patch this here.
829
  /mincl 0 def
830
 
831
	% Calculate bits needed to represent greatest page length.
832
  FirstPageLength OtherPageLengths { .max } forall
833
  minpl sub bitsneeded /maxplbits exch def
834
	% Calculate bits needed to represent the greatest Contents length.
835
  FirstPageNs 0 get Objects exch lget contentslength
836
  OtherPageNs { 0 get Objects exch lget contentslength .max } forall
837
  mincl sub bitsneeded /maxclbits exch def
838
 
839
	% Per Adobe documentation, Acrobat requires that item 5 (maxplbits)
840
	% be equal to item 9 (maxclbits).  Set both to the max of the two.
841
  maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def
842
 
843
        % Mapping from object number to shared object reference
844
  /shared_id_dict FirstPageNs length SharedNs length add dict begin
845
 
846
    SharedNs { 1 index def 1 add } forall
847
  pop
848
  currentdict end def 
849
 
850
                % Table F.3 Page offset hint table, header section
851
 
852
                % 1: Least number of objects in a page:
853
  FirstPageNs length OtherPageNs { length .min } forall
854
  /minnop 1 index def 32 bwn
855
		% 2: Location of first page's Page object:
856
  FirstPageNs 0 get omap XRef exch ld_get 32 bwn
857
		% 3: Bits needed to represent greatest # of objects in a page:
858
  FirstPageNs length OtherPageNs { length .max } forall
859
  minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
860
		% 4: Least length of a page:
861
  minpl 32 bwn
862
		% 5: Bits needed to represent the greatest page length:
863
  maxplbits 16 bwn
864
		% 6: Least start of Contents offset:
865
 
866
  /minsco 1 index def 32 bwn
867
		% 7: Bits needed to represent the greatest start of Contents
868
		% offset:
869
 
870
  /maxscobits 1 index def 16 bwn
871
		% 8: Least contents length:
872
  mincl 32 bwn
873
		% 9: Bits needed to represent the greatest Contents length:
874
  maxclbits 16 bwn
875
		% 10: Bits needed to represent the greatest number of Shared
876
   		% Object references:
877
  FirstPageNs length SharedPageNs { length .max } forall bitsneeded
878
  /maxsorbits 1 index def 16 bwn
879
		% 11: Bits needed to identify a Shared Object:
880
  FirstPageNs length SharedNs length add bitsneeded
881
  /sobits 1 index def 16 bwn
882
		% 12: Bits needed to represent numerator of fraction:
883
  2
884
  /numfbits 1 index def 16 bwn
885
		% 13: Denominator of fraction:
886
  1
887
  /denf 1 index def 16 bwn
888
 
889
                % Table F.4 Page offset hint table, per-page entry
890
 
891
                % 1: Number of objects in pages:
892
  FirstPageNs length minnop sub maxnopbits bwn
893
  OtherPageNs {
894
    length minnop sub maxnopbits bwn
895
  } forall
896
  bits bitflush
897
 
898
		% 2: Total length of pages in bytes;
899
  FirstPageLength minpl sub maxplbits bwn
900
  OtherPageLengths {
901
    minpl sub maxplbits bwn
902
  } forall
903
  bits bitflush
904
 
905
		% 3: Number of shared objects referenced from page:
906
  FirstPageNs length maxsorbits bwn 
907
  SharedPageNs { length maxsorbits bwn } forall
908
  bits bitflush
909
                % 4: A shared object identifier:
910
  FirstPageNs { shared_id_dict exch get sobits bwn } forall
911
  SharedPageNs {
912
    { shared_id_dict exch get sobits bwn
913
    } forall
914
  } forall
915
  bits bitflush
916
 
917
                % 5: Numerator of fractional position for each shared object:
918
  FirstPageNs { pop 0 numfbits bwn  } forall
919
  SharedPageNs {
920
    { pop 0 numfbits bwn 
921
    } forall
922
  } forall
923
  bits bitflush
924
 
925
		% 6: Contents offsets:
926
                % Following Implementation Note 133 section 6 is empty.
927
  maxscobits 0 gt {
928
    [FirstPageNs OtherPageNs aload pop] {
929
 
930
       maxscobits bwn
931
    } forall
932
    bits bitflush
933
  } if
934
 
935
                % 7: Contents lengths:
936
  [FirstPageNs OtherPageNs aload pop] {
937
 
938
  } forall
939
  bits bitflush
940
 
941
  end
942
 
943
} bind def
944
 
945
/writeSharedObjectHints {
946
  PDFOPTDEBUG { /writeSharedObjectHints == } if
947
  20 dict begin
948
  /bits OFile bitstream def
949
  /obj_count SharedNs length FirstPageNs length add def
950
 
951
 		% Table F.5 Shared object hint table, header section
952
 
953
              	% 1: Object number of first object in Shared Objects section
954
 
955
		% 2: Location of first object in Shared Objects section:
956
		% If there are no shared objects,
957
		% Acrobat sets this to the location of linearization
958
		% parameters object (the very first object).
959
  { pdfwriteheader } tomemory length 32 bwn
960
		% 3: Number of Shared Object entries for first page:
961
  FirstPageNs length 32 bwn
962
		% 4: Number of Shared Object entries for Shared Objects
963
		% section
964
  obj_count 32 bwn
965
		% 5: Bits needed to represent the greatest number of objects
966
		% in a shared object group (always 0, because all groups
967
		% have only 1 object):
968
 
969
		% 6: Least length of a Shared Object Group in bytes:
970
  16#7fffffff FirstPageNs { omlength .min } forall
971
                 SharedNs { omlength .min } forall
972
  /minsol 1 index def 32 bwn
973
		% 7: Bits needed to represent the greatest length of a
974
		% Shared Object Group:
975
 
976
       SharedNs { omlength .max } forall
977
  minsol sub bitsneeded
978
  /maxsolbits 1 index def 16 bwn
979
 
980
                % Table F.6 Shared object hint table, shared object group entry
981
 
982
                % 1: Lengths of shared object groups:
983
  FirstPageNs { omlength minsol sub maxsolbits bwn } forall
984
     SharedNs { omlength minsol sub maxsolbits bwn } forall
985
  bits bitflush
986
		% 2: MD5 flag:
987
  obj_count { 0 1 bwn } repeat
988
  bits bitflush
989
                % 3: No MD5 shared object signatures.
990
 
991
                % 4: No number_number_of_objects_in_the_group - 1
992
  end
993
} bind def
994
 
995
% ---------------- Main program ---------------- %
996
 
997
/pdfOptimize {		% <infile> <outfile> pdfOptimize -
998
  realtime 3 1 roll
999
  exch pdfdict begin pdfopenfile dup begin
1000
  40 dict begin
1001
  /IDict exch def
1002
  /OFile exch def
1003
  /starttime exch def
1004
  /now {
1005
    QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
1006
  } def
1007
  omapinit
1008
 
1009
	% Create and open a temporary file.
1010
	% Because of .setsafe, we have to open it as read-write, rather than
1011
	% opening for writing, then closing it and reopening it for reading.
1012
 
1013
  null (w+) .tempfile /TFile exch def /TFileName exch def
1014
  .setsafe
1015
 
1016
	% Read all objects into memory.
1017
 
1018
  Trailer touch
1019
  (Read objects) now
1020
 
1021
        % Encrypted files are not yet supported.
1022
  Trailer /Encrypt known {
1023
    (ERROR: Encrypted files are not yet supported.) = flush
1024
    /pdfOptimize cvx /limitcheck signalerror
1025
  } if
1026
 
1027
	% Replace indirect references to numbers.  This is needed
1028
	% for the Length of streams, and doesn't hurt anything else.
1029
 
1030
  replaceReferences
1031
  (Replaced references) now
1032
 
1033
	% Create the two new objects: the linearization parameter
1034
	% dictionary, and the Primary Hint Stream.
1035
 
1036
  /LPDict 10 dict def
1037
  /PHS 10 dict cvx def		% executable = stream
1038
  [LPDict PHS] createObjects
1039
  /LPDictN 1 index def 1 add
1040
  /PHSN exch def
1041
  PDFOPTDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if
1042
 
1043
	% Count the number of objects in the output.
1044
 
1045
 
1046
    Generations exch lget 0 ne { 1 add } if
1047
  } for
1048
  /NObjects exch def
1049
  QUIET not { NObjects =only ( objects total) = flush } if
1050
 
1051
	% Propagate inherited attributes down the page tree.
1052
 
1053
  propagateAttributes
1054
  (Propagated attributes) now
1055
 
1056
	% Identify the document-level objects (part 4).
1057
 
1058
  identifyDocumentObjects /CatalogNs exch def
1059
  QUIET not { CatalogNs === flush } if
1060
  (Identified Catalog) now
1061
 
1062
  	% Identify the first page's objects (part 6),
1063
	% including the Outlines tree if appropriate.
1064
 
1065
  pdfopencache
1066
  /FirstPageNs identifyFirstPageObjects def
1067
  QUIET not { FirstPageNs === flush } if
1068
  (Identified first page) now
1069
 
1070
	% Identify shared vs. non-shared objects for remaining pages
1071
	% (parts 7 and 8).
1072
 
1073
  identifyOtherPageObjects
1074
  /SharedNs exch def
1075
  /SharedPageNs exch def
1076
  /OtherPageNs exch def
1077
  QUIET not { OtherPageNs === flush SharedNs === flush } if
1078
  (Identified other pages) now
1079
 
1080
	% Identify objects not associated with any page (part 9).
1081
 
1082
  /NonPageNs identifyNonPageObjects def
1083
  QUIET not { NonPageNs { === } forall flush } if
1084
  (Identified non-pages) now
1085
 
1086
	% Assign final object numbers to all the objects.
1087
	% (The omap is currently empty.)
1088
 
1089
  /FirstPageXN assignObjectNumbers def
1090
  (Assigned objects #s) now
1091
 
1092
	% Write the document-level objects (part 4).
1093
 
1094
  { writePart4 } totemp
1095
  /CatalogTempEnd exch def /CatalogTempStart exch def
1096
  (Wrote Catalog) now
1097
 
1098
	% Write the first page's objects (part 6).
1099
 
1100
  { writePart6 } totemp
1101
  /FirstPageTempEnd exch def /FirstPageTempStart exch def
1102
  (Wrote first page) now
1103
 
1104
	% Write the non-shared objects for other pages (part 7).
1105
 
1106
  { writePart7 /OtherPageLengths exch def } totemp
1107
  /OtherPageTempEnd exch def /OtherPageTempStart exch def
1108
  (Wrote other pages) now
1109
 
1110
	% Write the shared objects for other pages (part 8).
1111
 
1112
  { writePart8 } totemp
1113
  /SharedTempEnd exch def /SharedTempStart exch def
1114
  (Wrote shared objects) now
1115
 
1116
	% Write the objects not associated with pages (part 9).
1117
 
1118
  { writePart9 } totemp
1119
  /NonPageTempEnd exch def /NonPageTempStart exch def
1120
 
1121
	% Compute conservative lengths of parts 2,3,5,11 of the output.
1122
	% It's OK for these to be too large, but not too small.
1123
 
1124
  % Make dummy XRef entres for LPDict and PHS.
1125
  XRef LPDictN omap 0 ld_put
1126
  XRef PHSN omap 0 ld_put
1127
 
1128
  /HeaderLength {	% this is exact
1129
    writePart1			% part 1
1130
  } tolength def
1131
  /CatalogLength	% this is exact
1132
    CatalogTempEnd CatalogTempStart sub def	% part 4
1133
  /FirstPageLength	% this is exact
1134
    FirstPageTempEnd FirstPageTempStart sub def	% part 6
1135
  /OtherObjectsLength	% this is exact
1136
    NonPageTempEnd OtherPageTempStart sub def	% parts 7,8,9
1137
  /ObjectsLength	% this is exact
1138
    CatalogLength FirstPageLength add OtherObjectsLength add def
1139
  /XrefLength {			% part 11
1140
	% The LPDict must end within the first 1024 bytes,
1141
	% so the start of the FirstPage xref table can't exceed 1024.
1142
    writePart11xref 1024 writePart11rest
1143
  } tolength def
1144
  /NominalFileLength 	% Make a generous allowance for parts 2,3,5.
1145
    HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def
1146
  /FirstPageXrefLength {	% part 3
1147
    NominalFileLength writePart3
1148
  } tolength def
1149
  /LPDictLength {		% part 2
1150
    NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2
1151
  } tolength def
1152
 
1153
	% Compute a few additional values from the above.
1154
 
1155
  /XrefBeginLength {
1156
    (xref\n0 ) ows
1157
    OFile FirstPageXN write=
1158
  } tolength def
1159
  HeaderLength LPDictLength add
1160
  /FirstPageXrefStart 1 index def
1161
  FirstPageXrefLength add
1162
  /CatalogStart 1 index def
1163
  CatalogLength add		% phsstart
1164
  /PHSStart exch def
1165
 
1166
	% Adjust the object positions ignoring PHS.
1167
	% (Writing the PHS needs these.)
1168
 
1169
 
1170
  % Make a temporary XRef entry for the PHS, for the benefit of omend.
1171
  XRef PHSN omap CatalogStart ld_put
1172
  (Adjusted positions) now
1173
 
1174
        % Construct the hint tables (part 5).
1175
 
1176
  { writePageOffsetHints } totemp
1177
  pop /PHSTempStart exch def
1178
  { writeSharedObjectHints } totemp
1179
  exch PHSTempStart sub PHS /S 3 -1 roll put
1180
  PHSTempStart sub /PHSTempLength exch def
1181
  (Wrote hints) now
1182
 
1183
  % Prepare to read TFile.
1184
%  TFile closefile
1185
%  /TFile TFileName (r) file def
1186
 
1187
  PHS
1188
    dup /File TFile put
1189
    dup /FilePosition PHSTempStart put
1190
    dup /Length PHSTempLength put
1191
  pop
1192
  /PHSLength { writePart5 } tolength def
1193
 
1194
	% Construct the linearization parameter dictionary (part 2).
1195
 
1196
  PHSStart
1197
  dup PHSLength add		% phsend
1198
  /FirstPageStart 1 index def
1199
  dup FirstPageLength add	% firstpageend
1200
  dup OtherObjectsLength add
1201
  /XrefStart 1 index def
1202
  XrefBeginLength add		% xref0start
1203
  dup XrefBeginLength sub XrefLength add	% fileend
1204
	% Because of a bug, Acrobat Reader doesn't recognize any file
1205
	% shorter than 1K as linearized.  Patch this here.
1206
  1024 .max
1207
  /FileLength 1 index def
1208
  createLPDict
1209
 
1210
	% Adjust the object positions again, taking the PHS into account.
1211
 
1212
  PHSStart 0 PHSLength adjustObjectPositions
1213
  (Readjusted positions) now
1214
 
1215
	% Finally, write the output file.
1216
 
1217
  writePart1
1218
  writePart2
1219
  FirstPageXrefStart padto
1220
  XrefStart writePart3
1221
  CatalogStart padto
1222
  CatalogTempStart CatalogTempEnd copyrange	% part 4
1223
  writePart5
1224
  FirstPageStart padto
1225
  FirstPageTempStart NonPageTempEnd copyrange	% parts 6,7,8,9
1226
  % No Overflow Hint Stream (part 10).
1227
  XrefStart padto
1228
  writePart11xref
1229
  { FirstPageXrefStart writePart11rest } tomemory
1230
  FileLength 1 index length sub padto ows
1231
  (Wrote output file) now
1232
 
1233
	% Wrap up.
1234
 
1235
  TFile closefile TFileName deletefile
1236
  end		% temporary dict
1237
  end		% IDict
1238
} bind def
1239
 
1240
end			% pdfoptdict
1241
.setglobal
1242
 
1243
% Check for command line arguments.
1244
[ shellarguments {
1245
  ] dup length 2 eq {
1246
	% Load the pdfwrite utilities if necessary.
1247
    /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
1248
    save exch
1249
    aload pop exch (r) file exch (w) file
1250
    3000000 setvmthreshold
1251
 
1252
    pdfoptdict begin pdfOptimize end
1253
    restore
1254
  } {
1255
    (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
1256
  } ifelse
1257
} {
1258
  pop
1259
} ifelse