pdfopt.ps 35 KB

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