| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* pdfmake_import.c — Cross-document object/page import. |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* See include/pdfmake_import.h for the public API and design notes. |
|
5
|
|
|
|
|
|
|
*/ |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
#include "pdfmake_import.h" |
|
8
|
|
|
|
|
|
|
#include "pdfmake_arena.h" |
|
9
|
|
|
|
|
|
|
#include "pdfmake_page.h" |
|
10
|
|
|
|
|
|
|
#include "pdfmake_buf.h" |
|
11
|
|
|
|
|
|
|
#include |
|
12
|
|
|
|
|
|
|
#include |
|
13
|
|
|
|
|
|
|
#include |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
/*============================================================================ |
|
16
|
|
|
|
|
|
|
* Import context |
|
17
|
|
|
|
|
|
|
*==========================================================================*/ |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
struct pdfmake_import_ctx { |
|
20
|
|
|
|
|
|
|
pdfmake_reader_t *src_reader; |
|
21
|
|
|
|
|
|
|
pdfmake_parser_t *src_parser; |
|
22
|
|
|
|
|
|
|
pdfmake_arena_t *src_arena; |
|
23
|
|
|
|
|
|
|
pdfmake_doc_t *dst; |
|
24
|
|
|
|
|
|
|
pdfmake_arena_t *dst_arena; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
/* Remap table: src_num -> dst_num (0 means not yet imported). |
|
27
|
|
|
|
|
|
|
* Sized at src_parser->xref_size. */ |
|
28
|
|
|
|
|
|
|
uint32_t *remap; |
|
29
|
|
|
|
|
|
|
size_t remap_size; |
|
30
|
|
|
|
|
|
|
}; |
|
31
|
|
|
|
|
|
|
|
|
32
|
24
|
|
|
|
|
|
pdfmake_import_ctx_t *pdfmake_import_ctx_new(pdfmake_reader_t *src_reader, |
|
33
|
|
|
|
|
|
|
pdfmake_doc_t *dst) { |
|
34
|
|
|
|
|
|
|
pdfmake_import_ctx_t *ctx; |
|
35
|
|
|
|
|
|
|
|
|
36
|
24
|
50
|
|
|
|
|
if (!src_reader || !dst) return NULL; |
|
|
|
50
|
|
|
|
|
|
|
37
|
24
|
50
|
|
|
|
|
if (!src_reader->parser) return NULL; |
|
38
|
|
|
|
|
|
|
|
|
39
|
24
|
|
|
|
|
|
ctx = calloc(1, sizeof(*ctx)); |
|
40
|
24
|
50
|
|
|
|
|
if (!ctx) return NULL; |
|
41
|
|
|
|
|
|
|
|
|
42
|
24
|
|
|
|
|
|
ctx->src_reader = src_reader; |
|
43
|
24
|
|
|
|
|
|
ctx->src_parser = src_reader->parser; |
|
44
|
24
|
|
|
|
|
|
ctx->src_arena = src_reader->parser->doc->arena; |
|
45
|
24
|
|
|
|
|
|
ctx->dst = dst; |
|
46
|
24
|
|
|
|
|
|
ctx->dst_arena = pdfmake_doc_arena(dst); |
|
47
|
|
|
|
|
|
|
|
|
48
|
24
|
|
|
|
|
|
ctx->remap_size = ctx->src_parser->xref_size + 1; |
|
49
|
24
|
|
|
|
|
|
ctx->remap = calloc(ctx->remap_size, sizeof(uint32_t)); |
|
50
|
24
|
50
|
|
|
|
|
if (!ctx->remap) { |
|
51
|
0
|
|
|
|
|
|
free(ctx); |
|
52
|
0
|
|
|
|
|
|
return NULL; |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
24
|
|
|
|
|
|
return ctx; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
24
|
|
|
|
|
|
void pdfmake_import_ctx_free(pdfmake_import_ctx_t *ctx) { |
|
59
|
24
|
50
|
|
|
|
|
if (!ctx) return; |
|
60
|
24
|
|
|
|
|
|
free(ctx->remap); |
|
61
|
24
|
|
|
|
|
|
free(ctx); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
/*============================================================================ |
|
65
|
|
|
|
|
|
|
* Object deep-copy (walks composite graph) |
|
66
|
|
|
|
|
|
|
*==========================================================================*/ |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
static pdfmake_obj_t import_obj(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src); |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
/* Forward decl — streams reached via a ref need the src object number so |
|
71
|
|
|
|
|
|
|
* we can decrypt their bytes through the reader. */ |
|
72
|
|
|
|
|
|
|
static pdfmake_obj_t import_stream_with_decrypt(pdfmake_import_ctx_t *ctx, |
|
73
|
|
|
|
|
|
|
pdfmake_obj_t src, |
|
74
|
|
|
|
|
|
|
uint32_t src_num); |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
/* Import an indirect src_num → dst_num. Handles cycles via remap slot. |
|
77
|
|
|
|
|
|
|
* Returns 0 on failure. */ |
|
78
|
259
|
|
|
|
|
|
static uint32_t import_ref_num(pdfmake_import_ctx_t *ctx, uint32_t src_num) { |
|
79
|
|
|
|
|
|
|
pdfmake_ref_t r; |
|
80
|
|
|
|
|
|
|
pdfmake_obj_t *src_obj; |
|
81
|
|
|
|
|
|
|
uint32_t dst_num; |
|
82
|
|
|
|
|
|
|
pdfmake_obj_t dst_obj; |
|
83
|
|
|
|
|
|
|
|
|
84
|
259
|
50
|
|
|
|
|
if (src_num == 0 || src_num >= ctx->remap_size) return 0; |
|
|
|
50
|
|
|
|
|
|
|
85
|
259
|
100
|
|
|
|
|
if (ctx->remap[src_num] != 0) return ctx->remap[src_num]; |
|
86
|
|
|
|
|
|
|
|
|
87
|
159
|
|
|
|
|
|
r.num = src_num; |
|
88
|
159
|
|
|
|
|
|
r.gen = 0; |
|
89
|
159
|
|
|
|
|
|
src_obj = pdfmake_parser_resolve(ctx->src_parser, r); |
|
90
|
159
|
50
|
|
|
|
|
if (!src_obj) return 0; |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
/* Reserve the dst slot before recursing so cycles terminate. */ |
|
93
|
159
|
|
|
|
|
|
dst_num = pdfmake_doc_add(ctx->dst, pdfmake_null()); |
|
94
|
159
|
50
|
|
|
|
|
if (dst_num == 0) return 0; |
|
95
|
159
|
|
|
|
|
|
ctx->remap[src_num] = dst_num; |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
/* Streams carry the encryption state of the source file — decrypt them |
|
98
|
|
|
|
|
|
|
* through the reader before copying so the destination stays readable |
|
99
|
|
|
|
|
|
|
* when the source was encrypted. Non-stream objects (names, numbers, |
|
100
|
|
|
|
|
|
|
* plain dicts) aren't per-object encrypted and import cleanly. */ |
|
101
|
159
|
100
|
|
|
|
|
if (src_obj->kind == PDFMAKE_STREAM) { |
|
102
|
51
|
|
|
|
|
|
dst_obj = import_stream_with_decrypt(ctx, *src_obj, src_num); |
|
103
|
|
|
|
|
|
|
} else { |
|
104
|
108
|
|
|
|
|
|
dst_obj = import_obj(ctx, *src_obj); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
/* Overwrite the reserved slot with the real imported object. */ |
|
108
|
159
|
|
|
|
|
|
ctx->dst->objects[dst_num - 1].obj = dst_obj; |
|
109
|
|
|
|
|
|
|
|
|
110
|
159
|
|
|
|
|
|
return dst_num; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
437
|
|
|
|
|
|
static pdfmake_obj_t import_name(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src) { |
|
114
|
|
|
|
|
|
|
const char *bytes; |
|
115
|
|
|
|
|
|
|
size_t len; |
|
116
|
|
|
|
|
|
|
uint32_t new_id; |
|
117
|
|
|
|
|
|
|
pdfmake_obj_t out; |
|
118
|
|
|
|
|
|
|
|
|
119
|
437
|
|
|
|
|
|
bytes = pdfmake_arena_name_bytes(ctx->src_arena, src.as.name.id); |
|
120
|
437
|
|
|
|
|
|
len = pdfmake_arena_name_len (ctx->src_arena, src.as.name.id); |
|
121
|
437
|
50
|
|
|
|
|
if (!bytes) return pdfmake_null(); |
|
122
|
437
|
|
|
|
|
|
new_id = pdfmake_arena_intern_name(ctx->dst_arena, bytes, len); |
|
123
|
437
|
|
|
|
|
|
out.kind = PDFMAKE_NAME; |
|
124
|
437
|
|
|
|
|
|
out.as.name.id = new_id; |
|
125
|
437
|
|
|
|
|
|
return out; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
0
|
|
|
|
|
|
static pdfmake_obj_t import_string(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src) { |
|
129
|
|
|
|
|
|
|
void *copy; |
|
130
|
|
|
|
|
|
|
pdfmake_obj_t out; |
|
131
|
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
copy = pdfmake_arena_memdup(ctx->dst_arena, src.as.str.bytes, src.as.str.len); |
|
133
|
0
|
0
|
|
|
|
|
if (!copy && src.as.str.len > 0) return pdfmake_null(); |
|
|
|
0
|
|
|
|
|
|
|
134
|
0
|
|
|
|
|
|
out.kind = PDFMAKE_STR; |
|
135
|
0
|
|
|
|
|
|
out.as.str.bytes = (const uint8_t *)copy; |
|
136
|
0
|
|
|
|
|
|
out.as.str.len = src.as.str.len; |
|
137
|
0
|
|
|
|
|
|
out.as.str.hex = src.as.str.hex; |
|
138
|
0
|
|
|
|
|
|
return out; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
80
|
|
|
|
|
|
static pdfmake_obj_t import_array(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src) { |
|
142
|
|
|
|
|
|
|
pdfmake_obj_t out; |
|
143
|
|
|
|
|
|
|
uint32_t i; |
|
144
|
|
|
|
|
|
|
pdfmake_obj_t elem; |
|
145
|
|
|
|
|
|
|
|
|
146
|
80
|
|
|
|
|
|
out = pdfmake_array_new(ctx->dst_arena); |
|
147
|
80
|
50
|
|
|
|
|
if (out.kind != PDFMAKE_ARRAY || !src.as.arr) return out; |
|
|
|
50
|
|
|
|
|
|
|
148
|
1693
|
100
|
|
|
|
|
for (i = 0; i < src.as.arr->len; i++) { |
|
149
|
1613
|
|
|
|
|
|
elem = import_obj(ctx, src.as.arr->items[i]); |
|
150
|
1613
|
50
|
|
|
|
|
if (!pdfmake_array_push(ctx->dst_arena, &out, elem)) break; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
80
|
|
|
|
|
|
return out; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
292
|
|
|
|
|
|
static pdfmake_obj_t import_dict(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src) { |
|
156
|
|
|
|
|
|
|
pdfmake_obj_t out; |
|
157
|
|
|
|
|
|
|
pdfmake_dict_iter_t it; |
|
158
|
|
|
|
|
|
|
const char *kb; |
|
159
|
|
|
|
|
|
|
size_t kl; |
|
160
|
|
|
|
|
|
|
uint32_t new_key; |
|
161
|
|
|
|
|
|
|
pdfmake_obj_t new_val; |
|
162
|
|
|
|
|
|
|
|
|
163
|
292
|
|
|
|
|
|
out = pdfmake_dict_new(ctx->dst_arena); |
|
164
|
292
|
50
|
|
|
|
|
if (out.kind != PDFMAKE_DICT) return out; |
|
165
|
|
|
|
|
|
|
|
|
166
|
292
|
|
|
|
|
|
pdfmake_dict_iter_init(&it, &src); |
|
167
|
1352
|
100
|
|
|
|
|
while (pdfmake_dict_iter_next(&it)) { |
|
168
|
1060
|
|
|
|
|
|
kb = pdfmake_arena_name_bytes(ctx->src_arena, it.current_key); |
|
169
|
1060
|
|
|
|
|
|
kl = pdfmake_arena_name_len (ctx->src_arena, it.current_key); |
|
170
|
1060
|
50
|
|
|
|
|
if (!kb) continue; |
|
171
|
1060
|
|
|
|
|
|
new_key = pdfmake_arena_intern_name(ctx->dst_arena, kb, kl); |
|
172
|
1060
|
50
|
|
|
|
|
if (new_key == 0) continue; |
|
173
|
1060
|
|
|
|
|
|
new_val = import_obj(ctx, *it.current_value); |
|
174
|
1060
|
|
|
|
|
|
pdfmake_dict_set(ctx->dst_arena, &out, new_key, new_val); |
|
175
|
|
|
|
|
|
|
} |
|
176
|
292
|
|
|
|
|
|
return out; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
/* Copy a stream verbatim without touching encryption state. Used for |
|
180
|
|
|
|
|
|
|
* streams that weren't reached through a ref (no src_num available). |
|
181
|
|
|
|
|
|
|
* Bytes already passed through the parser → in raw form (possibly |
|
182
|
|
|
|
|
|
|
* encrypted). The caller is responsible for pairing this with an |
|
183
|
|
|
|
|
|
|
* unencrypted destination or ensuring the source wasn't encrypted. */ |
|
184
|
0
|
|
|
|
|
|
static pdfmake_obj_t import_stream(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src) { |
|
185
|
|
|
|
|
|
|
pdfmake_obj_t out; |
|
186
|
|
|
|
|
|
|
pdfmake_obj_t src_dict_obj; |
|
187
|
|
|
|
|
|
|
pdfmake_obj_t new_dict_obj; |
|
188
|
|
|
|
|
|
|
void *copy; |
|
189
|
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
out = pdfmake_stream_new(ctx->dst_arena); |
|
191
|
0
|
0
|
|
|
|
|
if (out.kind != PDFMAKE_STREAM || !src.as.stream) return out; |
|
|
|
0
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
src_dict_obj.kind = PDFMAKE_DICT; |
|
194
|
0
|
|
|
|
|
|
src_dict_obj.as.dict = src.as.stream->dict; |
|
195
|
0
|
|
|
|
|
|
new_dict_obj = import_dict(ctx, src_dict_obj); |
|
196
|
0
|
|
|
|
|
|
out.as.stream->dict = new_dict_obj.as.dict; |
|
197
|
|
|
|
|
|
|
|
|
198
|
0
|
0
|
|
|
|
|
if (src.as.stream->raw && src.as.stream->raw_len > 0) { |
|
|
|
0
|
|
|
|
|
|
|
199
|
0
|
|
|
|
|
|
copy = pdfmake_arena_memdup(ctx->dst_arena, |
|
200
|
0
|
|
|
|
|
|
src.as.stream->raw, |
|
201
|
0
|
|
|
|
|
|
src.as.stream->raw_len); |
|
202
|
0
|
|
|
|
|
|
out.as.stream->raw = (const uint8_t *)copy; |
|
203
|
0
|
|
|
|
|
|
out.as.stream->raw_len = src.as.stream->raw_len; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
0
|
|
|
|
|
|
out.as.stream->filtered = 1; |
|
206
|
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
return out; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
/* Import a stream by src object number. When the source document is |
|
211
|
|
|
|
|
|
|
* encrypted we fetch fully decoded (decrypt + decompress) bytes through |
|
212
|
|
|
|
|
|
|
* the reader and store them raw in the destination — dropping /Filter, |
|
213
|
|
|
|
|
|
|
* /DecodeParms, and /Length from the dict so the writer emits the |
|
214
|
|
|
|
|
|
|
* plain-text bytes with a fresh /Length it will compute at write time. |
|
215
|
|
|
|
|
|
|
* |
|
216
|
|
|
|
|
|
|
* The trade-off is file-size: stored uncompressed rather than re-applying |
|
217
|
|
|
|
|
|
|
* /Filter. For a typical encrypted document this is fine; callers who |
|
218
|
|
|
|
|
|
|
* care can FlateDecode the whole output by emitting with compression |
|
219
|
|
|
|
|
|
|
* enabled on the destination. */ |
|
220
|
51
|
|
|
|
|
|
static pdfmake_obj_t import_stream_with_decrypt(pdfmake_import_ctx_t *ctx, |
|
221
|
|
|
|
|
|
|
pdfmake_obj_t src, |
|
222
|
|
|
|
|
|
|
uint32_t src_num) |
|
223
|
|
|
|
|
|
|
{ |
|
224
|
|
|
|
|
|
|
pdfmake_buf_t decoded; |
|
225
|
|
|
|
|
|
|
pdfmake_err_t err; |
|
226
|
|
|
|
|
|
|
pdfmake_obj_t out; |
|
227
|
|
|
|
|
|
|
pdfmake_obj_t src_dict_obj; |
|
228
|
|
|
|
|
|
|
pdfmake_obj_t new_dict_obj; |
|
229
|
|
|
|
|
|
|
uint32_t filter_key; |
|
230
|
|
|
|
|
|
|
uint32_t parms_key; |
|
231
|
|
|
|
|
|
|
uint32_t length_key; |
|
232
|
|
|
|
|
|
|
pdfmake_obj_t dict_wrapper; |
|
233
|
|
|
|
|
|
|
void *copy; |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
/* Fast path: no encryption → plain verbatim copy. */ |
|
236
|
51
|
50
|
|
|
|
|
if (!ctx->src_reader || |
|
237
|
51
|
50
|
|
|
|
|
!ctx->src_reader->crypt || |
|
238
|
51
|
50
|
|
|
|
|
!ctx->src_reader->authenticated) { |
|
239
|
0
|
|
|
|
|
|
return import_stream(ctx, src); |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
/* Fetch decoded bytes through the reader (decrypt + filter chain). */ |
|
243
|
51
|
|
|
|
|
|
pdfmake_buf_init(&decoded); |
|
244
|
51
|
|
|
|
|
|
err = pdfmake_reader_resolve_stream( |
|
245
|
|
|
|
|
|
|
ctx->src_reader, src_num, 0, &decoded); |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
/* On failure fall back to verbatim copy so the document still imports |
|
248
|
|
|
|
|
|
|
* (readers may show the image as broken, but the page structure is |
|
249
|
|
|
|
|
|
|
* preserved). */ |
|
250
|
51
|
50
|
|
|
|
|
if (err != PDFMAKE_OK) { |
|
251
|
0
|
|
|
|
|
|
pdfmake_buf_free(&decoded); |
|
252
|
0
|
|
|
|
|
|
return import_stream(ctx, src); |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
/* Build the destination stream: deep-copy the dict, strip /Filter + |
|
256
|
|
|
|
|
|
|
* /DecodeParms + /Length, then store the decoded bytes with |
|
257
|
|
|
|
|
|
|
* filtered=1 so the writer emits them verbatim (and writes a fresh |
|
258
|
|
|
|
|
|
|
* /Length). */ |
|
259
|
51
|
|
|
|
|
|
out = pdfmake_stream_new(ctx->dst_arena); |
|
260
|
51
|
50
|
|
|
|
|
if (out.kind != PDFMAKE_STREAM) { |
|
261
|
0
|
|
|
|
|
|
pdfmake_buf_free(&decoded); |
|
262
|
0
|
|
|
|
|
|
return out; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
51
|
|
|
|
|
|
src_dict_obj.kind = PDFMAKE_DICT; |
|
266
|
51
|
|
|
|
|
|
src_dict_obj.as.dict = src.as.stream->dict; |
|
267
|
51
|
|
|
|
|
|
new_dict_obj = import_dict(ctx, src_dict_obj); |
|
268
|
51
|
|
|
|
|
|
out.as.stream->dict = new_dict_obj.as.dict; |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
/* Drop filter-related entries so readers don't try to decode the |
|
271
|
|
|
|
|
|
|
* already-plain bytes. */ |
|
272
|
51
|
|
|
|
|
|
filter_key = pdfmake_arena_intern_name(ctx->dst_arena, "Filter", 6); |
|
273
|
51
|
|
|
|
|
|
parms_key = pdfmake_arena_intern_name(ctx->dst_arena, "DecodeParms", 11); |
|
274
|
51
|
|
|
|
|
|
length_key = pdfmake_arena_intern_name(ctx->dst_arena, "Length", 6); |
|
275
|
51
|
|
|
|
|
|
dict_wrapper.kind = PDFMAKE_DICT; |
|
276
|
51
|
|
|
|
|
|
dict_wrapper.as.dict = out.as.stream->dict; |
|
277
|
51
|
|
|
|
|
|
pdfmake_dict_del(&dict_wrapper, filter_key); |
|
278
|
51
|
|
|
|
|
|
pdfmake_dict_del(&dict_wrapper, parms_key); |
|
279
|
51
|
|
|
|
|
|
pdfmake_dict_del(&dict_wrapper, length_key); |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
/* Copy decoded bytes into the destination arena. */ |
|
282
|
51
|
50
|
|
|
|
|
if (decoded.len > 0) { |
|
283
|
51
|
|
|
|
|
|
copy = pdfmake_arena_memdup(ctx->dst_arena, |
|
284
|
51
|
|
|
|
|
|
decoded.data, decoded.len); |
|
285
|
51
|
|
|
|
|
|
out.as.stream->raw = (const uint8_t *)copy; |
|
286
|
51
|
|
|
|
|
|
out.as.stream->raw_len = decoded.len; |
|
287
|
|
|
|
|
|
|
} |
|
288
|
51
|
|
|
|
|
|
out.as.stream->filtered = 1; |
|
289
|
51
|
|
|
|
|
|
pdfmake_buf_free(&decoded); |
|
290
|
51
|
|
|
|
|
|
return out; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
2825
|
|
|
|
|
|
static pdfmake_obj_t import_obj(pdfmake_import_ctx_t *ctx, pdfmake_obj_t src) { |
|
294
|
2825
|
|
|
|
|
|
switch (src.kind) { |
|
295
|
0
|
|
|
|
|
|
case PDFMAKE_NULL: return pdfmake_null(); |
|
296
|
5
|
|
|
|
|
|
case PDFMAKE_BOOL: return pdfmake_bool((int)src.as.i); |
|
297
|
1798
|
|
|
|
|
|
case PDFMAKE_INT: return pdfmake_int(src.as.i); |
|
298
|
5
|
|
|
|
|
|
case PDFMAKE_REAL: return pdfmake_real(src.as.r); |
|
299
|
437
|
|
|
|
|
|
case PDFMAKE_NAME: return import_name(ctx, src); |
|
300
|
0
|
|
|
|
|
|
case PDFMAKE_STR: return import_string(ctx, src); |
|
301
|
80
|
|
|
|
|
|
case PDFMAKE_ARRAY: return import_array(ctx, src); |
|
302
|
241
|
|
|
|
|
|
case PDFMAKE_DICT: return import_dict(ctx, src); |
|
303
|
0
|
|
|
|
|
|
case PDFMAKE_STREAM:return import_stream(ctx, src); |
|
304
|
259
|
|
|
|
|
|
case PDFMAKE_REF: { |
|
305
|
259
|
|
|
|
|
|
uint32_t dst_num = import_ref_num(ctx, src.as.ref.num); |
|
306
|
259
|
50
|
|
|
|
|
if (dst_num == 0) return pdfmake_null(); |
|
307
|
259
|
|
|
|
|
|
return pdfmake_ref(dst_num, 0); |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
} |
|
310
|
0
|
|
|
|
|
|
return pdfmake_null(); |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
0
|
|
|
|
|
|
uint32_t pdfmake_import_object(pdfmake_import_ctx_t *ctx, uint32_t src_num) { |
|
314
|
0
|
0
|
|
|
|
|
if (!ctx) return 0; |
|
315
|
0
|
|
|
|
|
|
return import_ref_num(ctx, src_num); |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
/*============================================================================ |
|
319
|
|
|
|
|
|
|
* Page import |
|
320
|
|
|
|
|
|
|
*==========================================================================*/ |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
/* Resolve an obj through a single ref indirection, in the parser arena. */ |
|
323
|
88
|
|
|
|
|
|
static pdfmake_obj_t *parser_resolve(pdfmake_parser_t *parser, |
|
324
|
|
|
|
|
|
|
pdfmake_obj_t *obj) { |
|
325
|
88
|
50
|
|
|
|
|
if (!obj) return NULL; |
|
326
|
88
|
100
|
|
|
|
|
if (obj->kind == PDFMAKE_REF) { |
|
327
|
18
|
|
|
|
|
|
return pdfmake_parser_resolve(parser, obj->as.ref); |
|
328
|
|
|
|
|
|
|
} |
|
329
|
70
|
|
|
|
|
|
return obj; |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
/* Find a page's /Resources dict by walking up /Parent until one is found. |
|
333
|
|
|
|
|
|
|
* All name lookups happen in the parser's arena so ids stay consistent |
|
334
|
|
|
|
|
|
|
* for downstream import_obj calls. Returns a dict obj (in parser arena) |
|
335
|
|
|
|
|
|
|
* or NULL if none found. */ |
|
336
|
44
|
|
|
|
|
|
static pdfmake_obj_t *find_inherited_resources(pdfmake_import_ctx_t *ctx, |
|
337
|
|
|
|
|
|
|
pdfmake_obj_t *page_dict) { |
|
338
|
44
|
|
|
|
|
|
pdfmake_arena_t *pa = ctx->src_arena; |
|
339
|
44
|
|
|
|
|
|
uint32_t res_key = pdfmake_arena_intern_name(pa, "Resources", 9); |
|
340
|
44
|
|
|
|
|
|
uint32_t parent_key = pdfmake_arena_intern_name(pa, "Parent", 6); |
|
341
|
|
|
|
|
|
|
pdfmake_obj_t *current; |
|
342
|
|
|
|
|
|
|
int depth; |
|
343
|
|
|
|
|
|
|
pdfmake_obj_t *res; |
|
344
|
|
|
|
|
|
|
pdfmake_obj_t *parent; |
|
345
|
|
|
|
|
|
|
|
|
346
|
44
|
|
|
|
|
|
current = parser_resolve(ctx->src_parser, page_dict); |
|
347
|
44
|
|
|
|
|
|
depth = 0; |
|
348
|
44
|
50
|
|
|
|
|
while (current && current->kind == PDFMAKE_DICT && depth < 32) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
349
|
44
|
|
|
|
|
|
res = pdfmake_dict_get(current, res_key); |
|
350
|
44
|
|
|
|
|
|
res = parser_resolve(ctx->src_parser, res); |
|
351
|
44
|
50
|
|
|
|
|
if (res && res->kind == PDFMAKE_DICT) return res; |
|
|
|
50
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
parent = pdfmake_dict_get(current, parent_key); |
|
354
|
0
|
0
|
|
|
|
|
if (!parent) break; |
|
355
|
0
|
|
|
|
|
|
current = parser_resolve(ctx->src_parser, parent); |
|
356
|
0
|
|
|
|
|
|
depth++; |
|
357
|
|
|
|
|
|
|
} |
|
358
|
0
|
|
|
|
|
|
return NULL; |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
44
|
|
|
|
|
|
pdfmake_page_t *pdfmake_doc_import_page(pdfmake_import_ctx_t *ctx, |
|
362
|
|
|
|
|
|
|
size_t src_page_index) { |
|
363
|
|
|
|
|
|
|
pdfmake_reader_page_t *rp; |
|
364
|
|
|
|
|
|
|
double mbox[4]; |
|
365
|
|
|
|
|
|
|
double width; |
|
366
|
|
|
|
|
|
|
double height; |
|
367
|
|
|
|
|
|
|
pdfmake_page_t *dst_page; |
|
368
|
|
|
|
|
|
|
pdfmake_buf_t content; |
|
369
|
|
|
|
|
|
|
pdfmake_err_t cerr; |
|
370
|
|
|
|
|
|
|
pdfmake_obj_t *resources; |
|
371
|
|
|
|
|
|
|
pdfmake_obj_t dst_res; |
|
372
|
|
|
|
|
|
|
|
|
373
|
44
|
50
|
|
|
|
|
if (!ctx) return NULL; |
|
374
|
|
|
|
|
|
|
|
|
375
|
44
|
|
|
|
|
|
rp = pdfmake_reader_page_at(ctx->src_reader, src_page_index); |
|
376
|
44
|
50
|
|
|
|
|
if (!rp) return NULL; |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
/* Dimensions from media box */ |
|
379
|
44
|
50
|
|
|
|
|
if (pdfmake_reader_page_media_box(ctx->src_reader, rp, mbox) != PDFMAKE_OK) { |
|
380
|
0
|
|
|
|
|
|
return NULL; |
|
381
|
|
|
|
|
|
|
} |
|
382
|
44
|
|
|
|
|
|
width = mbox[2] - mbox[0]; |
|
383
|
44
|
|
|
|
|
|
height = mbox[3] - mbox[1]; |
|
384
|
44
|
50
|
|
|
|
|
if (width <= 0 || height <= 0) return NULL; |
|
|
|
50
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
/* Append a fresh page to dst */ |
|
387
|
44
|
|
|
|
|
|
dst_page = pdfmake_doc_add_page(ctx->dst, width, height); |
|
388
|
44
|
50
|
|
|
|
|
if (!dst_page) return NULL; |
|
389
|
|
|
|
|
|
|
|
|
390
|
44
|
|
|
|
|
|
dst_page->rotation = pdfmake_reader_page_rotation(ctx->src_reader, rp); |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
/* Content stream (decompressed bytes from reader, stored uncompressed |
|
393
|
|
|
|
|
|
|
* in dst so the writer emits them as-is without /Filter). */ |
|
394
|
44
|
50
|
|
|
|
|
if (pdfmake_buf_init(&content) == PDFMAKE_OK) { |
|
395
|
44
|
|
|
|
|
|
cerr = pdfmake_reader_page_content_bytes( |
|
396
|
|
|
|
|
|
|
ctx->src_reader, rp, &content); |
|
397
|
44
|
50
|
|
|
|
|
if (cerr == PDFMAKE_OK && content.len > 0) { |
|
|
|
50
|
|
|
|
|
|
|
398
|
44
|
|
|
|
|
|
pdfmake_page_set_content(dst_page, content.data, content.len); |
|
399
|
|
|
|
|
|
|
} |
|
400
|
44
|
|
|
|
|
|
pdfmake_buf_free(&content); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
/* Resolve /Resources using our own /Parent walk in the parser arena |
|
404
|
|
|
|
|
|
|
* (the reader's merged dict mixes reader-arena and parser-arena name |
|
405
|
|
|
|
|
|
|
* ids, which would corrupt key lookups during deep-copy). Child |
|
406
|
|
|
|
|
|
|
* nearest-ancestor wins; entry-level merging across ancestors is not |
|
407
|
|
|
|
|
|
|
* yet implemented. */ |
|
408
|
44
|
|
|
|
|
|
resources = find_inherited_resources(ctx, rp->page_dict); |
|
409
|
44
|
50
|
|
|
|
|
if (resources && resources->kind == PDFMAKE_DICT) { |
|
|
|
50
|
|
|
|
|
|
|
410
|
44
|
|
|
|
|
|
dst_res = import_obj(ctx, *resources); |
|
411
|
44
|
50
|
|
|
|
|
if (dst_res.kind == PDFMAKE_DICT) { |
|
412
|
44
|
|
|
|
|
|
dst_page->imported_resources = dst_res.as.dict; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
|
|
416
|
44
|
|
|
|
|
|
ctx->dst->finalized = 0; |
|
417
|
44
|
|
|
|
|
|
return dst_page; |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
|
|
420
|
0
|
|
|
|
|
|
size_t pdfmake_doc_import_all_pages(pdfmake_import_ctx_t *ctx) { |
|
421
|
|
|
|
|
|
|
size_t total; |
|
422
|
|
|
|
|
|
|
size_t imported; |
|
423
|
|
|
|
|
|
|
size_t i; |
|
424
|
|
|
|
|
|
|
|
|
425
|
0
|
0
|
|
|
|
|
if (!ctx) return 0; |
|
426
|
0
|
|
|
|
|
|
total = pdfmake_reader_page_count(ctx->src_reader); |
|
427
|
0
|
|
|
|
|
|
imported = 0; |
|
428
|
0
|
0
|
|
|
|
|
for (i = 0; i < total; i++) { |
|
429
|
0
|
0
|
|
|
|
|
if (!pdfmake_doc_import_page(ctx, i)) break; |
|
430
|
0
|
|
|
|
|
|
imported++; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
0
|
|
|
|
|
|
return imported; |
|
433
|
|
|
|
|
|
|
} |