Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpszip.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* XPS interpreter - zip container parsing */
18
19
#include "ghostxps.h"
20
#include "pagelist.h"
21
22
static int isfile(gs_memory_t *mem, char *path)
23
0
{
24
0
    gp_file *file = gp_fopen(mem, path, "rb");
25
0
    if (file)
26
0
    {
27
0
        gp_fclose(file);
28
0
        return 1;
29
0
    }
30
0
    return 0;
31
0
}
32
33
static inline int getshort(gp_file *file)
34
4.09k
{
35
4.09k
    int a = xps_getc(file);
36
4.09k
    int b = xps_getc(file);
37
4.09k
    return a | (b << 8);
38
4.09k
}
39
40
static inline int getlong(gp_file *file)
41
2.32k
{
42
2.32k
    int a = xps_getc(file);
43
2.32k
    int b = xps_getc(file);
44
2.32k
    int c = xps_getc(file);
45
2.32k
    int d = xps_getc(file);
46
2.32k
    return a | (b << 8) | (c << 16) | (d << 24);
47
2.32k
}
48
49
static void *
50
xps_zip_alloc_items(xps_context_t *ctx, int items, int size)
51
92
{
52
92
    void *item = xps_alloc(ctx, (size_t)items * size);
53
92
    if (!item) {
54
0
        gs_throw(gs_error_VMerror, "out of memory: item.\n");
55
0
        return NULL;
56
0
    }
57
92
    return item;
58
92
}
59
60
static void
61
xps_zip_free(xps_context_t *ctx, void *ptr)
62
92
{
63
92
    xps_free(ctx, ptr);
64
92
}
65
66
static int
67
xps_compare_entries(const void *a0, const void *b0)
68
753
{
69
753
    xps_entry_t *a = (xps_entry_t*) a0;
70
753
    xps_entry_t *b = (xps_entry_t*) b0;
71
753
    return xps_strcasecmp(a->name, b->name);
72
753
}
73
74
static xps_entry_t *
75
xps_find_zip_entry(xps_context_t *ctx, const char *name)
76
96
{
77
96
    int l = 0;
78
96
    int r = ctx->zip_count - 1;
79
344
    while (l <= r)
80
344
    {
81
344
        int m = (l + r) >> 1;
82
344
        int c = xps_strcasecmp(name, ctx->zip_table[m].name);
83
344
        if (c < 0)
84
89
            r = m - 1;
85
255
        else if (c > 0)
86
159
            l = m + 1;
87
96
        else
88
96
            return &ctx->zip_table[m];
89
344
    }
90
0
    return NULL;
91
96
}
92
93
/*
94
 * Inflate the data in a zip entry.
95
 */
