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