星火微课系统客户端
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

pdfopt.ps 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  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 9390 2009-01-23 19:04:40Z alexcher $
  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 dup //null ne { { omvisitnopage } forall } { pop } ifelse
  356. } if
  357. dup /AcroForm .knownget { omvisitnopage } 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. /filter_params 10 darray def
  389. /images 10 darray def
  390. /fontfiles 10 darray def
  391. {
  392. omforce
  393. % Stack: pageobj# extra page
  394. % Visit any extra objects if applicable.
  395. exch omvisitnopage
  396. % Visit Annots, if any.
  397. % We don't try to defer the drawing information.
  398. dup /Annots .knownget { omvisitnopage } if
  399. % Visit beads.
  400. dup /B .knownget { omvisitnopage } if
  401. % Visit resources dictionaries.
  402. dup /Resources .knownget {
  403. omforce dup {
  404. % Visit the first-level Resource dictionaries.
  405. omforce pop pop
  406. } forall {
  407. % Visit the resources themselves.
  408. % Skip Image XObjects, and FontFile streams if the
  409. % FontDescriptor Flags have bit 6 set.
  410. % We don't try to visit the resources in the order in which
  411. % the Contents stream(s) reference(s) them.
  412. exch dup /XObject eq {
  413. pop oforce {
  414. dup oforce /Subtype get /Image eq {
  415. dup oforce /DecodeParms .knownget {
  416. oforce {
  417. exch /JBIG2Globals eq {
  418. filter_params exch dadd
  419. } {
  420. pop
  421. } ifelse
  422. } forall
  423. } if
  424. images exch dadd
  425. } {
  426. omvisit
  427. } ifelse pop
  428. } forall
  429. } {
  430. /Font eq {
  431. oforce { identifyfont pop } forall
  432. } {
  433. oforce omvisit
  434. } ifelse
  435. } ifelse
  436. } forall
  437. } if
  438. % Visit the Contents stream(s).
  439. dup /Contents .knownget { omvisit } if
  440. % Visit Image XObjects. We don't try to visit them in
  441. % reference order.
  442. filter_params { omvisit } daforall
  443. images { omvisit } daforall
  444. % Visit FontFile streams. We don't try to visit them in
  445. % reference order.
  446. fontfiles { omvisit } daforall
  447. pop
  448. } visited end
  449. % Stack: pageobj# obj#s_larray
  450. [ 3 1 roll {
  451. 2 copy eq { pop } { exch } ifelse
  452. } lforall counttomark 1 roll ]
  453. PDFOPTDEBUG {
  454. (%Objects = ) print dup === flush
  455. } if
  456. } bind def
  457. % Identify the objects of the first page.
  458. /identifyFirstPageObjects { % - identifyFirstPageObjects <obj#s>
  459. Trailer /Root oget null
  460. 1 index /PageMode knownoget {
  461. /UseOutlines eq {
  462. 1 index /Outlines knownoget { exch pop } if
  463. } if
  464. } if exch pop
  465. 1 identifyPageObjects
  466. } bind def
  467. % Identify the non-shared objects of the other pages, and the shared objects.
  468. % Note that the page objects themselves may appear to be shared, because of
  469. % references from Dest entries in annotations, but they must be treated as
  470. % non-shared. Note also that some objects referenced on the first page may
  471. % also be referenced from other pages.
  472. /identifyOtherPageObjects { % - identifyOtherPageObjects [<pageobj#s> ...]
  473. % <sharedobj#s>
  474. 4 dict begin
  475. /marks lstring Objects llength lgrowto def
  476. % Collect objects of other pages and identify sharing.
  477. [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
  478. dup {
  479. { marks exch 2 copy lget 1 add 254 .min lput } forall
  480. } forall
  481. % Mark document-level and first page objects.
  482. CatalogNs { marks exch 255 lput } lforall
  483. FirstPageNs { marks exch 255 lput } forall
  484. % Mark the page objects themselves as non-shared.
  485. dup {
  486. 0 get marks exch 1 lput
  487. } forall
  488. % Collect the non-shared objects of each page.
  489. dup
  490. [ exch {
  491. [ exch {
  492. marks 1 index lget 1 ne { pop } if
  493. } forall ]
  494. } forall ]
  495. % Collect the shared objects of each page.
  496. exch
  497. [ exch {
  498. [ exch {
  499. marks 1 index lget dup 1 le exch 255 eq or { pop } if
  500. } forall ]
  501. } forall ]
  502. % Collect the shared objects.
  503. [ 1 1 marks llength 1 sub {
  504. marks 1 index lget dup 1 le exch 255 eq or { pop } if
  505. } for ]
  506. end
  507. } bind def
  508. % Identify objects not associated with any page.
  509. /identifyNonPageObjects { % - identifyNonPageObjects <obj#s>
  510. 4 dict begin
  511. /marks lstring Objects llength lgrowto def
  512. LPDictN marks exch 1 lput
  513. PHSN marks exch 1 lput
  514. CatalogNs { marks exch 1 lput } lforall
  515. FirstPageNs { marks exch 1 lput } forall
  516. SharedNs { marks exch 1 lput } forall
  517. OtherPageNs { { marks exch 1 lput } forall } forall
  518. %****** PUT THESE IN A REASONABLE ORDER ******
  519. /npobj larray
  520. 0
  521. 1 1 Objects llength 1 sub {
  522. marks 1 index lget 0 eq {
  523. Generations exch lget 0 ne { 1 add } if
  524. } {
  525. pop
  526. } ifelse
  527. } for
  528. lgrowto def
  529. 0
  530. 1 1 Objects llength 1 sub {
  531. marks 1 index lget 0 eq {
  532. % i
  533. Generations 1 index lget 0 ne {
  534. % i
  535. npobj 2 index % i nobj 0
  536. 3 -1 roll % nobj 0 i
  537. lput 1 add
  538. } {
  539. pop
  540. } ifelse
  541. } {
  542. pop
  543. } ifelse
  544. } for
  545. pop
  546. npobj
  547. end
  548. } bind def
  549. % ---------------- Assign object numbers ---------------- %
  550. % Assign object numbers to all objects that will be copied.
  551. % Return the first (translated) object number in the First Page xref table.
  552. /assignObjectNumbers { % - assignObjectNumbers -
  553. OtherPageNs { { omap pop } forall } forall
  554. SharedNs { omap pop } forall
  555. NonPageNs { omap pop } lforall
  556. % Assign object numbers for the First Page xref table last.
  557. LPDictN omap % don't pop, this is the return value
  558. CatalogNs { omap pop } lforall
  559. FirstPageNs { omap pop } forall
  560. PHSN omap pop
  561. } bind def
  562. % ---------------- Create the LPDict ---------------- %
  563. % Create the contents of the LPDict.
  564. /createLPDict { % <phsstart> <phsend> <firstpageend>
  565. % <xref0start> <filelength> createLPDict -
  566. LPDict
  567. dup /Linearized 1 put
  568. dup /L 4 -1 roll put % filelength
  569. dup /T 4 -1 roll put % xref0start
  570. dup /E 4 -1 roll put % firstpageend
  571. dup /H 5 -2 roll 1 index sub 2 array astore put % phsstart, end-start
  572. dup /O 1 pdffindpageref 0 get omap put
  573. /N pdfpagecount put
  574. } bind def
  575. % ---------------- Adjust object positions ---------------- %
  576. /adjustObjectPositions { % <boundary> <deltabelow> <deltaabove>
  577. % adjustObjectPositions -
  578. % Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
  579. % We handle the first two as special cases.
  580. XRef {
  581. % Stack: bdy below above key loc
  582. dup 5 index ge { 2 } { 3 } ifelse index add
  583. XRef 3 1 roll ld_put
  584. } ld_forall pop pop pop
  585. XRef LPDictN omap HeaderLength ld_put
  586. XRef PHSN omap PHSStart ld_put
  587. } bind def
  588. % ---------------- Write the output file ---------------- %
  589. % Write objects identified by object number.
  590. /writeobjn { % <obj#> writeobjn -
  591. Generations 1 index lget pdfwriteobj
  592. } bind def
  593. /writeobjns { % <obj#s> writeobjns -
  594. { writeobjn } forall
  595. } bind def
  596. /lwriteobjns { % <obj#s> writeobjns -
  597. { writeobjn } lforall
  598. } bind def
  599. % Write a part of the output file.
  600. /writePart { % <proc> <label> writePart -
  601. PDFOPTDEBUG {
  602. dup print ( count=) print count =only ( start=) print
  603. OFile { .fileposition } stopped { pop (???) } if =
  604. 2 .execn
  605. print ( end=) print
  606. OFile { .fileposition } stopped { pop (???) } if =
  607. } {
  608. pop exec
  609. } ifelse
  610. } bind def
  611. % Write the header.
  612. /writePart1 { % - writePart1 -
  613. {
  614. pdfwriteheader
  615. } (part1) writePart
  616. } bind def
  617. % Write the linearization parameters dictionary.
  618. /writePart2 { % - writePart2 -
  619. {
  620. LPDictN writeobjn
  621. } (part2) writePart
  622. } bind def
  623. % Write the First Page xref table and trailer.
  624. % Free variables: FirstPageXN.
  625. /writePart3 { % <xrefstart> writePart3 -
  626. {
  627. FirstPageXN NObjects 1 add 1 index sub pdfwritexref
  628. Trailer dup length 1 add dict copy Trailer xcheck { cvx } if
  629. dup /Size NObjects 1 add put
  630. dup /Prev 4 -1 roll put
  631. pdfwritetrailer
  632. 0 pdfwritestartxref
  633. } (part3) writePart
  634. } bind def
  635. % Write the Catalog and other required document-level objects.
  636. % Free variables: CatalogNs.
  637. /writePart4 { % - writePart4 -
  638. {
  639. CatalogNs lwriteobjns
  640. } (part4) writePart
  641. } bind def
  642. % Write the Primary Hint Stream.
  643. /writePart5 { % - writePart5 -
  644. {
  645. PHSN writeobjn
  646. } (part5) writePart
  647. } bind def
  648. % Write the First Page's objects.
  649. % Free variables: FirstPageNs.
  650. /writePart6 { % - writePart6 -
  651. {
  652. FirstPageNs writeobjns
  653. } (part6) writePart
  654. } bind def
  655. % Write the objects of other pages (Page + non-shared objects).
  656. % Free variables: OtherPageNs.
  657. /writePart7 { % - writePart7 <lengths>
  658. {
  659. [ OtherPageNs {
  660. OFile fileposition exch
  661. writeobjns OFile fileposition exch sub
  662. } forall ]
  663. } (part7) writePart
  664. } bind def
  665. % Write the shared objects of other pages.
  666. % Free variables: SharedNs.
  667. /writePart8 { % - writePart8 -
  668. {
  669. SharedNs writeobjns
  670. } (part8) writePart
  671. } bind def
  672. % Write the other objects not associated with pages.
  673. % Free variables: NonPageNs.
  674. /writePart9 { % - writePart9 -
  675. {
  676. NonPageNs { writeobjn } lforall
  677. } (part9) writePart
  678. } bind def
  679. % Write the main xref table and trailer.
  680. % Free variables: FirstPageXN.
  681. /writePart11xref { % writePart11 -
  682. {
  683. 0 FirstPageXN pdfwritexref
  684. } (part11xref) writePart
  685. } bind def
  686. /writePart11rest { % <part3start> writePart11rest -
  687. {
  688. << /Size FirstPageXN >> pdfwritetrailer
  689. pdfwritestartxref
  690. } (part11rest) writePart
  691. } bind def
  692. % ---------------- Write hint tables ---------------- %
  693. /bitsneeded { % <maxvalue> bitsneeded <#bits>
  694. 0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop
  695. } bind def
  696. % Find the start and end of objects in the output.
  697. /omstart { % <obj#> omstart <pos>
  698. PDFOPTDEBUG { (start\() print dup =only } if
  699. omap
  700. PDFOPTDEBUG { (=>) print dup =only } if
  701. XRef exch ld_get
  702. PDFOPTDEBUG { (\) = ) print dup = } if
  703. } bind def
  704. /omend { % <obj#> omend <pos>
  705. % The end of an object is the start of the next object.
  706. % The caller must be sure that this object is not the last one
  707. % in part 9.
  708. PDFOPTDEBUG { (end\() print dup =only } if
  709. omap
  710. PDFOPTDEBUG { (=>) print dup =only } if
  711. 1 add
  712. % Check that the requested object wasn't the last one in part 6:
  713. % the next object in the output file is the first in part 7.
  714. PHSN omap 1 index eq { pop 1 } if
  715. XRef exch ld_get
  716. PDFOPTDEBUG { (\) = ) print dup = } if
  717. } bind def
  718. /omlength { % <obj#> omlength <length>
  719. dup omend exch omstart sub
  720. } bind def
  721. % Find the Contents of a page.
  722. /contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true
  723. % <pagedict> contentsobjects false
  724. /Contents .knownget {
  725. dup oforce % ref []
  726. dup type /dicttype eq {
  727. pop 0 get dup true % ref ref
  728. } {
  729. exch pop % []
  730. dup length 0 ne {
  731. dup 0 get 0 get % [] 1st
  732. exch dup % 1st [] []
  733. length 1 sub get 0 get % 1st last
  734. true
  735. } {
  736. pop false
  737. } ifelse
  738. } ifelse
  739. } {
  740. false
  741. } ifelse
  742. } bind def
  743. /contentsstart { % <pagedict> contentsstart <pos> true
  744. % <pagedict> contentsstart false
  745. contentsobjects { pop omstart true } { false } ifelse
  746. } bind def
  747. /contentslength { % <pagedict> contentslength <length>
  748. contentsobjects { omend exch omstart sub } { 0 } ifelse
  749. } bind def
  750. /writePageOffsetHints {
  751. PDFOPTDEBUG { /writePageOffsetHints == } if
  752. 20 dict begin
  753. /bits OFile bitstream def
  754. % Calculate least length of a page.
  755. FirstPageLength OtherPageLengths { .min } forall
  756. /minpl exch def
  757. % Calculate least contents length.
  758. FirstPageNs 0 get Objects exch lget contentslength
  759. OtherPageNs { 0 get Objects exch lget contentslength .min } forall
  760. /mincl exch def
  761. % The Adobe documentation says that all versions of Acrobat
  762. % require item 8 (mincl) to be zero. Patch this here.
  763. /mincl 0 def
  764. % Calculate bits needed to represent greatest page length.
  765. FirstPageLength OtherPageLengths { .max } forall
  766. minpl sub bitsneeded /maxplbits exch def
  767. % Calculate bits needed to represent the greatest Contents length.
  768. FirstPageNs 0 get Objects exch lget contentslength
  769. OtherPageNs { 0 get Objects exch lget contentslength .max } forall
  770. mincl sub bitsneeded /maxclbits exch def
  771. % Per Adobe documentation, Acrobat requires that item 5 (maxplbits)
  772. % be equal to item 9 (maxclbits). Set both to the max of the two.
  773. maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def
  774. % Mapping from object number to shared object reference
  775. /shared_id_dict FirstPageNs length SharedNs length add dict begin
  776. 0 FirstPageNs { 1 index def 1 add } forall
  777. SharedNs { 1 index def 1 add } forall
  778. pop
  779. currentdict end def
  780. % Table F.3 Page offset hint table, header section
  781. % 1: Least number of objects in a page:
  782. FirstPageNs length OtherPageNs { length .min } forall
  783. /minnop 1 index def 32 bwn
  784. % 2: Location of first page's Page object:
  785. FirstPageNs 0 get omap XRef exch ld_get 32 bwn
  786. % 3: Bits needed to represent greatest # of objects in a page:
  787. FirstPageNs length OtherPageNs { length .max } forall
  788. minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
  789. % 4: Least length of a page:
  790. minpl 32 bwn
  791. % 5: Bits needed to represent the greatest page length:
  792. maxplbits 16 bwn
  793. % 6: Least start of Contents offset:
  794. 0 % (Acrobat requires that this be 0.)
  795. /minsco 1 index def 32 bwn
  796. % 7: Bits needed to represent the greatest start of Contents
  797. % offset:
  798. 0 % (Acrobat ignores this.)
  799. /maxscobits 1 index def 16 bwn
  800. % 8: Least contents length:
  801. mincl 32 bwn
  802. % 9: Bits needed to represent the greatest Contents length:
  803. maxclbits 16 bwn
  804. % 10: Bits needed to represent the greatest number of Shared
  805. % Object references:
  806. FirstPageNs length SharedPageNs { length .max } forall bitsneeded
  807. /maxsorbits 1 index def 16 bwn
  808. % 11: Bits needed to identify a Shared Object:
  809. FirstPageNs length SharedNs length add bitsneeded
  810. /sobits 1 index def 16 bwn
  811. % 12: Bits needed to represent numerator of fraction:
  812. 2
  813. /numfbits 1 index def 16 bwn
  814. % 13: Denominator of fraction:
  815. 1
  816. /denf 1 index def 16 bwn
  817. % Table F.4 Page offset hint table, per-page entry
  818. % 1: Number of objects in pages:
  819. FirstPageNs length minnop sub maxnopbits bwn
  820. OtherPageNs {
  821. length minnop sub maxnopbits bwn
  822. } forall
  823. bits bitflush
  824. % 2: Total length of pages in bytes;
  825. FirstPageLength minpl sub maxplbits bwn
  826. OtherPageLengths {
  827. minpl sub maxplbits bwn
  828. } forall
  829. bits bitflush
  830. % 3: Number of shared objects referenced from page:
  831. FirstPageNs length maxsorbits bwn
  832. SharedPageNs { length maxsorbits bwn } forall
  833. bits bitflush
  834. % 4: A shared object identifier:
  835. FirstPageNs { shared_id_dict exch get sobits bwn } forall
  836. SharedPageNs {
  837. { shared_id_dict exch get sobits bwn
  838. } forall
  839. } forall
  840. bits bitflush
  841. % 5: Numerator of fractional position for each shared object:
  842. FirstPageNs { pop 0 numfbits bwn } forall
  843. SharedPageNs {
  844. { pop 0 numfbits bwn
  845. } forall
  846. } forall
  847. bits bitflush
  848. % 6: Contents offsets:
  849. % Following Implementation Note 133 section 6 is empty.
  850. maxscobits 0 gt {
  851. [FirstPageNs OtherPageNs aload pop] {
  852. 0 get Objects exch lget contentsstart { minsco sub } { 0 } ifelse
  853. maxscobits bwn
  854. } forall
  855. bits bitflush
  856. } if
  857. % 7: Contents lengths:
  858. [FirstPageNs OtherPageNs aload pop] {
  859. 0 get Objects exch lget contentslength mincl sub maxclbits bwn
  860. } forall
  861. bits bitflush
  862. end
  863. } bind def
  864. /writeSharedObjectHints {
  865. PDFOPTDEBUG { /writeSharedObjectHints == } if
  866. 20 dict begin
  867. /bits OFile bitstream def
  868. /obj_count SharedNs length FirstPageNs length add def
  869. % Table F.5 Shared object hint table, header section
  870. % 1: Object number of first object in Shared Objects section
  871. 0 32 bwn
  872. % 2: Location of first object in Shared Objects section:
  873. % If there are no shared objects,
  874. % Acrobat sets this to the location of linearization
  875. % parameters object (the very first object).
  876. { pdfwriteheader } tomemory length 32 bwn
  877. % 3: Number of Shared Object entries for first page:
  878. FirstPageNs length 32 bwn
  879. % 4: Number of Shared Object entries for Shared Objects
  880. % section
  881. obj_count 32 bwn
  882. % 5: Bits needed to represent the greatest number of objects
  883. % in a shared object group (always 0, because all groups
  884. % have only 1 object):
  885. 0 16 bwn
  886. % 6: Least length of a Shared Object Group in bytes:
  887. 16#7fffffff FirstPageNs { omlength .min } forall
  888. SharedNs { omlength .min } forall
  889. /minsol 1 index def 32 bwn
  890. % 7: Bits needed to represent the greatest length of a
  891. % Shared Object Group:
  892. 0 FirstPageNs { omlength .max } forall
  893. SharedNs { omlength .max } forall
  894. minsol sub bitsneeded
  895. /maxsolbits 1 index def 16 bwn
  896. % Table F.6 Shared object hint table, shared object group entry
  897. % 1: Lengths of shared object groups:
  898. FirstPageNs { omlength minsol sub maxsolbits bwn } forall
  899. SharedNs { omlength minsol sub maxsolbits bwn } forall
  900. bits bitflush
  901. % 2: MD5 flag:
  902. obj_count { 0 1 bwn } repeat
  903. bits bitflush
  904. % 3: No MD5 shared object signatures.
  905. % 4: No number_number_of_objects_in_the_group - 1
  906. end
  907. } bind def
  908. % ---------------- Main program ---------------- %
  909. /pdfOptimize { % <infile> <outfile> pdfOptimize -
  910. realtime 3 1 roll
  911. exch pdfdict begin pdfopenfile dup begin
  912. 40 dict begin
  913. /IDict exch def
  914. /OFile exch def
  915. /starttime exch def
  916. /ToWrite 100 dict def
  917. /now {
  918. QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
  919. } def
  920. omapinit
  921. % Create and open a temporary file.
  922. % Because of .setsafe, we have to open it as read-write, rather than
  923. % opening for writing, then closing it and reopening it for reading.
  924. null (w+) .tempfile /TFile exch def /TFileName exch def
  925. .setsafe
  926. % Read all objects into memory.
  927. Trailer touch
  928. (Read objects) now
  929. % Encrypted files are not yet supported.
  930. Trailer /Encrypt known {
  931. (ERROR: Encrypted files are not yet supported.) = flush
  932. /pdfOptimize cvx /limitcheck signalerror
  933. } if
  934. % Replace indirect references to numbers. This is needed
  935. % for the Length of streams, and doesn't hurt anything else.
  936. replaceReferences
  937. (Replaced references) now
  938. % Create the two new objects: the linearization parameter
  939. % dictionary, and the Primary Hint Stream.
  940. /LPDict 10 dict def
  941. /PHS 10 dict cvx def % executable = stream
  942. [LPDict PHS] createObjects
  943. /LPDictN 1 index def 1 add
  944. /PHSN exch def
  945. PDFOPTDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if
  946. % Count the number of objects in the output.
  947. 0 0 1 Objects llength 1 sub {
  948. Generations exch lget 0 ne { 1 add } if
  949. } for
  950. /NObjects exch def
  951. QUIET not { NObjects =only ( objects total) = flush } if
  952. % Propagate inherited attributes down the page tree.
  953. propagateAttributes
  954. (Propagated attributes) now
  955. % Identify the document-level objects (part 4).
  956. identifyDocumentObjects /CatalogNs exch def
  957. QUIET not { CatalogNs === flush } if
  958. (Identified Catalog) now
  959. % Identify the first page's objects (part 6),
  960. % including the Outlines tree if appropriate.
  961. pdfopencache
  962. /FirstPageNs identifyFirstPageObjects def
  963. QUIET not { FirstPageNs === flush } if
  964. (Identified first page) now
  965. % Identify shared vs. non-shared objects for remaining pages
  966. % (parts 7 and 8).
  967. identifyOtherPageObjects
  968. /SharedNs exch def
  969. /SharedPageNs exch def
  970. /OtherPageNs exch def
  971. QUIET not { OtherPageNs === flush SharedNs === flush } if
  972. (Identified other pages) now
  973. % Identify objects not associated with any page (part 9).
  974. /NonPageNs identifyNonPageObjects def
  975. QUIET not { NonPageNs { === } forall flush } if
  976. (Identified non-pages) now
  977. % Assign final object numbers to all the objects.
  978. % (The omap is currently empty.)
  979. /FirstPageXN assignObjectNumbers def
  980. (Assigned objects #s) now
  981. % Write the document-level objects (part 4).
  982. { writePart4 } totemp
  983. /CatalogTempEnd exch def /CatalogTempStart exch def
  984. (Wrote Catalog) now
  985. % Write the first page's objects (part 6).
  986. { writePart6 } totemp
  987. /FirstPageTempEnd exch def /FirstPageTempStart exch def
  988. (Wrote first page) now
  989. % Write the non-shared objects for other pages (part 7).
  990. { writePart7 /OtherPageLengths exch def } totemp
  991. /OtherPageTempEnd exch def /OtherPageTempStart exch def
  992. (Wrote other pages) now
  993. % Write the shared objects for other pages (part 8).
  994. { writePart8 } totemp
  995. /SharedTempEnd exch def /SharedTempStart exch def
  996. (Wrote shared objects) now
  997. % Write the objects not associated with pages (part 9).
  998. { writePart9 } totemp
  999. /NonPageTempEnd exch def /NonPageTempStart exch def
  1000. % Compute conservative lengths of parts 2,3,5,11 of the output.
  1001. % It's OK for these to be too large, but not too small.
  1002. % Make dummy XRef entres for LPDict and PHS.
  1003. XRef LPDictN omap 0 ld_put
  1004. XRef PHSN omap 0 ld_put
  1005. /HeaderLength { % this is exact
  1006. writePart1 % part 1
  1007. } tolength def
  1008. /CatalogLength % this is exact
  1009. CatalogTempEnd CatalogTempStart sub def % part 4
  1010. /FirstPageLength % this is exact
  1011. FirstPageTempEnd FirstPageTempStart sub def % part 6
  1012. /OtherObjectsLength % this is exact
  1013. NonPageTempEnd OtherPageTempStart sub def % parts 7,8,9
  1014. /ObjectsLength % this is exact
  1015. CatalogLength FirstPageLength add OtherObjectsLength add def
  1016. /XrefLength { % part 11
  1017. % The LPDict must end within the first 1024 bytes,
  1018. % so the start of the FirstPage xref table can't exceed 1024.
  1019. writePart11xref 1024 writePart11rest
  1020. } tolength def
  1021. /NominalFileLength % Make a generous allowance for parts 2,3,5.
  1022. HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def
  1023. /FirstPageXrefLength { % part 3
  1024. NominalFileLength writePart3
  1025. } tolength def
  1026. /LPDictLength { % part 2
  1027. NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2
  1028. } tolength def
  1029. % Compute a few additional values from the above.
  1030. /XrefBeginLength {
  1031. (xref\n0 ) ows
  1032. OFile FirstPageXN write=
  1033. } tolength def
  1034. HeaderLength LPDictLength add
  1035. /FirstPageXrefStart 1 index def
  1036. FirstPageXrefLength add
  1037. /CatalogStart 1 index def
  1038. CatalogLength add % phsstart
  1039. /PHSStart exch def
  1040. % Adjust the object positions ignoring PHS.
  1041. % (Writing the PHS needs these.)
  1042. 0 0 CatalogStart CatalogTempStart sub adjustObjectPositions
  1043. % Make a temporary XRef entry for the PHS, for the benefit of omend.
  1044. XRef PHSN omap CatalogStart ld_put
  1045. (Adjusted positions) now
  1046. % Construct the hint tables (part 5).
  1047. { writePageOffsetHints } totemp
  1048. pop /PHSTempStart exch def
  1049. { writeSharedObjectHints } totemp
  1050. exch PHSTempStart sub PHS /S 3 -1 roll put
  1051. PHSTempStart sub /PHSTempLength exch def
  1052. (Wrote hints) now
  1053. % Prepare to read TFile.
  1054. % TFile closefile
  1055. % /TFile TFileName (r) file def
  1056. PHS
  1057. dup /File TFile put
  1058. dup /FilePosition PHSTempStart put
  1059. dup /Length PHSTempLength put
  1060. pop
  1061. /PHSLength { writePart5 } tolength def
  1062. % Construct the linearization parameter dictionary (part 2).
  1063. PHSStart
  1064. dup PHSLength add % phsend
  1065. /FirstPageStart 1 index def
  1066. dup FirstPageLength add % firstpageend
  1067. dup OtherObjectsLength add
  1068. /XrefStart 1 index def
  1069. XrefBeginLength add % xref0start
  1070. dup XrefBeginLength sub XrefLength add % fileend
  1071. % Because of a bug, Acrobat Reader doesn't recognize any file
  1072. % shorter than 1K as linearized. Patch this here.
  1073. % Acrobat 9 insists on 4KB
  1074. 4096 .max
  1075. /FileLength 1 index def
  1076. createLPDict
  1077. % Adjust the object positions again, taking the PHS into account.
  1078. PHSStart 0 PHSLength adjustObjectPositions
  1079. (Readjusted positions) now
  1080. % Finally, write the output file.
  1081. writePart1
  1082. writePart2
  1083. FirstPageXrefStart padto
  1084. XrefStart writePart3
  1085. CatalogStart padto
  1086. CatalogTempStart CatalogTempEnd copyrange % part 4
  1087. writePart5
  1088. FirstPageStart padto
  1089. FirstPageTempStart NonPageTempEnd copyrange % parts 6,7,8,9
  1090. % No Overflow Hint Stream (part 10).
  1091. XrefStart padto
  1092. writePart11xref
  1093. { FirstPageXrefStart writePart11rest } tomemory
  1094. FileLength 1 index length sub padto ows
  1095. (Wrote output file) now
  1096. % Wrap up.
  1097. TFile closefile TFileName deletefile
  1098. end % temporary dict
  1099. end % IDict
  1100. } bind def
  1101. end % pdfoptdict
  1102. .setglobal
  1103. % Check for command line arguments.
  1104. [ shellarguments {
  1105. ] dup length 2 eq {
  1106. % Load the pdfwrite utilities if necessary.
  1107. /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
  1108. save exch
  1109. aload pop exch (r) file exch (w) file
  1110. 3000000 setvmthreshold
  1111. 0 setobjectformat
  1112. pdfoptdict begin pdfOptimize end
  1113. restore
  1114. } {
  1115. (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
  1116. } ifelse
  1117. } {
  1118. pop
  1119. } ifelse