星火微课系统客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pdf_base.ps 40KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  1. % Copyright (C) 1994-2006 Artifex Software, Inc. 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: pdf_base.ps 10498 2009-12-13 01:31:59Z alexcher $
  16. % pdf_base.ps
  17. % Basic parser for PDF reader.
  18. % This handles basic parsing of the file (including the trailer
  19. % and cross-reference table), as well as objects, object references,
  20. % streams, and name/number trees; it doesn't include any facilities for
  21. % making marks on the page.
  22. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  23. .currentglobal true .setglobal
  24. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  25. pdfdict begin
  26. % Define the name interpretation dictionary for reading values.
  27. /valueopdict mark
  28. (<<) cvn { mark } bind % don't push an actual mark!
  29. (>>) cvn { { counttomark } stopped {
  30. ( **** File has an unbalanced >> \(close dictionary\).\n)
  31. pdfformaterror
  32. } if
  33. 131068 le {
  34. .dicttomark
  35. } {
  36. % Checks for other resources can be added if needed.
  37. counttomark 1 add index /XObject eq {
  38. counttomark mark exch 1 add 1 roll
  39. { counttomark
  40. dup 0 eq {
  41. pop pop ] exit
  42. } if
  43. 131068 .min
  44. mark exch 1 add 1 roll .dicttomark
  45. counttomark 1 add 1 roll
  46. } loop
  47. } {
  48. .dicttomark % will fail
  49. } ifelse
  50. } ifelse
  51. } bind
  52. ([) cvn { mark } bind % ditto
  53. (]) cvn dup load
  54. % /true true % see .pdfexectoken below
  55. % /false false % ibid.
  56. % /null null % ibid.
  57. /F dup cvx % see Objects section below
  58. /R dup cvx % see Objects section below
  59. /stream dup cvx % see Streams section below
  60. .dicttomark readonly def
  61. % ------ Utilities ------ %
  62. % Define a scratch string. The PDF language definition says that
  63. % no line in a PDF file can exceed 255 characters, but this string
  64. % is also used to search for %PDF-, which needs 1024 characters.
  65. /pdfstring 1024 string def
  66. % Read the previous line of a file. If we aren't at a line boundary,
  67. % read the line containing the current position.
  68. % Skip any blank lines.
  69. /prevline % - prevline <startpos> <substring>
  70. { PDFfile fileposition dup () pdfstring
  71. 2 index 257 sub 0 .max PDFfile exch setfileposition
  72. { % Stack: initpos linepos line string
  73. PDFfile fileposition
  74. PDFfile 2 index readline pop
  75. dup length 0 gt
  76. { 3 2 roll 5 -2 roll pop pop 2 index }
  77. { pop }
  78. ifelse
  79. % Stack: initpos linepos line string startpos
  80. PDFfile fileposition 5 index ge { exit } if
  81. pop
  82. }
  83. loop pop pop 3 -1 roll pop
  84. } bind def
  85. % Handle the PDF 1.2 #nn escape convention when reading from a file.
  86. % This should eventually be done in C.
  87. /.pdffixname { % <execname> .pdffixname <execname'>
  88. PDFversion 1.2 ge {
  89. dup .namestring (#) search {
  90. name#escape cvn exch pop
  91. } {
  92. pop
  93. } ifelse
  94. } if
  95. } bind def
  96. /name#escape % <post> <(#)> <pre> name#escape <string>
  97. { exch pop
  98. 1 index 2 () /SubFileDecode filter dup (x) readhexstring
  99. % Stack: post pre stream char t/f
  100. not { % tolerate, but complain about bad syntax
  101. pop closefile (#) concatstrings exch
  102. ( **** Warning: Invalid hex following '#' name escape, using literal '#' in name.\n)
  103. pdfformaterror
  104. } {
  105. exch closefile concatstrings
  106. exch 2 1 index length 2 sub getinterval
  107. } ifelse
  108. (#) search { name#escape } if concatstrings
  109. } bind def
  110. % Execute a file, interpreting its executable names in a given
  111. % dictionary. The name procedures may do whatever they want
  112. % to the operand stack.
  113. /.pdftokenerror { % <count> <opdict> <errtoken> .pdftokenerror -
  114. BXlevel 0 le {
  115. ( **** Unknown operator: ') pdfformaterror
  116. dup =string cvs pdfformaterror
  117. % Attempt a retry scan of the element after changing to PDFScanInvNum
  118. << /PDFScanInvNum true >> setuserparams
  119. =string cvs
  120. token pop exch pop dup type
  121. dup /integertype eq exch /realtype eq or {
  122. exch pop exch pop
  123. (', processed as number, value: ) pdfformaterror
  124. dup =string cvs pdfformaterror (\n) pdfformaterror
  125. << /PDFScanInvNum null >> setuserparams % reset to default scanning rules
  126. false % suppress any stack cleanup
  127. } {
  128. % error was non-recoverable with modified scanning rules
  129. ('\n) pdfformaterror
  130. true
  131. } ifelse
  132. } {
  133. true
  134. } ifelse
  135. { % clean up the operand stack if this was non-recoverable
  136. pop pop count exch sub { pop } repeat % pop all the operands
  137. } if
  138. } bind def
  139. /.pdfexectoken { % <count> <opdict> <exectoken> .pdfexectoken ?
  140. PDFDEBUG {
  141. pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } if
  142. PDFSTEP {
  143. pdfdict /PDFtokencount 2 copy .knownget { 1 add } { 1 } ifelse .forceput
  144. PDFSTEPcount 1 gt {
  145. pdfdict /PDFSTEPcount PDFSTEPcount 1 sub .forceput
  146. } {
  147. dup ==only
  148. ( step # ) print PDFtokencount =only
  149. ( ? ) print flush 1 false .outputpage
  150. (%stdin) (r) file 255 string readline {
  151. token {
  152. exch pop pdfdict /PDFSTEPcount 3 -1 roll .forceput
  153. } {
  154. pdfdict /PDFSTEPcount 1 .forceput
  155. } ifelse % token
  156. } {
  157. pop /PDFSTEP false def % EOF on stdin
  158. } ifelse % readline
  159. } ifelse % PDFSTEPcount > 1
  160. } {
  161. dup ==only () = flush
  162. } ifelse % PDFSTEP
  163. } if % PDFDEBUG
  164. 2 copy .knownget {
  165. exch pop exch pop exch pop exec
  166. } {
  167. % Normally, true, false, and null would appear in opdict
  168. % and be treated as "operators". However, there is a
  169. % special fast case in the PostScript interpreter for names
  170. % that are defined in, and only in, systemdict and/or
  171. % userdict: putting these three names in the PDF dictionaries
  172. % destroys this property for them, slowing down their
  173. % interpretation in all PostScript code. Therefore, we
  174. % check for them explicitly here instead.
  175. dup dup dup /true eq exch /false eq or exch /null eq or {
  176. exch pop exch pop //systemdict exch get
  177. } {
  178. % Hackish fix to detect missing whitespace after "endobj". Yet another
  179. % problem that (you guessed it!) Adobe Acrobat ignores silently
  180. 256 string cvs (endobj) anchorsearch {
  181. ( **** Missing whitespace after 'endobj'.\n) pdfformaterror
  182. exch pop cvn get exch pop exec
  183. } {
  184. .pdftokenerror
  185. } ifelse
  186. } ifelse
  187. } ifelse
  188. } bind def
  189. /PDFScanRules_true << /PDFScanRules true >> def
  190. /PDFScanRules_null << /PDFScanRules null >> def
  191. /.pdfrun { % <file> <opdict> .pdfrun -
  192. % Construct a procedure with the stack depth, file and opdict
  193. % bound into it.
  194. 1 index cvlit count 2 sub 3 1 roll mark
  195. /PDFScanRules .getuserparam //null eq {
  196. //PDFScanRules_true { setuserparams } 0 get % force PDF scanning mode
  197. mark 7 4 roll
  198. } {
  199. mark 5 2 roll
  200. } ifelse
  201. { % Stack: ..operands.. count opdict file
  202. { token } stopped {
  203. dup type /filetype eq { pop } if % pop the operand if it is restored
  204. ( **** Error reading a content stream. The page may be incomplete.\n)
  205. pdfformaterror
  206. //false
  207. } if {
  208. dup type /nametype eq {
  209. dup xcheck {
  210. .pdfexectoken
  211. } {
  212. .pdffixname
  213. exch pop exch pop PDFDEBUG {
  214. PDFSTEPcount 1 le {
  215. dup ==only ( ) print flush
  216. } if
  217. } if
  218. } ifelse
  219. } {
  220. exch pop exch pop PDFDEBUG {
  221. PDFSTEPcount 1 le {
  222. dup ==only ( ) print flush
  223. } if
  224. } if
  225. } ifelse
  226. } {
  227. (%%EOF) cvn cvx .pdfexectoken
  228. } ifelse
  229. }
  230. aload pop .packtomark cvx
  231. { loop } 0 get 2 packedarray cvx
  232. { stopped } 0 get
  233. /PDFScanRules .getuserparam //null eq {
  234. //PDFScanRules_null { setuserparams } 0 get % reset PDF scannig mode if it was off
  235. } if
  236. /PDFsource PDFsource
  237. { store { stop } if } aload pop .packtomark cvx
  238. /PDFsource 3 -1 roll store exec
  239. } bind def
  240. % Execute a file, like .pdfrun, for a marking context.
  241. % This temporarily rebinds LocalResources and DefaultQstate.
  242. /.pdfruncontext { % <resdict> <file> <opdict> .pdfruncontext -
  243. /.pdfrun load LocalResources DefaultQstate
  244. /LocalResources 7 -1 roll store
  245. /DefaultQstate qstate store
  246. 3 .execn
  247. /DefaultQstate exch store
  248. /LocalResources exch store
  249. } bind def
  250. % Get the depth of the PDF operand stack. The caller sets pdfemptycount
  251. % before calling .pdfrun or .pdfruncontext. It is initially set by
  252. % pdf_main, and is also set by any routine which changes the operand
  253. % stack depth (currently .pdfpaintproc, although there are other callers
  254. % of .pdfrun{context} which have not been checked for opstack depth.
  255. /.pdfcount { % - .pdfcount <count>
  256. count pdfemptycount sub
  257. } bind def
  258. % Read a token, but simply return false (no token read) in the case of an
  259. % error. This is messy because 'token' either may or may not pop its operand
  260. % if an error occurs, and because the return values are different depending
  261. % on whether the source is a file or a string. To avoid closing the file
  262. % check for '{' before trying 'token'.
  263. /token_nofail_dict mark
  264. ( ) { dup ( ) readstring pop pop } bind
  265. (\t) 1 index
  266. (\r) 1 index
  267. (\n) 1 index
  268. (\000) 1 index
  269. ({) { //null //true exit } bind
  270. .dicttomark def
  271. /token_nofail { % <file|string> token_nofail false
  272. % <file> token_nofail <token> true
  273. % <string> token_nofail <post> <token> true
  274. dup type /filetype eq {
  275. { dup ( ) .peekstring not { ({) } if
  276. //token_nofail_dict exch .knownget not {
  277. //null 1 index { token } .internalstopped exit
  278. } if
  279. exec
  280. } loop
  281. { % stack: source null [source]
  282. //null ne { pop } if pop //false
  283. } { % stack: source null ([post] token true | false)
  284. { 3 1 roll pop pop //true }
  285. { pop pop //false }
  286. ifelse
  287. } ifelse
  288. } {
  289. //null 1 index % stack: source null source
  290. { token } .internalstopped { % stack: source null [source]
  291. //null ne { pop } if pop //false
  292. } { % stack: source null ([post] token true | false)
  293. { 4 2 roll pop pop //true }
  294. { pop pop //false }
  295. ifelse
  296. } ifelse
  297. } ifelse
  298. } bind def
  299. currentdict /token_nofail_dict .undef
  300. % ================================ Objects ================================ %
  301. % Since we may have more than 64K objects, we have to use a 2-D array to
  302. % hold them (and the parallel Generations structure).
  303. /lshift 9 def
  304. /lnshift lshift neg def
  305. /lsubmask 1 lshift bitshift 1 sub def
  306. /lsublen lsubmask 1 add def
  307. /larray { % - larray <larray>
  308. [ [] ]
  309. } bind def
  310. /lstring { % - lstring <lstring>
  311. [ () ]
  312. } bind def
  313. /ltype { % <lseq> type <type>
  314. 0 get type
  315. } bind def
  316. /lget { % <lseq> <index> lget <value>
  317. dup //lsubmask and 3 1 roll //lnshift bitshift get exch get
  318. } bind def
  319. /lput { % <lseq> <index> <value> lput -
  320. 3 1 roll
  321. dup //lsubmask and 4 1 roll //lnshift bitshift get
  322. 3 1 roll put
  323. } bind def
  324. /llength { % <lseq> llength <length>
  325. dup length 1 sub dup //lshift bitshift
  326. 3 1 roll get length add
  327. } bind def
  328. % lgrowto assumes newlength > llength(lseq)
  329. /growto { % <string/array> <length> growto <string'/array'>
  330. 1 index type /stringtype eq { string } { array } ifelse
  331. 2 copy copy pop exch pop
  332. } bind def
  333. /lgrowto { % <lseq> <newlength> lgrowto <lseq'>
  334. dup //lsubmask add //lnshift bitshift dup 3 index length gt {
  335. % Add more sub-arrays. Start by completing the last existing one.
  336. % Stack: lseq newlen newtoplen
  337. 3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto
  338. % Stack: newlen newtoplen lseq
  339. [ exch aload pop
  340. counttomark 2 add -1 roll % newtoplen
  341. counttomark sub { dup 0 0 getinterval lsublen growto } repeat
  342. dup 0 0 getinterval ] exch
  343. } {
  344. pop
  345. } ifelse
  346. % Expand the last sub-array.
  347. 1 sub //lsubmask and 1 add
  348. exch dup dup length 1 sub 2 copy
  349. % Stack: newsublen lseq lseq len-1 lseq len-1
  350. get 5 -1 roll growto put
  351. } bind def
  352. /lforall { % <lseq> <proc> lforall -
  353. /forall cvx 2 packedarray cvx forall
  354. } bind def
  355. % We keep track of PDF objects using the following PostScript variables:
  356. %
  357. % Generations (lstring): Generations[N] holds 1+ the current
  358. % generation number for object number N. (As far as we can tell,
  359. % this is needed only for error checking.) For free objects,
  360. % Generations[N] is 0.
  361. %
  362. % Objects (larray): If object N is loaded, Objects[N] is the actual
  363. % object; otherwise, Objects[N] is an executable integer giving
  364. % the file offset of the object's location in the file. If
  365. % ObjectStream[N] is non-zero then Objects[N] contains the index
  366. % into the object stream instead of the file offset of the object.
  367. %
  368. % ObjectStream (larray): If object N is in an object stream then
  369. % ObjectStream[N] holds the object number of the object stream.
  370. % Otherwise ObjectStream[N] contains 0. If ObjectStream[N]
  371. % is non-zero then Objects[N] contains the index into the object
  372. % stream.
  373. %
  374. % GlobalObjects (dictionary): If object N has been resolved in
  375. % global VM, GlobalObjects[N] is the same as Objects[N]
  376. % (except that GlobalObjects itself is stored in global VM,
  377. % so the entry will not be deleted at the end of the page).
  378. %
  379. % IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in
  380. % global VM. This is an accelerator to avoid having to do a
  381. % dictionary lookup in GlobalObjects when resolving every object.
  382. % Initialize the PDF object tables.
  383. /initPDFobjects { % - initPDFobjects -
  384. /ObjectStream larray def
  385. /Objects larray def
  386. /Generations lstring def
  387. .currentglobal true .setglobal
  388. /GlobalObjects 20 dict def
  389. .setglobal
  390. /IsGlobal lstring def
  391. } bind def
  392. % Grow the tables to a specified size.
  393. /growPDFobjects { % <minsize> growPDFobjects -
  394. dup ObjectStream llength gt {
  395. dup ObjectStream exch lgrowto /ObjectStream exch def
  396. } if
  397. dup Objects llength gt {
  398. dup Objects exch lgrowto /Objects exch def
  399. } if
  400. dup Generations llength gt {
  401. dup Generations exch lgrowto /Generations exch def
  402. } if
  403. dup IsGlobal llength gt {
  404. dup IsGlobal exch lgrowto /IsGlobal exch def
  405. } if
  406. pop
  407. } bind def
  408. % We represent an unresolved object reference by a procedure of the form
  409. % {obj# gen# resolveR}. This is not a possible PDF object, because PDF has
  410. % no way to represent procedures. Since PDF in fact has no way to represent
  411. % any PostScript object that doesn't evaluate to itself, we can 'force'
  412. % a possibly indirect object painlessly with 'exec'.
  413. % Note that since we represent streams by executable dictionaries
  414. % (see below), we need both an xcheck and a type check to determine
  415. % whether an object has been resolved.
  416. /resolved? { % <object#> resolved? <value> true
  417. % <object#> resolved? false
  418. Objects 1 index lget dup xcheck { % Check if executable
  419. dup type /integertype eq { % Check if an integer
  420. % Check whether the object is in GlobalObjects.
  421. pop IsGlobal 1 index lget 0 eq { % 0 --> Not in GlabalObjects
  422. pop false % The object is not resolved
  423. } { % The object is in GlobalObjects
  424. % Update Objects from GlobalObjects
  425. PDFDEBUG { (%Global=>local: ) print dup == } if
  426. GlobalObjects 1 index get dup Objects 4 1 roll lput true
  427. } ifelse
  428. } { % Else object is executable but not integer
  429. exch pop true % Therefore must be executable dict. (stream)
  430. } ifelse
  431. } { % Else object is not executable.
  432. exch pop true % Therefore it must have been resolved.
  433. } ifelse
  434. } bind def
  435. /oforce /exec load def
  436. /oget { % <array> <index> oget <object>
  437. % <dict> <key> oget <object>
  438. % Before release 6.20, this procedure stored the resolved
  439. % object back into the referring slot. In order to support
  440. % PDF linearization, we no longer do this.
  441. get oforce
  442. } bind def
  443. /oforce_array { % <array> oforce_array <array>
  444. [ exch { oforce } forall ]
  445. } bind def
  446. /oforce_elems { % <array> oforce_elems <first> ... <last>
  447. { oforce } forall
  448. } bind def
  449. /oforce_recursive { % <any> oforce_recursive <any>
  450. oforce dup type dup /arraytype eq {
  451. pop [ exch { oforce_recursive } forall ]
  452. } {
  453. /dicttype eq {
  454. << exch { oforce_recursive exch oforce exch } forall >>
  455. } if
  456. } ifelse
  457. } bind def
  458. % A null value in a dictionary is equivalent to an omitted key;
  459. % we must check for this specially.
  460. /knownoget { % <dict> <key> knownoget <value> true
  461. % <dict> <key> knownoget false
  462. % See oget above regarding this procedure.
  463. .knownget {
  464. oforce dup //null eq { pop //false } { //true } ifelse
  465. } {
  466. //false
  467. } ifelse
  468. } bind def
  469. % See /knownoget above.
  470. /oknown { % <dict> <key> oknown <bool>
  471. .knownget { oforce //null ne } { //false } ifelse
  472. } bind def
  473. /knownogetdict { % <dict> <key> knownogetdict <dict> true
  474. % <dict> <key> knownogetdict false
  475. //knownoget exec dup {
  476. 1 index type /dicttype ne { pop pop //false } if
  477. } if
  478. } bind def
  479. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  480. % Per the specification, we convert these to nulls.
  481. /F { % <file#> <object#> <generation#> F <object>
  482. % Some PDF 1.1 files use F as a synonym for f!
  483. .pdfcount 3 lt { f } { pop pop pop null } ifelse
  484. } bind def
  485. % Verify the generation number for a specified object
  486. % Note: The values in Generations is the generation number plus 1.
  487. % If the value in Generations is zero then the object is free.
  488. /checkgeneration { % <object#> <generation#> checkgeneration <object#> <OK>
  489. Generations 2 index lget 1 sub 1 index eq { % If generation # match ...
  490. pop true % Then return true
  491. } { % Else not a match ...
  492. QUIET not { % Create warning message if not QUIET
  493. Generations 2 index lget 0 eq { % Check if object is free ...
  494. ( **** Warning: reference to free object: )
  495. 2 index =string cvs concatstrings ( ) concatstrings % put obj #
  496. exch =string cvs concatstrings ( R\n) concatstrings % put gen #
  497. } {
  498. ( **** Warning: wrong generation: )
  499. 2 index =string cvs concatstrings ( ) concatstrings % put obj #
  500. exch =string cvs concatstrings % put gen #
  501. (, xref gen#: ) concatstrings 1 index Generations % put xref gen #
  502. exch lget 1 sub =string cvs concatstrings (\n) concatstrings
  503. } ifelse
  504. pdfformaterror % Output warning message
  505. } { % Else QUIET ...
  506. pop % Pop generation number
  507. } ifelse
  508. % We should return false for an incorrect generation number, however
  509. % we are simply printing a warning and then returning true. This makes
  510. % Ghostscript tolerant of of bad generation numbers.
  511. true
  512. } ifelse
  513. } bind def
  514. /R { % <object#> <generation#> R <object>
  515. /resolveR cvx 3 packedarray cvx
  516. } bind def
  517. % If we encounter an object definition while reading sequentially,
  518. % we just store it away and keep going.
  519. /objopdict mark
  520. valueopdict { } forall
  521. /endobj dup cvx
  522. .dicttomark readonly def
  523. /obj { % <object#> <generation#> obj <object>
  524. PDFfile objopdict .pdfrun
  525. } bind def
  526. /endobj { % <object#> <generation#> <object> endobj <object>
  527. 3 1 roll
  528. % Read the xref entry if we haven't yet done so.
  529. % This is only needed for generation # checking.
  530. 1 index resolved? {
  531. pop
  532. } if
  533. checkgeneration {
  534. % The only global objects we bother to save are
  535. % (resource) dictionaries.
  536. 1 index dup gcheck exch type /dicttype eq and {
  537. PDFDEBUG { (%Local=>global: ) print dup == } if
  538. GlobalObjects 1 index 3 index put
  539. IsGlobal 1 index 1 put
  540. } if
  541. Objects exch 2 index lput
  542. } {
  543. pop pop null
  544. } ifelse
  545. } bind def
  546. % When resolving an object reference in an object stream, we stop at
  547. % the end of file. Note: Objects in an object stream do not have either
  548. % a starting 'obj' or and ending 'endobj'.
  549. /resolveobjstreamopdict mark
  550. valueopdict { } forall
  551. (%%EOF) cvn { exit } bind
  552. /endobj { % bug 689795
  553. ( **** Warning: Objects in an object stream should not have 'endobj'.\n)
  554. pdfformaterror
  555. } bind
  556. .dicttomark readonly def
  557. % Note: This version of this function is not currently being used.
  558. % Resolve all objects in an object stream
  559. /resolveobjectstream { % <object stream #> resolveobjectstream -
  560. PDFDEBUG { (%Resolving object stream: ) print } if
  561. 0 resolveR % Get the objectstream dict, all objstrms use 0 as the gen #
  562. dup /First get % Save location of first object onto the stack
  563. 1 index /N get % Save number of objects onto the stack
  564. 2 index false resolvestream % Convert stream dict into a stream
  565. /ReusableStreamDecode filter % We need to be able to position stream
  566. % Objectstreams begin with list of object numbers and locations
  567. % Create two arrays to hold object numbers and stream location
  568. 1 index array % Array for holding object number
  569. 2 index array % Array for holding stream object location
  570. % Get the object numbers and locations.
  571. 0 1 5 index 1 sub { % Loop and collect obj # and locations
  572. % Stack: objstreamdict First N objectstream [obj#] [loc] index
  573. 2 index 1 index % Setup to put obj# into object number array
  574. 5 index token pop put % Get stream, then get obj# and put into array
  575. 1 index 1 index % Setup to put object loc into location array
  576. 5 index token pop put % Get stream, get obj loc and put into array
  577. pop % Remove loop index
  578. } for
  579. % Create a bytestring big enough for reading any object data
  580. % Scan for the size of the largest object
  581. 0 0 % Init max object size and previous location
  582. 2 index { % Loop through all object locations
  583. % Stack: ... maxsize prevloc currentloc
  584. dup 4 1 roll % Save copy of object location into stack
  585. exch sub % Object size = currentloc - prevloc
  586. .max % Determine maximum object size
  587. exch % Put max size under previous location
  588. } forall
  589. pop % Remove previous location
  590. .bigstring % Create bytestring based upon max obj size
  591. % Move to the start of the object data
  592. 3 index 6 index % Get objectstream and start of first object
  593. setfileposition % Move to the start of the data
  594. % Read the data for all objects except the last. We do
  595. % not know the size of the last object so we need to treat
  596. % it as a special case.
  597. 0 1 6 index 2 sub {
  598. dup 4 index exch get % Get our current object number
  599. % Stack: objstreamdict First N objectstream [obj#] [loc]
  600. % bytestring loopindex object#
  601. dup resolved? { % If we already have this object
  602. (yyy) = pstack (yyy) = flush xxx
  603. pop pop % Remove object and object number
  604. 1 add 2 index exch get % Get location of next object
  605. 6 index add 6 index exch % Form location of next object and get stream
  606. setfileposition % Move to the start of the next object data
  607. } { % Else this is a new object ...
  608. % We are going to create a string for reading the object
  609. 2 index 0 % use our working string
  610. % Determine the size of the object
  611. 5 index 4 index 1 add get % Get location of the next object
  612. 6 index 5 index get % Get location of this object
  613. sub % Size of object = next loc - this loc
  614. getinterval % Create string for reading object
  615. 6 index exch readstring pop % Read object
  616. /ReusableStreamDecode filter % Convert string into a stream
  617. resolveobjstreamopdict .pdfrun % Get PDF object
  618. Objects exch 2 index exch lput % Put object into Objects array
  619. pop pop % Remove object # and loop index
  620. } ifelse
  621. } for
  622. pop pop % Remove our working string and loc array
  623. % Now read the last object in the object stream. Since it
  624. % is the last object, we can use the original stream and
  625. % terminate when we hit the end of the stream
  626. % Stack: objstreamdict First N objectstream [obj#]
  627. 2 index 1 sub get % Get our current object number
  628. dup resolved? not { % If we do not already have this object
  629. exch % Get our object stream
  630. resolveobjstreamopdict .pdfrun % Get PDF object
  631. Objects exch 2 index exch lput % Put object into Objects array
  632. } if
  633. pop pop pop pop % Clear stack
  634. } bind def
  635. /no_debug_dict <<
  636. /PDFDEBUG false
  637. >> readonly def
  638. % Resolve all objects in an object stream
  639. /resolveobjectstream { % <object stream #> resolveobjectstream -
  640. PDFDEBUG { (%Resolving object stream: ) print } if
  641. dup 0 resolveR % Get the objectstream dict, all objstrms use 0 as the gen #
  642. dup /Type get /ObjStm ne { % Verify type is object stream
  643. ( **** Incorrect Type in object stream dictionary.\n) pdfformaterror
  644. /resolveobjectstream cvx /typecheck signalerror
  645. } if
  646. dup /N get % Save number of objects onto the stack
  647. 1 index false resolvestream % Convert stream dict into a stream
  648. /ReusableStreamDecode filter % We need to be able to position stream
  649. % Objectstreams begin with list of object numbers and locations
  650. 1 index array % Create array for holding object number
  651. % Get the object numbers
  652. 0 1 4 index 1 sub { % Loop and collect obj numbers
  653. % Stack: strm# objstreamdict N PDFDEBUG objectstream [obj#] loopindex
  654. 1 index 1 index % Setup to put obj# into object number array
  655. 4 index token pop put % Get stream, then get obj# and put into array
  656. 2 index token pop pop pop % Get stream, get obj loc and clear stack
  657. } for
  658. % Move to the start of the object data
  659. 1 index 4 index /First get % Get objectstream and start of first object
  660. setfileposition % Move to the start of the data
  661. % We disable PDFDEBUG while reading the data stream. We will
  662. % print the data later
  663. PDFDEBUG { //no_debug_dict begin } if
  664. % Read the data for all objects. We check to see if we get
  665. % the number of objects that we expect.
  666. % Stack: strm# objstreamdict N objectstream [obj#] PDFDEBUG
  667. mark 3 -1 roll % Get objectstream
  668. count 4 index add % Determine stack depth with objects
  669. 3 1 roll
  670. resolveobjstreamopdict .pdfrun % Get PDF objects
  671. count counttomark 1 add index ne { % Check stack depth
  672. ( **** Incorrect object count in object stream.\n) pdfformaterror
  673. /resolveobjectstream cvx /rangecheck signalerror
  674. } if
  675. % We have the object data
  676. counttomark array astore % Put objects into an array
  677. exch pop exch pop % Remove mark and count
  678. currentdict //no_debug_dict eq { end } if % Restore debug context
  679. % Save the objects into Objects
  680. 0 1 2 index length 1 sub { % Loop through all objects
  681. % Stack: strm# objstreamdict N [obj#] [objects] loopindex
  682. dup 3 index exch get % Get our current object number
  683. % Stack: strm# objstreamdict N [obj#] [objects] loopindex obj#
  684. dup ObjectStream exch lget 7 index eq {
  685. dup resolved? { % If we already have this object
  686. pop pop % Remove object and object number
  687. } { % Else if we do not have this object
  688. PDFDEBUG { (%Resolving compressed object: [) print dup =only ( 0]) = } if
  689. Objects exch 3 index % Put the object into Objects
  690. 3 index get
  691. PDFDEBUG { dup === flush } if
  692. lput
  693. } ifelse
  694. } {
  695. pop % Ignore old object; remove object number.
  696. } ifelse
  697. pop % Remove loop index
  698. } for
  699. pop pop pop pop pop % Remove strm# objstream, N, (obj#], and [objects]
  700. } bind def
  701. currentdict /no_debug_dict undef
  702. % When resolving an object reference, we stop at the endobj or endstream.
  703. /resolveopdict mark
  704. valueopdict { } forall
  705. /endstream { endobj exit } bind
  706. /endobj { endobj exit } bind
  707. /endjobj { % Bug 689876.
  708. ( **** Operator 'endobj' is misspelled as 'endjobj'.\n) pdfformaterror
  709. endobj exit
  710. } bind
  711. /enbobj { % Bug 690397.
  712. ( **** Operator 'endobj' is misspelled as 'enbobj'.\n) pdfformaterror
  713. endobj exit
  714. } bind
  715. % OmniForm generates PDF file with endobj missing in some
  716. % objects. AR ignores this. So we have to do it too.
  717. /obj { pop pop endobj exit } bind
  718. .dicttomark readonly def
  719. /resolveR { % <object#> <generation#> resolveR <object>
  720. PDFDEBUG {
  721. PDFSTEPcount 1 le {
  722. (%Resolving: ) print 2 copy 2 array astore ==
  723. } if
  724. } if
  725. 1 index resolved? { % If object has already been resolved ...
  726. exch pop exch pop % then clear stack and return object
  727. } { % Else if not resolved ...
  728. PDFfile fileposition 3 1 roll % Save current file position
  729. 1 index Objects exch lget % Get location of object from xref
  730. 3 1 roll checkgeneration { % Verify the generation number
  731. % Stack: savepos objpos obj#
  732. ObjectStream 1 index lget dup 0 eq { % Check if obj in not an objstream
  733. pop exch PDFoffset add PDFfile exch setfileposition
  734. PDFfile token pop 2 copy ne
  735. { ( **** Unrecoverable error in xref!\n) pdfformaterror
  736. /resolveR cvx /rangecheck signalerror
  737. }
  738. if pop PDFfile token pop
  739. PDFfile token pop /obj ne
  740. { ( **** Unrecoverable error in xref!\n) pdfformaterror
  741. /resolveR cvx /rangecheck signalerror
  742. }
  743. if
  744. pdf_run_resolve % PDFfile resolveopdict .pdfrun
  745. } { % Else the object is in an ObjectStream
  746. % Process an objectstream object. We are going to resolve all
  747. % of the objects in sthe stream and place them into the Objects
  748. % array.
  749. % Stack: savepos objpos obj# objectstream#
  750. resolveobjectstream
  751. resolved? { % If object has already been resolved ...
  752. exch pop % Remove object pos from stack.
  753. } {
  754. pop pop null % Pop objpos and obj#, put null for object
  755. } ifelse
  756. } ifelse
  757. } { % Else the generation number is wrong
  758. % Don't cache if the generation # is wrong.
  759. pop pop null % Pop objpos and obj#, put null for object
  760. } ifelse % ifelse generation number is correct
  761. exch PDFfile exch setfileposition % Return to original file position
  762. } ifelse
  763. } bind def
  764. % ================================ Streams ================================ %
  765. % We represent a stream by an executable dictionary that contains,
  766. % in addition to the contents of the original stream dictionary:
  767. % /File - the file or string where the stream contents are stored,
  768. % if the stream is not an external one.
  769. % /FilePosition - iff File is a file, the position in the file
  770. % where the contents start.
  771. % /StreamKey - the key used to decrypt this stream, if any.
  772. % We do the real work of constructing the data stream only when the
  773. % contents are needed.
  774. % Construct a stream. The length is not reliable in the face of
  775. % different end-of-line conventions, but it's all we've got.
  776. %
  777. % PDF files are inconsistent about what may fall between the 'stream' keyword
  778. % and the actual stream data, and it appears that no one algorithm can
  779. % detect this reliably. We used to try to guess whether the file included
  780. % extraneous \r and/or \n characters, but we no longer attempt to do so,
  781. % especially since the PDF 1.2 specification states flatly that the only
  782. % legal terminators following the 'stream' keyword are \n or \r\n, both of
  783. % which are properly skipped and discarded by the token operator.
  784. % Unfortunately, this doesn't account for other whitespace characters that
  785. % may have preceded the EOL, such as spaces or tabs. Thus we back up one
  786. % character and scan until we find the \n terminator.
  787. /stream { % <dict> stream <modified_dict>
  788. dup /Length oget 0 eq {
  789. dup /Filter undef % don't confuse any filters that require data
  790. } if
  791. dup /F known dup PDFsource PDFfile eq or {
  792. not {
  793. dup /File PDFfile put
  794. % make sure that we are just past the EOL \n character
  795. PDFfile dup fileposition 1 sub setfileposition % back up one
  796. PDFfile read pop
  797. dup 13 eq {
  798. % If there had been a \n, token would have advanced over it
  799. % thus, if the terminator was \r, we have a format error!
  800. ( **** Warning: stream operator not terminated by valid EOL.\n) pdfformaterror
  801. pop % fileposition is OK (just past the \r).
  802. } {
  803. % Otherwise, scan past \n
  804. { 10 eq { exit } if
  805. PDFfile read pop
  806. } loop
  807. } ifelse
  808. dup /FilePosition PDFfile fileposition put
  809. PDFDEBUG {
  810. PDFSTEPcount 1 le {
  811. (%FilePosition: ) print dup /FilePosition get ==
  812. } if
  813. } if
  814. } if
  815. % Some (bad) PDf files have invalid stream lengths. This causes problems
  816. % if we reposition beyond the end of the file. So we compare the given
  817. % length to number of bytes left in the file.
  818. dup /Length oget
  819. dup PDFfile bytesavailable lt { % compare to to bytes left in file
  820. PDFfile fileposition % reposition to the end of stream
  821. add PDFfile exch setfileposition
  822. } {
  823. pop % bad stream length - do not reposition.
  824. % This will force a length warning below
  825. } ifelse
  826. } {
  827. pop
  828. % We're already reading from a stream, which we can't reposition.
  829. % Capture the sub-stream contents in a string.
  830. dup /Length oget string PDFsource exch readstring
  831. not {
  832. ( **** Warning: Unexpected EOF in stream!\n) pdfformaterror
  833. /stream cvx /rangecheck signalerror
  834. } if
  835. 1 index exch /File exch put
  836. } ifelse
  837. PDFsource token_nofail not { //null } if
  838. dup /endobj eq {
  839. % Another case that Acrobat Reader handles -- 'endobj' without 'endstream'.
  840. ( **** Warning: stream missing 'endstream'.\n) pdfformaterror
  841. pop /endstream % fake a valid endstream
  842. } if
  843. /endstream ne {
  844. ( **** Warning: stream Length incorrect.\n) pdfformaterror
  845. dup /Length undef % prevent the use of the incorrect length.
  846. cvx endobj exit % exit from .pdfrun now.
  847. } {
  848. PDFsource (??) .peekstring pop (>>) eq { % Bug 690161, sample #1
  849. ( **** Warning: Spurious '>>' after 'endstream' ignored.\n) pdfformaterror
  850. PDFsource (12) readstring pop pop
  851. } if
  852. } ifelse
  853. cvx
  854. } bind def
  855. /endstream {
  856. exit
  857. } bind def
  858. % Contrary to the published PDF (1.3) specification, Acrobat Reader
  859. % accepts abbreviated filter names everywhere, not just for in-line images,
  860. % and some applications (notably htmldoc) rely on this.
  861. /unabbrevfilterdict mark
  862. /AHx /ASCIIHexDecode /A85 /ASCII85Decode /CCF /CCITTFaxDecode
  863. /DCT /DCTDecode /Fl /FlateDecode /LZW /LZWDecode /RL /RunLengthDecode
  864. .dicttomark readonly def
  865. % Extract and apply filters.
  866. /filterparms { % <dict> <DPkey> <Fkey> filterparms
  867. % <dict> <parms> <filternames>
  868. 2 index exch knownoget {
  869. oforce_recursive
  870. exch 2 index exch knownoget {
  871. % Both filters and parameters.
  872. oforce_recursive
  873. exch dup type /nametype eq {
  874. 1 array astore exch
  875. dup type /arraytype ne { 1 array astore } if exch
  876. } if
  877. } {
  878. % Filters, but no parameters.
  879. //null exch
  880. dup type /nametype eq { 1 array astore } if
  881. } ifelse
  882. } {
  883. % No filters: ignore parameters, if any.
  884. pop //null { }
  885. } ifelse
  886. } bind def
  887. /filtername { % <filtername> filtername <filtername'>
  888. //unabbrevfilterdict 1 index .knownget { exch pop } if
  889. dup /Filter resourcestatus { pop pop } {
  890. Repaired exch % this error is not the creator's fault
  891. ( **** ERROR: Unable to process ) pdfformaterror
  892. 64 string cvs pdfformaterror
  893. ( data. Page will be missing data.\n) pdfformaterror
  894. /Repaired exch store % restore the previous "Repaired" state
  895. % provide a filter that returns EOF (no data)
  896. /.EOFDecode
  897. } ifelse
  898. } bind def
  899. /pdf_rules_dict << /PDFRules //true >> readonly def
  900. % Add PDF option to ASCII85Decode filter
  901. % <source> <name> add_A85_param <source> <dict'> <name>
  902. % <source> <dict> <name> add_A85_param <source> <dict'> <name>
  903. /add_A85_param {
  904. dup /ASCII85Decode eq {
  905. 1 index type /dicttype eq {
  906. 3 -1 roll dup length 1 add dict copy
  907. dup /PDFRules //true put
  908. 3 1 roll
  909. } {
  910. //pdf_rules_dict exch
  911. } ifelse
  912. } if
  913. } bind def
  914. currentdict /pdf_rules_dict undef
  915. /applyfilters { % <parms> <source> <filternames> applyfilters <stream>
  916. 2 index //null eq {
  917. { filtername add_A85_param filter }
  918. } {
  919. { % Stack: parms source filtername
  920. 2 index 0 oget dup //null eq { pop } {
  921. exch filtername dup /JBIG2Decode eq { exch jbig2cachectx exch } if
  922. } ifelse add_A85_param filter
  923. exch dup length 1 sub 1 exch getinterval exch
  924. }
  925. } ifelse forall exch pop
  926. } bind def
  927. % JBIG2 streams have an optional 'globals' stream obj for
  928. % sharing redundant data between page images. Here we resolve
  929. % that stream reference (if any) and run it through the decoder,
  930. % creating a special -jbig2globalctx- postscript object our
  931. % JBIG2Decode filter implementation looks for in the parm dict.
  932. /jbig2cachectx { % <parmdict> jbig2cachectx <parmdict>
  933. dup /JBIG2Globals knownoget {
  934. % make global ctx
  935. PDFfile fileposition exch % resolvestream is not reentrant
  936. true resolvestream % stack after: PDFfileposition -file-
  937. % Read the data in a loop until EOF to so we can move the strings into a bytestring
  938. [ { counttomark 1 add index 60000 string readstring not { exit } if } loop ]
  939. exch pop 0 1 index { length add } forall % compute the total length
  940. % now copy the data from the array of strings into a bytestring
  941. .bytestring exch 0 exch { 3 copy putinterval length add } forall pop
  942. .jbig2makeglobalctx
  943. PDFfile 3 -1 roll setfileposition
  944. 1 index exch
  945. /.jbig2globalctx exch put
  946. } if
  947. } bind def
  948. % When used with a PDF image dict, the JPXDecode filter needs to know
  949. % about any ColorSpace entries, since this overrides whatever is in
  950. % the image stream itself. We therefore propagate any such key into
  951. % a filter's DecodeParms.
  952. /jpxparmfix { % <streamdict> <readdata?> jpxparmfix <streamdict <readdata?>
  953. 1 index /Filter .knownget
  954. { /JPXDecode eq % we only need to do this for JPXDecode filters
  955. % TODO: handle filter arrays
  956. {
  957. 1 index /ColorSpace knownoget {
  958. 2 index /DecodeParms knownoget {
  959. % insert in the existing DecodeParms dict
  960. /ColorSpace 3 -1 roll put
  961. }{
  962. 1 dict % need to create a custom DecodeParms dict
  963. dup /ColorSpace 4 -1 roll put
  964. 2 index exch /DecodeParms exch put
  965. } ifelse
  966. } if
  967. } if
  968. } if
  969. } bind def
  970. % Resolve a stream dictionary to a PostScript stream.
  971. % Streams with no filters require special handling:
  972. % - Whether we are going to interpret the stream, or If we are just
  973. % going to read data from them, we impose a SubFileDecode filter
  974. % that reads just the requisite amount of data.
  975. % Note that, in general, resolving a stream repositions PDFfile.
  976. % Clients must save and restore the position of PDFfile themselves.
  977. /resolvestream { % <streamdict> <readdata?> resolvestream <stream>
  978. jpxparmfix
  979. 1 index /F knownoget {
  980. % This stream is stored on an external file.
  981. (r) file 3 -1 roll
  982. /FDecodeParms /FFilter filterparms
  983. % Stack: readdata? file dict parms filternames
  984. 4 -1 roll exch
  985. pdf_decrypt_stream
  986. applyfilters
  987. } {
  988. exch
  989. dup /Length knownoget { 0 eq } { //false } ifelse {
  990. () 0 () /SubFileDecode filter
  991. } {
  992. dup /FilePosition .knownget {
  993. 1 index /File get exch setfileposition
  994. } if
  995. % Stack: readdata? dict
  996. /DecodeParms /Filter filterparms
  997. % Stack: readdata? dict parms filternames
  998. 2 index /File get exch
  999. % Stack: readdata? dict parms file/string filternames
  1000. dup length 0 eq {
  1001. % All the PDF filters have EOD markers, but in this case
  1002. % there is no specified filter.
  1003. exch dup type /filetype eq 5 index or {
  1004. % Use length for any files or reading data from any source.
  1005. 3 index /Length knownoget not { 0 } if
  1006. } {
  1007. 0 % Otherwise length of 0 for whole string
  1008. } ifelse
  1009. 4 index /IDFlag known { pop } { () /SubFileDecode filter } ifelse
  1010. exch
  1011. pdf_decrypt_stream % add decryption if needed
  1012. pop exch pop
  1013. } {
  1014. % Stack: readdata? dict parms source filternames
  1015. exch 3 index /Length knownoget {
  1016. () /SubFileDecode filter
  1017. } if exch
  1018. pdf_decrypt_stream % add decryption if needed
  1019. applyfilters
  1020. } ifelse
  1021. } ifelse
  1022. } ifelse
  1023. % Stack: readdata? dict file
  1024. exch pop exch pop
  1025. } bind def
  1026. % ============================ Name/number trees ============================ %
  1027. /nameoget { % <nametree> <key> nameoget <obj|null>
  1028. exch /Names exch .treeget
  1029. } bind def
  1030. /numoget { % <numtree> <key> numoget <obj|null>
  1031. exch /Nums exch .treeget
  1032. } bind def
  1033. /.treeget { % <key> <leafkey> <tree> .treeget <obj|null>
  1034. dup /Kids knownoget {
  1035. exch pop .branchget
  1036. } {
  1037. exch get .leafget
  1038. } ifelse
  1039. } bind def
  1040. /.branchget { % <key> <leafkey> <kids> .branchget <obj|null>
  1041. dup length 0 eq {
  1042. pop pop pop null
  1043. } {
  1044. dup length -1 bitshift 2 copy oget
  1045. % Stack: key leafkey kids mid kids[mid]
  1046. dup /Limits oget aload pop
  1047. % Stack: key leafkey kids mid kids[mid] min max
  1048. 6 index lt {
  1049. pop pop
  1050. 1 add 1 index length 1 index sub getinterval .branchget
  1051. } {
  1052. 5 index gt {
  1053. pop
  1054. 0 exch getinterval .branchget
  1055. } {
  1056. exch pop exch pop .treeget
  1057. } ifelse
  1058. } ifelse
  1059. } ifelse
  1060. } bind def
  1061. /.leafget { % <key> <pairs> .leafget <obj|null>
  1062. dup length 2 eq {
  1063. dup 0 get 2 index eq { 1 oget } { pop null } ifelse
  1064. exch pop
  1065. } {
  1066. dup length -1 bitshift -2 and 2 copy oget
  1067. % Stack: key pairs mid pairs[mid]
  1068. 3 index gt { 0 exch } { 1 index length 1 index sub } ifelse
  1069. getinterval .leafget
  1070. } ifelse
  1071. } bind def
  1072. end % pdfdict
  1073. .setglobal