Coverage Report

Created: 2025-12-31 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/writer.c
Line
Count
Source
1
// Copyright (C) 2004-2025 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 void fz_write_pixmap_as_jpeg_default(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
160
0
{
161
0
  fz_write_pixmap_as_jpeg(ctx, out, pixmap, 90, 1);
162
0
}
163
164
fz_document_writer *fz_new_jpeg_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
165
0
{
166
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 0, fz_write_pixmap_as_jpeg_default);
167
0
}
168
169
fz_document_writer *fz_new_png_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
170
0
{
171
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 0, fz_write_pixmap_as_png);
172
0
}
173
174
fz_document_writer *fz_new_pam_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
175
0
{
176
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 0, fz_write_pixmap_as_pam);
177
0
}
178
179
fz_document_writer *fz_new_pnm_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
180
0
{
181
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 0, fz_write_pixmap_as_pnm);
182
0
}
183
184
fz_document_writer *fz_new_pgm_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
185
0
{
186
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 1, fz_write_pixmap_as_pnm);
187
0
}
188
189
fz_document_writer *fz_new_ppm_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
190
0
{
191
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 3, fz_write_pixmap_as_pnm);
192
0
}
193
194
fz_document_writer *fz_new_pbm_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
195
0
{
196
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 1, fz_write_pixmap_as_pbm);
197
0
}
198
199
fz_document_writer *fz_new_pkm_pixmap_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
200
0
{
201
0
  return fz_new_pixmap_writer_with_output(ctx, out, options, 4, fz_write_pixmap_as_pkm);
202
0
}
203
204
static int is_extension(const char *a, const char *ext)
205
0
{
206
0
  if (!a)
207
0
    return 0;
208
0
  if (a[0] == '.')
209
0
    ++a;
210
0
  return !fz_strcasecmp(a, ext);
211
0
}
212
213
static const char *prev_period(const char *start, const char *p)
214
0
{
215
0
  while (--p > start)
216
0
    if (*p == '.')
217
0
      return p;
218
0
  return NULL;
219
0
}
220
221
fz_document_writer *
222
fz_new_document_writer(fz_context *ctx, const char *path, const char *explicit_format, const char *options)
223
0
{
224
0
  const char *format = explicit_format;
225
0
  if (!format)
226
0
    format = strrchr(path, '.');
227
0
  while (format)
228
0
  {
229
0
#if FZ_ENABLE_OCR_OUTPUT
230
0
    if (is_extension(format, "ocr"))
231
0
      return fz_new_pdfocr_writer(ctx, path, options);
232
0
#endif
233
0
#if FZ_ENABLE_PDF
234
0
    if (is_extension(format, "pdf"))
235
0
      return fz_new_pdf_writer(ctx, path, options);
236
0
#endif
237
238
0
    if (is_extension(format, "cbz"))
239
0
      return fz_new_cbz_writer(ctx, path, options);
240
0
    if (is_extension(format, "csv"))
241
0
      return fz_new_csv_writer(ctx, path, options);
242
243
0
    if (is_extension(format, "svg"))
244
0
      return fz_new_svg_writer(ctx, path, options);
245
246
0
    if (is_extension(format, "png"))
247
0
      return fz_new_png_pixmap_writer(ctx, path, options);
248
0
    if (is_extension(format, "pam"))
249
0
      return fz_new_pam_pixmap_writer(ctx, path, options);
250
0
    if (is_extension(format, "pnm"))
251
0
      return fz_new_pnm_pixmap_writer(ctx, path, options);
252
0
    if (is_extension(format, "pgm"))
253
0
      return fz_new_pgm_pixmap_writer(ctx, path, options);
254
0
    if (is_extension(format, "ppm"))
255
0
      return fz_new_ppm_pixmap_writer(ctx, path, options);
256
0
    if (is_extension(format, "pbm"))
257
0
      return fz_new_pbm_pixmap_writer(ctx, path, options);
258
0
    if (is_extension(format, "pkm"))
259
0
      return fz_new_pkm_pixmap_writer(ctx, path, options);
260
0
    if (is_extension(format, "jpeg") || is_extension(format, "jpg"))
261
0
      return fz_new_jpeg_pixmap_writer(ctx, path, options);
262
263
0
    if (is_extension(format, "pcl"))
264
0
      return fz_new_pcl_writer(ctx, path, options);
265
0
    if (is_extension(format, "pclm"))
266
0
      return fz_new_pclm_writer(ctx, path, options);
267
0
    if (is_extension(format, "ps"))
268
0
      return fz_new_ps_writer(ctx, path, options);
269
0
    if (is_extension(format, "pwg"))
270
0
      return fz_new_pwg_writer(ctx, path, options);
271
272
0
    if (is_extension(format, "txt") || is_extension(format, "text"))
273
0
      return fz_new_text_writer(ctx, "text", path, options);
274
0
    if (is_extension(format, "html"))
275
0
      return fz_new_text_writer(ctx, "html", path, options);
276
0
    if (is_extension(format, "xhtml"))
277
0
      return fz_new_text_writer(ctx, "xhtml", path, options);
278
0
    if (is_extension(format, "stext") || is_extension(format, "stext.xml"))
279
0
      return fz_new_text_writer(ctx, "stext.xml", path, options);
280
0
    if (is_extension(format, "stext.json"))
281
0
      return fz_new_text_writer(ctx, "stext.json", path, options);
282
283
0
#if FZ_ENABLE_ODT_OUTPUT
284
0
    if (is_extension(format, "odt"))
285
0
      return fz_new_odt_writer(ctx, path, options);
286
0
#endif
287
0
#if FZ_ENABLE_DOCX_OUTPUT
288
0
    if (is_extension(format, "docx"))
289
0
      return fz_new_docx_writer(ctx, path, options);
290
0
#endif
291
0
    if (format != explicit_format)
292
0
      format = prev_period(path, format);
293
0
    else
294
0
      format = NULL;
295
0
  }
296
0
  fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot detect document format");
297
0
}
298
299
fz_document_writer *
300
fz_new_document_writer_with_output(fz_context *ctx, fz_output *out, const char *format, const char *options)
301
0
{
302
0
#if FZ_ENABLE_OCR_OUTPUT
303
0
  if (is_extension(format, "ocr"))
304
0
    return fz_new_pdfocr_writer_with_output(ctx, out, options);
305
0
#endif
306
0
#if FZ_ENABLE_PDF
307
0
  if (is_extension(format, "pdf"))
308
0
    return fz_new_pdf_writer_with_output(ctx, out, options);
309
0
#endif
310
311
0
  if (is_extension(format, "cbz"))
312
0
    return fz_new_cbz_writer_with_output(ctx, out, options);
313
0
  if (is_extension(format, "csv"))
314
0
    return fz_new_csv_writer_with_output(ctx, out, options);
315
316
0
  if (is_extension(format, "svg"))
317
0
    return fz_new_svg_writer_with_output(ctx, out, options);
318
319
0
  if (is_extension(format, "png"))
320
0
    return fz_new_png_pixmap_writer_with_output(ctx, out, options);
321
0
  if (is_extension(format, "pam"))
322
0
    return fz_new_pam_pixmap_writer_with_output(ctx, out, options);
323
0
  if (is_extension(format, "pnm"))
324
0
    return fz_new_pnm_pixmap_writer_with_output(ctx, out, options);
325
0
  if (is_extension(format, "pgm"))
326
0
    return fz_new_pgm_pixmap_writer_with_output(ctx, out, options);
327
0
  if (is_extension(format, "ppm"))
328
0
    return fz_new_ppm_pixmap_writer_with_output(ctx, out, options);
329
0
  if (is_extension(format, "pbm"))
330
0
    return fz_new_pbm_pixmap_writer_with_output(ctx, out, options);
331
0
  if (is_extension(format, "pkm"))
332
0
    return fz_new_pkm_pixmap_writer_with_output(ctx, out, options);
333
0
  if (is_extension(format, "jpeg") || is_extension(format, "jpg"))
334
0
    return fz_new_jpeg_pixmap_writer_with_output(ctx, out, options);
335
336
0
  if (is_extension(format, "pcl"))
337
0
    return fz_new_pcl_writer_with_output(ctx, out, options);
338
0
  if (is_extension(format, "pclm"))
339
0
    return fz_new_pclm_writer_with_output(ctx, out, options);
340
0
  if (is_extension(format, "ps"))
341
0
    return fz_new_ps_writer_with_output(ctx, out, options);
342
0
  if (is_extension(format, "pwg"))
343
0
    return fz_new_pwg_writer_with_output(ctx, out, options);
344
345
0
  if (is_extension(format, "txt") || is_extension(format, "text"))
346
0
    return fz_new_text_writer_with_output(ctx, "text", out, options);
347
0
  if (is_extension(format, "html"))
348
0
    return fz_new_text_writer_with_output(ctx, "html", out, options);
349
0
  if (is_extension(format, "xhtml"))
350
0
    return fz_new_text_writer_with_output(ctx, "xhtml", out, options);
351
0
  if (is_extension(format, "stext") || is_extension(format, "stext.xml"))
352
0
    return fz_new_text_writer_with_output(ctx, "stext.xml", out, options);
353
0
  if (is_extension(format, "stext.json"))
354
0
    return fz_new_text_writer_with_output(ctx, "stext.json", out, options);
355
356
0
#if FZ_ENABLE_ODT_OUTPUT
357
0
  if (is_extension(format, "odt"))
358
0
    return fz_new_odt_writer_with_output(ctx, out, options);
359
0
#endif
360
0
#if FZ_ENABLE_DOCX_OUTPUT
361
0
  if (is_extension(format, "docx"))
362
0
    return fz_new_docx_writer_with_output(ctx, out, options);
363
0
#endif
364
365
0
  fz_throw(ctx, FZ_ERROR_ARGUMENT, "unknown output document format: %s", format);
366
0
}
367
368
fz_document_writer *
369
fz_new_document_writer_with_buffer(fz_context *ctx, fz_buffer *buffer, const char *format, const char *options)
370
0
{
371
0
  fz_document_writer *wri;
372
0
  fz_output *out = fz_new_output_with_buffer(ctx, buffer);
373
0
  fz_try(ctx) {
374
0
    wri = fz_new_document_writer_with_output(ctx, out, format, options);
375
0
  }
376
0
  fz_catch(ctx) {
377
0
    fz_drop_output(ctx, out);
378
0
    fz_rethrow(ctx);
379
0
  }
380
0
  return wri;
381
0
}
382
383
void
384
fz_close_document_writer(fz_context *ctx, fz_document_writer *wri)
385
0
{
386
0
  if (wri->close_writer)
387
0
    wri->close_writer(ctx, wri);
388
0
  wri->close_writer = NULL;
389
0
}
390
391
void
392
fz_drop_document_writer(fz_context *ctx, fz_document_writer *wri)
393
0
{
394
0
  if (!wri)
395
0
    return;
396
397
0
  if (wri->close_writer)
398
0
    fz_warn(ctx, "dropping unclosed document writer");
399
0
  if (wri->dev)
400
0
    fz_drop_device(ctx, wri->dev);
401
0
  if (wri->drop_writer)
402
0
    wri->drop_writer(ctx, wri);
403
0
  fz_free(ctx, wri);
404
0
}
405
406
fz_device *
407
fz_begin_page(fz_context *ctx, fz_document_writer *wri, fz_rect mediabox)
408
0
{
409
0
  if (!wri)
410
0
    return NULL;
411
0
  if (wri->dev)
412
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "called begin page without ending the previous page");
413
0
  wri->dev = wri->begin_page(ctx, wri, mediabox);
