summaryrefslogtreecommitdiff
path: root/gs/lib/pdf_main.ps
blob: a46f12c3f584de09334484b1204480a080aaf004 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
%    Copyright (C) 1994, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
% 
% This file is part of Aladdin Ghostscript.
% 
% Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
% or distributor accepts any responsibility for the consequences of using it,
% or for whether it serves any particular purpose or works at all, unless he
% or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
% License (the "License") for full details.
% 
% Every copy of Aladdin Ghostscript must include a copy of the License,
% normally in a plain ASCII text file named PUBLIC.  The License grants you
% the right to copy, modify and redistribute Aladdin Ghostscript, but only
% under certain conditions described in the License.  Among other things, the
% License requires that the copyright notice and this notice be preserved on
% all copies.


% pdf_main.ps
% PDF file- and page-level operations.

/.setlanguagelevel where { pop 2 .setlanguagelevel } if
.currentglobal true .setglobal
/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
pdfdict begin

% Patch in an obsolete variable used by some third-party software.
/#? false def

% For simplicity, we use a single interpretation dictionary for all
% PDF graphics execution, even though this is too liberal.
/pdfopdict mark
  objopdict { } forall
  drawopdict { } forall
  /endstream { exit } bind
  (%%EOF) cvn { exit } bind		% for filters
	% PDF 1.1 operators
  /BX { /BXlevel BXlevel 1 add store } bind
  /EX { /BXlevel BXlevel 1 sub store } bind
  /PS { cvx exec } bind
	% PDF 1.2 operators
  /BMC { pop } bind
  /BDC { pop pop } bind
  /EMC { }
  /MP { pop } bind
  /DP { pop pop } bind
.dicttomark readonly def

% ======================== Main program ======================== %

end			% pdfdict
userdict begin

/defaultfontname /Times-Roman def

% Make sure the registered encodings are loaded, so we don't run the risk
% that some of the indices for their names will overflow the packed
% representation.  (Yes, this is a hack.)
SymbolEncoding pop
DingbatsEncoding pop

% Redefine 'run' so it recognizes PDF files.
systemdict begin
/.runps /run load def
/runpdfstring 50 string def		% length is arbitrary
/run
 { dup type /filetype ne { (r) file } if
   dup read
    { dup (%) 0 get eq
       { pop dup //runpdfstring
		% Some invalid files might have extra-long first lines....
	  {  { readline } .internalstopped not { pop pop exit } if
	    pop =string
	  }
	 loop
	 //runpdfstring (PDF-) anchorsearch
	  { pop pop runpdf }
	  { pop cvx .runexec }
	 ifelse
       }
       { 2 copy unread pop .runps
       }
      ifelse
    }
    { closefile
    }
   ifelse
 } bind odef
/runpdf			% <file> runpdf -
 { userdict begin
   /Page# null def
   /Page null def
   /DSCPageCount 0 def
   /PDFSave null def
   GS_PDF_ProcSet begin
   pdfdict begin
   pdfopen begin
   Trailer /Root oget /Pages oget /CropBox knownoget
    { mark /CropBox 3 -1 roll /PAGES pdfmark
    }
   if
   /FirstPage where { pop FirstPage } { 1 } ifelse
   1
   /LastPage where { pop LastPage } { pdfpagecount } ifelse
   QUIET not
    { (Processing pages ) print 2 index =only ( through ) print dup =only
      (.\n) print flush
    }
   if
    { dup /Page# exch store
      QUIET not { (Page ) print dup == flush } if
      pdfgetpage pdfshowpage
    } for
   currentdict pdfclose
   end			% temporary dict
   end			% pdfdict
   end			% userdict
 } bind def
end			% systemdict
% Redefine the procedure that the C code uses for running piped input.
% It is OK to use { (%stdin) run } here, because a startjob cannot occur.
/.runstdin {
  { (%stdin) run } execute0
} bind def

end			% userdict
pdfdict begin

% ======================== File parsing ======================== %

% Read the cross-reference and trailer sections.

/traileropdict mark
  (<<) cvn { mark } bind
  (>>) cvn /.dicttomark load
  ([) cvn { mark } bind		% ditto
  (]) cvn dup load
  /true true
  /false false
  /null null
  /R { /resolveR cvx 3 packedarray cvx } bind	% see Objects below
  /startxref /exit load