96
97
static int
98
xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf)
99
96
{
100
96
    z_stream stream;
101
96
    unsigned char *inbuf;
102
96
    int sig;
103
96
    int version, general, method;
104
96
    int namelength, extralength;
105
96
    int code;
106
107
96
    if_debug1m('|', ctx->memory, "zip: inflating entry '%s'\n", ent->name);
108
109
96
    if (xps_fseek(ctx->file, ent->offset, 0) < 0)
110
0
        return gs_throw1(-1, "seek to offset %d failed.", ent->offset);
111
112
96
    sig = getlong(ctx->file);
113
96
    if (sig != ZIP_LOCAL_FILE_SIG)
114
4
        return gs_throw1(-1, "wrong zip local file signature (0x%x)", sig);
115
116
92
    version = getshort(ctx->file);
117
92
    (void)version; /* Avoid unused variable compiler warning */
118
92
    general = getshort(ctx->file);
119
92
    if (general & ZIP_ENCRYPTED_FLAG)
120
0
        return gs_throw(-1, "zip file content is encrypted");
121
92
    method = getshort(ctx->file);
122
92
    (void) getshort(ctx->file); /* file time */
123
92
    (void) getshort(ctx->file); /* file date */
124
92
    (void) getlong(ctx->file); /* crc-32 */
125
92
    (void) getlong(ctx->file); /* csize */
126
92
    (void) getlong(ctx->file); /* usize */
127
92
    namelength = getshort(ctx->file);
128
92
    extralength = getshort(ctx->file);
129
130
92
    if (namelength < 0 || namelength > 65535)
131
0
        return gs_rethrow(gs_error_ioerror, "Illegal namelength (can't happen).\n");
132
92
    if (extralength < 0 || extralength > 65535)
133
0
        return gs_rethrow(gs_error_ioerror, "Illegal extralength (can't happen).\n");
134
135
92
    if (xps_fseek(ctx->file, namelength + extralength, 1) != 0)
136
0
        return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.\n", namelength + extralength);
137
138
92
    if (method == 0)
139
0
    {
140
0
        code = xps_fread(outbuf, 1, ent->usize, ctx->file);
141
0
        if (code != ent->usize)
142
0
            return gs_throw1(gs_error_ioerror, "Failed to read %d bytes", ent->usize);
143
0
    }
144
92
    else if (method == 8)
145
92
    {
146
92
        inbuf = xps_alloc(ctx, ent->csize);
147
92
        if (!inbuf) {
148
0
            return gs_rethrow(gs_error_VMerror, "out of memory.\n");
149
0
        }
150
151
92
        code = xps_fread(inbuf, 1, ent->csize, ctx->file);
152
92
        if (code != ent->csize) {
153
0
            xps_free(ctx, inbuf);
154
0
            return gs_throw1(gs_error_ioerror, "Failed to read %d bytes", ent->csize);
155
0
        }
156
157
92
        memset(&stream, 0, sizeof(z_stream));
158
92
        stream.zalloc = (alloc_func) xps_zip_alloc_items;
159
92
        stream.zfree = (free_func) xps_zip_free;
160
92
        stream.opaque = ctx;
161
92
        stream.next_in = inbuf;
162
92
        stream.avail_in = ent->csize;
163
92
        stream.next_out = outbuf;
164
92
        stream.avail_out = ent->usize;
165
166
92
        code = inflateInit2(&stream, -15);
167
92
        if (code != Z_OK)
168
0
        {
169
0
            xps_free(ctx, inbuf);
170
0
            return gs_throw1(-1, "zlib inflateInit2 error: %s", stream.msg);
171
0
        }
172
92
        code = inflate(&stream, Z_FINISH);
173
92
        if (code != Z_STREAM_END)
174
1
        {
175
1
            inflateEnd(&stream);
176
1
            xps_free(ctx, inbuf);
177
1
            return gs_throw1(-1, "zlib inflate error: %s", stream.msg);
178
1
        }
179
91
        code = inflateEnd(&stream);
180
91
        if (code != Z_OK)
181
0
        {
182
0
            xps_free(ctx, inbuf);
183
0
            return gs_throw1(-1, "zlib inflateEnd error: %s", stream.msg);
184
0
        }
185
186
91
        xps_free(ctx, inbuf);
187
188
        /* If the stream has less data than advertised, then zero the remainder. */
189
91
        if (stream.avail_out > 0)
190
16
        {
191
16
            gs_warn("truncated zipfile entry; possibly corrupt data");
192
16
            memset(stream.next_out, 0, stream.avail_out);
193
16
        }
194
91
    }
195
0
    else
196
0
    {
197
0
        return gs_throw1(-1, "unknown compression method (%d)", method);
198
0
    }
199
200
91
    return gs_okay;
201
92
}
202
203
/*
204
 * Read the central directory in a zip file.
205
 */
