Coverage Report

Created: 2024-10-28 06:24

/src/mupdf/source/fitz/writer.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2024 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include <string.h>
26
27
/* Return non-null terminated pointers to key/value entries in comma separated
28
 * option string. A plain key has the default value 'yes'. Use strncmp to compare
29
 * key/value strings. */
30
static const char *
31
fz_get_option(fz_context *ctx, const char **key, const char **val, const char *opts)
32
0
{
33
0
  if (!opts || *opts == 0)
34
0
    return NULL;
35
36
0
  if (*opts == ',')
37
0
    ++opts;
38
39
0
  *key = opts;
40
0
  while (*opts != 0 && *opts != ',' && *opts != '=')
41
0
    ++opts;
42
43
0
  if (*opts == '=')
44
0
  {
45
0
    *val = ++opts;
46
0
    while (*opts != 0 && *opts != ',')
47
0
      ++opts;
48
0
  }
49
0
  else
50
0
  {
51
0
    *val = "yes";
52
0
  }
53
54
0
  return opts;
55
0
}
56
57
int
58
fz_has_option(fz_context *ctx, const char *opts, const char *key, const char **val)
59
0
{
60
0
  const char *straw;
61
0
  size_t n = strlen(key);
62
0
  while ((opts = fz_get_option(ctx, &straw, val, opts)))
63
0
    if (!strncmp(straw, key, n) && (straw[n] == '=' || straw[n] == ',' || straw[n] == 0))
64
0
      return 1;
65
0
  return 0;
66
0
}
67
68
int
69
fz_option_eq(const char *a, const char *b)
70
0
{
71
0
  size_t n = strlen(b);
72
0
  return !strncmp(a, b, n) && (a[n] == ',' || a[n] == 0);
73
0
}
74
75
size_t
76
fz_copy_option(fz_context *ctx, const char *val, char *dest, size_t maxlen)
77
0
{
78
0
  const char *e = val;
79
0
  size_t len, len2;
80
81
0
  if (val == NULL) {
82
0
    if (maxlen)
83
0
      *dest = 0;
84
0
    return 0;
85
0
  }
86
87
0
  while (*e != ',' && *e != 0)
88
0
    e++;
89
90
0
  len = e-val;
91
0
  len2 = len+1; /* Allow for terminator */
92
0
  if (len > maxlen)
93
0
    len = maxlen;
94
0
  memcpy(dest, val, len);
95
0
  if (len < maxlen)
96
0
    memset(dest+len, 0, maxlen-len);
97
98
0
  return len2 >= maxlen ? len2 - maxlen : 0;
99
0
}
100
101
fz_document_writer *fz_new_document_writer_of_size(fz_context *ctx, size_t size, fz_document_writer_begin_page_fn *begin_page,
102
  fz_document_writer_end_page_fn *end_page, fz_document_writer_close_writer_fn *close, fz_document_writer_drop_writer_fn *drop)
