691 lines
15 KiB
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);
|
||
|
}
|