206
207
static int
208
xps_read_zip_dir(xps_context_t *ctx, int start_offset)
209
52
{
210
52
    int sig;
211
52
    int offset, count, read;
212
52
    int namesize, metasize, commentsize;
213
52
    int i;
214
215
52
    if (xps_fseek(ctx->file, start_offset, 0) != 0)
216
0
        return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.", start_offset);
217
218
52
    sig = getlong(ctx->file);
219
52
    if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
220
0
        return gs_throw1(-1, "wrong zip end of central directory signature (0x%x)", sig);
221
222
52
    (void) getshort(ctx->file); /* this disk */
223
52
    (void) getshort(ctx->file); /* start disk */
224
52
    (void) getshort(ctx->file); /* entries in this disk */
225
52
    count = getshort(ctx->file); /* entries in central directory disk */
226
52
    (void) getlong(ctx->file); /* size of central directory */
227
52
    offset = getlong(ctx->file); /* offset to central directory */
228
229
52
    if (count < 0 || count > 65535)
230
0
        return gs_rethrow(gs_error_rangecheck, "invalid number of entries in central directory disk (can't happen)");
231
232
52
    ctx->zip_count = count;
233
52
    ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * (size_t)count);
234
52
    if (!ctx->zip_table)
235
0
        return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry table");
236
237
52
    memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count);
238
239
52
    if (xps_fseek(ctx->file, offset, 0) != 0)
240
3
        return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", offset);
241
242
343
    for (i = 0; i < count; i++)
243
318
    {
244
318
        sig = getlong(ctx->file);
245
318
        if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
246
23
            return gs_throw1(-1, "wrong zip central directory signature (0x%x)", sig);
247
248
295
        (void) getshort(ctx->file); /* version made by */
249
295
        (void) getshort(ctx->file); /* version to extract */
250
295
        (void) getshort(ctx->file); /* general */
251
295
        (void) getshort(ctx->file); /* method */
252
295
        (void) getshort(ctx->file); /* last mod file time */
253
295
        (void) getshort(ctx->file); /* last mod file date */
254
295
        (void) getlong(ctx->file); /* crc-32 */
255
295
        ctx->zip_table[i].csize = getlong(ctx->file);
256
295
        ctx->zip_table[i].usize = getlong(ctx->file);
257
295
        namesize = getshort(ctx->file);
258
295
        if (namesize < 0 || namesize > 65535)
259
0
            return gs_rethrow(gs_error_rangecheck, "illegal namesize (can't happen)");
260
295
        metasize = getshort(ctx->file);
261
295
        commentsize = getshort(ctx->file);
262
295
        (void) getshort(ctx->file); /* disk number start */
263
295
        (void) getshort(ctx->file); /* int file atts */
264
295
        (void) getlong(ctx->file); /* ext file atts */
265
295
        ctx->zip_table[i].offset = getlong(ctx->file);
266
267
295
        if (ctx->zip_table[i].csize < 0 || ctx->zip_table[i].usize < 0)
268
0
            return gs_throw(gs_error_ioerror, "cannot read zip entries larger than 2GB");
269
270
295
        ctx->zip_table[i].name = xps_alloc(ctx, (size_t)namesize + 1);
271
295
        if (!ctx->zip_table[i].name)
272
0
            return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name");
273
274
295
        read = xps_fread(ctx->zip_table[i].name, 1, (size_t)namesize, ctx->file);
275
295
        if (read != namesize)
276
1
            return gs_throw1(gs_error_ioerror, "failed to read %d bytes", namesize);
277
278
294
        ctx->zip_table[i].name[namesize] = 0;
279
280
294
        if (xps_fseek(ctx->file, metasize, 1) != 0)
281
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", metasize);
282
294
        if (xps_fseek(ctx->file, commentsize, 1) != 0)
283
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", commentsize);
284
294
    }
285
286
25
    qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries);
287
288
309
    for (i = 0; i < ctx->zip_count; i++)
289
284
    {
290
284
        if_debug3m('|', ctx->memory, "zip entry '%s' csize=%d usize=%d\n",
291
284
                   ctx->zip_table[i].name,
292
284
                   ctx->zip_table[i].csize,
293
284
                   ctx->zip_table[i].usize);
294
284
    }
295
296
25
    return gs_okay;