103
0
{
104
0
  fz_document_writer *wri = Memento_label(fz_calloc(ctx, 1, size), "fz_document_writer");
105
106
0
  wri->begin_page = begin_page;
107
0
  wri->end_page = end_page;
108
0
  wri->close_writer = close;
109
0
  wri->drop_writer = drop;
110
111
0
  return wri;
112
0
}
113
114
static void fz_save_pixmap_as_jpeg_default(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
115
0
{
116
0
  fz_save_pixmap_as_jpeg(ctx, pixmap, filename, 90);
117
0
}
118
119
fz_document_writer *fz_new_jpeg_pixmap_writer(fz_context *ctx, const char *path, const char *options)
120
0
{
121
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.jpeg", 0, fz_save_pixmap_as_jpeg_default);
122
0
}
123
124
fz_document_writer *fz_new_png_pixmap_writer(fz_context *ctx, const char *path, const char *options)
125
0
{
126
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.png", 0, fz_save_pixmap_as_png);
127
0
}
128
129
fz_document_writer *fz_new_pam_pixmap_writer(fz_context *ctx, const char *path, const char *options)
130
0
{
131
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pam", 0, fz_save_pixmap_as_pam);
132
0
}
133
134
fz_document_writer *fz_new_pnm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
135
0
{
136
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pnm", 0, fz_save_pixmap_as_pnm);
137
0
}
138
139
fz_document_writer *fz_new_pgm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
140
0
{
141
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pgm", 1, fz_save_pixmap_as_pnm);
142
0
}
143
144
fz_document_writer *fz_new_ppm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
145
0
{
146
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.ppm", 3, fz_save_pixmap_as_pnm);
147
0
}
148
149
fz_document_writer *fz_new_pbm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
150
0
{
151
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pbm", 1, fz_save_pixmap_as_pbm);
152
0
}
153
154
fz_document_writer *fz_new_pkm_pixmap_writer(fz_context *ctx, const char *path, const char *options)
155
0
{
156
0
  return fz_new_pixmap_writer(ctx, path, options, "out-%04d.pkm", 4, fz_save_pixmap_as_pkm);
157
0
}
158
159
static int is_extension(const char *a, const char *ext)
160
0
{
161
0
  if (!a)
162
0
    return 0;
163
0
  if (a[0] == '.')
164
0
    ++a;
165
0
  return !fz_strcasecmp(a, ext);
166
0
}
167
168
static const char *prev_period(const char *start, const char *p)
169
0
{
170
0
  while (--p > start)
171
0
    if (*p == '.')
172
0
      return p;
173
0
  return NULL;
174
0
}
175
176
fz_document_writer *
177
fz_new_document_writer(fz_context *ctx, const char *path, const char *explicit_format, const char *options)
178
0
{
179
0
  const char *format = explicit_format;
180
0
  if (!format)
181
0
    format = strrchr(path, '.');
182
0
  while (format)
183
0
  {
184
0
#if FZ_ENABLE_OCR_OUTPUT
185
0
    if (is_extension(format, "ocr"))
186
0
      return fz_new_pdfocr_writer(ctx, path, options);
187
0
#endif
188
0
#if FZ_ENABLE_PDF
189
0
    if (is_extension(format, "pdf"))
190
0
      return fz_new_pdf_writer(ctx, path, options);
191
0
#endif
192
193
0
    if (is_extension(format, "cbz"))
194
0
      return fz_new_cbz_writer(ctx, path, options);
195
196
0
    if (is_extension(format, "svg"))
197
0
      return fz_new_svg_writer(ctx, path, options);
198
199
0
    if (is_extension(format, "png"))
200
0
      return fz_new_png_pixmap_writer(ctx, path, options);
201
0
    if (is_extension(format, "pam"))
202
0
      return fz_new_pam_pixmap_writer(ctx, path, options);
203
0
    if (is_extension(format, "pnm"))
204
0
      return fz_new_pnm_pixmap_writer(ctx, path, options);
205
0
    if (is_extension(format, "pgm"))
206
0
      return fz_new_pgm_pixmap_writer(ctx, path, options);
207
0
    if (is_extension(format, "ppm"))
208
0
      return fz_new_ppm_pixmap_writer(ctx, path, options);
209
0
    if (is_extension(format, "pbm"))
210
0
      return fz_new_pbm_pixmap_writer(ctx, path, options);
211
0
    if (is_extension(format, "pkm"))
212
0
      return fz_new_pkm_pixmap_writer(ctx, path, options);
213
0
    if (is_extension(format, "jpeg") || is_extension(format, "jpg"))
214
0
      return fz_new_jpeg_pixmap_writer(ctx, path, options);
215
216
0
    if (is_extension(format, "pcl"))
217
0
      return fz_new_pcl_writer(ctx, path, options);
218
0
    if (is_extension(format, "pclm"))
219
0
      return fz_new_pclm_writer(ctx, path, options);
220
0
    if (is_extension(format, "ps"))
221
0
      return fz_new_ps_writer(ctx, path, options);
222
0
    if (is_extension(format, "pwg"))
223
0
      return fz_new_pwg_writer(ctx, path, options);
224
225
0
    if (is_extension(format, "txt") || is_extension(format, "text"))
226
0
      return fz_new_text_writer(ctx, "text", path, options);
227
0
    if (is_extension(format, "html"))
228
0
      return fz_new_text_writer(ctx, "html", path, options);
229
0
    if (is_extension(format, "xhtml"))
230
0
      return fz_new_text_writer(ctx, "xhtml", path, options);
231
0
    if (is_extension(format, "stext") || is_extension(format, "stext.xml"))
232
0
      return fz_new_text_writer(ctx, "stext.xml", path, options);
233
0
    if (is_extension(format, "stext.json"))
234
0
      return fz_new_text_writer(ctx, "stext.json", path, options);
235
236
0
#if FZ_ENABLE_ODT_OUTPUT
237
0
    if (is_extension(format, "odt"))
238
0
      return fz_new_odt_writer(ctx, path, options);
239
0
#endif
240
0
#if FZ_ENABLE_DOCX_OUTPUT
241
0
    if (is_extension(format, "docx"))
242
0
      return fz_new_docx_writer(ctx, path, options);
243
0
#endif
244
0
    if (format != explicit_format)
245
0
      format = prev_period(path, format);
246
0
    else
247
0
      format = NULL;
248
0
  }
249
0
  fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot detect document format");
250
0
}
251
252
fz_document_writer *
253
fz_new_document_writer_with_output(fz_context *ctx, fz_output *out, const char *format, const char *options)
254
0
{
255
0
#if FZ_ENABLE_OCR_OUTPUT
256
0
  if (is_extension(format, "ocr"))
257
0
    return fz_new_pdfocr_writer_with_output(ctx, out, options);
258
0
#endif
259
0
#if FZ_ENABLE_PDF
260
0
  if (is_extension(format, "pdf"))
261
0
    return fz_new_pdf_writer_with_output(ctx, out, options);
262
0
#endif
263
264
0
  if (is_extension(format, "cbz"))
265
0
    return fz_new_cbz_writer_with_output(ctx, out, options);
266
267
0
  if (is_extension(format, "svg"))
268
0
    return fz_new_svg_writer_with_output(ctx, out, options);
269
270
0
  if (is_extension(format, "pcl"))
271
0
    return fz_new_pcl_writer_with_output(ctx, out, options);
272
0
  if (is_extension(format, "pclm"))
273
0
    return fz_new_pclm_writer_with_output(ctx, out, options);
274
0
  if (is_extension(format, "ps"))
275
0
    return fz_new_ps_writer_with_output(ctx, out, options);
276
0
  if (is_extension(format, "pwg"))
277
0
    return fz_new_pwg_writer_with_output(ctx, out, options);
278
279
0
  if (is_extension(format, "txt") || is_extension(format, "text"))
280
0
    return fz_new_text_writer_with_output(ctx, "text", out, options);
281
0
  if (is_extension(format, "html"))
282
0
    return fz_new_text_writer_with_output(ctx, "html", out, options);
283
0
  if (is_extension(format, "xhtml"))
284
0
    return fz_new_text_writer_with_output(ctx, "xhtml", out, options);
285
0
  if (is_extension(format, "stext") || is_extension(format, "stext.xml"))
286
0
    return fz_new_text_writer_with_output(ctx, "stext.xml", out, options);
287
0
  if (is_extension(format, "stext.json"))
288
0
    return fz_new_text_writer_with_output(ctx, "stext.json", out, options);
289
290
0
#if FZ_ENABLE_ODT_OUTPUT
291
0
  if (is_extension(format, "odt"))
292
0
    return fz_new_odt_writer_with_output(ctx, out, options);
293
0
#endif
294
0
#if FZ_ENABLE_DOCX_OUTPUT
295
0
  if (is_extension(format, "docx"))
296
0
    return fz_new_docx_writer_with_output(ctx, out, options);
297
0
#endif
298
299
0
  fz_throw(ctx, FZ_ERROR_ARGUMENT, "unknown output document format: %s", format);
300
0
}
301
302
fz_document_writer *
303
fz_new_document_writer_with_buffer(fz_context *ctx, fz_buffer *buffer, const char *format, const char *options)
304
0
{
305
0
  fz_document_writer *wri;
306
0
  fz_output *out = fz_new_output_with_buffer(ctx, buffer);
307
0
  fz_try(ctx) {
308
0
    wri = fz_new_document_writer_with_output(ctx, out, format, options);
309
0
  }
310
0
  fz_catch(ctx) {
311
0
    fz_drop_output(ctx, out);
312
0
    fz_rethrow(ctx);
313
0
  }
314
0
  return wri;
315
0
}
316
317
void
318
fz_close_document_writer(fz_context *ctx, fz_document_writer *wri)
319
0
{
320
0
  if (wri->close_writer)
321
0
    wri->close_writer(ctx, wri);
322
0
  wri->close_writer = NULL;
323
0
}
324
325
void
326
fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri)
327
0
{
328
0
  if (!wri)
329
0
    return;
330
331
0
  if (wri->close_writer)
332
0
    fz_warn(ctx, "dropping unclosed document writer");
333
0
  if (wri->dev)
334
0
    fz_drop_device(ctx, wri->dev);
335
0
  if (wri->drop_writer)
336
0
    wri->drop_writer(ctx, wri);
337
0
  fz_free(ctx, wri);
338
0
}
339
340
fz_device *
341
fz_begin_page(fz_context *ctx, fz_document_writer *wri, fz_rect mediabox)
342
0
{
343
0
  if (!wri)
344
0
    return NULL;
345
0
  if (wri->dev)
346
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "called begin page without ending the previous page");
347
0
  wri->dev = wri->begin_page(ctx, wri, mediabox);
