1#include "mupdf/fitz.h"
2#include "mupdf/pdf.h"
3#include "../fitz/fitz-imp.h"
4
5#include <assert.h>
6
7struct pdf_graft_map_s
8{
9 int refs;
10 int len;
11 pdf_document *src;
12 pdf_document *dst;
13 int *dst_from_src;
14};
15
16/*
17 Prepare a graft map object to allow objects
18 to be deep copied from one document to the given one, avoiding
19 problems with duplicated child objects.
20
21 dst: The document to copy objects to.
22
23 Note: all the source objects must come from the same document.
24*/
25pdf_graft_map *
26pdf_new_graft_map(fz_context *ctx, pdf_document *dst)
27{
28 pdf_graft_map *map = NULL;
29
30 map = fz_malloc_struct(ctx, pdf_graft_map);
31
32 map->dst = pdf_keep_document(ctx, dst);
33 map->refs = 1;
34 return map;
35}
36
37pdf_graft_map *
38pdf_keep_graft_map(fz_context *ctx, pdf_graft_map *map)
39{
40 return fz_keep_imp(ctx, map, &map->refs);
41}
42
43void
44pdf_drop_graft_map(fz_context *ctx, pdf_graft_map *map)
45{
46 if (fz_drop_imp(ctx, map, &map->refs))
47 {
48 pdf_drop_document(ctx, map->src);
49 pdf_drop_document(ctx, map->dst);
50 fz_free(ctx, map->dst_from_src);
51 fz_free(ctx, map);
52 }
53}
54
55/*
56 Return a deep copied object equivalent to the
57 supplied object, suitable for use within the given document.
58
59 dst: The document in which the returned object is to be used.
60
61 obj: The object deep copy.
62
63 Note: If grafting multiple objects, you should use a pdf_graft_map
64 to avoid potential duplication of target objects.
65*/
66pdf_obj *
67pdf_graft_object(fz_context *ctx, pdf_document *dst, pdf_obj *obj)
68{
69 pdf_document *src;
70 pdf_graft_map *map;
71
72 /* Primitive objects are not bound to a document, so can be re-used as is. */
73 src = pdf_get_bound_document(ctx, obj);
74 if (src == NULL)
75 return pdf_keep_obj(ctx, obj);
76
77 map = pdf_new_graft_map(ctx, dst);
78
79 fz_try(ctx)
80 obj = pdf_graft_mapped_object(ctx, map, obj);
81 fz_always(ctx)
82 pdf_drop_graft_map(ctx, map);
83 fz_catch(ctx)
84 fz_rethrow(ctx);
85
86 return obj;
87}
88
89/*
90 Return a deep copied object equivalent
91 to the supplied object, suitable for use within the target
92 document of the map.
93
94 map: A map targeted at the document in which the returned
95 object is to be used.
96
97 obj: The object deep copy.
98
99 Note: Copying multiple objects via the same graft map ensures
100 that any shared child are not duplicated more than once.
101*/
102pdf_obj *
103pdf_graft_mapped_object(fz_context *ctx, pdf_graft_map *map, pdf_obj *obj)
104{
105 pdf_obj *val, *key;
106 pdf_obj *new_obj = NULL;
107 pdf_obj *new_dict;
108 pdf_obj *new_array;
109 pdf_obj *ref = NULL;
110 fz_buffer *buffer = NULL;
111 pdf_document *src;
112 int new_num, src_num, len, i;
113
114 /* Primitive objects are not bound to a document, so can be re-used as is. */
115 src = pdf_get_bound_document(ctx, obj);
116 if (!src)
117 return pdf_keep_obj(ctx, obj);
118
119 if (map->src && src != map->src)
120 fz_throw(ctx, FZ_ERROR_GENERIC, "grafted objects must all belong to the same source document");
121
122 if (pdf_is_indirect(ctx, obj))
123 {
124 src_num = pdf_to_num(ctx, obj);
125
126 if (map->src == NULL)
127 {
128 fz_try(ctx)
129 {
130 map->src = pdf_keep_document(ctx, src);
131 map->len = pdf_xref_len(ctx, src);
132 map->dst_from_src = fz_calloc(ctx, map->len, sizeof(int));
133 }
134 fz_catch(ctx)
135 {
136 pdf_drop_document(ctx, map->src);
137 map->src = NULL;
138 fz_rethrow(ctx);
139 }
140 }
141
142 if (src_num < 1 || src_num >= map->len)
143 fz_throw(ctx, FZ_ERROR_GENERIC, "source object number out of range");
144
145 /* Check if we have done this one. If yes, then just
146 * return our indirect ref */
147 if (map->dst_from_src[src_num] != 0)
148 {
149 int dest_num = map->dst_from_src[src_num];
150 return pdf_new_indirect(ctx, map->dst, dest_num, 0);
151 }
152
153 fz_var(buffer);
154 fz_var(ref);
155 fz_var(new_obj);
156
157 fz_try(ctx)
158 {
159 /* Create new slot for our src object, set the mapping and call again
160 * using the resolved indirect reference */
161 new_num = pdf_create_object(ctx, map->dst);
162 map->dst_from_src[src_num] = new_num;
163 new_obj = pdf_graft_mapped_object(ctx, map, pdf_resolve_indirect(ctx, obj));
164
165 /* Return a ref to the new_obj making sure to attach any stream */
166 pdf_update_object(ctx, map->dst, new_num, new_obj);
167 ref = pdf_new_indirect(ctx, map->dst, new_num, 0);
168 if (pdf_is_stream(ctx, obj))
169 {
170 buffer = pdf_load_raw_stream_number(ctx, src, src_num);
171 pdf_update_stream(ctx, map->dst, ref, buffer, 1);
172 }
173 }
174 fz_always(ctx)
175 {
176 pdf_drop_obj(ctx, new_obj);
177 fz_drop_buffer(ctx, buffer);
178 }
179 fz_catch(ctx)
180 {
181 pdf_drop_obj(ctx, ref);
182 fz_rethrow(ctx);
183 }
184 return ref;
185 }
186 else if (pdf_is_dict(ctx, obj))
187 {
188 len = pdf_dict_len(ctx, obj);
189 new_dict = pdf_new_dict(ctx, map->dst, len);
190
191 fz_try(ctx)
192 {
193 for (i = 0; i < len; i++)
194 {
195 key = pdf_dict_get_key(ctx, obj, i);
196 val = pdf_dict_get_val(ctx, obj, i);
197 pdf_dict_put_drop(ctx, new_dict, key, pdf_graft_mapped_object(ctx, map, val));
198 }
199 }
200 fz_catch(ctx)
201 {
202 pdf_drop_obj(ctx, new_dict);
203 fz_rethrow(ctx);
204 }
205 return new_dict;
206 }
207 else if (pdf_is_array(ctx, obj))
208 {
209 /* Step through the array items handling indirect refs */
210 len = pdf_array_len(ctx, obj);
211 new_array = pdf_new_array(ctx, map->dst, len);
212
213 fz_try(ctx)
214 {
215 for (i = 0; i < len; i++)
216 {
217 val = pdf_array_get(ctx, obj, i);
218 pdf_array_push_drop(ctx, new_array, pdf_graft_mapped_object(ctx, map, val));
219 }
220 }
221 fz_catch(ctx)
222 {
223 pdf_drop_obj(ctx, new_array);
224 fz_rethrow(ctx);
225 }
226 return new_array;
227 }
228 else
229 {
230 assert("This never happens" == NULL);
231 return NULL;
232 }
233}
234