Coverage Report

Created: 2025-01-28 06:17

/src/mupdf/source/fitz/output-ps.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2021 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 "z-imp.h"
26
27
#include <limits.h>
28
29
typedef struct ps_band_writer_s
30
{
31
  fz_band_writer super;
32
  z_stream stream;
33
  int stream_started;
34
  int stream_ended;
35
  size_t input_size;
36
  unsigned char *input;
37
  size_t output_size;
38
  unsigned char *output;
39
} ps_band_writer;
40
41
void
42
fz_write_ps_file_header(fz_context *ctx, fz_output *out)
43
0
{
44
0
  fz_write_printf(ctx, out,
45
0
    "%%!PS-Adobe-3.0\n"
46
    //"%%%%BoundingBox: 0 0 612 792\n"
47
    //"%%%%HiResBoundingBox: 0 0 612 792\n"
48
0
    "%%%%Creator: MuPDF\n"
49
0
    "%%%%LanguageLevel: 2\n"
50
0
    "%%%%CreationDate: D:20160318101706Z00'00'\n"
51
0
    "%%%%DocumentData: Binary\n"
52
0
    "%%%%Pages: (atend)\n"
53
0
    "%%%%EndComments\n"
54
0
    "\n"
55
0
    "%%%%BeginProlog\n"
56
0
    "%%%%EndProlog\n"
57
0
    "\n"
58
0
    "%%%%BeginSetup\n"
59
0
    "%%%%EndSetup\n"
60
0
    "\n"
61
0
    );
62
0
}
63
64
void
65
fz_write_ps_file_trailer(fz_context *ctx, fz_output *out, int pages)
66
0
{
67
0
  fz_write_printf(ctx, out, "%%%%Trailer\n%%%%Pages: %d\n%%%%EOF\n", pages);
68
0
}
69
70
static void
71
ps_write_header(fz_context *ctx, fz_band_writer *writer_, fz_colorspace *cs)
72
0
{
73
0
  ps_band_writer *writer = (ps_band_writer *)writer_;
74
0
  fz_output *out = writer->super.out;
75
0
  int w = writer->super.w;
76
0
  int h = writer->super.h;
77
0
  int n = writer->super.n;
78
0
  int alpha = writer->super.alpha;
79
0
  int xres = writer->super.xres;
80
0
  int yres = writer->super.yres;
81
0
  int pagenum = writer->super.pagenum;
82
0
  int w_points = (w * 72 + (xres>>1)) / xres;
83
0
  int h_points = (h * 72 + (yres>>1)) / yres;
84
0
  float sx = (float) w / w_points;
85
0
  float sy = (float) h / h_points;
86
0
  int err;
87
88
0
  if (writer->super.s != 0)
89
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "Postscript writer cannot cope with spot colors");
90
91
0
  if (alpha != 0)
92
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "Postscript output cannot have alpha");
93
94
0
  writer->super.w = w;
95
0
  writer->super.h = h;
96
0
  writer->super.n = n;
97
98
0
  writer->stream.zalloc = fz_zlib_alloc;
99
0
  writer->stream.zfree = fz_zlib_free;
100
0
  writer->stream.opaque = ctx;
101
0
  writer->stream_started = 1;
102
103
0
  err = deflateInit(&writer->stream, Z_DEFAULT_COMPRESSION);
104
0
  if (err != Z_OK)
105
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "compression error %d", err);
106
107
0
  fz_write_printf(ctx, out, "%%%%Page: %d %d\n", pagenum, pagenum);
108
0
  fz_write_printf(ctx, out, "%%%%PageBoundingBox: 0 0 %d %d\n", w_points, h_points);
109
0
  fz_write_printf(ctx, out, "%%%%BeginPageSetup\n");
110
0
  fz_write_printf(ctx, out, "<</PageSize [%d %d]>> setpagedevice\n", w_points, h_points);
111
0
  fz_write_printf(ctx, out, "%%%%EndPageSetup\n\n");
112
0
  fz_write_printf(ctx, out, "/DataFile currentfile /FlateDecode filter def\n\n");
