eBookReaderSwitch/mupdf/source/fitz/output.c

691 lines
15 KiB
C

#define _LARGEFILE_SOURCE
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include "mupdf/fitz.h"
#include "fitz-imp.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
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_GENERIC, "cannot fwrite: %s", strerror(errno));
return;
}
n = fwrite(buffer, 1, count, file);
if (n < count && ferror(file))
fz_throw(ctx, FZ_ERROR_GENERIC, "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;
}
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_GENERIC, "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_GENERIC, "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);
}
/*
Create a new output object with the given
internal state and function pointers.
state: Internal state (opaque to everything but implementation).
write: Function to output a given buffer.
close: Cleanup function to destroy state when output closed.
May permissibly be null.
*/
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 = fz_malloc(ctx, bufsiz);
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)
{
}
/*
Open an output stream that writes to a
given path.
filename: The filename to write to (specified in UTF-8).
append: non-zero if we should append to the file, rather than
overwriting it.
*/
fz_output *
fz_new_output_with_path(fz_context *ctx, const char *filename, int append)
{
FILE *file;
fz_output *out;
if (!strcmp(filename, "/dev/null") || !fz_strcasecmp(filename, "nul:"))
return fz_new_output(ctx, 0, NULL, null_write, NULL, NULL);
#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_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
}
file = fz_fopen_utf8(filename, append ? "rb+" : "wb+");
#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_GENERIC, "cannot remove file '%s': %s", filename, strerror(errno));
}
file = fopen(filename, append ? "rb+" : "wb+");
#endif
if (!file)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", filename, strerror(errno));
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;
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_GENERIC, "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);
}
/*
Open an output stream that appends
to a buffer.
buf: The buffer to append to.
*/
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;
return out;
}
/*
Flush pending output and close an output stream.
*/
void
fz_close_output(fz_context *ctx, fz_output *out)
{
if (out == NULL)
return;
fz_flush_output(ctx, out);
if (out->close)
out->close(ctx, out->state);
out->close = NULL;
}
/*
Free an output stream. Don't forget to close it first!
*/
void
fz_drop_output(fz_context *ctx, fz_output *out)
{
if (out)
{
if (out->close)
fz_warn(ctx, "dropping unclosed output");
if (out->drop)
out->drop(ctx, out->state);
fz_free(ctx, out->bp);
if (out != &fz_stdout_global)
fz_free(ctx, out);
}
}
/*
Seek to the specified file position.
See fseek for arguments.
Throw an error on unseekable outputs.
*/
void
fz_seek_output(fz_context *ctx, fz_output *out, int64_t off, int whence)
{
if (out->seek == NULL)
fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot seek in unseekable output stream\n");
fz_flush_output(ctx, out);
out->seek(ctx, out->state, off, whence);
}
/*
Return the current file position.
Throw an error on untellable outputs.
*/
int64_t
fz_tell_output(fz_context *ctx, fz_output *out)
{
if (out->tell == NULL)
fz_throw(ctx, FZ_ERROR_GENERIC, "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);
}
/*
obtain the fz_output in the form of a fz_stream
This allows data to be read back from some forms of fz_output object.
When finished reading, the fz_stream should be released by calling
fz_drop_stream. Until the fz_stream is dropped, no further operations
should be performed on the fz_output object.
*/
fz_stream *
fz_stream_from_output(fz_context *ctx, fz_output *out)
{
if (out->as_stream == NULL)
fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot derive input stream from output stream");
fz_flush_output(ctx, out);
return out->as_stream(ctx, out->state);
}
static void
fz_write_emit(fz_context *ctx, void *out, int c)
{
fz_write_byte(ctx, out, c);
}
/*
va_list version of fz_write_printf.
*/
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);
}
/*
Format and write data to an output stream.
See fz_format_string for formatting details.
*/
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);
}
/*
Flush unwritten data.
*/
void
fz_flush_output(fz_context *ctx, fz_output *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);
}
/*
Write data to output.
data: Pointer to data to write.
size: Size of data to write in bytes.
*/
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);
}
}
/*
Write a string. Does not write zero terminator.
*/
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);
}
/*
Write a UTF-8 encoded unicode character.
*/
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, int size, int newline)
{
static const char set[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int 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_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;
}
/*
Cause a band writer to write the header for
a banded image with the given properties/dimensions etc. This
also configures the bandwriter for the format of the data to be
passed in future calls.
w, h: Width and Height of the entire page.
n: Number of components (including spots and alphas).
alpha: Number of alpha components.
xres, yres: X and Y resolutions in dpi.
cs: Colorspace (NULL for bitmaps)
seps: Separation details (or NULL).
*/
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;
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);
}
/*
Cause a band writer to write the next band
of data for an image.
stride: The byte offset from the first byte of the data
for a pixel to the first byte of the data for the same pixel
on the row below.
band_height: The number of lines in this band.
samples: Pointer to first byte of the data.
*/
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_GENERIC, "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_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);
}