summaryrefslogtreecommitdiff
path: root/source/xps/xps-zip.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/xps/xps-zip.c')
-rw-r--r--source/xps/xps-zip.c608
1 files changed, 68 insertions, 540 deletions
diff --git a/source/xps/xps-zip.c b/source/xps/xps-zip.c
index 0e00e918..e3d006ba 100644
--- a/source/xps/xps-zip.c
+++ b/source/xps/xps-zip.c
@@ -1,30 +1,26 @@
#include "mupdf/xps.h"
-#include <zlib.h>
-
-#define ZIP_LOCAL_FILE_SIG 0x04034b50
-#define ZIP_DATA_DESC_SIG 0x08074b50
-#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50
-#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50
-
-#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50
-#define ZIP64_END_OF_CENTRAL_DIRECTORY_SIG 0x06064b50
-#define ZIP64_EXTRA_FIELD_SIG 0x0001
-
-#define ZIP_ENCRYPTED_FLAG 0x1
-
static void xps_init_document(xps_document *doc);
xps_part *
-xps_new_part(xps_document *doc, char *name, int size)
+xps_new_part(xps_document *doc, char *name, unsigned char *data, int size)
{
xps_part *part;
- part = fz_malloc(doc->ctx, sizeof(xps_part));
- part->name = fz_strdup(doc->ctx, name);
- part->size = size;
- part->data = fz_malloc(doc->ctx, size + 1);
- part->data[size] = 0; /* null-terminate for xml parser */
+ part = fz_malloc_struct(doc->ctx, xps_part);
+ fz_try(doc->ctx)
+ {
+ part->name = fz_strdup(doc->ctx, name);
+ part->data = data;
+ part->size = size;
+ }
+ fz_catch(doc->ctx)
+ {
+ fz_free(doc->ctx, part->name);
+ fz_free(doc->ctx, part->data);
+ fz_free(doc->ctx, part);
+ fz_rethrow(doc->ctx);
+ }
return part;
}
@@ -37,552 +33,91 @@ xps_free_part(xps_document *doc, xps_part *part)
fz_free(doc->ctx, part);
}
-static inline int getshort(fz_stream *file)
-{
- int a = fz_read_byte(file);
- int b = fz_read_byte(file);
- return a | b << 8;
-}
-
-static inline int getlong(fz_stream *file)
-{
- int a = fz_read_byte(file);
- int b = fz_read_byte(file);
- int c = fz_read_byte(file);
- int d = fz_read_byte(file);
- return a | b << 8 | c << 16 | d << 24;
-}
-
-static inline int getlong64(fz_stream *file)
-{
- int a = getlong(file);
- int b = getlong(file);
- return b != 0 ? -1 : a;
-}
-
-static void *
-xps_zip_alloc_items(xps_document *doc, int items, int size)
-{
- return fz_malloc_array(doc->ctx, items, size);
-}
-
-static void
-xps_zip_free(xps_document *doc, void *ptr)
-{
- fz_free(doc->ctx, ptr);
-}
-
-static int
-xps_compare_entries(const void *a0, const void *b0)
-{
- xps_entry *a = (xps_entry*) a0;
- xps_entry *b = (xps_entry*) b0;
- return xps_strcasecmp(a->name, b->name);
-}
-
-static xps_entry *
-xps_lookup_zip_entry(xps_document *doc, char *name)
-{
- int l = 0;
- int r = doc->zip_count - 1;
- while (l <= r)
- {
- int m = (l + r) >> 1;
- int c = xps_strcasecmp(name, doc->zip_table[m].name);
- if (c < 0)
- r = m - 1;
- else if (c > 0)
- l = m + 1;
- else
- return &doc->zip_table[m];
- }
- return NULL;
-}
-
-static void
-xps_read_zip_entry(xps_document *doc, xps_entry *ent, unsigned char *outbuf)
-{
- z_stream stream;
- unsigned char *inbuf;
- int sig;
- int general;
- int method;
- int namelength, extralength;
- int code;
- fz_context *ctx = doc->ctx;
-
- fz_seek(doc->file, ent->offset, 0);
-
- sig = getlong(doc->file);
- if (sig != ZIP_LOCAL_FILE_SIG)
- {
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip local file signature (0x%x)", sig);
- }
-
- (void) getshort(doc->file); /* version */
- general = getshort(doc->file); /* general */
- if (general & ZIP_ENCRYPTED_FLAG)
- {
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zipfile content is encrypted");
- }
-
- method = getshort(doc->file);
- (void) getshort(doc->file); /* file time */
- (void) getshort(doc->file); /* file date */
- (void) getlong(doc->file); /* crc-32 */
- (void) getlong(doc->file); /* csize */
- (void) getlong(doc->file); /* usize */
- namelength = getshort(doc->file);
- extralength = getshort(doc->file);
-
- fz_seek(doc->file, namelength + extralength, 1);
-
- if (method == 0)
- {
- fz_read(doc->file, outbuf, ent->usize);
- }
- else if (method == 8)
- {
- inbuf = fz_malloc(ctx, ent->csize);
-
- fz_read(doc->file, inbuf, ent->csize);
-
- memset(&stream, 0, sizeof(z_stream));
- stream.zalloc = (alloc_func) xps_zip_alloc_items;
- stream.zfree = (free_func) xps_zip_free;
- stream.opaque = doc;
- stream.next_in = inbuf;
- stream.avail_in = ent->csize;
- stream.next_out = outbuf;
- stream.avail_out = ent->usize;
-
- code = inflateInit2(&stream, -15);
- if (code != Z_OK)
- {
- fz_free(ctx, inbuf);
- fz_throw(ctx, FZ_ERROR_GENERIC, "zlib inflateInit2 error: %s", stream.msg);
- }
- code = inflate(&stream, Z_FINISH);
- if (code != Z_STREAM_END)
- {
- inflateEnd(&stream);
- fz_free(ctx, inbuf);
- fz_throw(ctx, FZ_ERROR_GENERIC, "zlib inflate error: %s", stream.msg);
- }
- code = inflateEnd(&stream);
- if (code != Z_OK)
- {
- fz_free(ctx, inbuf);
- fz_throw(ctx, FZ_ERROR_GENERIC, "zlib inflateEnd error: %s", stream.msg);
- }
-
- fz_free(ctx, inbuf);
-
- if (stream.avail_out > 0)
- {
- fz_warn(ctx, "Truncated zipfile entry found, possibly corrupt data");
- memset(stream.next_out, 0, stream.avail_out);
- }
- }
- else
- {
- fz_throw(ctx, FZ_ERROR_GENERIC, "unknown compression method (%d)", method);
- }
-}
-
-/*
- * Read the central directory in a zip file.
- */
-
-static void
-xps_read_zip_dir(xps_document *doc, int start_offset)
-{
- int sig;
- int offset, count;
- int namesize, metasize, commentsize;
- int i;
-
- fz_seek(doc->file, start_offset, 0);
-
- sig = getlong(doc->file);
- if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip end of central directory signature (0x%x)", sig);
-
- (void) getshort(doc->file); /* this disk */
- (void) getshort(doc->file); /* start disk */
- (void) getshort(doc->file); /* entries in this disk */
- count = getshort(doc->file); /* entries in central directory disk */
- (void) getlong(doc->file); /* size of central directory */
- offset = getlong(doc->file); /* offset to central directory */
-
- /* ZIP64 */
- if (count == 0xFFFF || offset == 0xFFFFFFFF)
- {
- int offset64, count64;
-
- fz_seek(doc->file, start_offset - 20, 0);
-
- sig = getlong(doc->file);
- if (sig != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip64 end of central directory locator signature (0x%x)", sig);
-
- (void) getlong(doc->file); /* start disk */
- offset64 = getlong64(doc->file); /* offset to end of central directory record */
- if (offset64 < 0)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB aren't supported");
-
- fz_seek(doc->file, offset64, 0);
-
- sig = getlong(doc->file);
- if (sig != ZIP64_END_OF_CENTRAL_DIRECTORY_SIG)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip64 end of central directory signature (0x%x)", sig);
-
- (void) getlong64(doc->file); /* size of record */
- (void) getshort(doc->file); /* version made by */
- (void) getshort(doc->file); /* version to extract */
- (void) getlong(doc->file); /* disk number */
- (void) getlong(doc->file); /* disk number start */
- count64 = getlong64(doc->file); /* entries in central directory disk */
- (void) getlong64(doc->file); /* entries in central directory */
- (void) getlong64(doc->file); /* size of central directory */
- offset64 = getlong64(doc->file); /* offset to central directory */
-
- if (count == 0xFFFF)
- count = count64;
- if (offset == 0xFFFFFFFF)
- offset = offset64;
- if (count < 0 || offset < 0)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB aren't supported");
- }
-
- doc->zip_table = fz_malloc_array(doc->ctx, count, sizeof(xps_entry));
- memset(doc->zip_table, 0, count * sizeof(xps_entry));
- doc->zip_count = count;
-
- fz_seek(doc->file, offset, 0);
-
- for (i = 0; i < count; i++)
- {
- sig = getlong(doc->file);
- if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "wrong zip central directory signature (0x%x)", sig);
-
- (void) getshort(doc->file); /* version made by */
- (void) getshort(doc->file); /* version to extract */
- (void) getshort(doc->file); /* general */
- (void) getshort(doc->file); /* method */
- (void) getshort(doc->file); /* last mod file time */
- (void) getshort(doc->file); /* last mod file date */
- (void) getlong(doc->file); /* crc-32 */
- doc->zip_table[i].csize = getlong(doc->file);
- doc->zip_table[i].usize = getlong(doc->file);
- namesize = getshort(doc->file);
- metasize = getshort(doc->file);
- commentsize = getshort(doc->file);
- (void) getshort(doc->file); /* disk number start */
- (void) getshort(doc->file); /* int file atts */
- (void) getlong(doc->file); /* ext file atts */
- doc->zip_table[i].offset = getlong(doc->file);
-
- doc->zip_table[i].name = fz_malloc(doc->ctx, namesize + 1);
- fz_read(doc->file, (unsigned char*)doc->zip_table[i].name, namesize);
- doc->zip_table[i].name[namesize] = 0;
-
- while (metasize > 0)
- {
- int type = getshort(doc->file);
- int size = getshort(doc->file);
- if (type == ZIP64_EXTRA_FIELD_SIG)
- {
- int sizeleft = size;
- if (doc->zip_table[i].usize == 0xFFFFFFFF && sizeleft >= 8)
- {
- doc->zip_table[i].usize = getlong64(doc->file);
- sizeleft -= 8;
- }
- if (doc->zip_table[i].csize == 0xFFFFFFFF && sizeleft >= 8)
- {
- doc->zip_table[i].csize = getlong64(doc->file);
- sizeleft -= 8;
- }
- if (doc->zip_table[i].offset == 0xFFFFFFFF && sizeleft >= 8)
- {
- doc->zip_table[i].offset = getlong64(doc->file);
- sizeleft -= 8;
- }
- fz_seek(doc->file, sizeleft - size, 1);
- }
- fz_seek(doc->file, size, 1);
- metasize -= 4 + size;
- }
- if (doc->zip_table[i].usize < 0 || doc->zip_table[i].csize < 0 || doc->zip_table[i].offset < 0)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "zip64 files larger than 2 GB aren't supported");
-
- fz_seek(doc->file, commentsize, 1);
- }
-
- qsort(doc->zip_table, count, sizeof(xps_entry), xps_compare_entries);
-}
-
-static void
-xps_find_and_read_zip_dir(xps_document *doc)
-{
- unsigned char buf[512];
- int file_size, back, maxback;
- int i, n;
- fz_context *ctx = doc->ctx;
-
- fz_seek(doc->file, 0, SEEK_END);
- file_size = fz_tell(doc->file);
-
- maxback = fz_mini(file_size, 0xFFFF + sizeof buf);
- back = fz_mini(maxback, sizeof buf);
-
- while (back < maxback)
- {
- fz_seek(doc->file, file_size - back, 0);
- n = fz_read(doc->file, buf, sizeof buf);
- for (i = n - 4; i > 0; i--)
- {
- if (!memcmp(buf + i, "PK\5\6", 4))
- {
- xps_read_zip_dir(doc, file_size - back + i);
- return;
- }
- }
-
- back += sizeof buf - 4;
- }
-
- fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find end of central directory");
-}
-
/*
* Read and interleave split parts from a ZIP file.
*/
-static xps_part *
-xps_read_zip_part(xps_document *doc, char *partname)
+xps_part *
+xps_read_part(xps_document *doc, char *partname)
{
- char buf[2048];
- xps_entry *ent;
- xps_part *part;
- int count, size, offset, i;
+ fz_context *ctx = doc->ctx;
+ fz_archive *zip = doc->zip;
+ fz_buffer *buf, *tmp;
+ char path[2048];
+ unsigned char *data;
+ int size;
+ int count;
char *name;
- int seen_last = 0;
+ int seen_last;
name = partname;
if (name[0] == '/')
name ++;
/* All in one piece */
- ent = xps_lookup_zip_entry(doc, name);
- if (ent)
- {
- part = xps_new_part(doc, partname, ent->usize);
- fz_try(doc->ctx)
- {
- xps_read_zip_entry(doc, ent, part->data);
- }
- fz_catch(doc->ctx)
- {
- xps_free_part(doc, part);
- fz_rethrow(doc->ctx);
- }
- return part;
- }
-
- /* Count the number of pieces and their total size */
- count = 0;
- size = 0;
- while (!seen_last)
+ if (fz_has_archive_entry(ctx, zip, name))
{
- sprintf(buf, "%s/[%d].piece", name, count);
- ent = xps_lookup_zip_entry(doc, buf);
- if (!ent)
- {
- sprintf(buf, "%s/[%d].last.piece", name, count);
- ent = xps_lookup_zip_entry(doc, buf);
- seen_last = (ent != NULL);
- }
- if (!ent)
- break;
- count ++;
- size += ent->usize;
+ buf = fz_read_archive_entry(ctx, zip, name);
}
- if (!seen_last)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find all pieces for part '%s'", partname);
- /* Inflate the pieces */
- if (count)
+ /* Assemble all the pieces */
+ else
{
- part = xps_new_part(doc, partname, size);
- offset = 0;
- for (i = 0; i < count; i++)
+ buf = fz_new_buffer(ctx, 512);
+ seen_last = 0;
+ for (count = 0; !seen_last; ++count)
{
- if (i < count - 1)
- sprintf(buf, "%s/[%d].piece", name, i);
- else
- sprintf(buf, "%s/[%d].last.piece", name, i);
- ent = xps_lookup_zip_entry(doc, buf);
- fz_try(doc->ctx)
+ sprintf(path, "%s/[%d].piece", name, count);
+ if (fz_has_archive_entry(ctx, zip, path))
{
- xps_read_zip_entry(doc, ent, part->data + offset);
+ tmp = fz_read_archive_entry(ctx, zip, path);
+ fz_buffer_cat(ctx, buf, tmp);
+ fz_drop_buffer(ctx, tmp);
}
- fz_catch(doc->ctx)
- {
- xps_free_part(doc, part);
- fz_rethrow(doc->ctx);
- }
- offset += ent->usize;
- }
- return part;
- }
-
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find part '%s'", partname);
-}
-
-static int
-xps_has_zip_part(xps_document *doc, char *name)
-{
- char buf[2048];
- if (name[0] == '/')
- name++;
- if (xps_lookup_zip_entry(doc, name))
- return 1;
- sprintf(buf, "%s/[0].piece", name);
- if (xps_lookup_zip_entry(doc, buf))
- return 1;
- sprintf(buf, "%s/[0].last.piece", name);
- if (xps_lookup_zip_entry(doc, buf))
- return 1;
- return 0;
-}
-
-/*
- * Read and interleave split parts from files in the directory.
- */
-static xps_part *
-xps_read_dir_part(xps_document *doc, char *name)
-{
- char buf[2048];
- xps_part *part;
- FILE *file;
- int count, size, offset, i, n;
- int seen_last = 0;
-
- fz_strlcpy(buf, doc->directory, sizeof buf);
- fz_strlcat(buf, name, sizeof buf);
-
- /* All in one piece */
- file = fopen(buf, "rb");
- if (file)
- {
- fseek(file, 0, SEEK_END);
- size = ftell(file);
- fseek(file, 0, SEEK_SET);
- part = xps_new_part(doc, name, size);
- fread(part->data, 1, size, file);
- fclose(file);
- return part;
- }
-
- /* Count the number of pieces and their total size */
- count = 0;
- size = 0;
- while (!seen_last)
- {
- sprintf(buf, "%s%s/[%d].piece", doc->directory, name, count);
- file = fopen(buf, "rb");
- if (!file)
- {
- sprintf(buf, "%s%s/[%d].last.piece", doc->directory, name, count);
- file = fopen(buf, "rb");
- seen_last = (file != NULL);
- }
- if (!file)
- break;
- count ++;
- fseek(file, 0, SEEK_END);
- size += ftell(file);
- fclose(file);
- }
- if (!seen_last)
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find all pieces for part '%s'", name);
-
- /* Inflate the pieces */
- if (count)
- {
- part = xps_new_part(doc, name, size);
- offset = 0;
- for (i = 0; i < count; i++)
- {
- if (i < count - 1)
- sprintf(buf, "%s%s/[%d].piece", doc->directory, name, i);
else
- sprintf(buf, "%s%s/[%d].last.piece", doc->directory, name, i);
- file = fopen(buf, "rb");
- if (!file)
{
- xps_free_part(doc, part);
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot open file '%s'", buf);
+ sprintf(path, "%s/[%d].last.piece", name, count);
+ if (fz_has_archive_entry(ctx, zip, path))
+ {
+ tmp = fz_read_archive_entry(ctx, zip, path);
+ fz_buffer_cat(ctx, buf, tmp);
+ fz_drop_buffer(ctx, tmp);
+ seen_last = 1;
+ }
+ else
+ {
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find all pieces for part '%s'", partname);
+ }
}
- n = fread(part->data + offset, 1, size - offset, file);
- offset += n;
- fclose(file);
}
- return part;
}
- fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find part '%s'", name);
-}
+ fz_write_buffer_byte(ctx, buf, 0); /* zero-terminate */
-static int
-file_exists(xps_document *doc, char *name)
-{
- char buf[2048];
- FILE *file;
- fz_strlcpy(buf, doc->directory, sizeof buf);
- fz_strlcat(buf, name, sizeof buf);
- file = fopen(buf, "rb");
- if (file)
- {
- fclose(file);
- return 1;
- }
- return 0;
+ /* take over the data */
+ data = buf->data;
+ size = buf->len;
+ fz_free(ctx, buf);
+
+ return xps_new_part(doc, partname, data, size);
}
-static int
-xps_has_dir_part(xps_document *doc, char *name)
+int
+xps_has_part(xps_document *doc, char *name)
{
char buf[2048];
- if (file_exists(doc, name))
+ if (name[0] == '/')
+ name++;
+ if (fz_has_archive_entry(doc->ctx, doc->zip, name))
return 1;
sprintf(buf, "%s/[0].piece", name);
- if (file_exists(doc, buf))
+ if (fz_has_archive_entry(doc->ctx, doc->zip, buf))
return 1;
sprintf(buf, "%s/[0].last.piece", name);
- if (file_exists(doc, buf))
+ if (fz_has_archive_entry(doc->ctx, doc->zip, buf))
return 1;
return 0;
}
-xps_part *
-xps_read_part(xps_document *doc, char *partname)
-{
- if (doc->directory)
- return xps_read_dir_part(doc, partname);
- return xps_read_zip_part(doc, partname);
-}
-
-int
-xps_has_part(xps_document *doc, char *partname)
-{
- if (doc->directory)
- return xps_has_dir_part(doc, partname);
- return xps_has_zip_part(doc, partname);
-}
-
static xps_document *
xps_open_document_with_directory(fz_context *ctx, const char *directory)
{
@@ -591,7 +126,7 @@ xps_open_document_with_directory(fz_context *ctx, const char *directory)
doc = fz_malloc_struct(ctx, xps_document);
xps_init_document(doc);
doc->ctx = ctx;
- doc->directory = fz_strdup(ctx, directory);
+ doc->zip = fz_open_directory(ctx, directory);
fz_try(ctx)
{
@@ -614,11 +149,10 @@ xps_open_document_with_stream(fz_context *ctx, fz_stream *file)
doc = fz_malloc_struct(ctx, xps_document);
xps_init_document(doc);
doc->ctx = ctx;
- doc->file = fz_keep_stream(file);
+ doc->zip = fz_open_archive_with_stream(ctx, file);
fz_try(ctx)
{
- xps_find_and_read_zip_dir(doc);
xps_read_page_list(doc);
}
fz_catch(ctx)
@@ -671,17 +205,12 @@ void
xps_close_document(xps_document *doc)
{
xps_font_cache *font, *next;
- int i;
if (!doc)
return;
- if (doc->file)
- fz_close(doc->file);
-
- for (i = 0; i < doc->zip_count; i++)
- fz_free(doc->ctx, doc->zip_table[i].name);
- fz_free(doc->ctx, doc->zip_table);
+ if (doc->zip)
+ fz_close_archive(doc->ctx, doc->zip);
font = doc->font_table;
while (font)
@@ -696,7 +225,6 @@ xps_close_document(xps_document *doc)
xps_free_page_list(doc);
fz_free(doc->ctx, doc->start_part);
- fz_free(doc->ctx, doc->directory);
fz_free(doc->ctx, doc);
}
@@ -717,7 +245,7 @@ static void
xps_rebind(xps_document *doc, fz_context *ctx)
{
doc->ctx = ctx;
- fz_rebind_stream(doc->file, ctx);
+ fz_rebind_archive(doc->zip, ctx);
fz_rebind_device(doc->dev, ctx);
}