414
0
  return wri->dev;
415
0
}
416
417
void
418
fz_end_page(fz_context *ctx, fz_document_writer *wri)
419
0
{
420
0
  fz_device *dev;
421
422
0
  if (!wri)
423
0
    return;
424
0
  dev = wri->dev;
425
0
  wri->dev = NULL;
426
0
  wri->end_page(ctx, wri, dev);
427
0
}
428
429
void
430
fz_write_document(fz_context *ctx, fz_document_writer *wri, fz_document *doc)
431
0
{
432
0
  int i, n;
433
0
  fz_page *page = NULL;
434
0
  fz_device *dev;
435
436
0
  fz_var(page);
437
438
0
  n = fz_count_pages(ctx, doc);
439
0
  fz_try(ctx)
440
0
  {
441
0
    for (i = 0; i < n; i++)
442
0
    {
443
0
      page = fz_load_page(ctx, doc, i);
444
0
      dev = fz_begin_page(ctx, wri, fz_bound_page(ctx, page));
445
0
      fz_run_page(ctx, page, dev, fz_identity, NULL);
446
0
      fz_drop_page(ctx, page);
447
0
      page = NULL;
448
0
      fz_end_page(ctx, wri);
449
0
    }
450
0
  }
451
0
  fz_catch(ctx)
452
0
  {
453
0
    fz_drop_page(ctx, page);
454
0
    fz_rethrow(ctx);
455
0
  }
456
0
}