.dicttomark readonly def

% Because of EOL conversion, lines with fixed contents might be followed
% by one or more blanks.
/lineeq			% <filestr> <conststr> lineeq <bool>
 { anchorsearch
    { pop { ( ) anchorsearch not { () eq exit } if pop } loop }
    { pop false }
   ifelse
 } bind def
/linene { lineeq not } bind def

% Read (mostly scan) the cross-reference table.
/readxref		% <pos> readxref <trailerdict>
 { PDFoffset add PDFfile exch setfileposition
		% In some PDF files, this position actually points to
		% white space before the xref line.  Skip over this here.
   { PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
   } loop
   PDFfile exch setfileposition
   PDFfile pdfstring readline pop
   (xref) linene { /readxref cvx /syntaxerror signalerror } if
		% Store the xref table entry position for each object.
		% We only need to read the run headers, not every entry.
    { PDFfile token pop		% first object # or trailer
      dup /trailer eq { pop exit } if
      PDFfile pdfstring readline pop
      token pop			% entry count
      exch pop exch
		% This section might be adding new objects:
		% ensure that Objects and Generations are big enough.
		% Stack: count obj#
      2 copy add growPDFobjects
      PDFfile fileposition 3 -1 roll
       { Objects 2 index lget null eq	% later update might have set it
	  { Objects 2 index 2 index cvx lput }
         if exch 1 add exch 20 add
       }
      repeat PDFfile exch setfileposition pop
    } loop
   PDFfile traileropdict .pdfrun
 } bind def

% Open a PDF file and read the header, trailer, and cross-reference.
/pdfopen		% <file> pdfopen <dict>
 { pdfdict readonly pop		% can't do it any earlier than this
   15 dict begin
   /LocalResources 0 dict def
   /PSLevel1 where { pop } { /PSLevel1 false def } ifelse
   cvlit /PDFfile exch def
   /PDFsource PDFfile def
   PDFfile dup 0 setfileposition pdfstring readstring 
   not {/pdfopen cvx /syntaxerror signalerror} if
   (%PDF-) search not {/pdfopen cvx /syntaxerror signalerror} if
   length /PDFoffset exch def pop cvr /PDFversion exch def
   PDFfile dup dup 0 setfileposition bytesavailable 
	% Scan backwards over trailing control-character garbage
	% (nulls, ^Zs, EOLs, blanks).
    { 1 sub 2 copy setfileposition 1 index read pop
      32 gt {exit} if
    } loop
	% Stack: PDFfile endpos
	% The top of the stack is now the file position of the last
	% non-garbage character.
	% We can't use prevline to check for the %%EOF, because if the
	% %%EOF isn't followed by an EOL, prevline will read past the
	% end of the file, and the file will get closed.
	% Acrobat apparently accepts files in which the %%EOF was
	% truncated to %%EO (!); we do the same.
   5 sub 2 copy setfileposition
   1 index (xxxxxx) readstring pop
  (\015%%EO) anchorsearch { pop pop } {
    (\012%%EO) anchorsearch { pop pop } {
      pop /pdfopen cvx /syntaxerror signalerror
    } ifelse
  } ifelse
   1 add setfileposition
   prevline token pop dup type /integertype eq 
    { exch pop cvi		% xref start position
      exch PDFfile exch setfileposition
      prevline (startxref) linene { /pdfopen cvx /syntaxerror signalerror } if
      pop
    }	% else, this file has 'startxref #####' format
    { (startxref) ne { /pdfopen cvx /syntaxerror signalerror } if
      cvi		% xref start position
      exch PDFfile exch setfileposition
    }
    ifelse
		% Stack: xrefpos
   initPDFobjects
	% Read the last cross-reference table.
   readxref /Trailer exch def
   Trailer /Encrypt known
    { pdf_process_Encrypt	% signal error
    }
   if
	% Read any previous cross-reference tables.
   Trailer { /Prev .knownget not { exit } if readxref } loop
	% Create and initialize some caches.
   /PageCount pdfpagecount def
   /PageNumbers PageCount dict def
   /PageIndex PageCount array def
	% Copy bookmarks (outline) to the output.
   Trailer /Root oget /Outlines knownoget
    { /First knownoget
      { { dup writeoutline /Next knownoget not { exit } if } loop } if
    }
   if
   currentdict end
 } bind def