113
0
  switch(n)
114
0
  {
115
0
  case 1:
116
0
    fz_write_string(ctx, out, "/DeviceGray setcolorspace\n");
117
0
    break;
118
0
  case 3:
119
0
    fz_write_string(ctx, out, "/DeviceRGB setcolorspace\n");
120
0
    break;
121
0
  case 4:
122
0
    fz_write_string(ctx, out, "/DeviceCMYK setcolorspace\n");
123
0
    break;
124
0
  default:
125
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unexpected colorspace for ps output");
126
0
  }
127
0
  fz_write_printf(ctx, out,
128
0
    "<<\n"
129
0
    "/ImageType 1\n"
130
0
    "/Width %d\n"
131
0
    "/Height %d\n"
132
0
    "/ImageMatrix [ %g 0 0 -%g 0 %d ]\n"
133
0
    "/MultipleDataSources false\n"
134
0
    "/DataSource DataFile\n"
135
0
    "/BitsPerComponent 8\n"
136
    //"/Decode [0 1]\n"
137
0
    "/Interpolate false\n"
138
0
    ">>\n"
139
0
    "image\n"
140
0
    , w, h, sx, sy, h);
141
0
}
142
143
static void
144
ps_write_trailer(fz_context *ctx, fz_band_writer *writer_)
145
0
{
146
0
  ps_band_writer *writer = (ps_band_writer *)writer_;
147
0
  fz_output *out = writer->super.out;
148
0
  int err;
149
150
0
  writer->stream_ended = 1;
151
0
  err = deflateEnd(&writer->stream);
152
0
  if (err != Z_OK)
153
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "compression error %d", err);
154
155
0
  fz_write_data(ctx, out, writer->output, writer->output_size - writer->stream.avail_out);
156
0
  fz_write_string(ctx, out, "\nshowpage\n%%%%PageTrailer\n%%%%EndPageTrailer\n\n");
157
0
}
158
159
static void
160
ps_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
161
0
{
162
0
  ps_band_writer *writer = (ps_band_writer *)writer_;
163
164
0
  if (writer->stream_started && !writer->stream_ended)
165
0
  {
166
0
    int err = deflateEnd(&writer->stream);
167
0
    if (err != Z_OK)
168
0
      fz_warn(ctx, "ignoring compression error %d", err);
169
0
  }
170
171
0
  fz_free(ctx, writer->input);
172
0
  fz_free(ctx, writer->output);
173
0
}
174
175
void fz_write_pixmap_as_ps(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap)
176
0
{
177
0
  fz_band_writer *writer;
178
179
0
  fz_write_ps_file_header(ctx, out);
180
181
0
  writer = fz_new_ps_band_writer(ctx, out);
182
183
0
  fz_try(ctx)
184
0
  {
185
0
    fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
186
0
    fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
187
0
    fz_close_band_writer(ctx, writer);
188
0
  }
189
0
  fz_always(ctx)
190
0
  {
191
0
    fz_drop_band_writer(ctx, writer);
192
0
  }
193
0
  fz_catch(ctx)
194
0
  {
195
0
    fz_rethrow(ctx);
196
0
  }
197
198
0
  fz_write_ps_file_trailer(ctx, out, 1);
199
0
}
200
201
void fz_save_pixmap_as_ps(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append)
202
0
{
203
0
  fz_output *out = fz_new_output_with_path(ctx, filename, append);
204
0
  fz_try(ctx)
205
0
  {
206
0
    fz_write_pixmap_as_ps(ctx, out, pixmap);
207
0
    fz_close_output(ctx, out);
208
0
  }
209
0
  fz_always(ctx)
210
0
    fz_drop_output(ctx, out);
211
0
  fz_catch(ctx)
212
0
    fz_rethrow(ctx);
213
0
}
214
215
static void
216
ps_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int band_start, int band_height, const unsigned char *samples)
217
0
{
218
0
  ps_band_writer *writer = (ps_band_writer *)writer_;
219
0
  fz_output *out = writer->super.out;
220
0
  int w = writer->super.w;
221
0
  int h = writer->super.h;
222
0
  int n = writer->super.n;
223
0
  int x, y, i, err, finalband;
224
0
  size_t required_input;
225
0
  size_t required_output;
226
0
  size_t remain;
227
0
  unsigned char *o;
228
229
0
  if (!out)
230
0
    return;
231
232
233
0
  finalband = (band_start+band_height >= h);
234
0
  if (finalband)
235
0
    band_height = h - band_start;
236
237
0
  required_input = w;
238
0
  if (required_input > SIZE_MAX / n)
239
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "ps data too large.");
240
0
  required_input = required_input * n;
