| 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 | |