297
49
}
298
299
static int
300
xps_find_and_read_zip_dir(xps_context_t *ctx)
301
90
{
302
90
    int filesize, back, maxback;
303
90
    int i, n;
304
90
    char buf[512];
305
306
90
    if (xps_fseek(ctx->file, 0, SEEK_END) < 0)
307
0
        return gs_throw(-1, "seek to end failed.");
308
309
90
    filesize = xps_ftell(ctx->file);
310
311
90
    maxback = MIN(filesize, 0xFFFF + sizeof buf);
312
90
    back = MIN(maxback, sizeof buf);
313
314
1.04k
    while (back < maxback)
315
1.00k
    {
316
1.00k
        if (xps_fseek(ctx->file, filesize - back, 0) < 0)
317
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.\n", filesize - back);
318
319
1.00k
        n = xps_fread(buf, 1, sizeof buf, ctx->file);
320
1.00k
        if (n < 0)
321
0
            return gs_throw(-1, "cannot read end of central directory");
322
323
489k
        for (i = n - 4; i > 0; i--)
324
488k
            if (!memcmp(buf + i, "PK\5\6", 4))
325
52
                return xps_read_zip_dir(ctx, filesize - back + i);
326
327
955
        back += sizeof buf - 4;
328
955
    }
329
330
38
    return gs_throw(-1, "cannot find end of central directory");
331
90
}
332
333
/*
334
 * Read and interleave split parts from a ZIP file.
335
 */
336
337
static xps_part_t *
338
xps_read_zip_part(xps_context_t *ctx, const char *partname)
339
96
{
340
96
    char buf[2048];
341
96
    xps_entry_t *ent;
342
96
    xps_part_t *part;
343
96
    int count, size, offset, i;
344
96
    int code = 0;
345
96
    const char *name;
346
96
    int seen_last = 0;
347
348
96
    name = partname;
349
96
    if (name[0] == '/')
350
96
        name ++;
351
352
    /* All in one piece */
353
96
    ent = xps_find_zip_entry(ctx, name);
354
96
    if (ent)
355
96
    {
356
96
        part = xps_new_part(ctx, partname, ent->usize);
357
96
        if (part != NULL)
358
96
            code = xps_read_zip_entry(ctx, ent, part->data);
359
0
        else
360
0
            return NULL;
361
96
        if (code < 0)
362
5
        {
363
5
            xps_free_part(ctx, part);
364
5
            gs_rethrow1(-1, "cannot read zip entry '%s'", name);
365
5
            return NULL;
366
5
        }
367
91
        return part;
368
96
    }
369
370
    /* Count the number of pieces and their total size */
371
0
    count = 0;
372
0
    size = 0;
373
0
    while (!seen_last)
374
0
    {
375
0
        gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, count);
376
0
        ent = xps_find_zip_entry(ctx, buf);
377
0
        if (!ent)
378
0
        {
379
0
            gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, count);
380
0
            ent = xps_find_zip_entry(ctx, buf);
381
0
            seen_last = !!ent;
382
0
        }
383
0
        if (!ent)
384
0
            break;
385
0
        count ++;
386
        /* check for integer overflow */
387
0
        if (size > INT_MAX - ent->usize)
388
0
        {
389
0
            gs_throw1(-1, "part '%s' is too large", partname);
390
0
            return NULL;
391
0
        }
392
0
        size += ent->usize;
393
0
    }
394
0
    if (!seen_last)
395
0
    {
396
0
        gs_throw1(-1, "cannot find all pieces for part '%s'", partname);
397
0
        return NULL;
398
0
    }
399
400
    /* Inflate the pieces */
401
0
    if (count)
402
0
    {
403
0
        part = xps_new_part(ctx, partname, size);
404
0
        offset = 0;
405
0
        for (i = 0; i < count; i++)
406
0
        {
407
0
            if (i < count - 1)
408
0
                gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, i);
409
0
            else
410
0
                gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, i);
411
0
            ent = xps_find_zip_entry(ctx, buf);
412
0
            if (!ent)
413
0
                gs_warn("missing piece");
414
0
            else
415
0
            {
416
0
                code = xps_read_zip_entry(ctx, ent, part->data + offset);
417
0
                if (code < 0)
418
0
                {
419
0
                    xps_free_part(ctx, part);
420
0
                    gs_rethrow1(-1, "cannot read zip entry '%s'", buf);
421
0
                    return NULL;
422
0
                }
423
0
                offset += ent->usize;
424
0
            }
