Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/pdf/pdf_repair.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2020-2022 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, for further information.
14
*/
15
16
/* Routines to attempt repair of PDF files in the event of an error */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "strmio.h"
21
#include "stream.h"
22
#include "pdf_deref.h"
23
#include "pdf_dict.h"
24
#include "pdf_file.h"
25
#include "pdf_misc.h"
26
#include "pdf_repair.h"
27
28
static int pdfi_repair_add_object(pdf_context *ctx, int64_t obj, int64_t gen, gs_offset_t offset)
29
1.28M
{
30
    /* Although we can handle object numbers larger than this, on some systems (32-bit Windows)
31
     * memset is limited to a (signed!) integer for the size of memory to clear. We could deal
32
     * with this by clearing the memory in blocks, but really, this is almost certainly a
33
     * corrupted file or something.
34
     */
35
1.28M
    if (obj >= 0x7ffffff / sizeof(xref_entry) || obj < 1 || gen < 0 || offset < 0)
36
997
        return_error(gs_error_rangecheck);
37
38
1.28M
    if (ctx->xref_table == NULL) {
39
25.6k
        ctx->xref_table = (xref_table_t *)gs_alloc_bytes(ctx->memory, sizeof(xref_table_t), "repair xref table");
40
25.6k
        if (ctx->xref_table == NULL) {
41
0
            return_error(gs_error_VMerror);
42
0
        }
43
25.6k
        memset(ctx->xref_table, 0x00, sizeof(xref_table_t));
44
25.6k
        ctx->xref_table->xref = (xref_entry *)gs_alloc_bytes(ctx->memory, (obj + 1) * sizeof(xref_entry), "repair xref table");
45
25.6k
        if (ctx->xref_table->xref == NULL){
46
0
            gs_free_object(ctx->memory, ctx->xref_table, "failed to allocate xref table entries for repair");
47
0
            ctx->xref_table = NULL;
48
0
            return_error(gs_error_VMerror);
49
0
        }
50
25.6k
        memset(ctx->xref_table->xref, 0x00, (obj + 1) * sizeof(xref_entry));
51
25.6k
        ctx->xref_table->ctx = ctx;
52
25.6k
        ctx->xref_table->type = PDF_XREF_TABLE;
53
25.6k
        ctx->xref_table->xref_size = obj + 1;
54
#if REFCNT_DEBUG
55
        ctx->xref_table->UID = ctx->ref_UID++;
56
        dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID);
57
#endif
58
25.6k
        pdfi_countup(ctx->xref_table);
59
1.25M
    } else {
60
1.25M
        if (ctx->xref_table->xref_size < (obj + 1)) {
61
450k
            xref_entry *new_xrefs;
62
63
450k
            new_xrefs = (xref_entry *)gs_alloc_bytes(ctx->memory, (obj + 1) * sizeof(xref_entry), "read_xref_stream allocate xref table entries");
64
450k
            if (new_xrefs == NULL){
65
0
                pdfi_countdown(ctx->xref_table);
66
0
                ctx->xref_table = NULL;
67
0
                return_error(gs_error_VMerror);
68
0
            }
69
450k
            memset(new_xrefs, 0x00, (obj + 1) * sizeof(xref_entry));
70
450k
            memcpy(new_xrefs, ctx->xref_table->xref, ctx->xref_table->xref_size * sizeof(xref_entry));
71
450k
            gs_free_object(ctx->memory, ctx->xref_table->xref, "reallocated xref entries");
72
450k
            ctx->xref_table->xref = new_xrefs;
73
450k
            ctx->xref_table->xref_size = obj + 1;
74
450k
        }
75
1.25M
    }
76
1.28M
    ctx->xref_table->xref[obj].compressed = false;
77
1.28M
    ctx->xref_table->xref[obj].free = false;
78
1.28M
    ctx->xref_table->xref[obj].object_num = obj;
79
1.28M
    ctx->xref_table->xref[obj].u.uncompressed.generation_num = gen;
80
1.28M
    ctx->xref_table->xref[obj].u.uncompressed.offset = offset;
81
1.28M
    return 0;
