-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpdfxref.pas
354 lines (267 loc) · 8.38 KB
/
pdfxref.pas
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
unit pdfXRef;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
interface
uses classes,pdfVars,pdfParse;
type
// Standard - "text" representatioion of the XRef record
PRawXRefLine=^TRawXRefLine;
TRawXRefLine=record
Offset: array[0..9] of char; // 10 digits object offset in the file
Sp1: Char; // 1 space
Gen: Array[0..4] of char; // 5 digits object genetration number
Sp2: Char; // 1 space
n: Char; // 1 char "n" or "f"
Eol: Array[0..1] of char; // CR+LF
end;
TXRefSection= class(TObject)
private
FTrailer: TPDFVar;
FNextSection: TXRefSection;
FStart: integer;
FOrderStart: Integer;
FCount: integer;
FObjects: PIndirectObject;
function GetXRef(ObjectNum: integer): PIndirectObject;
procedure SetXRef(ObjectNum: Integer; ARef: PIndirectObject);
function IndexOf(ObjectNum: integer): integer;
procedure InternalFree;
public
constructor Create(AStart,ACount: Integer);
constructor Parse(Parser: pdfParser);
destructor Destroy;override;
function Contains(ObjectNum:Integer):boolean;
property pdfObject[index: integer]:PIndirectObject read GetXRef Write SetXRef;default;
end;
TXRefTable=class(TObject)
private
FSections: TXRefSection;
FLastSection: TXRefSection;
FRootVar: TPDFVar;
FObjectsCount: Integer;
FMaxObjectNum: Integer;
FMinObjectNum: Integer;
FCachedSection: TXRefSection; // Last Accessed section for faster item iterration
procedure InternalClearList;
function GetXRefByObjectNumber(ObjectNum: integer): PIndirectObject;
function GetXRefByOrder(Index: Integer): PIndirectObject;
public
constructor Create;
destructor Destroy;override;
procedure AddSection(Section: TXRefSection);
procedure Parse(parser: pdfParser);
property RootVar: TPDFVar read FRootVar;
property Count: integer read FObjectsCount;
property XObjects[ObjectNumber: Integer]: PIndirectObject read GetXRefByObjectNumber;default; // returns the xref of the given object number
property Items[index: integer]: PIndirectObject read GetXRefByOrder; // for iterration purposes only
end;
implementation
uses sysutils,pdfNotifiers,pdfContainers;
// XRefSection Class;
Constructor TXRefSection.Create(AStart: Integer; ACount: Integer);
begin
Inherited Create;
FTrailer.Init(ptDictionary,pdfDictionary.Create);
FNextSection:=nil;
FCount:=ACount;
FStart:=AStart;
FOrderStart:=0;
FObjects:=nil;
if FCount>0 then GetMem(FObjects,FCount*SizeOf(TIndirectObject));
end;
procedure TXRefSection.InternalFree;
var i: integer;
begin
if FObjects=nil then exit; // nothing to do
for i := 0 to FCount - 1 do FObjects[i].Obj.Done;
FreeMem(FObjects);
end;
Destructor TXRefSection.Destroy;
begin
FTrailer.Done;
InternalFree;
Inherited Destroy;
end;
Constructor TXRefSection.Parse(parser: PdfParser);
var Raw: PRawXRefLine;
i,xi: integer;
j: byte;
begin
Inherited Create;
FTrailer.Done;
FCount:=-1;
FStart:=-1;
FOrderStart:=0;
FObjects:=nil;
//try to get base object number
if not parser.GetInteger(FStart) then exit;
//try to get objects count
if not parser.GetInteger(FCount) then exit;
if FCount>0 then GetMem(FObjects,FCount*SizeOf(TIndirectObject))
else exit;
parser.SkipTo(['0'..'9']); // Find The First XRef Start
Raw:=PRawXRefLine(parser.Cursor); //just a holder for typecast
for i := 0 to FCount - 1 do
with raw[i] do
begin
FObjects[i].number:=FStart+i;
xi:=00;
for j := 0 to SizeOf(Offset) - 1 do
xi:=xi*10 + (ord(Offset[j])-ord('0'));
FObjects[i].offset:=xi;
xi:=00;
for j := 0 to SizeOf(Gen) - 1 do
xi:=xi*10 + (ord(Gen[j])-ord('0'));
FObjects[i].Generation:=xi;
FObjects[i].n:=n='n';
FObjects[i].Obj.init(true);
end;
parser.Skip(SizeOf(RawXRefLine)*FCount); // jump at the end of xref
end;
function TXRefSection.IndexOf(ObjectNum: Integer):integer;
var i: integer;
begin
i:=ObjectNum-FStart;
if (i>-1) and (i<FCount) then result:=i
else result:=-1;
end;
function TXRefSection.Contains(ObjectNum: Integer): boolean;
begin
result:=IndexOf(ObjectNum)<>-1;
end;
function TXRefSection.GetXRef(ObjectNum: Integer):PIndirectObject;
var i: Integer;
begin
result:=nil;
i:=IndexOf(ObjectNum);
if i<>-1 then Result:=@FObjects[i];
end;
procedure TXRefSection.SetXRef(ObjectNum: Integer; ARef: PIndirectObject);
var i: Integer;
begin
i:=IndexOf(ObjectNum);
if (i<>-1) and (ARef<>nil) then FObjects[i]:=ARef^;
end;
// TXRefTable;
constructor TXRefTable.Create;
begin
inherited create;
FSections:=nil;
FLastSection:=nil;
FCachedSection:=nil;
FMaxObjectNum:=0;
FMinObjectNum:=high(Integer);
InternalClearList;
FRootVar.Init(false);
end;
destructor TXRefTable.Destroy;
begin
InternalClearList;
inherited Destroy;
end;
procedure TXRefTable.InternalClearList;
var Section,Next: TXRefSection;
begin
Section:= FSections;
while Section<>nil do
begin
Next:=Section.FNextSection;
Section.Free;
Section:=Next;
end;
FObjectsCount:=0;
end;
function TXRefTable.GetXRefByObjectNumber(ObjectNum: integer): PIndirectObject;
var Section: TXRefSection;
begin
Section:=FSections;
Result:=nil;
while Section<>nil do
begin
Result:=Section.GetXRef(ObjectNum);
if Result<>nil then exit;
Section:=Section.FNextSection;
end;
end;
function TXRefTable.GetXRefByOrder(Index: Integer): PIndirectObject;
var Section : TXRefSection;
Cnt : Integer;
begin
Result:=nil;
if (index<0) or (index>FObjectsCount-1) then exit;
if (FCachedSection<>nil) and
(Index>=FCachedSection.FOrderStart) then
begin
section:=FCachedSection;
cnt:=Section.FOrderStart;
end
else
begin
cnt:=00;
Section:=FSections;
end;
while Section<>nil do
begin
if Index<Cnt+Section.FCount then Break;
inc(Cnt,Section.FCount);
Section:=Section.FNextSection;
end;
if Section<>nil then Result:=@Section.FObjects[index-cnt];
FCachedSection:=Section;
end;
procedure TXRefTable.AddSection(Section: TXRefSection);
begin
if Section=nil then exit;
// is this the first entry?;
if FSections=nil then FSections:=Section;
// add it to the end of the chain
if FLastSection<>nil then FLastSection.FNextSection:=Section;
// make sure this is the end of the chain;
FLastSection:=section;
Section.FNextSection:=nil;
// Set the first objnum in the section
Section.FOrderStart:=FObjectsCount;
// add new objects count to the global count
inc(FObjectsCount,Section.FCount);
end;
procedure TXRefTable.Parse(Parser: PdfParser);
var
PrevVar: PPDFVar;
Section: TXRefSection;
i: integer;
SMax: Integer;
begin
i:=Parser.FindStartXRef;
Parser.Jump(i);
with Parser do
while true do
begin
if not compare('xref') then exit;
// if pdf_ReadToken(Cursor)<>'xref' then exit;
// xref found, let's read it
// Create New XRefSection for each subsection
while True do
begin
Section:=TXRefSection.Parse(Parser);
if Section=nil then exit;
AddSection(Section);
with Section do
begin
SMax:=Section.FStart+Section.FCount-1;
if FMaxObjectNum<SMax then FMaxObjectNum:=SMax;
if FMinObjectNum>FStart then FMinObjectNum:=FStart;
end;
// pdfnotify(ntInfo,format('min obj num %d | max obj num %d',[FMinObjectNum,FMaxObjectNum]),nil);
//check for and process further subsections ...
if not (Current in ['0'..'9']) then break;
end;
// parse 'trailer dictionary' if any ...
if not compare('trailer') then break;
ParseVar(Section.FTrailer);
{PrevVar:=}Section.FTrailer.Container.FItems:=0;//FindInDictionary('/Prev',Names);
// if FRootVar.varType=ptInvalid then FRootVar:=Section.FTrailer.Container.FindInDictionary('/Root')^; // root is an indirect reference
// pdfNotify(ntInfo,'trailer is +'+Trailervar.asString,nil);
if PrevVar<>nil then parser.Jump(PrevVar.AsInteger);
end;
end;
end.