425
0
        }
426
0
        return part;
427
0
    }
428
429
0
    return NULL;
430
0
}
431
432
/*
433
 * Read and interleave split parts from files in the directory.
434
 */
435
436
static xps_part_t *
437
xps_read_dir_part(xps_context_t *ctx, const char *name)
438
0
{
439
0
    char buf[2048];
440
0
    xps_part_t *part;
441
0
    gp_file *file;
442
0
    int count, size, offset, i, n;
443
0
    int seen_last = 0;
444
445
0
    gs_strlcpy(buf, ctx->directory, sizeof buf);
446
0
    gs_strlcat(buf, name, sizeof buf);
447
448
    /* All in one piece */
449
0
    file = gp_fopen(ctx->memory, buf, "rb");
450
0
    if (file)
451
0
    {
452
0
        if (xps_fseek(file, 0, SEEK_END) != 0) {
453
0
            gp_fclose(file);
454
0
            return NULL;
455
0
        }
456
457
0
        size = xps_ftell(file);
458
0
        if (size < 0) {
459
0
            gp_fclose(file);
460
0
            return NULL;
461
0
        }
462
463
0
        if (xps_fseek(file, 0, SEEK_SET) != 0) {
464
0
            gp_fclose(file);
465
0
            return NULL;
466
0
        }
467
468
0
        part = xps_new_part(ctx, name, size);
469
0
        count = xps_fread(part->data, 1, size, file);
470
0
        gp_fclose(file);
471
0
        if (count == size)
472
0
            return part;
473
0
        else
474
0
            return NULL;
475
0
    }
476
477
    /* Count the number of pieces and their total size */
478
0
    count = 0;
479
0
    size = 0;
480
0
    while (!seen_last)
481
0
    {
482
0
        gs_snprintf(buf, sizeof(buf), "%s%s/[%d].piece", ctx->directory, name, count);
483
0
        file = gp_fopen(ctx->memory, buf, "rb");
484
0
        if (!file)
485
0
        {
486
0
            gs_snprintf(buf, sizeof(buf), "%s%s/[%d].last.piece", ctx->directory, name, count);
487
0
            file = gp_fopen(ctx->memory, buf, "rb");
488
0
            seen_last = !!file;
489
0
        }
490
0
        if (!file)
491
0
            break;
492
0
        count ++;
493
0
        if (xps_fseek(file, 0, SEEK_END) < 0)
494
0
            break;;
495
0
        size += xps_ftell(file);
496
0
        gp_fclose(file);
497
0
    }
498
0
    if (!seen_last)
499
0
    {
500
0
        gs_throw1(-1, "cannot find all pieces for part '%s'", name);
501
0
        return NULL;
502
0
    }
503
504
    /* Inflate the pieces */
505
506
0
    part = xps_new_part(ctx, name, size);
507
0
    if (!part)
508
0
    {
509
0
        gs_rethrow1(-1, "failed to create part '%s'", name);
510
0
        return NULL;
511
0
    }
512
513
0
    offset = 0;
514
0
    for (i = 0; i < count; i++)
515
0
    {
516
0
        if (i < count - 1)
517
0
            gs_snprintf(buf, sizeof(buf), "%s%s/[%d].piece", ctx->directory, name, i);
518
0
        else
519
0
            gs_snprintf(buf, sizeof(buf), "%s%s/[%d].last.piece", ctx->directory, name, i);
520
0
        file = gp_fopen(ctx->memory, buf, "rb");
521
0
        n = xps_fread(part->data + offset, 1, size - offset, file);
522
0
        offset += n;
523
0
        gp_fclose(file);
524
0
    }
525
0
    return part;
526
527
0
}
528
529
xps_part_t *
530
xps_read_part(xps_context_t *ctx, const char *partname)
531
96
{
532
96
    if (ctx->directory)
533
0
        return xps_read_dir_part(ctx, partname);
534
96
    return xps_read_zip_part(ctx, partname);
535
96
}
536
537
/*
538
 * Read and process the XPS document.
539
 */
