pdfopt.ps 31 KB

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