1 | /* |
2 | * << Haru Free PDF Library >> -- hpdf_xref.c |
3 | * |
4 | * URL: http://libharu.org |
5 | * |
6 | * Copyright (c) 1999-2006 Takeshi Kanno <takeshi_kanno@est.hi-ho.ne.jp> |
7 | * Copyright (c) 2007-2009 Antony Dovgal <tony@daylessday.org> |
8 | * |
9 | * Permission to use, copy, modify, distribute and sell this software |
10 | * and its documentation for any purpose is hereby granted without fee, |
11 | * provided that the above copyright notice appear in all copies and |
12 | * that both that copyright notice and this permission notice appear |
13 | * in supporting documentation. |
14 | * It is provided "as is" without express or implied warranty. |
15 | * |
16 | */ |
17 | |
18 | #include "hpdf_conf.h" |
19 | #include "hpdf_utils.h" |
20 | #include "hpdf_objects.h" |
21 | |
22 | static HPDF_STATUS |
23 | WriteTrailer (HPDF_Xref xref, |
24 | HPDF_Stream stream); |
25 | |
26 | |
27 | HPDF_Xref |
28 | HPDF_Xref_New (HPDF_MMgr mmgr, |
29 | HPDF_UINT32 offset) |
30 | { |
31 | HPDF_Xref xref; |
32 | HPDF_XrefEntry new_entry; |
33 | |
34 | HPDF_PTRACE((" HPDF_Xref_New\n" )); |
35 | |
36 | xref = (HPDF_Xref)HPDF_GetMem (mmgr, sizeof(HPDF_Xref_Rec)); |
37 | if (!xref) |
38 | return NULL; |
39 | |
40 | HPDF_MemSet (xref, 0, sizeof(HPDF_Xref_Rec)); |
41 | xref->mmgr = mmgr; |
42 | xref->error = mmgr->error; |
43 | xref->start_offset = offset; |
44 | |
45 | xref->entries = HPDF_List_New (mmgr, HPDF_DEFALUT_XREF_ENTRY_NUM); |
46 | if (!xref->entries) |
47 | goto Fail; |
48 | |
49 | xref->addr = 0; |
50 | |
51 | if (xref->start_offset == 0) { |
52 | new_entry = (HPDF_XrefEntry)HPDF_GetMem (mmgr, |
53 | sizeof(HPDF_XrefEntry_Rec)); |
54 | if (!new_entry) |
55 | goto Fail; |
56 | |
57 | if (HPDF_List_Add (xref->entries, new_entry) != HPDF_OK) { |
58 | HPDF_FreeMem (mmgr, new_entry); |
59 | goto Fail; |
60 | } |
61 | |
62 | /* add first entry which is free entry and whose generation |
63 | * number is 0 |
64 | */ |
65 | new_entry->entry_typ = HPDF_FREE_ENTRY; |
66 | new_entry->byte_offset = 0; |
67 | new_entry->gen_no = HPDF_MAX_GENERATION_NUM; |
68 | new_entry->obj = NULL; |
69 | } |
70 | |
71 | xref->trailer = HPDF_Dict_New (mmgr); |
72 | if (!xref->trailer) |
73 | goto Fail; |
74 | |
75 | return xref; |
76 | |
77 | Fail: |
78 | HPDF_PTRACE((" HPDF_Xref_New failed\n" )); |
79 | HPDF_Xref_Free (xref); |
80 | return NULL; |
81 | } |
82 | |
83 | |
84 | void |
85 | HPDF_Xref_Free (HPDF_Xref xref) |
86 | { |
87 | HPDF_UINT i; |
88 | HPDF_XrefEntry entry; |
89 | HPDF_Xref tmp_xref; |
90 | |
91 | HPDF_PTRACE((" HPDF_Xref_Free\n" )); |
92 | |
93 | /* delete xref entries. where prev element is not NULL, |
94 | * delete all xref entries recursively. |
95 | */ |
96 | while (xref) { |
97 | /* delete all objects belong to the xref. */ |
98 | |
99 | if (xref->entries) { |
100 | for (i = 0; i < xref->entries->count; i++) { |
101 | entry = HPDF_Xref_GetEntry (xref, i); |
102 | if (entry->obj) |
103 | HPDF_Obj_ForceFree (xref->mmgr, entry->obj); |
104 | HPDF_FreeMem (xref->mmgr, entry); |
105 | } |
106 | |
107 | HPDF_List_Free(xref->entries); |
108 | } |
109 | |
110 | if (xref->trailer) |
111 | HPDF_Dict_Free (xref->trailer); |
112 | |
113 | tmp_xref = xref->prev; |
114 | HPDF_FreeMem (xref->mmgr, xref); |
115 | xref = tmp_xref; |
116 | } |
117 | } |
118 | |
119 | |
120 | HPDF_STATUS |
121 | HPDF_Xref_Add (HPDF_Xref xref, |
122 | void *obj) |
123 | { |
124 | HPDF_XrefEntry entry; |
125 | HPDF_Obj_Header *; |
126 | |
127 | HPDF_PTRACE((" HPDF_Xref_Add\n" )); |
128 | |
129 | if (!obj) { |
130 | if (HPDF_Error_GetCode (xref->error) == HPDF_OK) |
131 | return HPDF_SetError (xref->error, HPDF_INVALID_OBJECT, 0); |
132 | else |
133 | return HPDF_INVALID_OBJECT; |
134 | } |
135 | |
136 | header = (HPDF_Obj_Header *)obj; |
137 | |
138 | if (header->obj_id & HPDF_OTYPE_DIRECT || |
139 | header->obj_id & HPDF_OTYPE_INDIRECT) |
140 | return HPDF_SetError(xref->error, HPDF_INVALID_OBJECT, 0); |
141 | |
142 | if (xref->entries->count >= HPDF_LIMIT_MAX_XREF_ELEMENT) { |
143 | HPDF_SetError(xref->error, HPDF_XREF_COUNT_ERR, 0); |
144 | goto Fail; |
145 | } |
146 | |
147 | /* in the following, we have to dispose the object when an error is |
148 | * occured. |
149 | */ |
150 | |
151 | entry = (HPDF_XrefEntry)HPDF_GetMem (xref->mmgr, |
152 | sizeof(HPDF_XrefEntry_Rec)); |
153 | if (entry == NULL) |
154 | goto Fail; |
155 | |
156 | if (HPDF_List_Add(xref->entries, entry) != HPDF_OK) { |
157 | HPDF_FreeMem (xref->mmgr, entry); |
158 | goto Fail; |
159 | } |
160 | |
161 | entry->entry_typ = HPDF_IN_USE_ENTRY; |
162 | entry->byte_offset = 0; |
163 | entry->gen_no = 0; |
164 | entry->obj = obj; |
165 | header->obj_id = xref->start_offset + xref->entries->count - 1 + |
166 | HPDF_OTYPE_INDIRECT; |
167 | |
168 | header->gen_no = entry->gen_no; |
169 | |
170 | return HPDF_OK; |
171 | |
172 | Fail: |
173 | HPDF_Obj_ForceFree(xref->mmgr, obj); |
174 | return HPDF_Error_GetCode (xref->error); |
175 | } |
176 | |
177 | HPDF_XrefEntry |
178 | HPDF_Xref_GetEntry (HPDF_Xref xref, |
179 | HPDF_UINT index) |
180 | { |
181 | HPDF_PTRACE((" HPDF_Xref_GetEntry\n" )); |
182 | |
183 | return (HPDF_XrefEntry)HPDF_List_ItemAt(xref->entries, index); |
184 | } |
185 | |
186 | |
187 | HPDF_XrefEntry |
188 | HPDF_Xref_GetEntryByObjectId (HPDF_Xref xref, |
189 | HPDF_UINT obj_id) |
190 | { |
191 | HPDF_Xref tmp_xref = xref; |
192 | |
193 | HPDF_PTRACE((" HPDF_Xref_GetEntryByObjectId\n" )); |
194 | |
195 | while (tmp_xref) { |
196 | HPDF_UINT i; |
197 | |
198 | if (tmp_xref->entries->count + tmp_xref->start_offset > obj_id) { |
199 | HPDF_SetError (xref->error, HPDF_INVALID_OBJ_ID, 0); |
200 | return NULL; |
201 | } |
202 | |
203 | if (tmp_xref->start_offset < obj_id) { |
204 | for (i = 0; i < tmp_xref->entries->count; i++) { |
205 | if (tmp_xref->start_offset + i == obj_id) { |
206 | HPDF_XrefEntry entry = HPDF_Xref_GetEntry(tmp_xref, i); |
207 | |
208 | return entry; |
209 | } |
210 | } |
211 | } |
212 | |
213 | tmp_xref = tmp_xref->prev; |
214 | } |
215 | |
216 | return NULL; |
217 | } |
218 | |
219 | |
220 | HPDF_STATUS |
221 | HPDF_Xref_WriteToStream (HPDF_Xref xref, |
222 | HPDF_Stream stream, |
223 | HPDF_Encrypt e) |
224 | { |
225 | HPDF_STATUS ret; |
226 | HPDF_UINT i; |
227 | char buf[HPDF_SHORT_BUF_SIZ]; |
228 | char* pbuf; |
229 | char* eptr = buf + HPDF_SHORT_BUF_SIZ - 1; |
230 | HPDF_UINT str_idx; |
231 | HPDF_Xref tmp_xref = xref; |
232 | |
233 | /* write each objects of xref to the specified stream */ |
234 | |
235 | HPDF_PTRACE((" HPDF_Xref_WriteToStream\n" )); |
236 | |
237 | while (tmp_xref) { |
238 | if (tmp_xref->start_offset == 0) |
239 | str_idx = 1; |
240 | else |
241 | str_idx = 0; |
242 | |
243 | for (i = str_idx; i < tmp_xref->entries->count; i++) { |
244 | HPDF_XrefEntry entry = |
245 | (HPDF_XrefEntry)HPDF_List_ItemAt (tmp_xref->entries, i); |
246 | HPDF_UINT obj_id = tmp_xref->start_offset + i; |
247 | HPDF_UINT16 gen_no = entry->gen_no; |
248 | |
249 | entry->byte_offset = stream->size; |
250 | |
251 | pbuf = buf; |
252 | pbuf = HPDF_IToA (pbuf, obj_id, eptr); |
253 | *pbuf++ = ' '; |
254 | pbuf = HPDF_IToA (pbuf, gen_no, eptr); |
255 | HPDF_StrCpy(pbuf, " obj\012" , eptr); |
256 | |
257 | if ((ret = HPDF_Stream_WriteStr (stream, buf)) != HPDF_OK) |
258 | return ret; |
259 | |
260 | if (e) |
261 | HPDF_Encrypt_InitKey (e, obj_id, gen_no); |
262 | |
263 | if ((ret = HPDF_Obj_WriteValue (entry->obj, stream, e)) != HPDF_OK) |
264 | return ret; |
265 | |
266 | if ((ret = HPDF_Stream_WriteStr (stream, "\012endobj\012" )) |
267 | != HPDF_OK) |
268 | return ret; |
269 | } |
270 | |
271 | tmp_xref = tmp_xref->prev; |
272 | } |
273 | |
274 | /* start to write cross-reference table */ |
275 | |
276 | tmp_xref = xref; |
277 | |
278 | while (tmp_xref) { |
279 | tmp_xref->addr = stream->size; |
280 | |
281 | pbuf = buf; |
282 | pbuf = (char *)HPDF_StrCpy (pbuf, "xref\012" , eptr); |
283 | pbuf = HPDF_IToA (pbuf, tmp_xref->start_offset, eptr); |
284 | *pbuf++ = ' '; |
285 | pbuf = HPDF_IToA (pbuf, tmp_xref->entries->count, eptr); |
286 | HPDF_StrCpy (pbuf, "\012" , eptr); |
287 | ret = HPDF_Stream_WriteStr (stream, buf); |
288 | if (ret != HPDF_OK) |
289 | return ret; |
290 | |
291 | for (i = 0; i < tmp_xref->entries->count; i++) { |
292 | HPDF_XrefEntry entry = HPDF_Xref_GetEntry(tmp_xref, i); |
293 | |
294 | pbuf = buf; |
295 | pbuf = HPDF_IToA2 (pbuf, entry->byte_offset, HPDF_BYTE_OFFSET_LEN + |
296 | 1); |
297 | *pbuf++ = ' '; |
298 | pbuf = HPDF_IToA2 (pbuf, entry->gen_no, HPDF_GEN_NO_LEN + 1); |
299 | *pbuf++ = ' '; |
300 | *pbuf++ = entry->entry_typ; |
301 | HPDF_StrCpy (pbuf, "\015\012" , eptr); /* Acrobat 8.15 requires both \r and \n here */ |
302 | ret = HPDF_Stream_WriteStr (stream, buf); |
303 | if (ret != HPDF_OK) |
304 | return ret; |
305 | } |
306 | |
307 | tmp_xref = tmp_xref->prev; |
308 | } |
309 | |
310 | /* write trailer dictionary */ |
311 | ret = WriteTrailer (xref, stream); |
312 | |
313 | return ret; |
314 | } |
315 | |
316 | static HPDF_STATUS |
317 | WriteTrailer (HPDF_Xref xref, |
318 | HPDF_Stream stream) |
319 | { |
320 | HPDF_UINT max_obj_id = xref->entries->count + xref->start_offset; |
321 | HPDF_STATUS ret; |
322 | |
323 | HPDF_PTRACE ((" WriteTrailer\n" )); |
324 | |
325 | if ((ret = HPDF_Dict_AddNumber (xref->trailer, "Size" , max_obj_id)) |
326 | != HPDF_OK) |
327 | return ret; |
328 | |
329 | if (xref->prev) |
330 | if ((ret = HPDF_Dict_AddNumber (xref->trailer, "Prev" , |
331 | xref->prev->addr)) != HPDF_OK) |
332 | return ret; |
333 | |
334 | if ((ret = HPDF_Stream_WriteStr (stream, "trailer\012" )) != HPDF_OK) |
335 | return ret; |
336 | |
337 | if ((ret = HPDF_Dict_Write (xref->trailer, stream, NULL)) != HPDF_OK) |
338 | return ret; |
339 | |
340 | if ((ret = HPDF_Stream_WriteStr (stream, "\012startxref\012" )) != HPDF_OK) |
341 | return ret; |
342 | |
343 | if ((ret = HPDF_Stream_WriteUInt (stream, xref->addr)) != HPDF_OK) |
344 | return ret; |
345 | |
346 | if ((ret = HPDF_Stream_WriteStr (stream, "\012%%EOF\012" )) != HPDF_OK) |
347 | return ret; |
348 | |
349 | return HPDF_OK; |
350 | } |
351 | |
352 | |