82
1.28M
}
83
84
int pdfi_repair_file(pdf_context *ctx)
85
132k
{
86
132k
    int code = 0;
87
132k
    gs_offset_t offset, saved_offset;
88
132k
    int64_t object_num = 0, generation_num = 0;
89
132k
    int i;
90
132k
    gs_offset_t outer_saved_offset[3];
91
92
132k
    if (ctx->repaired) {
93
95.5k
        pdfi_set_error(ctx, 0, NULL, E_PDF_UNREPAIRABLE, "pdfi_repair_file", (char *)"%% Trying to repair file for second time -- unrepairable");
94
95.5k
        return_error(gs_error_undefined);
95
95.5k
    }
96
97
37.2k
    saved_offset = pdfi_unread_tell(ctx);
98
99
37.2k
    ctx->repaired = true;
100
37.2k
    pdfi_set_error(ctx, 0, NULL, E_PDF_REPAIRED, "pdfi_repair_file", NULL);
101
102
37.2k
    pdfi_clearstack(ctx);
103
104
37.2k
    if(ctx->args.pdfdebug)
105
37.2k
        dmprintf(ctx->memory, "%% Error encountered in opening PDF file, attempting repair\n");
106
107
    /* First try to locate a %PDF header. If we can't find one, abort this, the file is too broken
108
     * and may not even be a PDF file.
109
     */
110
37.2k
    pdfi_seek(ctx, ctx->main_stream, 0, SEEK_SET);
111
37.2k
    {
112
37.2k
        static const char test[] = "%PDF";
113
37.2k
        int index = 0;
114
115
148k
        do {
116
148k
            int c = pdfi_read_byte(ctx, ctx->main_stream);
117
148k
            if (c < 0)
118
55
                goto exit;
119
120
148k
            if (c == test[index])
121
148k
                index++;
122
0
            else
123
0
                index = 0;
124
148k
        } while (index < 4);
125
37.2k
        if (index != 4) {
126
0
            code = gs_note_error(gs_error_undefined);
127
0
            goto exit;
128
0
        }
129
37.2k
        pdfi_unread(ctx, ctx->main_stream, (byte *)test, 4);
130
37.2k
        pdfi_skip_comment(ctx, ctx->main_stream);
131
37.2k
    }
132
37.2k
    if (ctx->main_stream->eof == true) {
133
260
        code = gs_note_error(gs_error_ioerror);
134
260
        goto exit;
135
260
    }
136
137
    /* First pass, identify all the objects of the form x y obj */
138
139
1.26M
    do {
140
1.26M
        code = pdfi_skip_white(ctx, ctx->main_stream);
141
1.26M
        if (code < 0) {
142
0
            if (code != gs_error_VMerror && code != gs_error_ioerror) {
143
0
                pdfi_clearstack(ctx);
144
0
                continue;
145
0
            } else
146
0
                goto exit;
147
0
        }
148
1.26M
        offset = pdfi_unread_tell(ctx);
149
1.26M
        outer_saved_offset[0] = outer_saved_offset[1] = outer_saved_offset[2] = 0;
150
11.8M
        do {
151
11.8M
            outer_saved_offset[0] = outer_saved_offset[1];
152
11.8M
            outer_saved_offset[1] = outer_saved_offset[2];
153
11.8M
            outer_saved_offset[2] = pdfi_unread_tell(ctx);
154
155
11.8M
            object_num = 0;
156
157
11.8M
            code = pdfi_read_token(ctx, ctx->main_stream, 0, 0);
158
11.8M
            if (code < 0) {
159
271k
                if (code != gs_error_VMerror && code != gs_error_ioerror) {
160
271k
                    pdfi_clearstack(ctx);
161
271k
                    continue;
162
271k
                } else
163
172
                    goto exit;
164
271k
            }
165
11.5M
            if (pdfi_count_stack(ctx) > 0) {
166
9.21M
                if (pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) {
167
2.15M
                    pdf_obj *k = ctx->stack_top[-1];
168
2.15M
                    pdf_num *n;
169
170
2.15M
                    if (k == PDF_TOKEN_AS_OBJ(TOKEN_OBJ)) {
171
1.24M
                        gs_offset_t saved_offset[3];
172
173
1.24M
                        offset = outer_saved_offset[0];
174
175
1.24M
                        saved_offset[0] = saved_offset[1] = saved_offset[2] = 0;
176
177
1.24M
                        if (pdfi_count_stack(ctx) < 3 || pdfi_type_of(ctx->stack_top[-3]) != PDF_INT || pdfi_type_of(ctx->stack_top[-2]) != PDF_INT) {
178
3.55k
                            pdfi_clearstack(ctx);
179
3.55k
                            continue;
180
3.55k
                        }
181
1.24M
                        n = (pdf_num *)ctx->stack_top[-3];
182
1.24M
                        object_num = n->value.i;
183
1.24M
                        n = (pdf_num *)ctx->stack_top[-2];
184
1.24M
                        generation_num = n->value.i;
185
1.24M
                        pdfi_clearstack(ctx);
186
187
35.9M
                        do {
188
                            /* move all the saved offsets up by one */
189
35.9M
                            saved_offset[0] = saved_offset[1];
190
35.9M
                            saved_offset[1] = saved_offset[2];
191
35.9M
                            saved_offset[2] = pdfi_unread_tell(ctx);
192
193
35.9M
                            code = pdfi_read_token(ctx, ctx->main_stream, 0, 0);
194
35.9M
                            if (code < 0) {
195
324k
                                if (code != gs_error_VMerror && code != gs_error_ioerror)
196
324k
                                    continue;
197
74
                                goto exit;
198
324k
                            }
199
35.6M
                            if (code == 0 && ctx->main_stream->eof)
200
7.90k
                                break;
201
202
35.6M
                            if (pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) {
203
1.34M
                                pdf_obj *k = ctx->stack_top[-1];
204
205
1.34M
                                if (k == PDF_TOKEN_AS_OBJ(TOKEN_OBJ)) {
206
                                    /* Found obj while looking for endobj, store the existing 'obj'
207
                                     * and start afresh.
208
                                     */
209
17.3k
                                    code = pdfi_repair_add_object(ctx, object_num, generation_num, offset);
210
17.3k
                                    if (pdfi_count_stack(ctx) < 3 || pdfi_type_of(ctx->stack_top[-3]) != PDF_INT || pdfi_type_of(ctx->stack_top[-2]) != PDF_INT) {
211
2.82k
                                        pdfi_clearstack(ctx);
212
2.82k
                                        break;
213
2.82k
                                    }
214
14.5k
                                    n = (pdf_num *)ctx->stack_top[-3];
215
14.5k
                                    object_num = n->value.i;
216
14.5k
                                    n = (pdf_num *)ctx->stack_top[-2];
217
14.5k
                                    generation_num = n->value.i;
218
14.5k
                                    pdfi_clearstack(ctx);
219
14.5k
                                    offset = saved_offset[0];
220
14.5k
                                    continue;
221
17.3k
                                }
222
223
1.32M
                                if (k == PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)) {
224
573k
                                    code = pdfi_repair_add_object(ctx, object_num, generation_num, offset);
225
573k
                                    if (code < 0)
226
57
                                        goto exit;
227
573k
                                    pdfi_clearstack(ctx);
228
573k
                                    break;
229
755k
                                } else {
230
755k
                                    if (k == PDF_TOKEN_AS_OBJ(TOKEN_STREAM)) {
231
657k
                                        static const char test[] = "endstream";
232
657k
                                        int index = 0;
233
234
1.99G
                                        do {
235
1.99G
                                            int c = pdfi_read_byte(ctx, ctx->main_stream);
236
1.99G
                                            if (c == EOFC)
237
10.5k
                                                break;
238
1.99G
                                            if (c < 0)
239
0
                                                goto exit;
240
1.99G
                                            if (c == test[index])
241
17.4M
                                                index++;
242
1.97G
                                            else if (c == test[0]) /* Pesky 'e' appears twice */
243
158k
                                                index = 1;
244
1.97G
                                            else
245
1.97G
                                                index = 0;
246
1.99G
                                        } while (index < 9);
247
658k
                                        do {
248
658k
                                            code = pdfi_read_bare_keyword(ctx, ctx->main_stream);
249
658k
                                            if (code == gs_error_VMerror || code == gs_error_ioerror)
250
0
                                                goto exit;
251
658k
                                            if (code == TOKEN_ENDOBJ || code == TOKEN_INVALID_KEY) {
252
657k
                                                code = pdfi_repair_add_object(ctx, object_num, generation_num, offset);
253
657k
                                                if (code == gs_error_VMerror || code == gs_error_ioerror)
254
0
                                                    goto exit;
255
657k
                                                break;
256
657k
                                            }
257
658k
                                        } while(ctx->main_stream->eof == false);
258
259
657k
                                        pdfi_clearstack(ctx);
260
657k
                                        break;
261
657k
                                    } else {
262
97.8k
                                        pdfi_clearstack(ctx);
263
97.8k
                                    }
264
755k
                                }
265
1.32M
                            }
266
35.6M
                        } while(1);
267
1.24M
                        break;
268
1.24M
                    } else {
269
910k
                        if (k == PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)) {
270
16.5k
                            pdfi_clearstack(ctx);
271
16.5k
                        } else
272
894k
                            if (k == PDF_TOKEN_AS_OBJ(TOKEN_STARTXREF)) {
273
10.2k
                                code = pdfi_read_token(ctx, ctx->main_stream, 0, 0);
274
10.2k
                                if (code < 0 && code != gs_error_VMerror && code != gs_error_ioerror)
275
5
                                    continue;
276
10.2k
                                if (code < 0)
277
0
                                    goto exit;
278
10.2k
                                pdfi_clearstack(ctx);
279
884k
                            } else {
280
884k
                                if (k == PDF_TOKEN_AS_OBJ(TOKEN_TRAILER)) {
281
21.6k
                                    code = pdfi_read_bare_object(ctx, ctx->main_stream, 0, 0, 0);
282
21.6k
                                    if (code == 0 && pdfi_count_stack(ctx) > 0 && pdfi_type_of(ctx->stack_top[-1]) == PDF_DICT) {
283
9.48k
                                        if (ctx->Trailer) {
284
2.76k
                                            pdf_dict *d = (pdf_dict *)ctx->stack_top[-1];
285
2.76k
                                            bool known = false;
286
287
2.76k
                                            code = pdfi_dict_known(ctx, d, "Root", &known);
288
2.76k
                                            if (code == 0 && known) {
289
1.98k
                                                pdfi_countdown(ctx->Trailer);
290
1.98k
                                                ctx->Trailer = (pdf_dict *)ctx->stack_top[-1];
291
1.98k
                                                pdfi_countup(ctx->Trailer);
292
1.98k
                                            }
293
6.71k
                                        } else {
294
6.71k
                                            ctx->Trailer = (pdf_dict *)ctx->stack_top[-1];
295
6.71k
                                            pdfi_countup(ctx->Trailer);
296
6.71k
                                        }
297
9.48k
                                    }
298
21.6k
                                }
299
884k
                                pdfi_clearstack(ctx);
300
884k
                            }
301
910k
                    }
302
910k
                    code = pdfi_skip_white(ctx, ctx->main_stream);
303
910k
                    if (code < 0) {
304
0
                        if (code != gs_error_VMerror && code != gs_error_ioerror) {
305
0
                            pdfi_clearstack(ctx);
306
0
                            continue;
307
0
                        } else
308
0
                            goto exit;
309
0
                    }
310
910k
                }
311
7.96M
                if (pdfi_count_stack(ctx) > 0 && pdfi_type_of(ctx->stack_top[-1]) != PDF_INT)
312
2.31M
                    pdfi_clearstack(ctx);
313
7.96M
            }
314
11.5M
        } while (ctx->main_stream->eof == false);