540
541
static int
542
xps_read_and_process_metadata_part(xps_context_t *ctx, const char *name)
543
72
{
544
72
    xps_part_t *part;
545
72
    int code;
546
547
72
    part = xps_read_part(ctx, name);
548
72
    if (!part)
549
3
        return gs_rethrow1(-1, "cannot read zip part '%s'", name);
550
551
69
    code = xps_parse_metadata(ctx, part);
552
69
    if (code)
553
0
    {
554
0
        xps_free_part(ctx, part);
555
0
        return gs_rethrow1(code, "cannot process metadata part '%s'", name);
556
0
    }
557
558
69
    xps_free_part(ctx, part);
559
560
69
    return gs_okay;
561
69
}
562
563
static int
564
xps_read_and_process_page_part(xps_context_t *ctx, char *name)
565
22
{
566
22
    xps_part_t *part;
567
22
    int code;
568
569
22
    part = xps_read_part(ctx, name);
570
22
    if (!part)
571
2
        return gs_rethrow1(-1, "cannot read zip part '%s'", name);
572
573
20
    code = xps_parse_fixed_page(ctx, part);
574
20
    if (code)
575
17
    {
576
17
        xps_free_part(ctx, part);
577
17
        return gs_rethrow1(code, "cannot parse fixed page part '%s'", name);
578
17
    }
579
580
3
    xps_free_part(ctx, part);
581
582
3
    return gs_okay;
583
20
}
584
585
/* XPS page reordering based upon Device PageList setting */
586
static int
587
xps_reorder_add_page(xps_context_t* ctx, xps_page_t ***page_ptr, xps_page_t* page_to_add)
588
0
{
589
0
    xps_page_t* new_page;
590
591
0
    new_page = xps_alloc(ctx, sizeof(xps_page_t));
592
0
    if (!new_page)
593
0
    {
594
0
        return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n");
595
0
    }
596
597
0
    new_page->name = xps_strdup(ctx, page_to_add->name);
598
0
    if (!new_page->name)
599
0
    {
600
0
        return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n");
601
0
    }
602
0
    new_page->height = page_to_add->height;
603
0
    new_page->width = page_to_add->width;
604
0
    new_page->next = NULL;
605
606
0
    **page_ptr = new_page;
607
0
    *page_ptr = &(new_page->next);
608
609
0
    return 0;
610
0
}
611
612
613
static int
614
xps_reorder_pages(xps_context_t *ctx)
615
0
{
616
0
    char *page_list = ctx->page_range->page_list;
617
0
    xps_page_t **page_ptr_array, *page = ctx->first_page;
618
0
    int count = 0, k;
619
0
    int code = 0;
620
0
    int start;
621
0
    int end;
622
0
    xps_page_t* first_page = NULL;
623
0
    xps_page_t* last_page;
624
0
    xps_page_t** page_tail = &first_page;
625
0
    int *page_range_array;
626
0
    int ranges_count = 0;
627
628
0
    if (page == NULL)
629
0
        return 0;
630
631
0
    while (page != NULL)
632
0
    {
633
0
        count++;
634
0
        page = page->next;
635
0
    }
636
637
    /* Create an array of pointers to the current pages */
638
0
    page_ptr_array = xps_alloc(ctx, (size_t)sizeof(xps_page_t*) * count);
639
0
    if (page_ptr_array == NULL)
640
0
        return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_pages\n");
641
642
0
    page = ctx->first_page;
643
0
    for (k = 0; k < count; k++)
644
0
    {
645
0
        page_ptr_array[k] = page;
646
0
        page = page->next;
647
0
    }
648
649
    /* Use the common function to parse the page_list into a 'page_range_array' */
650
0
    ranges_count = pagelist_parse_to_array(page_list, ctx->memory, count, &page_range_array);
651
0
    if (ranges_count <= 0)
652
0
        return gs_throw(gs_error_typecheck, "Bad page list: xps_reorder_pages\n");
653
654
0
    ranges_count--;     /* ignore the final marker range 0, 0, 0 */
655
656
    /* start processing ranges, ignoring the "ordered" flag at the start of the array */
657
0
    for (k = 1; k < 1 + 3 * ranges_count; k += 3) {
658
0
        start = page_range_array[k+1];
659
0
        end = page_range_array[k+2];
660
661
0
        if (start == end)
662
0
            code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
663
0
        else if (start < end) {
664
0
            do {
665
0
                code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
666
0
                if (code < 0)
667
0
                    break;
668
0
                start += ((page_range_array[k] == 0) ? 1 : 2);  /* double bump for even/odd */
669
0
            } while (start <= end);
670
0
        } else { /* start > end -- reverse direction */
671
0
            do {
672
0
                code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
673
0
                if (code < 0)
674
0
                    break;
675
0
                start -= ((page_range_array[k] == 0) ? 1 : 2);  /* double bump for even/odd */
676
0
            } while (start >= end);
677
0
        }
678
0
    }
679
0
    pagelist_free_range_array(ctx->memory, page_range_array); /* done with all ranges */
680
681
    /* Replace the pages. */
682
0
    if (first_page != NULL)
683
0
    {
684
        /* Set to the last page not its next pointer (Thanks to Robin Watts) */
685
0
        last_page = (xps_page_t*)(((char*)page_tail) - offsetof(xps_page_t, next));
686
687
0
        xps_free_fixed_pages(ctx);
688
0
        xps_free(ctx, page_ptr_array);
689
0
        ctx->first_page = first_page;
690
0
        ctx->last_page = last_page;
691
0
    }
692
0
    else
693
0
        return gs_throw(gs_error_rangecheck, "Bad page list: xps_reorder_pages\n");
694
695
0
    return code;
696
0
}
697
698
/*
699
 * Called by xpstop.c
700
 */
