Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/output.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/output.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,864 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#define _LARGEFILE_SOURCE +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#include "mupdf/fitz.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#ifdef _WIN32 +#include <io.h> +#include <windows.h> +#else +#include <unistd.h> +#endif + +static void +file_write(fz_context *ctx, void *opaque, const void *buffer, size_t count) +{ + FILE *file = opaque; + size_t n; + + if (count == 0) + return; + + if (count == 1) + { + int x = putc(((unsigned char*)buffer)[0], file); + if (x == EOF && ferror(file)) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot fwrite: %s", strerror(errno)); + return; + } + + n = fwrite(buffer, 1, count, file); + if (n < count && ferror(file)) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot fwrite: %s", strerror(errno)); +} + +static void +stdout_write(fz_context *ctx, void *opaque, const void *buffer, size_t count) +{ + file_write(ctx, stdout, buffer, count); +} + +static fz_output fz_stdout_global = { + NULL, + stdout_write, + NULL, + NULL, + NULL, +}; + +fz_output *fz_stdout(fz_context *ctx) +{ + return &fz_stdout_global; +} + +static void +stderr_write(fz_context *ctx, void *opaque, const void *buffer, size_t count) +{ + file_write(ctx, stderr, buffer, count); +} + +static fz_output fz_stderr_global = { + NULL, + stderr_write, + NULL, + NULL, + NULL, +}; + +fz_output *fz_stderr(fz_context *ctx) +{ + return &fz_stderr_global; +} + +#ifdef _WIN32 +static void +stdods_write(fz_context *ctx, void *opaque, const void *buffer, size_t count) +{ + char *buf = fz_malloc(ctx, count+1); + + memcpy(buf, buffer, count); + buf[count] = 0; + OutputDebugStringA(buf); + fz_free(ctx, buf); +} + +static fz_output fz_stdods_global = { + NULL, + stdods_write, + NULL, + NULL, + NULL, +}; + +fz_output *fz_stdods(fz_context *ctx) +{ + return &fz_stdods_global; +} +#endif + +fz_output *fz_stddbg(fz_context *ctx) +{ + if (ctx->stddbg) + return ctx->stddbg; + + return fz_stderr(ctx); +} + +void fz_set_stddbg(fz_context *ctx, fz_output *out) +{ + if (ctx == NULL) + return; + + ctx->stddbg = out; +} + +static void +file_seek(fz_context *ctx, void *opaque, int64_t off, int whence) +{ + FILE *file = opaque; +#ifdef _WIN32 + int n = _fseeki64(file, off, whence); +#else + int n = fseeko(file, off, whence); +#endif + if (n < 0) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot fseek: %s", strerror(errno)); +} + +static int64_t +file_tell(fz_context *ctx, void *opaque) +{ + FILE *file = opaque; +#ifdef _WIN32 + int64_t off = _ftelli64(file); +#else + int64_t off = ftello(file); +#endif + if (off == -1) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot ftell: %s", strerror(errno)); + return off; +} + +static void +file_drop(fz_context *ctx, void *opaque) +{ + FILE *file = opaque; + int n = fclose(file); + if (n < 0) + fz_warn(ctx, "cannot fclose: %s", strerror(errno)); +} + +static fz_stream * +file_as_stream(fz_context *ctx, void *opaque) +{ + FILE *file = opaque; + fflush(file); + return fz_open_file_ptr_no_close(ctx, file); +} + +static void file_truncate(fz_context *ctx, void *opaque) +{ + FILE *file = opaque; + fflush(file); + +#ifdef _WIN32 + { + __int64 pos = _ftelli64(file); + if (pos >= 0) + _chsize_s(fileno(file), pos); + } +#else + { + off_t pos = ftello(file); + if (pos >= 0) + (void)ftruncate(fileno(file), pos); + } +#endif +} + +fz_output * +fz_new_output(fz_context *ctx, int bufsiz, void *state, fz_output_write_fn *write, fz_output_close_fn *close, fz_output_drop_fn *drop) +{ + fz_output *out = NULL; + + fz_var(out); + + fz_try(ctx) + { + out = fz_malloc_struct(ctx, fz_output); + out->state = state; + out->write = write; + out->close = close; + out->drop = drop; + if (bufsiz > 0) + { + out->bp = Memento_label(fz_malloc(ctx, bufsiz), "output_buf"); + out->wp = out->bp; + out->ep = out->bp + bufsiz; + } + } + fz_catch(ctx) + { + if (drop) + drop(ctx, state); + fz_free(ctx, out); + fz_rethrow(ctx); + } + return out; +} + +static void null_write(fz_context *ctx, void *opaque, const void *buffer, size_t count) +{ +} + +fz_output * +fz_new_output_with_path(fz_context *ctx, const char *filename, int append) +{ + FILE *file; + + if (filename == NULL) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "no output to write to"); + + if (!strcmp(filename, "/dev/null")) + return fz_new_output(ctx, 0, NULL, null_write, NULL, NULL); + if (!strcmp(filename, "/dev/stdout")) + return fz_stdout(ctx); + if (!strcmp(filename, "/dev/stderr")) + return fz_stderr(ctx); + + /* If <append> is false, we use fopen()'s 'x' flag to force an error if + * some other process creates the file immediately after we have removed + * it - this avoids vulnerability where a less-privilege process can create + * a link and get us to overwrite a different file. See: + * https://bugs.ghostscript.com/show_bug.cgi?id=701797 + * http://www.open-std.org/jtc1/sc22//WG14/www/docs/n1339.pdf + */ +#ifdef _WIN32 + /* Ensure we create a brand new file. We don't want to clobber our old file. */ + if (!append) + { + if (fz_remove_utf8(filename) < 0) + if (errno != ENOENT) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot remove file '%s': %s", filename, strerror(errno)); + } +#if defined(__MINGW32__) || defined(__MINGW64__) + file = fz_fopen_utf8(filename, append ? "rb+" : "wb+"); /* 'x' flag not supported. */ +#else + file = fz_fopen_utf8(filename, append ? "rb+" : "wb+x"); +#endif + if (append) + { + if (file == NULL) + file = fz_fopen_utf8(filename, "wb+"); + else + fseek(file, 0, SEEK_END); + } +#else + /* Ensure we create a brand new file. We don't want to clobber our old file. */ + if (!append) + { + if (remove(filename) < 0) + if (errno != ENOENT) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot remove file '%s': %s", filename, strerror(errno)); + } + file = fopen(filename, append ? "rb+" : "wb+x"); + if (file == NULL && append) + file = fopen(filename, "wb+"); +#endif + if (!file) + fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open file '%s': %s", filename, strerror(errno)); + + return fz_new_output_with_file_ptr(ctx, file); +} + +fz_output * +fz_new_output_with_file_ptr(fz_context *ctx, FILE *file) +{ + fz_output *out; + + if (!file) + return fz_new_output(ctx, 0, NULL, null_write, NULL, NULL); + + setvbuf(file, NULL, _IONBF, 0); /* we do our own buffering */ + out = fz_new_output(ctx, 8192, file, file_write, NULL, file_drop); + out->seek = file_seek; + out->tell = file_tell; + out->as_stream = file_as_stream; + out->truncate = file_truncate; + + return out; +} + +static void +buffer_write(fz_context *ctx, void *opaque, const void *data, size_t len) +{ + fz_buffer *buffer = opaque; + fz_append_data(ctx, buffer, data, len); +} + +static void +buffer_seek(fz_context *ctx, void *opaque, int64_t off, int whence) +{ + fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot seek in buffer: %s", strerror(errno)); +} + +static int64_t +buffer_tell(fz_context *ctx, void *opaque) +{ + fz_buffer *buffer = opaque; + return (int64_t)buffer->len; +} + +static void +buffer_drop(fz_context *ctx, void *opaque) +{ + fz_buffer *buffer = opaque; + fz_drop_buffer(ctx, buffer); +} + +static void +buffer_reset(fz_context *ctx, void *opaque) +{ +} + +fz_output * +fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf) +{ + fz_output *out = fz_new_output(ctx, 0, fz_keep_buffer(ctx, buf), buffer_write, NULL, buffer_drop); + out->seek = buffer_seek; + out->tell = buffer_tell; + out->reset = buffer_reset; + return out; +} + +void +fz_close_output(fz_context *ctx, fz_output *out) +{ + if (out == NULL) + return; + fz_flush_output(ctx, out); + if (!out->closed && out->close) + out->close(ctx, out->state); + out->closed = 1; +} + +void +fz_drop_output(fz_context *ctx, fz_output *out) +{ + if (out) + { + if (!out->closed) + fz_warn(ctx, "dropping unclosed output"); + if (out->drop) + out->drop(ctx, out->state); + fz_free(ctx, out->bp); + if (out != &fz_stdout_global && out != &fz_stderr_global) + fz_free(ctx, out); + } +} + +void +fz_reset_output(fz_context *ctx, fz_output *out) +{ + if (!out) + return; + if (out->reset == NULL) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot reset this output"); + + out->reset(ctx, out->state); + out->closed = 0; +} + +void +fz_seek_output(fz_context *ctx, fz_output *out, int64_t off, int whence) +{ + if (out->seek == NULL) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot seek in unseekable output stream\n"); + fz_flush_output(ctx, out); + out->seek(ctx, out->state, off, whence); +} + +int64_t +fz_tell_output(fz_context *ctx, fz_output *out) +{ + if (out->tell == NULL) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot tell in untellable output stream\n"); + if (out->bp) + return out->tell(ctx, out->state) + (out->wp - out->bp); + return out->tell(ctx, out->state); +} + +fz_stream * +fz_stream_from_output(fz_context *ctx, fz_output *out) +{ + if (out->as_stream == NULL) + return NULL; + fz_flush_output(ctx, out); + return out->as_stream(ctx, out->state); +} + +void +fz_truncate_output(fz_context *ctx, fz_output *out) +{ + if (out->truncate == NULL) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot truncate this output stream"); + fz_flush_output(ctx, out); + out->truncate(ctx, out->state); +} + +static void +fz_write_emit(fz_context *ctx, void *out, int c) +{ + fz_write_byte(ctx, out, c); +} + +void +fz_write_vprintf(fz_context *ctx, fz_output *out, const char *fmt, va_list args) +{ + fz_format_string(ctx, out, fz_write_emit, fmt, args); +} + +void +fz_write_printf(fz_context *ctx, fz_output *out, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + fz_format_string(ctx, out, fz_write_emit, fmt, args); + va_end(args); +} + +void +fz_flush_output(fz_context *ctx, fz_output *out) +{ + fz_write_bits_sync(ctx, out); + if (out->wp > out->bp) + { + out->write(ctx, out->state, out->bp, out->wp - out->bp); + out->wp = out->bp; + } +} + +void +fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x) +{ + if (out->bp) + { + if (out->wp == out->ep) + { + out->write(ctx, out->state, out->bp, out->wp - out->bp); + out->wp = out->bp; + } + *out->wp++ = x; + } + else + { + out->write(ctx, out->state, &x, 1); + } +} + +void +fz_write_char(fz_context *ctx, fz_output *out, char x) +{ + fz_write_byte(ctx, out, (unsigned char)x); +} + +void +fz_write_data(fz_context *ctx, fz_output *out, const void *data_, size_t size) +{ + const char *data = data_; + + if (out->bp) + { + if (size >= (size_t) (out->ep - out->bp)) /* too large for buffer */ + { + if (out->wp > out->bp) + { + out->write(ctx, out->state, out->bp, out->wp - out->bp); + out->wp = out->bp; + } + out->write(ctx, out->state, data, size); + } + else if (out->wp + size <= out->ep) /* fits in current buffer */ + { + memcpy(out->wp, data, size); + out->wp += size; + } + else /* fits if we flush first */ + { + size_t n = out->ep - out->wp; + memcpy(out->wp, data, n); + out->write(ctx, out->state, out->bp, out->ep - out->bp); + memcpy(out->bp, data + n, size - n); + out->wp = out->bp + size - n; + } + } + else + { + out->write(ctx, out->state, data, size); + } +} + +void +fz_write_buffer(fz_context *ctx, fz_output *out, fz_buffer *buf) +{ + fz_write_data(ctx, out, buf->data, buf->len); +} + +void +fz_write_string(fz_context *ctx, fz_output *out, const char *s) +{ + fz_write_data(ctx, out, s, strlen(s)); +} + +void +fz_write_int32_be(fz_context *ctx, fz_output *out, int x) +{ + char data[4]; + + data[0] = x>>24; + data[1] = x>>16; + data[2] = x>>8; + data[3] = x; + + fz_write_data(ctx, out, data, 4); +} + +void +fz_write_uint32_be(fz_context *ctx, fz_output *out, unsigned int x) +{ + fz_write_int32_be(ctx, out, (unsigned int)x); +} + +void +fz_write_int32_le(fz_context *ctx, fz_output *out, int x) +{ + char data[4]; + + data[0] = x; + data[1] = x>>8; + data[2] = x>>16; + data[3] = x>>24; + + fz_write_data(ctx, out, data, 4); +} + +void +fz_write_uint32_le(fz_context *ctx, fz_output *out, unsigned int x) +{ + fz_write_int32_le(ctx, out, (int)x); +} + +void +fz_write_int16_be(fz_context *ctx, fz_output *out, int x) +{ + char data[2]; + + data[0] = x>>8; + data[1] = x; + + fz_write_data(ctx, out, data, 2); +} + +void +fz_write_uint16_be(fz_context *ctx, fz_output *out, unsigned int x) +{ + fz_write_int16_be(ctx, out, (int)x); +} + +void +fz_write_int16_le(fz_context *ctx, fz_output *out, int x) +{ + char data[2]; + + data[0] = x; + data[1] = x>>8; + + fz_write_data(ctx, out, data, 2); +} + +void +fz_write_uint16_le(fz_context *ctx, fz_output *out, unsigned int x) +{ + fz_write_int16_le(ctx, out, (int)x); +} + +void +fz_write_float_le(fz_context *ctx, fz_output *out, float f) +{ + union {float f; int32_t i;} u; + u.f = f; + fz_write_int32_le(ctx, out, u.i); +} + +void +fz_write_float_be(fz_context *ctx, fz_output *out, float f) +{ + union {float f; int32_t i;} u; + u.f = f; + fz_write_int32_be(ctx, out, u.i); +} + +void +fz_write_rune(fz_context *ctx, fz_output *out, int rune) +{ + char data[10]; + fz_write_data(ctx, out, data, fz_runetochar(data, rune)); +} + +void +fz_write_base64(fz_context *ctx, fz_output *out, const unsigned char *data, size_t size, int newline) +{ + static const char set[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t i; + for (i = 0; i + 3 <= size; i += 3) + { + int c = data[i]; + int d = data[i+1]; + int e = data[i+2]; + if (newline && (i & 15) == 0) + fz_write_byte(ctx, out, '\n'); + fz_write_byte(ctx, out, set[c>>2]); + fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]); + fz_write_byte(ctx, out, set[((d&15)<<2)|(e>>6)]); + fz_write_byte(ctx, out, set[e&63]); + } + if (size - i == 2) + { + int c = data[i]; + int d = data[i+1]; + fz_write_byte(ctx, out, set[c>>2]); + fz_write_byte(ctx, out, set[((c&3)<<4)|(d>>4)]); + fz_write_byte(ctx, out, set[((d&15)<<2)]); + fz_write_byte(ctx, out, '='); + } + else if (size - i == 1) + { + int c = data[i]; + fz_write_byte(ctx, out, set[c>>2]); + fz_write_byte(ctx, out, set[((c&3)<<4)]); + fz_write_byte(ctx, out, '='); + fz_write_byte(ctx, out, '='); + } +} + +void +fz_write_base64_buffer(fz_context *ctx, fz_output *out, fz_buffer *buf, int newline) +{ + unsigned char *data; + size_t size = fz_buffer_storage(ctx, buf, &data); + fz_write_base64(ctx, out, data, size, newline); +} + +void +fz_append_base64(fz_context *ctx, fz_buffer *out, const unsigned char *data, size_t size, int newline) +{ + static const char set[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t i; + for (i = 0; i + 3 <= size; i += 3) + { + int c = data[i]; + int d = data[i+1]; + int e = data[i+2]; + if (newline && (i & 15) == 0) + fz_append_byte(ctx, out, '\n'); + fz_append_byte(ctx, out, set[c>>2]); + fz_append_byte(ctx, out, set[((c&3)<<4)|(d>>4)]); + fz_append_byte(ctx, out, set[((d&15)<<2)|(e>>6)]); + fz_append_byte(ctx, out, set[e&63]); + } + if (size - i == 2) + { + int c = data[i]; + int d = data[i+1]; + fz_append_byte(ctx, out, set[c>>2]); + fz_append_byte(ctx, out, set[((c&3)<<4)|(d>>4)]); + fz_append_byte(ctx, out, set[((d&15)<<2)]); + fz_append_byte(ctx, out, '='); + } + else if (size - i == 1) + { + int c = data[i]; + fz_append_byte(ctx, out, set[c>>2]); + fz_append_byte(ctx, out, set[((c&3)<<4)]); + fz_append_byte(ctx, out, '='); + fz_append_byte(ctx, out, '='); + } +} + +void +fz_append_base64_buffer(fz_context *ctx, fz_buffer *out, fz_buffer *buf, int newline) +{ + unsigned char *data; + size_t size = fz_buffer_storage(ctx, buf, &data); + fz_append_base64(ctx, out, data, size, newline); +} + + +void +fz_save_buffer(fz_context *ctx, fz_buffer *buf, const char *filename) +{ + fz_output *out = fz_new_output_with_path(ctx, filename, 0); + fz_try(ctx) + { + fz_write_data(ctx, out, buf->data, buf->len); + fz_close_output(ctx, out); + } + fz_always(ctx) + fz_drop_output(ctx, out); + fz_catch(ctx) + fz_rethrow(ctx); +} + +fz_band_writer *fz_new_band_writer_of_size(fz_context *ctx, size_t size, fz_output *out) +{ + fz_band_writer *writer = fz_calloc(ctx, size, 1); + writer->out = out; + return writer; +} + +void fz_write_header(fz_context *ctx, fz_band_writer *writer, int w, int h, int n, int alpha, int xres, int yres, int pagenum, fz_colorspace *cs, fz_separations *seps) +{ + if (writer == NULL || writer->band == NULL) + return; + + if (w <= 0 || h <= 0 || n <= 0 || alpha < 0 || alpha > 1) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Invalid bandwriter header dimensions/setup"); + + writer->w = w; + writer->h = h; + writer->s = fz_count_active_separations(ctx, seps); + writer->n = n; + writer->alpha = alpha; + writer->xres = xres; + writer->yres = yres; + writer->pagenum = pagenum; + writer->line = 0; + writer->seps = fz_keep_separations(ctx, seps); + writer->header(ctx, writer, cs); +} + +void fz_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_height, const unsigned char *samples) +{ + if (writer == NULL || writer->band == NULL) + return; + if (writer->line + band_height > writer->h) + band_height = writer->h - writer->line; + if (band_height < 0) { + fz_throw(ctx, FZ_ERROR_LIMIT, "Too much band data!"); + } + if (band_height > 0) { + writer->band(ctx, writer, stride, writer->line, band_height, samples); + writer->line += band_height; + } + if (writer->line == writer->h && writer->trailer) { + writer->trailer(ctx, writer); + /* Protect against more band_height == 0 calls */ + writer->line++; + } +} + +void fz_close_band_writer(fz_context *ctx, fz_band_writer *writer) +{ + if (writer == NULL) + return; + if (writer->close != NULL) + writer->close(ctx, writer); + writer->close = NULL; +} + +void fz_drop_band_writer(fz_context *ctx, fz_band_writer *writer) +{ + if (writer == NULL) + return; + if (writer->drop != NULL) + writer->drop(ctx, writer); + fz_drop_separations(ctx, writer->seps); + fz_free(ctx, writer); +} + +int fz_output_supports_stream(fz_context *ctx, fz_output *out) +{ + return out != NULL && out->as_stream != NULL; +} + +void fz_write_bits(fz_context *ctx, fz_output *out, unsigned int data, int num_bits) +{ + while (num_bits) + { + /* How many bits will be left in the current byte after we + * insert these bits? */ + int n = 8 - num_bits - out->buffered; + if (n >= 0) + { + /* We can fit our data in. */ + out->bits |= data << n; + out->buffered += num_bits; + num_bits = 0; + } + else + { + /* There are 8 - out->buffered bits left to be filled. We have + * num_bits to fill it with, which is more, so we need to throw + * away the bottom 'num_bits - (8 - out->buffered)' bits. That's + * num_bits + out->buffered - 8 = -(8 - num_bits - out_buffered) = -n */ + out->bits |= data >> -n; + data &= ~(out->bits << -n); + num_bits = -n; + out->buffered = 8; + } + if (out->buffered == 8) + { + fz_write_byte(ctx, out, out->bits); + out->buffered = 0; + out->bits = 0; + } + } + +} + +void fz_write_bits_sync(fz_context *ctx, fz_output *out) +{ + if (out->buffered == 0) + return; + fz_write_bits(ctx, out, 0, 8 - out->buffered); +} + +void +fz_write_stream(fz_context *ctx, fz_output *out, fz_stream *in) +{ + size_t z; + + while ((z = fz_available(ctx, in, 4096)) != 0) + { + fz_write_data(ctx, out, in->rp, z); + in->rp += z; + } +}
