Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
#include "mupdf/pdf.h"
2
3
/*
4
* Check if an object is a stream or not.
5
*/
6
int
7
pdf_is_stream(fz_context *ctx, pdf_document *doc, int num, int gen)
8
{
9
pdf_xref_entry *entry;
10
11
if (num <= 0 || num >= pdf_xref_len(ctx, doc))
12
return 0;
13
14
entry = pdf_cache_object(ctx, doc, num, gen);
15
16
return entry->stm_ofs != 0 || entry->stm_buf;
17
}
18
19
/*
20
* Scan stream dictionary for an explicit /Crypt filter
21
*/
22
static int
23
pdf_stream_has_crypt(fz_context *ctx, pdf_obj *stm)
24
{
25
pdf_obj *filters;
26
pdf_obj *obj;
27
int i;
28
29
filters = pdf_dict_geta(ctx, stm, PDF_NAME_Filter, PDF_NAME_F);
30
if (filters)
31
{
32
if (pdf_name_eq(ctx, filters, PDF_NAME_Crypt))
33
return 1;
34
if (pdf_is_array(ctx, filters))
35
{
36
int n = pdf_array_len(ctx, filters);
37
for (i = 0; i < n; i++)
38
{
39
obj = pdf_array_get(ctx, filters, i);
40
if (pdf_name_eq(ctx, obj, PDF_NAME_Crypt))
41
return 1;
42
}
43
}
44
}
45
return 0;
46
}
47
48
static fz_jbig2_globals *
49
pdf_load_jbig2_globals(fz_context *ctx, pdf_document *doc, pdf_obj *dict)
50
{
51
fz_jbig2_globals *globals;
52
fz_buffer *buf = NULL;
53
54
fz_var(buf);
55
56
if ((globals = pdf_find_item(ctx, fz_drop_jbig2_globals_imp, dict)) != NULL)
57
{
58
return globals;
59
}
60
61
fz_try(ctx)
62
{
63
buf = pdf_load_stream(ctx, doc, pdf_to_num(ctx, dict), pdf_to_gen(ctx, dict));
64
globals = fz_load_jbig2_globals(ctx, buf->data, buf->len);
65
pdf_store_item(ctx, dict, globals, buf->len);
66
}
67
fz_always(ctx)
68
{
69
fz_drop_buffer(ctx, buf);
70
}
71
fz_catch(ctx)
72
{
73
fz_rethrow(ctx);
74
}
75
76
return globals;
77
}
78
79
static void
80
build_compression_params(fz_context *ctx, pdf_obj *f, pdf_obj *p, fz_compression_params *params)
81
{
82
int predictor = pdf_to_int(ctx, pdf_dict_get(ctx, p, PDF_NAME_Predictor));
83
pdf_obj *columns_obj = pdf_dict_get(ctx, p, PDF_NAME_Columns);
84
int columns = pdf_to_int(ctx, columns_obj);
85
int colors = pdf_to_int(ctx, pdf_dict_get(ctx, p, PDF_NAME_Colors));
86
int bpc = pdf_to_int(ctx, pdf_dict_get(ctx, p, PDF_NAME_BitsPerComponent));
87
88
params->type = FZ_IMAGE_RAW;
89
90
if (pdf_name_eq(ctx, f, PDF_NAME_CCITTFaxDecode) || pdf_name_eq(ctx, f, PDF_NAME_CCF))
91
{
92
pdf_obj *k = pdf_dict_get(ctx, p, PDF_NAME_K);
93
pdf_obj *eol = pdf_dict_get(ctx, p, PDF_NAME_EndOfLine);
94
pdf_obj *eba = pdf_dict_get(ctx, p, PDF_NAME_EncodedByteAlign);
95
pdf_obj *rows = pdf_dict_get(ctx, p, PDF_NAME_Rows);
96
pdf_obj *eob = pdf_dict_get(ctx, p, PDF_NAME_EndOfBlock);
97
pdf_obj *bi1 = pdf_dict_get(ctx, p, PDF_NAME_BlackIs1);
98
99
params->type = FZ_IMAGE_FAX;
100
params->u.fax.k = (k ? pdf_to_int(ctx, k) : 0);
101
params->u.fax.end_of_line = (eol ? pdf_to_bool(ctx, eol) : 0);
102
params->u.fax.encoded_byte_align = (eba ? pdf_to_bool(ctx, eba) : 0);
103
params->u.fax.columns = (columns_obj ? columns : 1728);
104
params->u.fax.rows = (rows ? pdf_to_int(ctx, rows) : 0);
105
params->u.fax.end_of_block = (eob ? pdf_to_bool(ctx, eob) : 1);
106
params->u.fax.black_is_1 = (bi1 ? pdf_to_bool(ctx, bi1) : 0);
107
}
108
else if (pdf_name_eq(ctx, f, PDF_NAME_DCTDecode) || pdf_name_eq(ctx, f, PDF_NAME_DCT))
109
{
110
pdf_obj *ct = pdf_dict_get(ctx, p, PDF_NAME_ColorTransform);
111
112
params->type = FZ_IMAGE_JPEG;
113
params->u.jpeg.color_transform = (ct ? pdf_to_int(ctx, ct) : -1);
114
}
115
else if (pdf_name_eq(ctx, f, PDF_NAME_RunLengthDecode) || pdf_name_eq(ctx, f, PDF_NAME_RL))
116
{
117
params->type = FZ_IMAGE_RLD;
118
}
119
else if (pdf_name_eq(ctx, f, PDF_NAME_FlateDecode) || pdf_name_eq(ctx, f, PDF_NAME_Fl))
120
{
121
params->type = FZ_IMAGE_FLATE;
122
params->u.flate.predictor = predictor;
123
params->u.flate.columns = columns;
124
params->u.flate.colors = colors;
125
params->u.flate.bpc = bpc;
126
}
127
else if (pdf_name_eq(ctx, f, PDF_NAME_LZWDecode) || pdf_name_eq(ctx, f, PDF_NAME_LZW))
128
{
129
pdf_obj *ec = pdf_dict_get(ctx, p, PDF_NAME_EarlyChange);
130
131
params->type = FZ_IMAGE_LZW;
132
params->u.lzw.predictor = predictor;
133
params->u.lzw.columns = columns;
134
params->u.lzw.colors = colors;
135
params->u.lzw.bpc = bpc;
136
params->u.lzw.early_change = (ec ? pdf_to_int(ctx, ec) : 1);
137
}
138
}
139
140
/*
141
* Create a filter given a name and param dictionary.
142
*/
143
static fz_stream *
144
build_filter(fz_context *ctx, fz_stream *chain, pdf_document *doc, pdf_obj *f, pdf_obj *p, int num, int gen, fz_compression_params *params)
145
{
146
fz_compression_params local_params;
147
148
if (params == NULL)
149
params = &local_params;
150
151
build_compression_params(ctx, f, p, params);
152
153
/* If we were using params we were passed in, and we successfully
154
* recognised the image type, we can use the existing filter and
155
* shortstop here. */
156
if (params != &local_params && params->type != FZ_IMAGE_RAW)
157
return chain;
158
159
if (params->type != FZ_IMAGE_RAW)
160
return fz_open_image_decomp_stream(ctx, chain, params, NULL);
161
162
if (pdf_name_eq(ctx, f, PDF_NAME_ASCIIHexDecode) || pdf_name_eq(ctx, f, PDF_NAME_AHx))
163
return fz_open_ahxd(ctx, chain);
164
165
else if (pdf_name_eq(ctx, f, PDF_NAME_ASCII85Decode) || pdf_name_eq(ctx, f, PDF_NAME_A85))
166
return fz_open_a85d(ctx, chain);
167
168
else if (pdf_name_eq(ctx, f, PDF_NAME_JBIG2Decode))
169
{
170
fz_jbig2_globals *globals = NULL;
171
pdf_obj *obj = pdf_dict_get(ctx, p, PDF_NAME_JBIG2Globals);
172
if (pdf_is_indirect(ctx, obj))
173
globals = pdf_load_jbig2_globals(ctx, doc, obj);
174
/* fz_open_jbig2d takes possession of globals */
175
return fz_open_jbig2d(ctx, chain, globals);
176
}
177
178
else if (pdf_name_eq(ctx, f, PDF_NAME_JPXDecode))
179
return chain; /* JPX decoding is special cased in the image loading code */
180
181
else if (pdf_name_eq(ctx, f, PDF_NAME_Crypt))
182
{
183
pdf_obj *name;
184
185
if (!doc->crypt)
186
{
187
fz_warn(ctx, "crypt filter in unencrypted document");
188
return chain;
189
}
190
191
name = pdf_dict_get(ctx, p, PDF_NAME_Name);
192
if (pdf_is_name(ctx, name))
193
return pdf_open_crypt_with_filter(ctx, chain, doc->crypt, name, num, gen);
194
195
return chain;
196
}
197
198
fz_warn(ctx, "unknown filter name (%s)", pdf_to_name(ctx, f));
199
return chain;
200
}
201
202
/*
203
* Build a chain of filters given filter names and param dicts.
204
* If head is given, start filter chain with it.
205
* Assume ownership of head.
206
*/
207
static fz_stream *
208
build_filter_chain(fz_context *ctx, fz_stream *chain, pdf_document *doc, pdf_obj *fs, pdf_obj *ps, int num, int gen, fz_compression_params *params)
209
{
210
pdf_obj *f;
211
pdf_obj *p;
212
int i, n;
213
214
fz_try(ctx)
215
{
216
n = pdf_array_len(ctx, fs);
217
for (i = 0; i < n; i++)
218
{
219
fz_stream *chain2;
220
221
f = pdf_array_get(ctx, fs, i);
222
p = pdf_array_get(ctx, ps, i);
223
chain2 = chain;
224
chain = NULL;
225
chain = build_filter(ctx, chain2, doc, f, p, num, gen, (i == n-1 ? params : NULL));
226
}
227
}
228
fz_catch(ctx)
229
{
230
fz_drop_stream(ctx, chain);
231
fz_rethrow(ctx);
232
}
233
234
return chain;
235
}
236
237
/*
238
* Build a filter for reading raw stream data.
239
* This is a null filter to constrain reading to the stream length (and to
240
* allow for other people accessing the file), followed by a decryption
241
* filter.
242
*
243
* orig_num and orig_gen are used purely to seed the encryption.
244
*/
245
static fz_stream *
246
pdf_open_raw_filter(fz_context *ctx, fz_stream *chain, pdf_document *doc, pdf_obj *stmobj, int num, int orig_num, int orig_gen, int offset)
247
{
248
int hascrypt;
249
int len;
250
251
if (num > 0 && num < pdf_xref_len(ctx, doc))
252
{
253
pdf_xref_entry *entry = pdf_get_xref_entry(ctx, doc, num);
254
if (entry->stm_buf)
255
return fz_open_buffer(ctx, entry->stm_buf);
256
}
257
258
/* don't close chain when we close this filter */
259
fz_keep_stream(ctx, chain);
260
261
len = pdf_to_int(ctx, pdf_dict_get(ctx, stmobj, PDF_NAME_Length));
262
chain = fz_open_null(ctx, chain, len, offset);
263
264
hascrypt = pdf_stream_has_crypt(ctx, stmobj);
265
if (doc->crypt && !hascrypt)
266
chain = pdf_open_crypt(ctx, chain, doc->crypt, orig_num, orig_gen);
267
268
return chain;
269
}
270
271
/*
272
* Construct a filter to decode a stream, constraining
273
* to stream length and decrypting.
274
*/
275
static fz_stream *
276
pdf_open_filter(fz_context *ctx, pdf_document *doc, fz_stream *chain, pdf_obj *stmobj, int num, int gen, int offset, fz_compression_params *imparams)
277
{
278
pdf_obj *filters;
279
pdf_obj *params;
280
281
filters = pdf_dict_geta(ctx, stmobj, PDF_NAME_Filter, PDF_NAME_F);
282
params = pdf_dict_geta(ctx, stmobj, PDF_NAME_DecodeParms, PDF_NAME_DP);
283
284
chain = pdf_open_raw_filter(ctx, chain, doc, stmobj, num, num, gen, offset);
285
286
fz_var(chain);
287
288
fz_try(ctx)
289
{
290
if (pdf_is_name(ctx, filters))
291
{
292
fz_stream *chain2 = chain;
293
chain = NULL;
294
chain = build_filter(ctx, chain2, doc, filters, params, num, gen, imparams);
295
}
296
else if (pdf_array_len(ctx, filters) > 0)
297
{
298
fz_stream *chain2 = chain;
299
chain = NULL;
300
chain = build_filter_chain(ctx, chain2, doc, filters, params, num, gen, imparams);
301
}
302
}
303
fz_catch(ctx)
304
{
305
fz_drop_stream(ctx, chain);
306
fz_rethrow(ctx);
307
}
308
309
return chain;
310
}
311
312
/*
313
* Construct a filter to decode a stream, without
314
* constraining to stream length, and without decryption.
315
*/
316
fz_stream *
317
pdf_open_inline_stream(fz_context *ctx, pdf_document *doc, pdf_obj *stmobj, int length, fz_stream *chain, fz_compression_params *imparams)
318
{
319
pdf_obj *filters;
320
pdf_obj *params;
321
322
filters = pdf_dict_geta(ctx, stmobj, PDF_NAME_Filter, PDF_NAME_F);
323
params = pdf_dict_geta(ctx, stmobj, PDF_NAME_DecodeParms, PDF_NAME_DP);
324
325
/* don't close chain when we close this filter */
326
fz_keep_stream(ctx, chain);
327
328
if (pdf_is_name(ctx, filters))
329
return build_filter(ctx, chain, doc, filters, params, 0, 0, imparams);
330
if (pdf_array_len(ctx, filters) > 0)
331
return build_filter_chain(ctx, chain, doc, filters, params, 0, 0, imparams);
332
333
if (imparams)
334
imparams->type = FZ_IMAGE_RAW;
335
return fz_open_null(ctx, chain, length, fz_tell(ctx, chain));
336
}
337
338
void
339
pdf_load_compressed_inline_image(fz_context *ctx, pdf_document *doc, pdf_obj *dict, int length, fz_stream *stm, int indexed, fz_image *image)
340
{
341
fz_compressed_buffer *bc = fz_malloc_struct(ctx, fz_compressed_buffer);
342
343
fz_try(ctx)
344
{
345
int dummy_l2factor = 0;
346
bc->buffer = fz_new_buffer(ctx, 1024);
347
348
stm = pdf_open_inline_stream(ctx, doc, dict, length, stm, &bc->params);
349
stm = fz_open_leecher(ctx, stm, bc->buffer);
350
stm = fz_open_image_decomp_stream(ctx, stm, &bc->params, &dummy_l2factor);
351
352
image->tile = fz_decomp_image_from_stream(ctx, stm, image, indexed, 0, 0);
353
}
354
fz_catch(ctx)
355
{
356
fz_drop_compressed_buffer(ctx, bc);
357
fz_rethrow(ctx);
358
}
359
image->buffer = bc;
360
}
361
362
/*
363
* Open a stream for reading the raw (compressed but decrypted) data.
364
*/
365
fz_stream *
366
pdf_open_raw_stream(fz_context *ctx, pdf_document *doc, int num, int gen)
367
{
368
return pdf_open_raw_renumbered_stream(ctx, doc, num, gen, num, gen);
369
}
370
371
fz_stream *
372
pdf_open_raw_renumbered_stream(fz_context *ctx, pdf_document *doc, int num, int gen, int orig_num, int orig_gen)
373
{
374
pdf_xref_entry *x;
375
376
if (num <= 0 || num >= pdf_xref_len(ctx, doc))
377
fz_throw(ctx, FZ_ERROR_GENERIC, "object id out of range (%d %d R)", num, gen);
378
379
x = pdf_cache_object(ctx, doc, num, gen);
380
if (x->stm_ofs == 0)
381
fz_throw(ctx, FZ_ERROR_GENERIC, "object is not a stream");
382
383
return pdf_open_raw_filter(ctx, doc->file, doc, x->obj, num, orig_num, orig_gen, x->stm_ofs);
384
}
385
386
static fz_stream *
387
pdf_open_image_stream(fz_context *ctx, pdf_document *doc, int num, int gen, int orig_num, int orig_gen, fz_compression_params *params)
388
{
389
pdf_xref_entry *x;
390
391
if (num <= 0 || num >= pdf_xref_len(ctx, doc))
392
fz_throw(ctx, FZ_ERROR_GENERIC, "object id out of range (%d %d R)", num, gen);
393
394
x = pdf_cache_object(ctx, doc, num, gen);
395
if (x->stm_ofs == 0 && x->stm_buf == NULL)
396
fz_throw(ctx, FZ_ERROR_GENERIC, "object is not a stream");
397
398
return pdf_open_filter(ctx, doc, doc->file, x->obj, orig_num, orig_gen, x->stm_ofs, params);
399
}
400
401
/*
402
* Open a stream for reading uncompressed data.
403
* Put the opened file in doc->stream.
404
* Using doc->file while a stream is open is a Bad idea.
405
*/
406
fz_stream *
407
pdf_open_stream(fz_context *ctx, pdf_document *doc, int num, int gen)
408
{
409
return pdf_open_image_stream(ctx, doc, num, gen, num, gen, NULL);
410
}
411
412
fz_stream *
413
pdf_open_stream_with_offset(fz_context *ctx, pdf_document *doc, int num, int gen, pdf_obj *dict, int stm_ofs)
414
{
415
if (stm_ofs == 0)
416
fz_throw(ctx, FZ_ERROR_GENERIC, "object is not a stream");
417
418
return pdf_open_filter(ctx, doc, doc->file, dict, num, gen, stm_ofs, NULL);
419
}
420
421
/*
422
* Load raw (compressed but decrypted) contents of a stream into buf.
423
*/
424
fz_buffer *
425
pdf_load_raw_stream(fz_context *ctx, pdf_document *doc, int num, int gen)
426
{
427
return pdf_load_raw_renumbered_stream(ctx, doc, num, gen, num, gen);
428
}
429
430
fz_buffer *
431
pdf_load_raw_renumbered_stream(fz_context *ctx, pdf_document *doc, int num, int gen, int orig_num, int orig_gen)
432
{
433
fz_stream *stm;
434
pdf_obj *dict;
435
int len;
436
fz_buffer *buf;
437
438
if (num > 0 && num < pdf_xref_len(ctx, doc))
439
{
440
pdf_xref_entry *entry = pdf_get_xref_entry(ctx, doc, num);
441
if (entry->stm_buf)
442
return fz_keep_buffer(ctx, entry->stm_buf);
443
}
444
445
dict = pdf_load_object(ctx, doc, num, gen);
446
447
len = pdf_to_int(ctx, pdf_dict_get(ctx, dict, PDF_NAME_Length));
448
449
pdf_drop_obj(ctx, dict);
450
451
stm = pdf_open_raw_renumbered_stream(ctx, doc, num, gen, orig_num, orig_gen);
452
453
buf = fz_read_all(ctx, stm, len);
454
455
fz_drop_stream(ctx, stm);
456
return buf;
457
}
458
459
static int
460
pdf_guess_filter_length(int len, char *filter)
461
{
462
if (!strcmp(filter, "ASCIIHexDecode"))
463
return len / 2;
464
if (!strcmp(filter, "ASCII85Decode"))
465
return len * 4 / 5;
466
if (!strcmp(filter, "FlateDecode"))
467
return len * 3;
468
if (!strcmp(filter, "RunLengthDecode"))
469
return len * 3;
470
if (!strcmp(filter, "LZWDecode"))
471
return len * 2;
472
return len;
473
}
474
475
/* Check if an entry has a cached stream and return whether it is directly
476
* reusable. A buffer is directly reusable only if the stream is
477
* uncompressed, or if it is compressed purely a compression method we can
478
* return details of in fz_compression_params.
479
*
480
* If the stream is reusable return 1, and set params as required, otherwise
481
* return 0. */
482
static int
483
can_reuse_buffer(fz_context *ctx, pdf_xref_entry *entry, fz_compression_params *params)
484
{
485
pdf_obj *f;
486
pdf_obj *p;
487
488
if (!entry || !entry->obj || !entry->stm_buf)
489
return 0;
490
491
if (params)
492
params->type = FZ_IMAGE_RAW;
493
494
f = pdf_dict_geta(ctx, entry->obj, PDF_NAME_Filter, PDF_NAME_F);
495
/* If there are no filters, it's uncompressed, and we can use it */
496
if (!f)
497
return 1;
498
499
p = pdf_dict_geta(ctx, entry->obj, PDF_NAME_DecodeParms, PDF_NAME_DP);
500
if (pdf_is_array(ctx, f))
501
{
502
int len = pdf_array_len(ctx, f);
503
504
/* Empty array of filters. It's uncompressed. We can cope. */
505
if (len == 0)
506
return 1;
507
/* 1 filter is the most we can hope to cope with - if more,*/
508
if (len != 1)
509
return 0;
510
p = pdf_array_get(ctx, p, 0);
511
}
512
if (pdf_is_null(ctx, f))
513
return 1; /* Null filter is uncompressed */
514
if (!pdf_is_name(ctx, f))
515
return 0;
516
517
/* There are filters, so unless we have the option of shortstopping,
518
* we can't use the existing buffer. */
519
if (!params)
520
return 0;
521
522
build_compression_params(ctx, f, p, params);
523
524
return (params->type == FZ_IMAGE_RAW) ? 0 : 1;
525
526
}
527
528
static fz_buffer *
529
pdf_load_image_stream(fz_context *ctx, pdf_document *doc, int num, int gen, int orig_num, int orig_gen, fz_compression_params *params, int *truncated)
530
{
531
fz_stream *stm = NULL;
532
pdf_obj *dict, *obj;
533
int i, len, n;
534
fz_buffer *buf;
535
536
fz_var(buf);
537
538
if (num > 0 && num < pdf_xref_len(ctx, doc))
539
{
540
pdf_xref_entry *entry = pdf_get_xref_entry(ctx, doc, num);
541
/* Return ref to existing buffer, but only if uncompressed,
542
* or shortstoppable */
543
if (can_reuse_buffer(ctx, entry, params))
544
return fz_keep_buffer(ctx, entry->stm_buf);
545
}
546
547
dict = pdf_load_object(ctx, doc, num, gen);
548
549
len = pdf_to_int(ctx, pdf_dict_get(ctx, dict, PDF_NAME_Length));
550
obj = pdf_dict_get(ctx, dict, PDF_NAME_Filter);
551
len = pdf_guess_filter_length(len, pdf_to_name(ctx, obj));
552
n = pdf_array_len(ctx, obj);
553
for (i = 0; i < n; i++)
554
len = pdf_guess_filter_length(len, pdf_to_name(ctx, pdf_array_get(ctx, obj, i)));
555
556
pdf_drop_obj(ctx, dict);
557
558
stm = pdf_open_image_stream(ctx, doc, num, gen, orig_num, orig_gen, params);
559
560
fz_try(ctx)
561
{
562
if (truncated)
563
buf = fz_read_best(ctx, stm, len, truncated);
564
else
565
buf = fz_read_all(ctx, stm, len);
566
}
567
fz_always(ctx)
568
{
569
fz_drop_stream(ctx, stm);
570
}
571
fz_catch(ctx)
572
{
573
fz_rethrow_message(ctx, "cannot read raw stream (%d %d R)", num, gen);
574
}
575
576
return buf;
577
}
578
579
/*
580
* Load uncompressed contents of a stream into buf.
581
*/
582
fz_buffer *
583
pdf_load_stream(fz_context *ctx, pdf_document *doc, int num, int gen)
584
{
585
return pdf_load_image_stream(ctx, doc, num, gen, num, gen, NULL, NULL);
586
}
587
588
fz_buffer *
589
pdf_load_renumbered_stream(fz_context *ctx, pdf_document *doc, int num, int gen, int orig_num, int orig_gen, int *truncated)
590
{
591
return pdf_load_image_stream(ctx, doc, num, gen, orig_num, orig_gen, NULL, truncated);
592
}
593
594
fz_compressed_buffer *
595
pdf_load_compressed_stream(fz_context *ctx, pdf_document *doc, int num, int gen)
596
{
597
fz_compressed_buffer *bc = fz_malloc_struct(ctx, fz_compressed_buffer);
598
599
fz_try(ctx)
600
{
601
bc->buffer = pdf_load_image_stream(ctx, doc, num, gen, num, gen, &bc->params, NULL);
602
}
603
fz_catch(ctx)
604
{
605
fz_free(ctx, bc);
606
fz_rethrow(ctx);
607
}
608
return bc;
609
}
610
611
static fz_stream *
612
pdf_open_object_array(fz_context *ctx, pdf_document *doc, pdf_obj *list)
613
{
614
fz_stream *stm;
615
int i, n;
616
617
n = pdf_array_len(ctx, list);
618
stm = fz_open_concat(ctx, n, 1);
619
620
fz_var(i); /* Workaround Mac compiler bug */
621
for (i = 0; i < n; i++)
622
{
623
pdf_obj *obj = pdf_array_get(ctx, list, i);
624
fz_try(ctx)
625
{
626
fz_concat_push(ctx, stm, pdf_open_stream(ctx, doc, pdf_to_num(ctx, obj), pdf_to_gen(ctx, obj)));
627
}
628
fz_catch(ctx)
629
{
630
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
631
fz_warn(ctx, "cannot load content stream part %d/%d", i + 1, n);
632
continue;
633
}
634
}
635
636
return stm;
637
}
638
639
fz_stream *
640
pdf_open_contents_stream(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
641
{
642
int num, gen;
643
644
if (pdf_is_array(ctx, obj))
645
return pdf_open_object_array(ctx, doc, obj);
646
647
num = pdf_to_num(ctx, obj);
648
gen = pdf_to_gen(ctx, obj);
649
if (pdf_is_stream(ctx, doc, num, gen))
650
return pdf_open_image_stream(ctx, doc, num, gen, num, gen, NULL);
651
652
fz_throw(ctx, FZ_ERROR_GENERIC, "pdf object stream missing (%d %d R)", num, gen);
653
}
654
655