348
0
  return wri->dev;
349
0
}
350
351
void
352
fz_end_page(fz_context *ctx, fz_document_writer *wri)
353
0
{
354
0
  fz_device *dev;
355
356
0
  if (!wri)
357
0
    return;
358
0
  dev = wri->dev;
359
0
  wri->dev = NULL;
360
0
  wri->end_page(ctx, wri, dev);
361
0
}
362
363
void
364
fz_write_document(fz_context *ctx, fz_document_writer *wri, fz_document *doc)
365
0
{
366
0
  int i, n;
367
0
  fz_page *page = NULL;
368
0
  fz_device *dev;
369
370
0
  fz_var(page);
371
372
0
  n = fz_count_pages(ctx, doc);
373
0
  fz_try(ctx)
374
0
  {
375
0
    for (i = 0; i < n; i++)
376
0
    {
377
0
      page = fz_load_page(ctx, doc, i);
378
0
      dev = fz_begin_page(ctx, wri, fz_bound_page(ctx, page));
379
0
      fz_run_page(ctx, page, dev, fz_identity, NULL);
380
0
      fz_drop_page(ctx, page);
381
0
      page = NULL;
382
0
      fz_end_page(ctx, wri);
383
0
    }
384
0
  }
385
0
  fz_catch(ctx)
386
0
  {
387
0
    fz_drop_page(ctx, page);
388
0
    fz_rethrow(ctx);
389
0
  }
390
0
}