% Write the outline structure for a file.  Uses linkdest (below).
/writeoutline		% <outlinedict> writeoutline -
 { mark
   0 2 index /First knownoget
    { { exch 1 add exch /Next knownoget not { exit } if } loop }
   if
		% stack: dict mark count
   dup 0 eq
    { pop 1 index
    }
    { 2 index /Count knownoget { 0 lt { neg } if } if
      /Count exch 3 index
    }
   ifelse linkdest /Title oget /Title exch /OUT pdfmark
   /First knownoget
    { { dup writeoutline /Next knownoget not { exit } if } loop }
   if
 } bind def

% Close a PDF file.
/pdfclose		% <dict> pdfclose -
 { begin
   PDFfile closefile
   end
 } bind def

% ======================== Page accessing ======================== %

% Get a (possibly inherited) attribute of a page.
/pget			% <pagedict> <key> pget <value> -true-
			% <pagedict> <key> pget -false-
 { 2 copy knownoget
    { exch pop exch pop true
    }
    { exch /Parent knownoget
       { exch pget }
       { pop false }
      ifelse
    }
   ifelse
 } bind def

% Get the value of a resource on a given page.
/rget {			% <resname> <pagedict> <restype> rget <value> -true-
			% <resname> <pagedict> <restype> rget -false-
  LocalResources 1 index knownoget {
     3 index knownoget
  } {
    false
  } ifelse {
    exch pop exch pop exch pop true
  } {
    exch /Resources pget {
      exch knownoget { exch knownoget } { pop false } ifelse
    } {
      pop pop false
    } ifelse
  } ifelse
} bind def

% Get the total number of pages in the document.
/pdfpagecount		% - pdfpagecount <int>
 { Trailer /Root oget /Pages oget /Count oget
 } bind def

% Find the N'th page of the document by iterating through the Pages tree.
% The first page is numbered 1.
/pdffindpage		% <int> pdffindpage <pagedict>
 { dup Trailer /Root oget /Pages oget
    {		% We should be able to tell when we reach a leaf
		% by finding a Type unequal to /Pages.  Unfortunately,
		% some files distributed by Adobe lack the Type key
		% in some of the Pages nodes!  Instead, we check for Kids.
      dup /Kids knownoget not { exit } if
      exch pop null
      0 1 3 index length 1 sub
       { 2 index exch oget
	 dup /Kids known { dup /Count oget } { 1 } ifelse
		% Stack: index kids null node count
	 dup 5 index ge { pop exch pop exit } if
	 5 -1 roll exch sub 4 1 roll pop
       }
      for exch pop
      dup null eq { pop pop 1 null exit } if
    }
   loop
		% Stack: index countleft node
   1 index 1 ne { pop pop /pdffindpage cvx /rangecheck signalerror } if
   exch pop
   PageIndex 2 index 1 sub 2 index put
   PageNumbers 1 index 3 index put
   exch pop
 } bind def

% Find the N'th page of the document.
% The first page is numbered 1.
/pdfgetpage		% <int> pdfgetpage <pagedict>
 { PageIndex 1 index 1 sub get dup null ne
    { exch pop }
    { pop pdffindpage }
   ifelse
 } bind def

% Find the page number of a page object (inverse of pdfgetpage).
/pdfpagenumber		% <pagedict> pdfpagenumber <int>
 {	% We use the simplest and stupidest of all possible algorithms....
   PageNumbers 1 index .knownget
    { exch pop
    }
    { 1 1 PageCount 1 add	% will give a rangecheck if not found
       { dup pdfgetpage oforce 2 index eq { exit } if pop
       }
      for exch pop
    }
   ifelse
 } bind def

% Display a given page.
/boxrect		% [<llx> <lly> <urx> <ury>] boxrect <x> <y> <w> <h>
 { aload pop exch 3 index sub exch 2 index sub
 } bind def
