1 #include "mupdf/fitz.h"
2 
3 #include <string.h>
4 
5 /* Return non-null terminated pointers to key/value entries in comma separated
6  * option string. A plain key has the default value 'yes'. Use strncmp to compare
7  * key/value strings. */
8 static const char *
fz_get_option(fz_context * ctx,const char ** key,const char ** val,const char * opts)9 fz_get_option(fz_context *ctx, const char **key, const char **val, const char *opts)
10 {
11 	if (!opts || *opts == 0)
12 		return NULL;
13 
14 	if (*opts == ',')
15 		++opts;
16 
17 	*key = opts;
18 	while (*opts != 0 && *opts != ',' && *opts != '=')
19 		++opts;
20 
21 	if (*opts == '=')
22 	{
23 		*val = ++opts;
24 		while (*opts != 0 && *opts != ',')
25 			++opts;
26 	}
27 	else
28 	{
29 		*val = "yes";
30 	}
31 
32 	return opts;
33 }
34 
35 int
fz_has_option(fz_context * ctx,const char * opts,const char * key,const char ** val)36 fz_has_option(fz_context *ctx, const char *opts, const char *key, const char **val)
37 {
38 	const char *straw;
39 	size_t n = strlen(key);
40 	while ((opts = fz_get_option(ctx, &straw, val, opts)))
41 		if (!strncmp(straw, key, n) && (straw[n] == '=' || straw[n] == ',' || straw[n] == 0))
42 			return 1;
43 	return 0;
44 }
45 
46 int
fz_option_eq(const char * a,const char * b)47 fz_option_eq(const char *a, const char *b)
48 {
49 	size_t n = strlen(b);
50 	return !strncmp(a, b, n) && (a[n] == ',' || a[n] == 0);
51 }
52 
53 size_t
fz_copy_option(fz_context * ctx,const char * val,char * dest,size_t maxlen)54 fz_copy_option(fz_context *ctx, const char *val, char *dest, size_t maxlen)
55 {
56 	const char *e = val;
57 	size_t len, len2;
58 
59 	if (val == NULL) {
60 		if (maxlen)
61 			*dest = 0;
62 		return 0;
63 	}
64 
65 	while (*e != ',' && *e != 0)
66 		e++;
67 
68 	len = e-val;
69 	len2 = len+1; /* Allow for terminator */
70 	if (len > maxlen)
71 		len = maxlen;
72 	memcpy(dest, val, len);
73 	if (len < maxlen)
74 		memset(dest+len, 0, maxlen-len);
75 
76 	return len2 >= maxlen ? len2 - maxlen : 0;
77 }
78 
fz_new_document_writer_of_size(fz_context * ctx,size_t size,fz_document_writer_begin_page_fn * begin_page,fz_document_writer_end_page_fn * end_page,fz_document_writer_close_writer_fn * close,fz_document_writer_drop_writer_fn * drop)79 fz_document_writer *fz_new_document_writer_of_size(fz_context *ctx, size_t size, fz_document_writer_begin_page_fn *begin_page,
80 	fz_document_writer_end_page_fn *end_page, fz_document_writer_close_writer_fn *close, fz_document_writer_drop_writer_fn *drop)
81 {
82 	fz_document_writer *wri = Memento_label(fz_calloc(ctx, 1, size), "fz_document_writer");
83 
84 	wri->begin_page = begin_page;
85 	wri->end_page = end_page;
86 	wri->close_writer = close;
87 	wri->drop_writer = drop;
88 
89 	return wri;
90 }
91 
fz_new_png_pixmap_writer(fz_context * ctx,const char * path,const char * options)92 fz_document_writer *fz_new_png_pixmap_writer(fz_context *ctx, const char *path, const char *options)
93 {
94 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.png", 0, fz_save_pixmap_as_png);
95 }
96 
fz_new_pam_pixmap_writer(fz_context * ctx,const char * path,const char * options)97 fz_document_writer *fz_new_pam_pixmap_writer(fz_context *ctx, const char *path, const char *options)
98 {
99 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pam", 0, fz_save_pixmap_as_pam);
100 }
101 
fz_new_pnm_pixmap_writer(fz_context * ctx,const char * path,const char * options)102 fz_document_writer *fz_new_pnm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
103 {
104 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pnm", 0, fz_save_pixmap_as_pnm);
105 }
106 
fz_new_pgm_pixmap_writer(fz_context * ctx,const char * path,const char * options)107 fz_document_writer *fz_new_pgm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
108 {
109 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pgm", 1, fz_save_pixmap_as_pnm);
110 }
111 
fz_new_ppm_pixmap_writer(fz_context * ctx,const char * path,const char * options)112 fz_document_writer *fz_new_ppm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
113 {
114 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.ppm", 3, fz_save_pixmap_as_pnm);
115 }
116 
fz_new_pbm_pixmap_writer(fz_context * ctx,const char * path,const char * options)117 fz_document_writer *fz_new_pbm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
118 {
119 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pbm", 1, fz_save_pixmap_as_pbm);
120 }
121 
fz_new_pkm_pixmap_writer(fz_context * ctx,const char * path,const char * options)122 fz_document_writer *fz_new_pkm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
123 {
124 	return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pkm", 4, fz_save_pixmap_as_pkm);
125 }
126 
is_extension(const char * a,const char * ext)127 static int is_extension(const char *a, const char *ext)
128 {
129 	if (a[0] == '.')
130 		++a;
131 	return !fz_strcasecmp(a, ext);
132 }
133 
prev_period(const char * start,const char * p)134 static const char *prev_period(const char *start, const char *p)
135 {
136 	while (--p > start)
137 		if (*p == '.')
138 			return p;
139 	return NULL;
140 }
141 
142 fz_document_writer *
fz_new_document_writer(fz_context * ctx,const char * path,const char * explicit_format,const char * options)143 fz_new_document_writer(fz_context *ctx, const char *path, const char *explicit_format, const char *options)
144 {
145 	const char *format = explicit_format;
146 	if (!format)
147 		format = strrchr(path, '.');
148 	while (format)
149 	{
150 #if FZ_ENABLE_PDF
151 		if (is_extension(format, "pdf"))
152 			return fz_new_pdf_writer(ctx, path, options);
153 #endif
154 
155 		if (is_extension(format, "cbz"))
156 			return fz_new_cbz_writer(ctx, path, options);
157 
158 		if (is_extension(format, "svg"))
159 			return fz_new_svg_writer(ctx, path, options);
160 
161 		if (is_extension(format, "png"))
162 			return fz_new_png_pixmap_writer(ctx, path, options);
163 		if (is_extension(format, "pam"))
164 			return fz_new_pam_pixmap_writer(ctx, path, options);
165 		if (is_extension(format, "pnm"))
166 			return fz_new_pnm_pixmap_writer(ctx, path, options);
167 		if (is_extension(format, "pgm"))
168 			return fz_new_pgm_pixmap_writer(ctx, path, options);
169 		if (is_extension(format, "ppm"))
170 			return fz_new_ppm_pixmap_writer(ctx, path, options);
171 		if (is_extension(format, "pbm"))
172 			return fz_new_pbm_pixmap_writer(ctx, path, options);
173 		if (is_extension(format, "pkm"))
174 			return fz_new_pkm_pixmap_writer(ctx, path, options);
175 
176 		if (is_extension(format, "pcl"))
177 			return fz_new_pcl_writer(ctx, path, options);
178 		if (is_extension(format, "pclm"))
179 			return fz_new_pclm_writer(ctx, path, options);
180 		if (is_extension(format, "ps"))
181 			return fz_new_ps_writer(ctx, path, options);
182 		if (is_extension(format, "pwg"))
183 			return fz_new_pwg_writer(ctx, path, options);
184 
185 		if (is_extension(format, "txt") || is_extension(format, "text"))
186 			return fz_new_text_writer(ctx, "text", path, options);
187 		if (is_extension(format, "html"))
188 			return fz_new_text_writer(ctx, "html", path, options);
189 		if (is_extension(format, "xhtml"))
190 			return fz_new_text_writer(ctx, "xhtml", path, options);
191 		if (is_extension(format, "stext") || is_extension(format, "stext.xml"))
192 			return fz_new_text_writer(ctx, "stext.xml", path, options);
193 		if (is_extension(format, "stext.json"))
194 			return fz_new_text_writer(ctx, "stext.json", path, options);
195 
196 		if (format != explicit_format)
197 			format = prev_period(path, format);
198 		else
199 			format = NULL;
200 	}
201 	fz_throw(ctx, FZ_ERROR_GENERIC, "cannot detect document format");
202 }
203 
204 fz_document_writer *
fz_new_document_writer_with_output(fz_context * ctx,fz_output * out,const char * format,const char * options)205 fz_new_document_writer_with_output(fz_context *ctx, fz_output *out, const char *format, const char *options)
206 {
207 	if (is_extension(format, "cbz"))
208 		return fz_new_cbz_writer_with_output(ctx, out, options);
209 #if FZ_ENABLE_PDF
210 	if (is_extension(format, "pdf"))
211 		return fz_new_pdf_writer_with_output(ctx, out, options);
212 #endif
213 
214 	if (is_extension(format, "pcl"))
215 		return fz_new_pcl_writer_with_output(ctx, out, options);
216 	if (is_extension(format, "pclm"))
217 		return fz_new_pclm_writer_with_output(ctx, out, options);
218 	if (is_extension(format, "ps"))
219 		return fz_new_ps_writer_with_output(ctx, out, options);
220 	if (is_extension(format, "pwg"))
221 		return fz_new_pwg_writer_with_output(ctx, out, options);
222 
223 	if (is_extension(format, "txt") || is_extension(format, "text"))
224 		return fz_new_text_writer_with_output(ctx, "text", out, options);
225 	if (is_extension(format, "html"))
226 		return fz_new_text_writer_with_output(ctx, "html", out, options);
227 	if (is_extension(format, "xhtml"))
228 		return fz_new_text_writer_with_output(ctx, "xhtml", out, options);
229 	if (is_extension(format, "stext") || is_extension(format, "stext.xml"))
230 		return fz_new_text_writer_with_output(ctx, "stext.xml", out, options);
231 	if (is_extension(format, "stext.json"))
232 		return fz_new_text_writer_with_output(ctx, "stext.json", out, options);
233 
234 	fz_throw(ctx, FZ_ERROR_GENERIC, "unknown output document format: %s", format);
235 }
236 
237 void
fz_close_document_writer(fz_context * ctx,fz_document_writer * wri)238 fz_close_document_writer(fz_context *ctx, fz_document_writer *wri)
239 {
240 	if (wri->close_writer)
241 		wri->close_writer(ctx, wri);
242 	wri->close_writer = NULL;
243 }
244 
245 void
fz_drop_document_writer(fz_context * ctx,fz_document_writer * wri)246 fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri)
247 {
248 	if (!wri)
249 		return;
250 
251 	if (wri->close_writer)
252 		fz_warn(ctx, "dropping unclosed document writer");
253 	if (wri->drop_writer)
254 		wri->drop_writer(ctx, wri);
255 	if (wri->dev)
256 		fz_drop_device(ctx, wri->dev);
257 	fz_free(ctx, wri);
258 }
259 
260 fz_device *
fz_begin_page(fz_context * ctx,fz_document_writer * wri,fz_rect mediabox)261 fz_begin_page(fz_context *ctx, fz_document_writer *wri, fz_rect mediabox)
262 {
263 	if (!wri)
264 		return NULL;
265 	if (wri->dev)
266 		fz_throw(ctx, FZ_ERROR_GENERIC, "called begin page without ending the previous page");
267 	wri->dev = wri->begin_page(ctx, wri, mediabox);
268 	return wri->dev;
269 }
270 
271 void
fz_end_page(fz_context * ctx,fz_document_writer * wri)272 fz_end_page(fz_context *ctx, fz_document_writer *wri)
273 {
274 	fz_device *dev;
275 
276 	if (!wri)
277 		return;
278 	dev = wri->dev;
279 	wri->dev = NULL;
280 	wri->end_page(ctx, wri, dev);
281 }
282 
283 void
fz_write_document(fz_context * ctx,fz_document_writer * wri,fz_document * doc)284 fz_write_document(fz_context *ctx, fz_document_writer *wri, fz_document *doc)
285 {
286 	int i, n;
287 	fz_page *page = NULL;
288 	fz_device *dev;
289 
290 	fz_var(page);
291 
292 	n = fz_count_pages(ctx, doc);
293 	fz_try(ctx)
294 	{
295 		for (i = 0; i < n; i++)
296 		{
297 			page = fz_load_page(ctx, doc, i);
298 			dev = fz_begin_page(ctx, wri, fz_bound_page(ctx, page));
299 			fz_run_page(ctx, page, dev, fz_identity, NULL);
300 			fz_drop_page(ctx, page);
301 			page = NULL;
302 			fz_end_page(ctx, wri);
303 		}
304 	}
305 	fz_catch(ctx)
306 	{
307 		fz_drop_page(ctx, page);
308 		fz_rethrow(ctx);
309 	}
310 }
311