241
0
  if (required_input > SIZE_MAX / band_height)
242
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "ps data too large.");
243
0
  required_input *= band_height;
244
0
  required_output = required_input >= UINT_MAX ? UINT_MAX : deflateBound(&writer->stream, (uLong)required_input);
245
0
  if (required_output < required_input || required_output > UINT_MAX)
246
0
    required_output = UINT_MAX;
247
248
0
  if (writer->input == NULL || writer->input_size < required_input)
249
0
  {
250
0
    fz_free(ctx, writer->input);
251
0
    writer->input = NULL;
252
0
    writer->input = Memento_label(fz_malloc(ctx, required_input), "pswriter_input");
253
0
    writer->input_size = required_input;
254
0
  }
255
256
0
  if (writer->output == NULL || writer->output_size < required_output)
257
0
  {
258
0
    fz_free(ctx, writer->output);
259
0
    writer->output = NULL;
260
0
    writer->output = Memento_label(fz_malloc(ctx, required_output), "pswriter_output");
261
0
    writer->output_size = required_output;
262
0
  }
263
264
0
  o = writer->input;
265
0
  for (y = 0; y < band_height; y++)
266
0
  {
267
0
    for (x = 0; x < w; x++)
268
0
    {
269
0
      for (i = n; i > 0; i--)
270
0
        *o++ = *samples++;
271
0
    }
272
0
    samples += stride - w*n;
273
0
  }
274
275
0
  remain = o - writer->input;
276
0
  o = writer->input;
277
278
0
  do
279
0
  {
280
0
    size_t eaten;
281
282
0
    writer->stream.next_in = o;
283
0
    writer->stream.avail_in = (uInt)(remain <= UINT_MAX ? remain : UINT_MAX);
284
0
    writer->stream.next_out = writer->output;
285
0
    writer->stream.avail_out = writer->output_size <= UINT_MAX ? (uInt)writer->output_size : UINT_MAX;
286
287
0
    err = deflate(&writer->stream, (finalband && remain == writer->stream.avail_in) ? Z_FINISH : Z_NO_FLUSH);
288
0
    if (err != Z_OK && err != Z_STREAM_END)
289
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "compression error %d", err);
290
291
    /* We are guaranteed that writer->stream.next_in will have been updated for the
292
     * data that has been eaten. */
293
0
    eaten = (writer->stream.next_in - o);
294
0
    remain -= eaten;
295
0
    o += eaten;
296
297
    /* We are guaranteed that writer->stream.next_out will have been updated for the
298
     * data that has been written. */
299
0
    if (writer->stream.next_out != writer->output)
300
0
      fz_write_data(ctx, out, writer->output, writer->output_size - writer->stream.avail_out);
301
302
    /* Zlib only guarantees to have finished when we have no more data to feed in, and
303
     * the last call to deflate did not return with avail_out == 0. (i.e. no more is
304
     * buffered internally.) */
305
0
  }
306
0
  while (remain != 0 || writer->stream.avail_out == 0);