315
1.26M
    } while(ctx->main_stream->eof == false);
316
317
36.6k
    pdfi_seek(ctx, ctx->main_stream, 0, SEEK_SET);
318
36.6k
    ctx->main_stream->eof = false;
319
320
    /* Second pass, examine every object we have located to see if its an ObjStm */
321
36.6k
    if (ctx->xref_table == NULL || ctx->xref_table->xref_size < 1) {
322
6.73k
        code = gs_note_error(gs_error_syntaxerror);
323
6.73k
        goto exit;
324
6.73k
    }
325
326
64.9M
    for (i=1;i < ctx->xref_table->xref_size;i++) {
327
64.9M
        if (ctx->xref_table->xref[i].object_num != 0) {
328
            /* At this stage, all the objects we've found must be uncompressed */
329
2.40M
            if (ctx->xref_table->xref[i].u.uncompressed.offset > ctx->main_stream_length) {
330
                /* This can only happen if we had read an xref table before we tried to repair
331
                 * the file, and the table has entries we didn't find in the file. So
332
                 * mark the entry as free, and offset of 0, and just carry on.
333
                 */
334
1.16k
                ctx->xref_table->xref[i].free = 1;
335
1.16k
                ctx->xref_table->xref[i].u.uncompressed.offset = 0;
336
1.16k
                continue;
337
1.16k
            }
338
339
2.40M
            pdfi_seek(ctx, ctx->main_stream, ctx->xref_table->xref[i].u.uncompressed.offset, SEEK_SET);
340
63.1M
            do {
341
63.1M
                code = pdfi_read_token(ctx, ctx->main_stream, 0, 0);
342
63.1M
                if (ctx->main_stream->eof == true || (code < 0 && code != gs_error_ioerror && code != gs_error_VMerror)) {
343
                    /* object offset is beyond EOF or object is broken (possibly due to multiple xref
344
                     * errors) ignore the error and carry on, if the object gets used then we will
345
                     * error out at that point.
346
                     */
347
131k
                    code = 0;
348
131k
                    break;
349
131k
                }
350
63.0M
                if (code < 0)
351
0
                    goto exit;
352
63.0M
                if (pdfi_type_of(ctx->stack_top[-1]) == PDF_FAST_KEYWORD) {
353
4.93M
                    pdf_obj *k = ctx->stack_top[-1];
354
355
4.93M
                    if (k == PDF_TOKEN_AS_OBJ(TOKEN_OBJ)) {
356
1.71M
                        continue;
357
1.71M
                    }
358
3.21M
                    if (k == PDF_TOKEN_AS_OBJ(TOKEN_ENDOBJ)) {
359
1.64M
                        if (pdfi_count_stack(ctx) > 1) {
360
1.62M
                            if (pdfi_type_of(ctx->stack_top[-2]) == PDF_DICT) {
361
965k
                                pdf_dict *d = (pdf_dict *)ctx->stack_top[-2];
362
965k
                                pdf_obj *o = NULL;
363
364
965k
                                code = pdfi_dict_knownget_type(ctx, d, "Type", PDF_NAME, &o);
365
965k
                                if (code < 0) {
366
51
                                    pdfi_clearstack(ctx);
367
51
                                    continue;
368
51
                                }
369
965k
                                if (code > 0) {
370
282k
                                    pdf_name *n = (pdf_name *)o;
371
372
282k
                                    if (pdfi_name_is(n, "Catalog")) {
373
38.5k
                                        pdfi_countdown(ctx->Root); /* In case it was already set */
374
38.5k
                                        ctx->Root = (pdf_dict *)ctx->stack_top[-2];
375
38.5k
                                        pdfi_countup(ctx->Root);
376
38.5k
                                    }
377
282k
                                }
378
965k
                                pdfi_countdown(o);
379
965k
                            }
380
1.62M
                        }
381
1.64M
                        pdfi_clearstack(ctx);
382
1.64M
                        break;
383
1.64M
                    }
384
1.57M
                    if (k == PDF_TOKEN_AS_OBJ(TOKEN_STREAM)) {
385
633k
                        pdf_dict *d;
386
633k
                        pdf_name *n = NULL;
387
388
633k
                        if (pdfi_count_stack(ctx) <= 1) {
389
517
                            pdfi_clearstack(ctx);
390
517
                            break;;
391
0
                        }
392
632k
                        d = (pdf_dict *)ctx->stack_top[-2];
393
632k
                        if (pdfi_type_of(d) != PDF_DICT) {
394
8.38k
                            pdfi_clearstack(ctx);
395
8.38k
                            break;;
396
0
                        }
397
624k
                        code = pdfi_dict_knownget_type(ctx, d, "Type", PDF_NAME, (pdf_obj **)&n);
398
624k
                        if (code < 0) {
399
14
                            if (ctx->args.pdfstoponerror || code == gs_error_VMerror) {
400
0
                                pdfi_clearstack(ctx);
401
0
                                goto exit;
402
0
                            }
403
14
                        }
404
624k
                        if (code > 0) {
405
281k
                            if (pdfi_name_is(n, "ObjStm")) {
406
34.5k
                                int64_t N;
407
34.5k
                                int obj_num, offset;
408
34.5k
                                int j;
409
34.5k
                                pdf_c_stream *compressed_stream;
410
34.5k
                                pdf_stream *stream;
411
412
34.5k
                                offset = pdfi_unread_tell(ctx);
413
34.5k
                                pdfi_seek(ctx, ctx->main_stream, offset, SEEK_SET);
414
415
34.5k
                                code = pdfi_obj_dict_to_stream(ctx, d, &stream, true);
416
34.5k
                                if (code == 0)
417
34.5k
                                    code = pdfi_filter(ctx, stream, ctx->main_stream, &compressed_stream, false);
418
419
34.5k
                                pdfi_countdown(stream);
420
421
34.5k
                                if (code == 0) {
422
34.4k
                                    code = pdfi_dict_get_int(ctx, d, "N", &N);
423
34.4k
                                    if (code == 0) {
424
1.29M
                                        for (j=0;j < N; j++) {
425
1.26M
                                            code = pdfi_read_bare_int(ctx, compressed_stream, &obj_num);
426
1.26M
                                            if (code <= 0)
427
1.46k
                                                break;
428
1.26M
                                            else {
429
1.26M
                                                code = pdfi_read_bare_int(ctx, compressed_stream, &offset);
430
1.26M
                                                if (code > 0) {
431
1.26M
                                                    if (obj_num < 1) {
432
20
                                                        pdfi_close_file(ctx, compressed_stream);
433
20
                                                        pdfi_countdown(n);
434
20
                                                        pdfi_clearstack(ctx);
435
20
                                                        code = gs_note_error(gs_error_rangecheck);
436
20
                                                        goto exit;
437
20
                                                    }
438
1.26M
                                                    if (obj_num >= ctx->xref_table->xref_size)
439
37.0k
                                                        code = pdfi_repair_add_object(ctx, obj_num, 0, 0);
440
441
1.26M
                                                    if (code >= 0) {
442
1.26M
                                                        ctx->xref_table->xref[obj_num].compressed = true;
443
1.26M
                                                        ctx->xref_table->xref[obj_num].free = false;
444
1.26M
                                                        ctx->xref_table->xref[obj_num].object_num = obj_num;
445
1.26M
                                                        ctx->xref_table->xref[obj_num].u.compressed.compressed_stream_num = i;
446
1.26M
                                                        ctx->xref_table->xref[obj_num].u.compressed.object_index = j;
447
1.26M
                                                    }
448
1.26M
                                                }
449
1.26M
                                            }
450
1.26M
                                        }
451
34.4k
                                    }
452
34.4k
                                    pdfi_close_file(ctx, compressed_stream);
453
34.4k
                                }
454
34.5k
                                if (code < 0) {
455
1.05k
                                    if (ctx->args.pdfstoponerror || code == gs_error_VMerror) {
456
0
                                        pdfi_countdown(n);
457
0
                                        pdfi_clearstack(ctx);
458
0
                                        goto exit;
459
0
                                    }
460
1.05k
                                }
461
34.5k
                            }
462
281k
                        }
463
624k
                        pdfi_countdown(n);
464
624k
                        pdfi_clearstack(ctx);
465
624k
                        break;
466
624k
                    }
467
1.57M
                }
468
63.0M
            } while (1);
469
2.40M
        }
470
64.9M
    }
471
472
37.2k
exit:
473
37.2k
    if (code > 0)
474
9.31k
        code = 0;
475
37.2k
    pdfi_seek(ctx, ctx->main_stream, saved_offset, SEEK_SET);
476
37.2k
    ctx->main_stream->eof = false;
477
37.2k
    return code;
478
29.9k
}