701
702
int
703
xps_process_file(xps_context_t *ctx, const char *filename)
704
90
{
705
90
    char buf[2048];
706
90
    xps_document_t *doc;
707
90
    xps_page_t *page;
708
90
    int code;
709
90
    char *p;
710
711
90
    ctx->file = xps_fopen(ctx->memory, filename, "rb");
712
90
    if (!ctx->file)
713
0
        return gs_throw1(-1, "cannot open file: '%s'", filename);
714
715
90
    if (strstr(filename, ".fpage"))
716
0
    {
717
0
        xps_part_t *part;
718
0
        int size;
719
720
0
        if_debug0m('|', ctx->memory, "zip: single page mode\n");
721
0
        gs_strlcpy(buf, filename, sizeof buf);
722
0
        while (1)
723
0
        {
724
0
            p = strrchr(buf, '/');
725
0
            if (!p)
726
0
                p = strrchr(buf, '\\');
727
0
            if (!p)
728
0
                break;
729
0
            gs_strlcpy(p, "/_rels/.rels", buf + sizeof buf - p);
730
0
            if_debug1m('|', ctx->memory, "zip: testing if '%s' exists\n", buf);
731
0
            if (isfile(ctx->memory, buf))
732
0
            {
733
0
                *p = 0;
734
0
                ctx->directory = xps_strdup(ctx, buf);
735
0
                if_debug1m('|', ctx->memory, "zip: using '%s' as root directory\n", ctx->directory);
736
0
                break;
737
0
            }
738
0
            *p = 0;
739
0
        }
740
0
        if (!ctx->directory)
741
0
        {
742
0
            if_debug0m('|', ctx->memory, "zip: no /_rels/.rels found; assuming absolute paths\n");
743
0
            ctx->directory = xps_strdup(ctx, "");
744
0
        }
745
746
0
        if (xps_fseek(ctx->file, 0, SEEK_END) != 0) {
747
0
            code = gs_rethrow(gs_error_ioerror, "xps_fseek to file end failed");
748
0
            goto cleanup;
749
0
        }
750
751
0
        size = xps_ftell(ctx->file);
752
0
        if (size < 0) {
753
0
            code = gs_rethrow(gs_error_ioerror, "xps_ftell raised an error");
754
0
            goto cleanup;
755
0
        }
756
757
0
        if (xps_fseek(ctx->file, 0, SEEK_SET) != 0) {
758
0
            code = gs_rethrow(gs_error_ioerror, "xps_fseek to file begin failed");
759
0
            goto cleanup;
760
0
        }
761
762
0
        part = xps_new_part(ctx, filename, size);
763
0
        code = xps_fread(part->data, 1, size, ctx->file);
764
0
        if (code != size) {
765
0
            code = gs_rethrow1(gs_error_ioerror, "failed to read %d bytes", size);
766
0
            xps_free_part(ctx, part);
767
0
            goto cleanup;
768
0
        }
769
770
0
        code = xps_parse_fixed_page(ctx, part);
771
0
        if (code)
772
0
        {
773
0
            code = gs_rethrow1(code, "cannot parse fixed page part '%s'", part->name);
774
0
            xps_free_part(ctx, part);
775
0
            goto cleanup;
776
0
        }
777
778
0
        xps_free_part(ctx, part);
779
0
        code = gs_okay;
780
0
        goto cleanup;
781
0
    }
782
783
90
    if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels"))
784
0
    {
785
0
        gs_strlcpy(buf, filename, sizeof buf);
786
0
        p = strstr(buf, "/_rels/.rels");
787
0
        if (!p)
788
0
            p = strstr(buf, "\\_rels\\.rels");
789
0
        *p = 0;
790
0
        ctx->directory = xps_strdup(ctx, buf);
791
0
        if_debug1m('|', ctx->memory, "zip: using '%s' as root directory\n", ctx->directory);
792
0
    }
793
90
    else
794
90
    {
795
90
        code = xps_find_and_read_zip_dir(ctx);
796
90
        if (code < 0)
797
65
        {
798
65
            code = gs_rethrow(code, "cannot read zip central directory");
799
65
            goto cleanup;
800
65
        }
801
90
    }
802
803
25
    code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels");
804
25
    if (code)
805
1
    {
806
1
        code = gs_rethrow(code, "cannot process root relationship part");
807
1
        goto cleanup;
808
1
    }
809
810
24
    if (!ctx->start_part)
811
0
    {
812
0
        code = gs_rethrow(-1, "cannot find fixed document sequence start part");
813
0
        goto cleanup;
814
0
    }
815
816
24
    code = xps_read_and_process_metadata_part(ctx, ctx->start_part);
817
24
    if (code)
818
1
    {
819
1
        code = gs_rethrow(code, "cannot process FixedDocumentSequence part");
820
1
        goto cleanup;
821
1
    }
822
823
45
    for (doc = ctx->first_fixdoc; doc; doc = doc->next)
824
23
    {
825
23
        code = xps_read_and_process_metadata_part(ctx, doc->name);
826
23
        if (code)
827
1
        {
828
1
            code = gs_rethrow(code, "cannot process FixedDocument part");
829
1
            goto cleanup;
830
1
        }
831
23
    }
832
833
    /* If we have a page list, adjust pages now */
834
22
    if (ctx->page_range && ctx->page_range->page_list)
835
0
    {
836
0
        code = xps_reorder_pages(ctx);
837
0
        if (code)
838
0
        {
839
0
            code = gs_rethrow(code, "invalid page range setting");
840
0
            goto cleanup;
841
0
        }
842
0
    }
843
844
25
    for (page = ctx->first_page; page; page = page->next)
845
22
    {
846
22
        code = xps_read_and_process_page_part(ctx, page->name);
847
22
        if (code)
848
19
        {
849
19
            code = gs_rethrow(code, "cannot process FixedPage part");
850
19
            goto cleanup;
851
19
        }
852
22
    }
853
854
3
    code = gs_okay;
855
856
90
cleanup:
857
90
    if (ctx->directory)
858
90
        xps_free(ctx, ctx->directory);
859
90
    if (ctx->file)
860
90
        xps_fclose(ctx->file);
861
90
    return code;
862
3
}