From f5f7c0e4dd83257f526b158e3998970717852a0e Mon Sep 17 00:00:00 2001 From: Robin Watts Date: Wed, 17 Jul 2013 18:42:33 +0100 Subject: Initial work on progressive loading We are testing this using a new -p flag to mupdf that sets a bitrate at which data will appear to arrive progressively as time goes on. For example: mupdf -p 102400 pdf_reference17.pdf Details of the scheme used here are presented in docs/progressive.txt --- source/fitz/error.c | 7 ++ source/fitz/filter-dct.c | 2 +- source/fitz/image.c | 2 +- source/fitz/stream-prog.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++ source/fitz/stream-read.c | 16 +++- 5 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 source/fitz/stream-prog.c (limited to 'source/fitz') diff --git a/source/fitz/error.c b/source/fitz/error.c index fb0dc77e..0da7d29e 100644 --- a/source/fitz/error.c +++ b/source/fitz/error.c @@ -180,3 +180,10 @@ void fz_rethrow_message(fz_context *ctx, const char *fmt, ...) throw(ctx->error); } + +void fz_rethrow_if(fz_context *ctx, int err) +{ + assert(ctx && ctx->error && ctx->error->errcode >= FZ_ERROR_NONE); + if (ctx->error->errcode == err) + fz_rethrow(ctx); +} diff --git a/source/fitz/filter-dct.c b/source/fitz/filter-dct.c index 1a55e584..189d2aae 100644 --- a/source/fitz/filter-dct.c +++ b/source/fitz/filter-dct.c @@ -53,7 +53,7 @@ static boolean fill_input_buffer(j_decompress_ptr cinfo) } fz_catch(ctx) { - /* FIXME: TRYLATER */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); return 0; } src->next_input_byte = chain->rp; diff --git a/source/fitz/image.c b/source/fitz/image.c index 62561762..0aeb449d 100644 --- a/source/fitz/image.c +++ b/source/fitz/image.c @@ -157,7 +157,7 @@ fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, in } fz_catch(ctx) { - /* FIXME: TryLater? */ + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignoring error at end of image"); } } diff --git a/source/fitz/stream-prog.c b/source/fitz/stream-prog.c new file mode 100644 index 00000000..e35a1ac5 --- /dev/null +++ b/source/fitz/stream-prog.c @@ -0,0 +1,181 @@ +#include "mupdf/fitz/stream.h" +#include "mupdf/fitz/string.h" + +#if defined(_WIN32) && !defined(NDEBUG) +#include "windows.h" + +static void +show_progress(int av, int pos) +{ + char text[80]; + sprintf(text, "Have %d, Want %d\n", av, pos); + OutputDebugStringA(text); +} +#else +#define show_progress(A,B) do {} while (0) +#endif + +/* File stream - progressive reading to simulate http download */ + +typedef struct prog_state +{ + int fd; + int length; + int available; + int bps; + clock_t start_time; +} prog_state; + +static int read_prog(fz_stream *stm, unsigned char *buf, int len) +{ + prog_state *ps = (prog_state *)stm->state; + int n; + + /* Simulate more data having arrived */ + if (ps->available < ps->length) + { + int av = (int)((float)(clock() - ps->start_time) * ps->bps / (CLOCKS_PER_SEC*8)); + if (av > ps->length) + av = ps->length; + ps->available = av; + /* Limit any fetches to be within the data we have */ + if (av < ps->length && len + stm->pos > av) + { + len = av - stm->pos; + if (len <= 0) + { + show_progress(av, stm->pos); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data yet"); + } + } + } + + n = (len > 0 ? read(ps->fd, buf, len) : 0); + if (n < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno)); + return n; +} + +static void seek_prog(fz_stream *stm, int offset, int whence) +{ + prog_state *ps = (prog_state *)stm->state; + int n; + + /* Simulate more data having arrived */ + if (ps->available < ps->length) + { + int av = (int)((float)(clock() - ps->start_time) * ps->bps / (CLOCKS_PER_SEC*8)); + if (av > ps->length) + av = ps->length; + ps->available = av; + } + if (ps->available < ps->length) + { + if (whence == SEEK_END) + { + show_progress(ps->available, ps->length); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek to end yet"); + } + } + if (whence == SEEK_CUR) + { + whence = SEEK_SET; + offset += stm->pos; + if (offset > ps->available) + { + show_progress(ps->available, offset); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek (relatively) to offset yet"); + } + } + if (whence == SEEK_SET) + { + if (offset > ps->available) + { + show_progress(ps->available, offset); + fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek to offset yet"); + } + } + + n = lseek(ps->fd, offset, whence); + if (n < 0) + fz_throw(stm->ctx, FZ_ERROR_GENERIC, "cannot lseek: %s", strerror(errno)); + stm->pos = n; + stm->rp = stm->bp; + stm->wp = stm->bp; +} + +static void close_prog(fz_context *ctx, void *state) +{ + prog_state *ps = (prog_state *)state; + int n = close(ps->fd); + if (n < 0) + fz_warn(ctx, "close error: %s", strerror(errno)); + fz_free(ctx, state); +} + +static int meta_prog(fz_stream *stm, int key, int size, void *ptr) +{ + prog_state *ps = (prog_state *)stm->state; + switch(key) + { + case FZ_STREAM_META_PROGRESSIVE: + return 1; + break; + case FZ_STREAM_META_LENGTH: + return ps->length; + } + return -1; +} + +fz_stream * +fz_open_fd_progressive(fz_context *ctx, int fd, int bps) +{ + fz_stream *stm; + prog_state *state; + + state = fz_malloc_struct(ctx, prog_state); + state->fd = fd; + state->bps = bps; + state->start_time = clock(); + state->available = 0; + + state->length = lseek(state->fd, 0, SEEK_END); + lseek(state->fd, 0, SEEK_SET); + + fz_try(ctx) + { + stm = fz_new_stream(ctx, state, read_prog, close_prog); + } + fz_catch(ctx) + { + fz_free(ctx, state); + fz_rethrow(ctx); + } + stm->seek = seek_prog; + stm->meta = meta_prog; + + return stm; +} + +fz_stream * +fz_open_file_progressive(fz_context *ctx, const char *name, int bps) +{ +#ifdef _WIN32 + char *s = (char*)name; + wchar_t *wname, *d; + int c, fd; + d = wname = fz_malloc(ctx, (strlen(name)+1) * sizeof(wchar_t)); + while (*s) { + s += fz_chartorune(&c, s); + *d++ = c; + } + *d = 0; + fd = _wopen(wname, O_BINARY | O_RDONLY, 0); + fz_free(ctx, wname); +#else + int fd = open(name, O_BINARY | O_RDONLY, 0); +#endif + if (fd == -1) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s", name); + return fz_open_fd_progressive(ctx, fd, bps); +} diff --git a/source/fitz/stream-read.c b/source/fitz/stream-read.c index ee3d1cad..d433ce32 100644 --- a/source/fitz/stream-read.c +++ b/source/fitz/stream-read.c @@ -84,7 +84,7 @@ fz_fill_buffer(fz_stream *stm) } fz_catch(stm->ctx) { - /* FIXME: TryLater */ + fz_rethrow_if(stm->ctx, FZ_ERROR_TRYLATER); fz_warn(stm->ctx, "read error; treating as end of file"); stm->error = 1; } @@ -134,7 +134,11 @@ fz_read_best(fz_stream *stm, int initial, int *truncated) } fz_catch(ctx) { - /* FIXME: TryLater */ + if (fz_caught(ctx) == FZ_ERROR_TRYLATER) + { + fz_drop_buffer(ctx, buf); + fz_rethrow(ctx); + } if (truncated) { *truncated = 1; @@ -184,6 +188,7 @@ fz_tell(fz_stream *stm) void fz_seek(fz_stream *stm, int offset, int whence) { + stm->avail = 0; /* Reset bit reading */ if (stm->seek) { if (whence == 1) @@ -217,3 +222,10 @@ fz_seek(fz_stream *stm, int offset, int whence) else fz_warn(stm->ctx, "cannot seek"); } + +int fz_stream_meta(fz_stream *stm, int key, int size, void *ptr) +{ + if (!stm || !stm->meta) + return -1; + return stm->meta(stm, key, size, ptr); +} -- cgit v1.2.3