diff options
author | Tor Andersson <tor@ghostscript.com> | 2005-05-20 14:57:18 +0200 |
---|---|---|
committer | Tor Andersson <tor@ghostscript.com> | 2005-05-20 14:57:18 +0200 |
commit | 4214d9ea3d5d219f1e6e3f3ade5a5b1dee794159 (patch) | |
tree | 0d542f9886435e9dcf599644c566b86b62e5e1e9 | |
parent | 4c68e8b9d7565eecdda712ddf1b64d30dfe2722a (diff) | |
download | mupdf-4214d9ea3d5d219f1e6e3f3ade5a5b1dee794159.tar.xz |
zip archive reading
-rw-r--r-- | Jamfile | 13 | ||||
-rw-r--r-- | Jamrules | 2 | ||||
-rw-r--r-- | TODO | 11 | ||||
-rw-r--r-- | include/samus.h | 9 | ||||
-rw-r--r-- | samus/sa_zip.c | 463 | ||||
-rw-r--r-- | stream/filt_flate.c | 56 |
6 files changed, 540 insertions, 14 deletions
@@ -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 # @@ -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`" ; @@ -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); } } |