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. */
8static const char *
9fz_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
35int
36fz_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
46int
47fz_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
53int
54fz_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
79/*
80 Internal function to allocate a
81 block for a derived document_writer structure, with the base
82 structure's function pointers populated correctly, and the extra
83 space zero initialised.
84*/
85fz_document_writer *fz_new_document_writer_of_size(fz_context *ctx, size_t size, fz_document_writer_begin_page_fn *begin_page,
86 fz_document_writer_end_page_fn *end_page, fz_document_writer_close_writer_fn *close, fz_document_writer_drop_writer_fn *drop)
87{
88 fz_document_writer *wri = Memento_label(fz_calloc(ctx, 1, size), "fz_document_writer");
89
90 wri->begin_page = begin_page;
91 wri->end_page = end_page;
92 wri->close_writer = close;
93 wri->drop_writer = drop;
94
95 return wri;
96}
97
98fz_document_writer *fz_new_png_pixmap_writer(fz_context *ctx, const char *path, const char *options)
99{
100 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.png", 0, fz_save_pixmap_as_png);
101}
102
103fz_document_writer *fz_new_pam_pixmap_writer(fz_context *ctx, const char *path, const char *options)
104{
105 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pam", 0, fz_save_pixmap_as_pam);
106}
107
108fz_document_writer *fz_new_pnm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
109{
110 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pnm", 0, fz_save_pixmap_as_pnm);
111}
112
113fz_document_writer *fz_new_pgm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
114{
115 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pgm", 1, fz_save_pixmap_as_pnm);
116}
117
118fz_document_writer *fz_new_ppm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
119{
120 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.ppm", 3, fz_save_pixmap_as_pnm);
121}
122
123fz_document_writer *fz_new_pbm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
124{
125 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pbm", 1, fz_save_pixmap_as_pbm);
126}
127
128fz_document_writer *fz_new_pkm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
129{
130 return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pkm", 4, fz_save_pixmap_as_pkm);
131}
132
133/*
134 Create a new fz_document_writer, for a
135 file of the given type.
136
137 path: The document name to write (or NULL for default)
138
139 format: Which format to write (currently cbz, html, pdf, pam, pbm,
140 pgm, pkm, png, ppm, pnm, svg, text, xhtml)
141
142 options: NULL, or pointer to comma separated string to control
143 file generation.
144*/
145fz_document_writer *
146fz_new_document_writer(fz_context *ctx, const char *path, const char *format, const char *options)
147{
148 if (!format)
149 {
150 format = strrchr(path, '.');
151 if (!format)
152 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot detect document format");
153 format += 1; /* skip the '.' */
154 }
155
156 if (!fz_strcasecmp(format, "cbz"))
157 return fz_new_cbz_writer(ctx, path, options);
158#if FZ_ENABLE_PDF
159 if (!fz_strcasecmp(format, "pdf"))
160 return fz_new_pdf_writer(ctx, path, options);
161#endif
162 if (!fz_strcasecmp(format, "svg"))
163 return fz_new_svg_writer(ctx, path, options);
164
165 if (!fz_strcasecmp(format, "png"))
166 return fz_new_png_pixmap_writer(ctx, path, options);
167 if (!fz_strcasecmp(format, "pam"))
168 return fz_new_pam_pixmap_writer(ctx, path, options);
169 if (!fz_strcasecmp(format, "pnm"))
170 return fz_new_pnm_pixmap_writer(ctx, path, options);
171 if (!fz_strcasecmp(format, "pgm"))
172 return fz_new_pgm_pixmap_writer(ctx, path, options);
173 if (!fz_strcasecmp(format, "ppm"))
174 return fz_new_ppm_pixmap_writer(ctx, path, options);
175 if (!fz_strcasecmp(format, "pbm"))
176 return fz_new_pbm_pixmap_writer(ctx, path, options);
177 if (!fz_strcasecmp(format, "pkm"))
178 return fz_new_pkm_pixmap_writer(ctx, path, options);
179
180 if (!fz_strcasecmp(format, "pcl"))
181 return fz_new_pcl_writer(ctx, path, options);
182 if (!fz_strcasecmp(format, "pclm"))
183 return fz_new_pclm_writer(ctx, path, options);
184 if (!fz_strcasecmp(format, "ps"))
185 return fz_new_ps_writer(ctx, path, options);
186 if (!fz_strcasecmp(format, "pwg"))
187 return fz_new_pwg_writer(ctx, path, options);
188
189 if (!fz_strcasecmp(format, "txt") || !fz_strcasecmp(format, "text"))
190 return fz_new_text_writer(ctx, "text", path, options);
191 if (!fz_strcasecmp(format, "html"))
192 return fz_new_text_writer(ctx, format, path, options);
193 if (!fz_strcasecmp(format, "xhtml"))
194 return fz_new_text_writer(ctx, format, path, options);
195 if (!fz_strcasecmp(format, "stext"))
196 return fz_new_text_writer(ctx, format, path, options);
197
198 fz_throw(ctx, FZ_ERROR_GENERIC, "unknown output document format: %s", format);
199}
200
201/*
202 Called to end the process of writing
203 pages to a document.
204
205 This writes any file level trailers required. After this
206 completes successfully the file is up to date and complete.
207*/
208void
209fz_close_document_writer(fz_context *ctx, fz_document_writer *wri)
210{
211 if (wri->close_writer)
212 wri->close_writer(ctx, wri);
213 wri->close_writer = NULL;
214}
215
216/*
217 Called to discard a fz_document_writer.
218 This may be called at any time during the process to release all
219 the resources owned by the writer.
220
221 Calling drop without having previously called close may leave
222 the file in an inconsistent state.
223*/
224void
225fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri)
226{
227 if (!wri)
228 return;
229
230 if (wri->close_writer)
231 fz_warn(ctx, "dropping unclosed document writer");
232 if (wri->drop_writer)
233 wri->drop_writer(ctx, wri);
234 if (wri->dev)
235 fz_drop_device(ctx, wri->dev);
236 fz_free(ctx, wri);
237}
238
239/*
240 Called to start the process of writing a page to
241 a document.
242
243 mediabox: page size rectangle in points.
244
245 Returns a fz_device to write page contents to.
246*/
247fz_device *
248fz_begin_page(fz_context *ctx, fz_document_writer *wri, fz_rect mediabox)
249{
250 if (!wri)
251 return NULL;
252 if (wri->dev)
253 fz_throw(ctx, FZ_ERROR_GENERIC, "called begin page without ending the previous page");
254 wri->dev = wri->begin_page(ctx, wri, mediabox);
255 return wri->dev;
256}
257
258/*
259 Called to end the process of writing a page to a
260 document.
261*/
262void
263fz_end_page(fz_context *ctx, fz_document_writer *wri)
264{
265 fz_device *dev;
266
267 if (!wri)
268 return;
269 dev = wri->dev;
270 wri->dev = NULL;
271 wri->end_page(ctx, wri, dev);
272}
273