pdf_base.ps 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. % Copyright (C) 1994, 1996, 1997, 1998, 1999, 2000 Aladdin Enterprises. All rights reserved.
  2. %
  3. % This file is part of AFPL Ghostscript.
  4. %
  5. % AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or
  6. % distributor accepts any responsibility for the consequences of using it, or
  7. % for whether it serves any particular purpose or works at all, unless he or
  8. % she says so in writing. Refer to the Aladdin Free Public License (the
  9. % "License") for full details.
  10. %
  11. % Every copy of AFPL Ghostscript must include a copy of the License, normally
  12. % in a plain ASCII text file named PUBLIC. The License grants you the right
  13. % to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14. % conditions described in the License. Among other things, the License
  15. % requires that the copyright notice and this notice be preserved on all
  16. % copies.
  17. % $Id: pdf_base.ps,v 1.14 2001/03/20 05:04:59 alexcher Exp $
  18. % pdf_base.ps
  19. % Basic parser for PDF reader.
  20. % This handles basic parsing of the file (including the trailer
  21. % and cross-reference table), as well as objects, object references,
  22. % streams, and name/number trees; it doesn't include any facilities for
  23. % making marks on the page.
  24. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  25. .currentglobal true .setglobal
  26. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  27. pdfdict begin
  28. % Define the name interpretation dictionary for reading values.
  29. /valueopdict mark
  30. (<<) cvn { mark } bind % don't push an actual mark!
  31. (>>) cvn /.dicttomark load
  32. ([) cvn { mark } bind % ditto
  33. (]) cvn dup load
  34. % /true true % see .pdfexectoken below
  35. % /false false % ibid.
  36. % /null null % ibid.
  37. /F dup cvx % see Objects section below
  38. /R dup cvx % see Objects section below
  39. /stream dup cvx % see Streams section below
  40. .dicttomark readonly def
  41. % ------ Utilities ------ %
  42. % Define a scratch string. The PDF language definition says that
  43. % no line in a PDF file can exceed 255 characters.
  44. /pdfstring 255 string def
  45. % Read the previous line of a file. If we aren't at a line boundary,
  46. % read the line containing the current position.
  47. % Skip any blank lines.
  48. /prevline % - prevline <startpos> <substring>
  49. { PDFfile fileposition dup () pdfstring
  50. 2 index 257 sub 0 .max PDFfile exch setfileposition
  51. { % Stack: initpos linepos line string
  52. PDFfile fileposition
  53. PDFfile 2 index readline pop
  54. dup length 0 gt
  55. { 3 2 roll 5 -2 roll pop pop 2 index }
  56. { pop }
  57. ifelse
  58. % Stack: initpos linepos line string startpos
  59. PDFfile fileposition 5 index ge { exit } if
  60. pop
  61. }
  62. loop pop pop 3 -1 roll pop
  63. } bind def
  64. % Handle the PDF 1.2 #nn escape convention when reading from a file.
  65. % This should eventually be done in C.
  66. /.pdffixname { % <execname> .pdffixname <execname'>
  67. PDFversion 1.2 ge {
  68. dup .namestring (#) search {
  69. name#escape cvn exch pop
  70. } {
  71. pop
  72. } ifelse
  73. } if
  74. } bind def
  75. /name#escape % <post> <(#)> <pre> name#escape <string>
  76. { exch pop
  77. 1 index 2 () /SubFileDecode filter dup (x) readhexstring
  78. % Stack: post pre stream char t/f
  79. not { /.pdftoken cvx /syntaxerror signalerror } if
  80. exch closefile concatstrings
  81. exch 2 1 index length 2 sub getinterval
  82. (#) search { name#escape } if concatstrings
  83. } bind def
  84. % Execute a file, interpreting its executable names in a given
  85. % dictionary. The name procedures may do whatever they want
  86. % to the operand stack.
  87. /.pdftokenerror { % <count> <opdict> <errtoken> .pdftokenerror -
  88. BXlevel 0 le {
  89. (%stderr) (w) file
  90. dup (****************Unknown operator: ) writestring
  91. dup 2 index .writecvs dup (\n) writestring flushfile
  92. } if pop pop
  93. count exch sub { pop } repeat % pop all the operands
  94. } bind def
  95. /.pdfexectoken { % <count> <opdict> <exectoken> .pdfexectoken ?
  96. DEBUG { dup == flush } if
  97. 2 copy .knownget {
  98. exch pop exch pop exch pop exec
  99. } {
  100. % Normally, true, false, and null would appear in opdict
  101. % and be treated as "operators". However, there is a
  102. % special fast case in the PostScript interpreter for names
  103. % that are defined in, and only in, systemdict and/or
  104. % userdict: putting these three names in the PDF dictionaries
  105. % destroys this property for them, slowing down their
  106. % interpretation in all PostScript code. Therefore, we
  107. % check for them explicitly here instead.
  108. dup dup dup /true eq exch /false eq or exch /null eq or {
  109. exch pop exch pop //systemdict exch get
  110. } {
  111. .pdftokenerror
  112. } ifelse
  113. } ifelse
  114. } bind def
  115. /.pdfrun { % <file> <opdict> .pdfrun -
  116. % Construct a procedure with the stack depth, file and opdict
  117. % bound into it.
  118. 1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll
  119. { % Stack: ..operands.. count opdict file
  120. token {
  121. dup type /nametype eq {
  122. dup xcheck {
  123. .pdfexectoken
  124. } {
  125. .pdffixname
  126. exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  127. } ifelse
  128. } {
  129. exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  130. } ifelse
  131. } {
  132. (%%EOF) cvn cvx .pdfexectoken
  133. } ifelse
  134. }
  135. aload pop .packtomark cvx
  136. /loop cvx 2 packedarray cvx
  137. { stopped /PDFsource } aload pop
  138. PDFsource
  139. { store { stop } if } aload pop .packtomark cvx
  140. /PDFsource 3 -1 roll store exec
  141. } bind def
  142. % Execute a file, like .pdfrun, for a marking context.
  143. % This temporarily rebinds LocalResources and DefaultMatrix.
  144. /.pdfruncontext { % <resdict> <file> <opdict> .pdfruncontext -
  145. /.pdfrun load LocalResources DefaultMatrix
  146. /LocalResources 7 -1 roll store
  147. /DefaultMatrix matrix currentmatrix store
  148. 3 .execn
  149. /DefaultMatrix exch store
  150. /LocalResources exch store
  151. } bind def
  152. % Get the depth of the PDF operand stack. The main program (pdf_main.ps)
  153. % sets pdfemptycount before calling .pdfrun.
  154. /.pdfcount { % - .pdfcount <count>
  155. count pdfemptycount sub
  156. } bind def
  157. % ------ File reading ------ %
  158. % Read the cross-reference entry for an (unresolved) object.
  159. % The caller must save and restore the PDFfile position if desired.
  160. % For invalid (free) objects, we return 0.
  161. /readxrefentry % <object#> readxrefentry <objpos>
  162. { dup Objects exch lget
  163. PDFfile exch setfileposition
  164. PDFfile token pop % object position
  165. PDFfile token pop % generation #
  166. PDFfile token pop % n or f
  167. dup /n eq
  168. { pop 1 add dup 255 gt
  169. { Generations ltype /stringtype eq
  170. { % Convert Generations from a string to an array.
  171. larray Generations llength lgrowto dup
  172. 0 1 2 index llength 1 sub
  173. { Generations 1 index lget lput dup
  174. }
  175. for pop /Generations exch store
  176. }
  177. if
  178. }
  179. if
  180. }
  181. { /f eq
  182. { pop 0 }
  183. { /readxrefentry cvx /syntaxerror signalerror }
  184. ifelse
  185. }
  186. ifelse
  187. % Stack: obj# objpos 1+gen#
  188. Generations 4 -1 roll 3 -1 roll lput
  189. } bind def
  190. % ================================ Objects ================================ %
  191. % Since we may have more than 64K objects, we have to use a 2-D array to
  192. % hold them (and the parallel Generations structure).
  193. /lshift 9 def
  194. /lnshift lshift neg def
  195. /lsubmask 1 lshift bitshift 1 sub def
  196. /lsublen lsubmask 1 add def
  197. /larray { % - larray <larray>
  198. [ [] ]
  199. } bind def
  200. /lstring { % - lstring <lstring>
  201. [ () ]
  202. } bind def
  203. /ltype { % <lseq> type <type>
  204. 0 get type
  205. } bind def
  206. /lget { % <lseq> <index> lget <value>
  207. dup //lsubmask and 3 1 roll //lnshift bitshift get exch get
  208. } bind def
  209. /lput { % <lseq> <index> <value> lput -
  210. 3 1 roll
  211. dup //lsubmask and 4 1 roll //lnshift bitshift get
  212. 3 1 roll put
  213. } bind def
  214. /llength { % <lseq> llength <length>
  215. dup length 1 sub dup //lshift bitshift
  216. 3 1 roll get length add
  217. } bind def
  218. % lgrowto assumes newlength > llength(lseq)
  219. /growto { % <string/array> <length> growto <string'/array'>
  220. 1 index type /stringtype eq { string } { array } ifelse
  221. 2 copy copy pop exch pop
  222. } bind def
  223. /lgrowto { % <lseq> <newlength> lgrowto <lseq'>
  224. dup //lsubmask add //lnshift bitshift dup 3 index length gt {
  225. % Add more sub-arrays. Start by completing the last existing one.
  226. % Stack: lseq newlen newtoplen
  227. 3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto
  228. % Stack: newlen newtoplen lseq
  229. [ exch aload pop
  230. counttomark 2 add -1 roll % newtoplen
  231. counttomark sub { dup 0 0 getinterval lsublen growto } repeat
  232. dup 0 0 getinterval ] exch
  233. } {
  234. pop
  235. } ifelse
  236. % Expand the last sub-array.
  237. 1 sub //lsubmask and 1 add
  238. exch dup dup length 1 sub 2 copy
  239. % Stack: newsublen lseq lseq len-1 lseq len-1
  240. get 5 -1 roll growto put
  241. } bind def
  242. /lforall { % <lseq> <proc> lforall -
  243. /forall cvx 2 packedarray cvx forall
  244. } bind def
  245. % We keep track of PDF objects using the following PostScript variables:
  246. %
  247. % Generations (lstring): Generations[N] holds 1+ the current
  248. % generation number for object number N. (As far as we can tell,
  249. % this is needed only for error checking.) For free objects,
  250. % Generations[N] is 0.
  251. %
  252. % Objects (larray): If object N is loaded, Objects[N] is the actual
  253. % object; otherwise, Objects[N] is an executable integer giving
  254. % the file offset of the object's entry in the cross-reference
  255. % table.
  256. %
  257. % GlobalObjects (dictionary): If object N has been resolved in
  258. % global VM, GlobalObjects[N] is the same as Objects[N]
  259. % (except that GlobalObjects itself is stored in global VM,
  260. % so the entry will not be deleted at the end of the page).
  261. %
  262. % IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in
  263. % global VM. This is an accelerator to avoid having to do a
  264. % dictionary lookup in GlobalObjects when resolving every object.
  265. % Initialize the PDF object tables.
  266. /initPDFobjects { % - initPDFobjects -
  267. /Objects larray def
  268. /Generations lstring def
  269. .currentglobal true .setglobal
  270. /GlobalObjects 20 dict def
  271. .setglobal
  272. /IsGlobal lstring def
  273. } bind def
  274. % Grow the tables to a specified size.
  275. /growPDFobjects { % <minsize> growPDFobjects -
  276. dup Objects llength gt {
  277. dup Objects exch lgrowto /Objects exch def
  278. } if
  279. dup Generations llength gt {
  280. dup Generations exch lgrowto /Generations exch def
  281. } if
  282. dup IsGlobal llength gt {
  283. dup IsGlobal exch lgrowto /IsGlobal exch def
  284. } if
  285. pop
  286. } bind def
  287. % We represent an unresolved object reference by a procedure of the form
  288. % {obj# gen# resolveR}. This is not a possible PDF object, because PDF has
  289. % no way to represent procedures. Since PDF in fact has no way to represent
  290. % any PostScript object that doesn't evaluate to itself, we can 'force'
  291. % a possibly indirect object painlessly with 'exec'.
  292. % Note that since we represent streams by executable dictionaries
  293. % (see below), we need both an xcheck and a type check to determine
  294. % whether an object has been resolved.
  295. /resolved? { % <object#> resolved? <value> true
  296. % <object#> resolved? false
  297. Objects 1 index lget dup xcheck {
  298. dup type /integertype eq {
  299. % Check whether the object is in GlobalObjects.
  300. pop IsGlobal 1 index lget 0 eq {
  301. pop false
  302. } {
  303. % Update Objects from GlobalObjects
  304. DEBUG { (%Global=>local: ) print dup == } if
  305. GlobalObjects 1 index get dup Objects 4 1 roll lput true
  306. } ifelse
  307. } {
  308. exch pop true
  309. } ifelse
  310. } {
  311. exch pop true
  312. } ifelse
  313. } bind def
  314. /oforce /exec load def
  315. /oget { % <array> <index> oget <object>
  316. % <dict> <key> oget <object>
  317. % Before release 6.20, this procedure stored the resolved
  318. % object back into the referring slot. In order to support
  319. % PDF linearization, we no longer do this.
  320. get oforce
  321. } bind def
  322. % A null value in a dictionary is equivalent to an omitted key;
  323. % we must check for this specially.
  324. /knownoget { % <dict> <key> knownoget <value> true
  325. % <dict> <key> knownoget false
  326. % See oget above regarding this procedure.
  327. .knownget {
  328. oforce dup null eq { pop false } { true } ifelse
  329. } {
  330. false
  331. } ifelse
  332. } bind def
  333. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  334. % Per the specification, we convert these to nulls.
  335. /F { % <file#> <object#> <generation#> F <object>
  336. % Some PDF 1.1 files use F as a synonym for f!
  337. .pdfcount 3 lt { f } { pop pop pop null } ifelse
  338. } bind def
  339. /checkgeneration { % <object#> <generation#> checkgeneration <object#> <OK>
  340. Generations 2 index lget 1 sub 1 index eq {
  341. pop true
  342. } {
  343. QUIET not {
  344. Generations 2 index lget 0 eq {
  345. (Warning: reference to free object: )
  346. } {
  347. (Warning: wrong generation: )
  348. } ifelse print 1 index =only ( ) print =only ( R) =
  349. } {
  350. pop
  351. } ifelse false
  352. } ifelse
  353. } bind def
  354. /R { % <object#> <generation#> R <object>
  355. /resolveR cvx 3 packedarray cvx
  356. } bind def
  357. % If we encounter an object definition while reading sequentially,
  358. % we just store it away and keep going.
  359. /objopdict mark
  360. valueopdict { } forall
  361. /endobj dup cvx
  362. .dicttomark readonly def
  363. /obj { % <object#> <generation#> obj <object>
  364. PDFfile objopdict .pdfrun
  365. } bind def
  366. /endobj { % <object#> <generation#> <object> endobj <object>
  367. 3 1 roll
  368. % Read the xref entry if we haven't yet done so.
  369. % This is only needed for generation # checking.
  370. 1 index resolved? {
  371. pop
  372. } {
  373. PDFfile fileposition
  374. 2 index readxrefentry pop
  375. PDFoffset add PDFfile exch setfileposition
  376. } ifelse
  377. checkgeneration {
  378. % The only global objects we bother to save are
  379. % (resource) dictionaries.
  380. 1 index dup gcheck exch type /dicttype eq and {
  381. DEBUG { (%Local=>global: ) print dup == } if
  382. GlobalObjects 1 index 3 index put
  383. IsGlobal 1 index 1 put
  384. } if
  385. Objects exch 2 index lput
  386. } {
  387. pop pop null
  388. } ifelse
  389. } bind def
  390. % When resolving an object reference, we stop at the endobj.
  391. /resolveopdict mark
  392. valueopdict { } forall
  393. /endobj { endobj exit } bind
  394. % OmniForm generates PDF file with endobj missing in some
  395. % objects. AR ignores this. So we have to do it too.
  396. /obj { pop pop endobj exit } bind
  397. .dicttomark readonly def
  398. /resolveR { % <object#> <generation#> resolveR <object>
  399. DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
  400. 1 index resolved? {
  401. exch pop exch pop
  402. } {
  403. PDFfile fileposition 3 1 roll
  404. 1 index readxrefentry
  405. 3 1 roll checkgeneration {
  406. % Stack: savepos objpos obj#
  407. exch PDFoffset add PDFfile exch setfileposition
  408. PDFfile token pop 2 copy ne
  409. { (xref error!) = /resolveR cvx /rangecheck signalerror
  410. }
  411. if pop PDFfile token pop
  412. PDFfile token pop /obj ne
  413. { (xref error!) = /resolveR cvx /rangecheck signalerror
  414. }
  415. if
  416. pdf_run_resolve % PDFfile resolveopdict .pdfrun
  417. }
  418. { % Don't cache if the generation # is wrong.
  419. pop pop null
  420. } ifelse
  421. exch PDFfile exch setfileposition
  422. } ifelse
  423. } bind def
  424. % ================================ Streams ================================ %
  425. % We represent a stream by an executable dictionary that contains,
  426. % in addition to the contents of the original stream dictionary:
  427. % /File - the file or string where the stream contents are stored,
  428. % if the stream is not an external one.
  429. % /FilePosition - iff File is a file, the position in the file
  430. % where the contents start.
  431. % /StreamKey - the key used to decrypt this stream, if any.
  432. % We do the real work of constructing the data stream only when the
  433. % contents are needed.
  434. % Construct a stream. The length is not reliable in the face of
  435. % different end-of-line conventions, but it's all we've got.
  436. %
  437. % PDF files are inconsistent about what may fall between the 'stream' keyword
  438. % and the actual stream data, and it appears that no one algorithm can
  439. % detect this reliably. We used to try to guess whether the file included
  440. % extraneous \r and/or \n characters, but we no longer attempt to do so,
  441. % especially since the PDF 1.2 specification states flatly that the only
  442. % legal terminators following the 'stream' keyword are \n or \r\n, both of
  443. % which are properly skipped and discarded by the token operator.
  444. /stream { % <dict> stream <modified_dict>
  445. dup /F known dup PDFsource PDFfile eq or {
  446. not {
  447. dup /File PDFfile put
  448. dup /FilePosition PDFfile fileposition put
  449. DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
  450. } if
  451. PDFfile fileposition 1 index /Length oget add
  452. PDFfile exch setfileposition
  453. } {
  454. pop
  455. % We're already reading from a stream, which we can't reposition.
  456. % Capture the sub-stream contents in a string.
  457. dup /Length oget string PDFsource exch readstring
  458. not {
  459. (Unexpected EOF in stream!) =
  460. /stream cvx /rangecheck signalerror
  461. } if
  462. 1 index exch /File exch put
  463. } ifelse
  464. PDFsource token pop
  465. /endstream ne { /stream cvx /syntaxerror signalerror } if
  466. cvx
  467. } bind def
  468. /endstream {
  469. exit
  470. } bind def
  471. % Contrary to the published PDF (1.3) specification, Acrobat Reader
  472. % accepts abbreviated filter names everywhere, not just for in-line images,
  473. % and some applications (notably htmldoc) rely on this.
  474. /unabbrevfilterdict mark
  475. /AHx /ASCIIHexDecode /A85 /ASCII85Decode /CCF /CCITTFaxDecode
  476. /DCT /DCTDecode /Fl /FlateDecode /LZW /LZWDecode /RL /RunLengthDecode
  477. .dicttomark readonly def
  478. % Extract and apply filters.
  479. /filterparms { % <dict> <DPkey> <Fkey> filterparms
  480. % <dict> <parms> <filternames>
  481. 2 index exch .knownget {
  482. exch 2 index exch .knownget {
  483. % Both filters and parameters.
  484. exch dup type /nametype eq {
  485. 1 array astore exch 1 array astore exch
  486. } if
  487. } {
  488. % Filters, but no parameters.
  489. null exch
  490. dup type /nametype eq { 1 array astore } if
  491. } ifelse
  492. } {
  493. % No filters: ignore parameters, if any.
  494. pop null { }
  495. } ifelse
  496. } bind def
  497. /filtername { % <filtername> filtername <filtername'>
  498. //unabbrevfilterdict 1 index .knownget { exch pop } if
  499. } bind def
  500. /applyfilters { % <parms> <source> <filternames> applyfilters <stream>
  501. 2 index null eq {
  502. { filtername filter }
  503. } {
  504. { % Stack: parms stream filtername
  505. 2 index 0 oget dup null eq { pop } { exch } ifelse filtername filter
  506. exch dup length 1 sub 1 exch getinterval exch
  507. }
  508. } ifelse forall exch pop
  509. } bind def
  510. % Resolve a stream dictionary to a PostScript stream.
  511. % Streams with no filters require special handling:
  512. % - If we are going to interpret their contents, we let endstream
  513. % terminate the interpretation loop;
  514. % - If we are just going to read data from them, we impose
  515. % a SubFileDecode filter that reads just the requisite amount of data.
  516. % Note that, in general, resolving a stream repositions PDFfile.
  517. % Clients must save and restore the position of PDFfile themselves.
  518. /resolvestream { % <streamdict> <readdata?> resolvestream <stream>
  519. 1 index /F .knownget {
  520. % This stream is stored on an external file.
  521. (r) file 3 -1 roll
  522. /FDecodeParms /FFilter filterparms
  523. % Stack: readdata? file dict parms filternames
  524. 4 -1 roll exch
  525. pdf_decrypt_stream
  526. applyfilters
  527. } {
  528. exch dup /FilePosition .knownget {
  529. 1 index /File get exch setfileposition
  530. } if
  531. % Stack: readdata? dict
  532. /DecodeParms /Filter filterparms
  533. % Stack: readdata? dict parms filternames
  534. 2 index /File get exch
  535. % Stack: readdata? dict parms file/string filternames
  536. pdf_decrypt_stream % add decryption if needed
  537. dup length 0 eq {
  538. % All the PDF filters have EOD markers, but in this case
  539. % there is no specified filter.
  540. pop exch pop
  541. % Stack: readdata? dict file/string
  542. 2 index {
  543. % We're going to read data; use a SubFileDecode filter.
  544. 1 index /Length oget () /SubFileDecode filter
  545. } {
  546. dup type /filetype ne {
  547. % Use a SubFileDecode filter to read from a string.
  548. 0 () /SubFileDecode filter
  549. } if
  550. } ifelse
  551. } {
  552. applyfilters
  553. } ifelse
  554. } ifelse
  555. % Stack: readdata? dict file
  556. exch pop exch pop
  557. } bind def
  558. % ============================ Name/number trees ============================ %
  559. /nameoget { % <nametree> <key> nameoget <obj|null>
  560. exch /Names exch .treeget
  561. } bind def
  562. /numoget { % <numtree> <key> numoget <obj|null>
  563. exch /Nums exch .treeget
  564. } bind def
  565. /.treeget { % <key> <leafkey> <tree> .treeget <obj|null>
  566. dup /Kids knownoget {
  567. exch pop .branchget
  568. } {
  569. exch get .leafget
  570. } ifelse
  571. } bind def
  572. /.branchget { % <key> <leafkey> <kids> .branchget <obj|null>
  573. dup length 0 eq {
  574. pop pop pop null
  575. } {
  576. dup length -1 bitshift 2 copy oget
  577. % Stack: key leafkey kids mid kids[mid]
  578. dup /Limits oget aload pop
  579. % Stack: key leafkey kids mid kids[mid] min max
  580. 6 index lt {
  581. pop pop
  582. 1 add 1 index length 1 index sub getinterval .branchget
  583. } {
  584. 5 index gt {
  585. pop
  586. 0 exch getinterval .branchget
  587. } {
  588. exch pop exch pop .treeget
  589. } ifelse
  590. } ifelse
  591. } ifelse
  592. } bind def
  593. /.leafget { % <key> <pairs> .leafget <obj|null>
  594. dup length 2 eq {
  595. dup 0 get 2 index eq { 1 oget } { pop null } ifelse
  596. exch pop
  597. } {
  598. dup length -1 bitshift -2 and 2 copy oget
  599. % Stack: key pairs mid pairs[mid]
  600. 3 index gt { 0 exch } { 1 index length 1 index sub } ifelse
  601. getinterval .leafget
  602. } ifelse
  603. } bind def
  604. end % pdfdict
  605. .setglobal