307
0
}
308
309
fz_band_writer *fz_new_ps_band_writer(fz_context *ctx, fz_output *out)
310
0
{
311
0
  ps_band_writer *writer = fz_new_band_writer(ctx, ps_band_writer, out);
312
313
0
  writer->super.header = ps_write_header;
314
0
  writer->super.band = ps_write_band;
315
0
  writer->super.trailer = ps_write_trailer;
316
0
  writer->super.drop = ps_drop_band_writer;
317
318
0
  return &writer->super;
319
0
}
320
321
/* High-level document writer interface */
322
323
typedef struct
324
{
325
  fz_document_writer super;
326
  fz_draw_options draw;
327
  fz_pixmap *pixmap;
328
  fz_output *out;
329
  int count;
330
} fz_ps_writer;
331
332
static fz_device *
333
ps_begin_page(fz_context *ctx, fz_document_writer *wri_, fz_rect mediabox)
334
0
{
335
0
  fz_ps_writer *wri = (fz_ps_writer*)wri_;
336
0
  wri->count++;
337
0
  return fz_new_draw_device_with_options(ctx, &wri->draw, mediabox, &wri->pixmap);
338
0
}
339
340
static void
341
ps_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev)
342
0
{
343
0
  fz_ps_writer *wri = (fz_ps_writer*)wri_;
344
0
  fz_pixmap *pix = wri->pixmap;
345
0
  fz_band_writer *bw;
346
347
0
  fz_try(ctx)
348
0
  {
349
0
    fz_close_device(ctx, dev);
350
0
    bw = fz_new_ps_band_writer(ctx, wri->out);
351
0
    fz_write_header(ctx, bw, pix->w, pix->h, pix->n, pix->alpha, pix->xres, pix->yres, 0, pix->colorspace, pix->seps);
352
0
    fz_write_band(ctx, bw, pix->stride, pix->h, pix->samples);
353
0
    fz_close_band_writer(ctx, bw);
354
0
  }
355
0
  fz_always(ctx)
356
0
  {
357
0
    fz_drop_device(ctx, dev);
358
0
    fz_drop_band_writer(ctx, bw);
359
0
    fz_drop_pixmap(ctx, wri->pixmap);
360
0
    wri->pixmap = NULL;
361
0
  }
362
0
  fz_catch(ctx)
363
0
    fz_rethrow(ctx);
364
0
}
365
366
static void
367
ps_close_writer(fz_context *ctx, fz_document_writer *wri_)
368
0
{
369
0
  fz_ps_writer *wri = (fz_ps_writer*)wri_;
370
0
  fz_write_ps_file_trailer(ctx, wri->out, wri->count);
371
0
  fz_close_output(ctx, wri->out);
372
0
}
373
374
static void
375
ps_drop_writer(fz_context *ctx, fz_document_writer *wri_)
376
0
{
377
0
  fz_ps_writer *wri = (fz_ps_writer*)wri_;
378
0
  fz_drop_pixmap(ctx, wri->pixmap);
379
0
  fz_drop_output(ctx, wri->out);
380
0
}
381
382
fz_document_writer *
383
fz_new_ps_writer_with_output(fz_context *ctx, fz_output *out, const char *options)
384
0
{
385
0
  fz_ps_writer *wri = NULL;
386
387
0
  fz_var(wri);
388
389
0
  fz_try(ctx)
390
0
  {
391
0
    wri = fz_new_derived_document_writer(ctx, fz_ps_writer, ps_begin_page, ps_end_page, ps_close_writer, ps_drop_writer);
392
0
    fz_parse_draw_options(ctx, &wri->draw, options);
393
0
    wri->out = out;
394
0
    fz_write_ps_file_header(ctx, wri->out);
395
0
  }
396
0
  fz_catch(ctx)
397
0
  {
398
0
    fz_drop_output(ctx, out);
399
0
    fz_free(ctx, wri);
400
0
    fz_rethrow(ctx);
401
0
  }
402
403
0
  return (fz_document_writer*)wri;
404
0
}
405
406
fz_document_writer *
407
fz_new_ps_writer(fz_context *ctx, const char *path, const char *options)
408
0
{
409
0
  fz_output *out = fz_new_output_with_path(ctx, path ? path : "out.ps", 0);
410
0
  return fz_new_ps_writer_with_output(ctx, out, options);
411
0
}