summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ghostscript.com>2005-05-20 14:57:18 +0200
committerTor Andersson <tor@ghostscript.com>2005-05-20 14:57:18 +0200
commit4214d9ea3d5d219f1e6e3f3ade5a5b1dee794159 (patch)
tree0d542f9886435e9dcf599644c566b86b62e5e1e9
parent4c68e8b9d7565eecdda712ddf1b64d30dfe2722a (diff)
downloadmupdf-4214d9ea3d5d219f1e6e3f3ade5a5b1dee794159.tar.xz
zip archive reading
-rw-r--r--Jamfile13
-rw-r--r--Jamrules2
-rw-r--r--TODO11
-rw-r--r--include/samus.h9
-rw-r--r--samus/sa_zip.c463
-rw-r--r--stream/filt_flate.c56
6 files changed, 540 insertions, 14 deletions
diff --git a/Jamfile b/Jamfile
index 2b480bd6..7156b95d 100644
--- a/Jamfile
+++ b/Jamfile
@@ -264,6 +264,19 @@ Library libmupdf :
;
# --------------------------------------------------------------------------
+
+SubDir TOP samus ;
+
+Library libsamus :
+ # sa_zip.c
+ # sa_xml.c
+
+ ;
+
+Main foo : sa_zip.c ;
+LinkLibraries foo : $(FITZLIBS) ;
+
+# --------------------------------------------------------------------------
#
# Build simple portable apps
#
diff --git a/Jamrules b/Jamrules
index 2ada18c9..f363e56a 100644
--- a/Jamrules
+++ b/Jamrules
@@ -8,7 +8,7 @@ OPTIM = ; # disable jam's built-in optimization flags
if $(OS) = MINGW
{
FREETYPECC ?= -Ih:/package/include ;
- FREETYPELD ?= -lfreetype ;
+ FREETYPELD ?= -Lh:/package/lib -lfreetype ;
}
FREETYPECC ?= "`freetype-config --cflags`" ;
diff --git a/TODO b/TODO
index a9e7e23c..c8cd318a 100644
--- a/TODO
+++ b/TODO
@@ -2,8 +2,8 @@ lazy nametree
lazy pagetree
cmap one-to-many mapping
-builtin standard cmap files
-put unicode strings in text object, not font
+builtin standard cmap files (?)
+put unicode strings in text object, not font (a la metro)
xml parser
unicode normaliser
@@ -18,6 +18,13 @@ public / private api
top-level "driver" architecture (metro/pdf/whatever input)
+fix the shading code:
+ 3 levels of detail patch mesh (quad, patch, tensor)
+ subdivide to triangles on the fly
+ draw tris as before
+ reuse more code in the parsing
+ error cleanup
+
---
immediate plan:
diff --git a/include/samus.h b/include/samus.h
new file mode 100644
index 00000000..b1514959
--- /dev/null
+++ b/include/samus.h
@@ -0,0 +1,9 @@
+#ifdef _SAMUS_H_
+#error "samus.h must only be included once"
+#endif
+#define _SAMUS_H_
+
+#ifndef _FITZ_H_
+#error "fitz.h must be included before samus.h"
+#endif
+
diff --git a/samus/sa_zip.c b/samus/sa_zip.c
new file mode 100644
index 00000000..a94f82e0
--- /dev/null
+++ b/samus/sa_zip.c
@@ -0,0 +1,463 @@
+/*
+ * Support for a subset of PKZIP format v4.5:
+ * - no encryption
+ * - no multi-disk
+ * - only Store and Deflate
+ * - ZIP64 format (long long sizes and offsets) [TODO]
+ */
+
+#include "fitz.h"
+#include "samus.h"
+
+typedef struct sa_zip_s sa_zip;
+typedef struct sa_zipent_s sa_zipent;
+
+struct sa_zipent_s
+{
+ unsigned offset;
+ char *name;
+ unsigned csize;
+ unsigned usize;
+};
+
+struct sa_zip_s
+{
+ fz_file *file;
+ int len, cap;
+ sa_zipent *table;
+};
+
+typedef unsigned char byte;
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+
+static inline ushort read2(fz_file *f)
+{
+ byte a = fz_readbyte(f);
+ byte b = fz_readbyte(f);
+ return (b << 8) | a;
+}
+
+static inline ulong read4(fz_file *f)
+{
+ byte a = fz_readbyte(f);
+ byte b = fz_readbyte(f);
+ byte c = fz_readbyte(f);
+ byte d = fz_readbyte(f);
+ return (d << 24) | (c << 16) | (b << 8) | a;
+}
+
+static fz_error *growzip(sa_zip *zip)
+{
+ sa_zipent *newtab;
+ int newcap;
+
+ if (zip->cap)
+ newcap = zip->cap * 2;
+ else
+ newcap = 100;
+
+ newtab = fz_realloc(zip->table, newcap * sizeof(sa_zipent));
+ if (!newtab)
+ return fz_outofmem;
+
+ memset(newtab + zip->cap, 0, (newcap - zip->cap) * sizeof(sa_zipent));
+ zip->cap = newcap;
+ zip->table = newtab;
+
+ return nil;
+}
+
+static fz_error *scanzipent(sa_zip *zip, sa_zipent *ent)
+{
+ ulong csize, usize;
+ ulong namesize, metasize;
+
+ (void) read2(zip->file); /* version */
+ (void) read2(zip->file); /* general */
+ (void) read2(zip->file); /* method */
+ (void) read2(zip->file); /* time */
+ (void) read2(zip->file); /* date */
+ (void) read4(zip->file); /* crc-32 */
+ csize = read4(zip->file);
+ usize = read4(zip->file);
+ namesize = read2(zip->file);
+ metasize = read2(zip->file);
+
+ ent->name = fz_malloc(namesize + 1);
+ if (!ent->name)
+ return fz_outofmem;
+
+ fz_read(zip->file, ent->name, namesize);
+ ent->name[namesize] = 0;
+ ent->csize = csize;
+ ent->usize = usize;
+
+ fz_seek(zip->file, metasize, 1);
+ fz_seek(zip->file, csize, 1);
+
+ return nil;
+}
+
+static fz_error *scanzip(sa_zip *zip)
+{
+ fz_error *error;
+ ulong offset;
+ ulong sign;
+
+ fz_seek(zip->file, 0, 0);
+
+ while (1)
+ {
+ offset = fz_tell(zip->file);
+
+ sign = read4(zip->file);
+
+ switch (sign)
+ {
+
+ /* local file header */
+ case 0x04034b50:
+ if (zip->len + 1 > zip->cap)
+ {
+ error = growzip(zip);
+ if (error)
+ return error;
+ }
+
+ zip->table[zip->len].offset = offset;
+
+ error = scanzipent(zip, zip->table + zip->len);
+ if (error)
+ return error;
+
+ zip->len ++;
+
+ break;
+
+ /* data descriptor */
+ case 0x08074b50:
+ (void) read4(zip->file); /* crc-32 */
+ (void) read4(zip->file); /* compressed size */
+ (void) read4(zip->file); /* uncompressed size */
+ break;
+
+ /* central directory */
+ case 0x02014b50:
+ return fz_ferror(zip->file);
+
+ default:
+ return fz_throw("ioerror: unknown zip signature");
+ }
+ }
+}
+
+static fz_error *readzipdir(sa_zip *zip, int startoffset)
+{
+ ulong sign;
+ ulong csize, usize;
+ ulong namesize, metasize, comsize;
+ ulong offset;
+ int i;
+
+ fz_seek(zip->file, startoffset, 0);
+
+ for (i = 0; i < zip->len; i++)
+ {
+ sign = read4(zip->file);
+ if (sign != 0x02014b50)
+ return fz_throw("ioerror: unknown zip signature");
+
+ (void) read2(zip->file); /* version made by */
+ (void) read2(zip->file); /* version to extract */
+ (void) read2(zip->file); /* general */
+ (void) read2(zip->file); /* method */
+ (void) read2(zip->file); /* last mod file time */
+ (void) read2(zip->file); /* last mod file date */
+ (void) read4(zip->file); /* crc-32 */
+ csize = read4(zip->file);
+ usize = read4(zip->file);
+ namesize = read2(zip->file);
+ metasize = read2(zip->file);
+ comsize = read2(zip->file);
+ (void) read2(zip->file); /* disk number start */
+ (void) read2(zip->file); /* int file atts */
+ (void) read4(zip->file); /* ext file atts */
+ offset = read4(zip->file);
+
+ zip->table[i].offset = offset;
+ zip->table[i].name = fz_malloc(namesize + 1);
+ zip->table[i].csize = csize;
+ zip->table[i].usize = usize;
+ if (!zip->table[i].name)
+ return fz_outofmem;
+
+ fz_read(zip->file, zip->table[i].name, namesize);
+ zip->table[i].name[namesize] = 0;
+
+ fz_seek(zip->file, metasize, 1);
+ fz_seek(zip->file, comsize, 1);
+ }
+
+ return fz_ferror(zip->file);
+}
+
+static fz_error *readzipendofdir(sa_zip *zip, int startoffset)
+{
+ ulong sign;
+ ulong count;
+ ulong offset;
+
+ fz_seek(zip->file, startoffset, 0);
+
+ sign = read4(zip->file);
+ if (sign != 0x06054b50)
+ return fz_throw("ioerror: unknown zip signature");
+
+ (void) read2(zip->file); /* this disk */
+ (void) read2(zip->file); /* start disk */
+ (void) read2(zip->file); /* ents in this disk */
+ count = read2(zip->file); /* ents in central directory */
+ (void) read4(zip->file); /* size of central directory */
+ offset = read4(zip->file); /* offset to central directory */
+
+ zip->len = zip->cap = count;
+ zip->table = fz_malloc(zip->cap * sizeof(sa_zipent));
+ if (!zip->table)
+ return fz_outofmem;
+
+ memset(zip->table, 0, zip->cap * sizeof(sa_zipent));
+
+ return readzipdir(zip, offset);
+}
+
+static fz_error *findzipendofdir(sa_zip *zip)
+{
+ byte buf[512];
+ int filesize;
+ int maxback;
+ int backread;
+ int offset;
+ int len;
+ int i;
+
+ filesize = fz_seek(zip->file, 0, 2);
+ if (filesize == -1)
+ return fz_ferror(zip->file);
+
+ maxback = MIN(filesize, 0xFFFF + sizeof buf);
+
+ backread = MIN(maxback, sizeof buf);
+ while (backread < maxback)
+ {
+ fz_seek(zip->file, filesize - backread, 0);
+ len = fz_read(zip->file, buf, sizeof buf);
+ if (len < 0)
+ return fz_ferror(zip->file);
+
+ for (i = len - 4; i > 0; i--)
+ {
+ if (buf[i+0] == 0x50 && buf[i+1] == 0x4b &&
+ buf[i+2] == 0x05 && buf[i+3] == 0x06)
+ {
+ offset = filesize - backread + i;
+ return readzipendofdir(zip, offset);
+ }
+ }
+
+ backread += sizeof buf - 4;
+ }
+
+ return fz_throw("ioerror: no 'end of central directory' in zip");
+}
+
+/*
+ * Open a ZIP archive for reading.
+ * Load the table of contents.
+ */
+fz_error *
+sa_openzip(sa_zip **zipp, char *filename)
+{
+ fz_error *error;
+ sa_zip *zip;
+
+ zip = *zipp = fz_malloc(sizeof(sa_zip));
+ if (!zip)
+ return fz_outofmem;
+
+ zip->file = nil;
+ zip->len = 0;
+ zip->cap = 0;
+ zip->table = nil;
+
+ error = fz_openfile(&zip->file, filename, FZ_READ);
+ if (error)
+ return error;
+
+ error = findzipendofdir(zip);
+ if (error)
+ {
+ fz_warn("%s", error->msg);
+ fz_droperror(error);
+ return scanzip(zip);
+ }
+
+ return nil;
+}
+
+/*
+ * Free the table of contents and close the underlying file.
+ */
+void
+sa_closezip(sa_zip *zip)
+{
+ int i;
+
+ if (zip->file)
+ fz_closefile(zip->file);
+
+ for (i = 0; i < zip->len; i++)
+ if (zip->table[i].name)
+ fz_free(zip->table[i].name);
+
+ fz_free(zip->table);
+}
+
+/*
+ * Print a table of contents of the zip archive
+ */
+void
+sa_debugzip(sa_zip *zip)
+{
+ int i;
+
+ for (i = 0; i < zip->len; i++)
+ {
+ printf("%6u ", zip->table[i].csize);
+ printf("%6u ", zip->table[i].usize);
+ printf("%s\n", zip->table[i].name);
+ }
+}
+
+/*
+ * Seek and push decoding filter to read an individual file in the zip archive.
+ */
+fz_error *
+sa_openzipstream(sa_zip *zip, char *name)
+{
+ fz_error *error;
+ fz_filter *filter;
+ fz_obj *obj;
+ ulong sign, version, general, method;
+ ulong csize, usize;
+ ulong namesize, metasize;
+ int t;
+ int i;
+
+ for (i = 0; i < zip->len; i++)
+ {
+ if (!strcmp(name, zip->table[i].name))
+ {
+ t = fz_seek(zip->file, zip->table[i].offset, 0);
+ if (t < 0)
+ return fz_ferror(zip->file);
+
+ sign = read4(zip->file);
+ if (sign != 0x04034b50)
+ return fz_throw("ioerror: unknown zip signature");
+
+ version = read2(zip->file);
+ general = read2(zip->file);
+ method = read2(zip->file);
+ (void) read2(zip->file); /* time */
+ (void) read2(zip->file); /* date */
+ (void) read4(zip->file); /* crc-32 */
+ csize = read4(zip->file);
+ usize = read4(zip->file);
+ namesize = read2(zip->file);
+ metasize = read2(zip->file);
+
+ fz_seek(zip->file, namesize, 1);
+ fz_seek(zip->file, metasize, 1);
+
+ if ((version & 0xff) > 45)
+ return fz_throw("ioerror: unsupported zip version");
+ if (general & 0x0001)
+ return fz_throw("ioerror: encrypted zip entry");
+
+ switch (method)
+ {
+ case 0:
+printf("null filter\n");
+ error = fz_newnullfilter(&filter, csize);
+ break;
+ case 8:
+printf("flated filter\n");
+ error = fz_packobj(&obj, "<</ZIP true>>");
+ if (error)
+ return error;
+ error = fz_newflated(&filter, obj);
+ fz_dropobj(obj);
+ break;
+ default:
+ error = fz_throw("ioerror: unsupported compression method");
+ break;
+ }
+ if (error)
+ return error;
+
+ error = fz_pushfilter(zip->file, filter);
+ fz_dropfilter(filter);
+ if (error)
+ return error;
+
+ return nil;
+ }
+ }
+
+ return fz_throw("ioerror: file not found in zip: '%s'", name);
+}
+
+/*
+ * Pop decompression filter and clean up after reading a file in the archive.
+ */
+void
+sa_closezipstream(sa_zip *zip)
+{
+ fz_popfilter(zip->file);
+}
+
+
+int main(int argc, char **argv)
+{
+ fz_error *error;
+ fz_buffer *buf;
+ sa_zip *zip;
+ int i;
+
+ error = sa_openzip(&zip, argv[1]);
+ if (error)
+ fz_abort(error);
+
+ sa_debugzip(zip);
+
+ for (i = 2; i < argc; i++)
+ {
+ error = sa_openzipstream(zip, argv[i]);
+ if (error)
+ fz_abort(error);
+ error = fz_readfile(&buf, zip->file);
+ if (error)
+ fz_abort(error);
+ sa_closezipstream(zip);
+
+ fwrite(buf->rp, 1, buf->wp - buf->rp, stdout);
+
+ fz_dropbuffer(buf);
+ }
+
+ sa_closezip(zip);
+
+ return 0;
+}
+
diff --git a/stream/filt_flate.c b/stream/filt_flate.c
index c34a49b7..777d6d56 100644
--- a/stream/filt_flate.c
+++ b/stream/filt_flate.c
@@ -21,6 +21,8 @@ fz_error *
fz_newflated(fz_filter **fp, fz_obj *params)
{
fz_error *eo;
+ fz_obj *obj;
+ int zipfmt;
int ei;
FZ_NEWFILTER(fz_flate, f, flated);
@@ -31,8 +33,24 @@ fz_newflated(fz_filter **fp, fz_obj *params)
f->z.next_in = nil;
f->z.avail_in = 0;
- ei = inflateInit(&f->z);
- if (ei != Z_OK) {
+ zipfmt = 0;
+
+ if (params)
+ {
+ obj = fz_dictgets(params, "ZIP");
+ if (obj) zipfmt = fz_tobool(obj);
+ }
+
+ if (zipfmt)
+ {
+ /* if windowbits is negative the zlib header is skipped */
+ ei = inflateInit2(&f->z, -15);
+ }
+ else
+ ei = inflateInit(&f->z);
+
+ if (ei != Z_OK)
+ {
eo = fz_throw("ioerror: inflateInit: %s", f->z.msg);
fz_free(f);
return eo;
@@ -74,18 +92,21 @@ fz_processflated(fz_filter *f, fz_buffer *in, fz_buffer *out)
in->rp = in->wp - zp->avail_in;
out->wp = out->ep - zp->avail_out;
- if (err == Z_STREAM_END) {
+ if (err == Z_STREAM_END)
+ {
out->eof = 1;
return fz_iodone;
}
- else if (err == Z_OK) {
+ else if (err == Z_OK)
+ {
if (in->rp == in->wp && !in->eof)
return fz_ioneedin;
if (out->wp == out->ep)
return fz_ioneedout;
return fz_ioneedin; /* hmm, what's going on here? */
}
- else {
+ else
+ {
return fz_throw("ioerror: inflate: %s", zp->msg);
}
}
@@ -96,15 +117,20 @@ fz_newflatee(fz_filter **fp, fz_obj *params)
fz_obj *obj;
fz_error *eo;
int effort;
+ int zipfmt;
int ei;
FZ_NEWFILTER(fz_flate, f, flatee);
effort = -1;
+ zipfmt = 0;
- if (params) {
+ if (params)
+ {
obj = fz_dictgets(params, "Effort");
if (obj) effort = fz_toint(obj);
+ obj = fz_dictgets(params, "ZIP");
+ if (obj) effort = fz_tobool(obj);
}
f->z.zalloc = zmalloc;
@@ -113,8 +139,13 @@ fz_newflatee(fz_filter **fp, fz_obj *params)
f->z.next_in = nil;
f->z.avail_in = 0;
- ei = deflateInit(&f->z, effort);
- if (ei != Z_OK) {
+ if (zipfmt)
+ ei = deflateInit2(&f->z, effort, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+ else
+ ei = deflateInit(&f->z, effort);
+
+ if (ei != Z_OK)
+ {
eo = fz_throw("ioerror: deflateInit: %s", f->z.msg);
fz_free(f);
return eo;
@@ -158,18 +189,21 @@ fz_processflatee(fz_filter *f, fz_buffer *in, fz_buffer *out)
in->rp = in->wp - zp->avail_in;
out->wp = out->ep - zp->avail_out;
- if (err == Z_STREAM_END) {
+ if (err == Z_STREAM_END)
+ {
out->eof = 1;
return fz_iodone;
}
- else if (err == Z_OK) {
+ else if (err == Z_OK)
+ {
if (in->rp == in->wp && !in->eof)
return fz_ioneedin;
if (out->wp == out->ep)
return fz_ioneedout;
return fz_ioneedin; /* hmm? */
}
- else {
+ else
+ {
return fz_throw("ioerror: deflate: %s", zp->msg);
}
}