/resolvedest {		% <name|string|other> resolvedest <other|null>
  dup type /nametype eq {
    Trailer /Root oget /Dests knownoget {
      exch knownoget not { null } if
    } {
      null
    } ifelse
  } {
    dup type /stringtype eq {
      Trailer /Root oget /Names oget /Dests knownoget {
	exch nameoget
      } {
	pop null
      } ifelse
    } if
  } ifelse
} bind def
/linkdest {		% <link|outline> linkdest
			%   ([/Page <n>] /View <view> | ) <link|outline>
  dup /Dest knownoget
    { resolvedest
      dup type /dicttype eq { /D get } if
      dup null eq
       { pop }
       { dup 0 oget
	 dup null eq
	  { pop }
	  { dup type /integertype ne { pdfpagenumber } if
	    /Page exch 4 -2 roll
	  }
	 ifelse
	 dup length 1 sub 1 exch getinterval /View exch 3 -1 roll
       }
      ifelse
    }
   if
} bind def
% <pagedict> mark ... -proc- -
/namedactions 8 dict dup begin
  /FirstPage {
    /Page 1 3 -1 roll
  } def
  /LastPage {
    counttomark 2 add index pdfpagecount /Page exch 3 -1 roll
  } def
  /NextPage {
    counttomark 2 add index pdfpagenumber 1 add /Page exch 3 -1 roll
  } def
  /PrevPage {
    counttomark 2 add index pdfpagenumber 1 sub /Page exch 3 -1 roll
  } def
end readonly def
% <pagedict> <annotdict> -proc- -
/annottypes 5 dict dup begin
  /Text {
    mark exch
     { /Rect /Open /Contents }
     { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
    forall pop /ANN pdfmark
  } bind def
  /Link {
    mark exch
     { /Rect /Border }
     { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
    forall dup /A knownoget {
      dup /D knownoget {
	exch pop exch dup length dict copy dup /Dest 4 -1 roll put
      } {
	/N knownoget {		% Assume /S /Named
	  namedactions exch .knownget { exec } if
	} if
      } ifelse
    } if
    linkdest pop /LNK pdfmark
  } bind def
end readonly def

/pdfshowpage		% <pagedict> pdfshowpage -
 { dup /Page exch store
   pdfshowpage_init 
   pdfshowpage_setpage 
   save /PDFSave exch store
   (before exec) VMDEBUG
     pdfshowpage_finish
   (after exec) VMDEBUG
   PDFSave restore
 } bind def

/pdfpagecontents	% <pagedict> pdfpagecontents <contents>
 { } bind def

/pdfshowpage_init 	% <pagedict> pdfshowpage_init <pagedict>
 { /DSCPageCount DSCPageCount 1 add store
 } bind def

/pdfshowpage_setpage {	% <pagedict> pdfshowpage_setpage <pagedict>
  4 dict begin		% for setpagedevice
	% Stack: pagedict
	% We want to look at Rotate for displays, but not for printers.
	% The following is a hack, but we don't know a better way to do this.
  currentpagedevice dup /OutputFile known not {
    /Orientation 2 index /Rotate pget not { 0 } if 90 idiv
	% Rotate specifies *clockwise* rotation!
    neg 3 and def
  } if
	% Stack: pagedict currentpagedict
  1 index /MediaBox pget {
			% Set the page size.
    boxrect [ 2 index 2 index ]
	% Stack: pagedict currentpagedict llx lly width height pagesize
    /PageSize exch def
			% Set the offset on the page.
	% Stack: pagedict currentpagedict llx lly width height
    pop pop exch neg exch neg
	% Stack: pagedict currentpagedict -llx -lly
    [ 3 1 roll /translate load 4 index /Install .knownget {
      1 array astore 0 /get load /exec load
    } if ] cvx
	% Stack: pagedict currentpagedict installproc
    /Install exch def
  } if
	% Stack: pagedict currentpagedict
  pop currentdict end setpagedevice
} bind def

/pdfshowpage_finish	% <pagedict> pdfshowpage_finish -
 {
	% Copy crop box.
   dup /CropBox pget
    { boxrect rectclip
      dup /CropBox knownoget { mark /CropBox 3 -1 roll /PAGE pdfmark } if
    }
   if

	% Copy annotations and links.
   dup /Annots knownoget
    { 0 1 2 index length 1 sub
       { 1 index exch oget
         dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse
       }
      for pop
    }
   if

	% Display the actual page contents.
   2 dict begin
   /BXlevel 0 def
   matrix currentmatrix beginpage setmatrix
   /Contents knownoget not { 0 array } if
   dup type /arraytype ne { 1 array astore } if
    { oforce false resolvestream pdfopdict .pdfrun } forall
   endpage
   end			% scratch dict
 } bind def

end			% pdfdict
.setglobal