diff options
-rw-r--r-- | Jamfile | 3 | ||||
-rw-r--r-- | apps/pdfclean.c | 27 | ||||
-rw-r--r-- | apps/pdfmerge.c | 54 | ||||
-rw-r--r-- | apps/pdfrip.c | 74 | ||||
-rw-r--r-- | apps/pdfselect.c | 299 | ||||
-rw-r--r-- | include/mupdf.h | 1 | ||||
-rw-r--r-- | include/mupdf/version.h | 8 | ||||
-rw-r--r-- | mupdf/pagetree.c | 9 | ||||
-rw-r--r-- | mupdf/xref.c | 2 |
9 files changed, 417 insertions, 60 deletions
@@ -260,5 +260,6 @@ Main pdfclean : apps/pdfclean.c ; Main pdfdebug : apps/pdfdebug.c ; Main pdfrip : apps/pdfrip.c ; Main pdfmerge : apps/pdfmerge.c ; -LinkLibraries pdfclean pdfdebug pdfrip pdfmerge : libmupdf libfitz ; +Main pdfselect : apps/pdfselect.c ; +LinkLibraries pdfclean pdfdebug pdfrip pdfmerge pdfselect : libmupdf libfitz ; diff --git a/apps/pdfclean.c b/apps/pdfclean.c index a55b94ef..05bef10f 100644 --- a/apps/pdfclean.c +++ b/apps/pdfclean.c @@ -5,14 +5,14 @@ void usage() { fprintf(stderr, "usage: pdfclean [options] infile.pdf outfile.pdf\n" - " -r\trebuild xref table\n" - " -g\tgarbage collect unused objects\n" - " -x\texpand compressed streams\n" - " -d -\tset user password for decryption\n" - " -e\tencrypt outfile\n" + " -d -\tpassword for decryption\n" + " -g \tgarbage collect unused objects\n" + " -r \trebuild xref table\n" + " -x \texpand compressed streams\n" + " -e \tencrypt outfile\n" " -u -\tset user password for encryption\n" " -o -\tset owner password\n" - " -p -\tset permissions\n" + " -p -\tset permissions (combine letters 'pmca')\n" " -n -\tkey length in bits: 40 <= n <= 128\n" ); exit(1); @@ -91,7 +91,7 @@ int main(int argc, char **argv) char *userpw = ""; char *ownerpw = ""; - int perms = -4; /* 0xfffffffc */ + unsigned perms = 0xfffff0c0; /* nothing allowed */ int keylen = 40; char *password = ""; @@ -105,7 +105,18 @@ int main(int argc, char **argv) case 'e': ++ doencrypt; break; case 'u': userpw = optarg; break; case 'o': ownerpw = optarg; break; - case 'p': perms = atoi(optarg); break; + case 'p': + /* see TABLE 3.15 User access permissions */ + perms = 0xfffff0c0; + if (strchr(optarg, 'p')) /* print */ + perms |= (1 << 2) | (1 << 11); + if (strchr(optarg, 'm')) /* modify */ + perms |= (1 << 3) | (1 << 10); + if (strchr(optarg, 'c')) /* copy */ + perms |= (1 << 4) | (1 << 9); + if (strchr(optarg, 'a')) /* annotate / forms */ + perms |= (1 << 5) | (1 << 8); + break; case 'n': keylen = atoi(optarg); break; case 'd': password = optarg; break; default: usage(); diff --git a/apps/pdfmerge.c b/apps/pdfmerge.c index 5ad161f0..8c431308 100644 --- a/apps/pdfmerge.c +++ b/apps/pdfmerge.c @@ -4,14 +4,10 @@ void usage() { fprintf(stderr, - "usage: pdfmerge [options] file.pdf pages ...\n" + "usage: pdfmerge [options] file1.pdf file2.pdf ...\n" + " -d -\tpassword for decryption\n" " -o -\toutput file name (default out.pdf)\n" - " -d -\tset user password for decryption\n" - " -e\tencrypt outfile\n" - " -U -\tset user password for encryption\n" - " -O -\tset owner password\n" - " -P -\tset permissions\n" - " -N -\tkey length in bits: 40 <= n <= 128\n" + " -v \tverbose\n" ); exit(1); } @@ -35,25 +31,15 @@ int main(int argc, char **argv) int i, k; int c; - pdf_crypt *encrypt = 0; - int doencrypt = 0; - - char *userpw = ""; - char *ownerpw = ""; - int perms = -4; /* 0xfffffffc */ - int keylen = 40; + int verbose = 0; char *password = ""; - while ((c = getopt(argc, argv, "reo:U:O:P:N:")) != -1) + while ((c = getopt(argc, argv, "vo:d:")) != -1) { switch (c) { - case 'e': ++ doencrypt; break; + case 'v': ++ verbose; break; case 'o': savename = optarg; break; - case 'U': userpw = optarg; break; - case 'O': ownerpw = optarg; break; - case 'P': perms = atoi(optarg); break; - case 'N': keylen = atoi(optarg); break; case 'd': password = optarg; break; default: usage(); } @@ -84,6 +70,12 @@ int main(int argc, char **argv) for (i = optind; i < argc; i++) { + if (verbose) + { + printf("loading pdf '%s' ", argv[i]); + fflush(stdout); + } + error = pdf_newxref(&src); if (error) fz_abort(error); @@ -111,6 +103,9 @@ int main(int argc, char **argv) if (error) fz_abort(error); + if (verbose) + printf("(%d pages)\n", srcpages->count); + for (k = 0; k < srcpages->count; k++) { fz_dictdels(srcpages->pobj[k], "Parent"); @@ -152,6 +147,10 @@ int main(int argc, char **argv) * Create and relink Pages object */ + if (verbose) + printf("creating pdf '%s' (%d pages)\n", + savename, fz_arraylen(dstrefs)); + error = pdf_allocobject(dst, &pagesoid, &pagesgid); if (error) fz_abort(error); @@ -213,20 +212,7 @@ int main(int argc, char **argv) * Write out the new PDF */ - if (doencrypt) - { - fz_obj *id = fz_dictgets(dst->trailer, "ID"); - if (!id) - fz_packobj(&id, "[(ABCDEFGHIJKLMNOP)(ABCDEFGHIJKLMNOP)]"); - else - fz_keepobj(id); - error = pdf_newencrypt(&encrypt, userpw, ownerpw, perms, keylen, id); - if (error) - fz_abort(error); - fz_dropobj(id); - } - - error = pdf_savexref(dst, savename, encrypt); + error = pdf_savexref(dst, savename, nil); if (error) fz_abort(error); diff --git a/apps/pdfrip.c b/apps/pdfrip.c index c9c761ce..b160ea65 100644 --- a/apps/pdfrip.c +++ b/apps/pdfrip.c @@ -6,10 +6,19 @@ float zoom = 1.0; char *namefmt = nil; fz_renderer *gc; int nbands = 1; +int verbose = 0; void usage() { - fprintf(stderr, "usage: pdfrip [-d] [-b bands] [-o out-%%02d.ppm] [-p password] [-z zoom] file.pdf pages...\n"); + fprintf(stderr, +"usage: pdfrip [options] file.pdf pageranges\n" +" -b -\trender page in N bands (default 1)\n" +" -d -\tpassword for decryption\n" +" -o -\toutput filename format (default out-%%03d.ppm)\n" +" -t \tshow display tree\n" +" -v \tverbose\n" +" -z -\tzoom factor (default 1.0 = 72 dpi)\n" + ); exit(1); } @@ -31,6 +40,9 @@ void showpage(pdf_xref *xref, fz_obj *pageobj, int pagenum) int w, h; int b, bh; + if (verbose) + printf("page %d\n", pagenum); + sprintf(namebuf, namefmt, pagenum); error = pdf_loadpage(&page, xref, pageobj); @@ -81,7 +93,8 @@ void showpage(pdf_xref *xref, fz_obj *pageobj, int pagenum) for (b = 0; b < nbands; b++) { - printf("band %d / %d\n", b, nbands); + if (verbose) + printf("render band %d of %d\n", b + 1, nbands); memset(pix->samples, 0xff, pix->w * pix->h * 4); @@ -119,20 +132,20 @@ int main(int argc, char **argv) pdf_xref *xref; pdf_pagetree *pages; int c; - static char namebuf[256]; char *password = ""; fz_cpudetect(); fz_accelerate(); - while ((c = getopt(argc, argv, "dz:p:o:b:")) != -1) + while ((c = getopt(argc, argv, "Vtvz:d:o:b:")) != -1) { switch (c) { - case 'p': password = optarg; break; + case 't': ++showtree; break; + case 'v': ++verbose; break; + case 'd': password = optarg; break; case 'z': zoom = atof(optarg); break; - case 'd': ++showtree; break; case 'o': namefmt = optarg; break; case 'b': nbands = atoi(optarg); break; default: usage(); @@ -146,6 +159,10 @@ int main(int argc, char **argv) if (!namefmt) { +#if 1 + namefmt = "out-%03d.ppm"; +#else + char namebuf[256]; char *s; s = strrchr(filename, '/'); if (!s) @@ -159,8 +176,12 @@ int main(int argc, char **argv) else strcat(namebuf, "-%03d.ppm"); namefmt = namebuf; +#endif } + if (verbose) + printf("loading pdf: '%s'\n", filename); + error = pdf_newxref(&xref); if (error) fz_abort(error); @@ -184,10 +205,11 @@ int main(int argc, char **argv) if (error) fz_abort(error); + if (verbose) + pdf_debugpagetree(pages); + if (optind == argc) - { - printf("number of pages: %d\n", pdf_getpagecount(pages)); - } + printf("%d pages\n", pdf_getpagecount(pages)); error = fz_newrenderer(&gc, pdf_devicergb, 0, 1024 * 512); if (error) @@ -195,11 +217,35 @@ int main(int argc, char **argv) for ( ; optind < argc; optind++) { - int page = atoi(argv[optind]); - if (page < 1 || page > pdf_getpagecount(pages)) - fprintf(stderr, "page out of bounds: %d\n", page); - printf("page %d\n", page); - showpage(xref, pdf_getpageobject(pages, page - 1), page); + int spage, epage, page; + char *spec = argv[optind]; + char *dash = strchr(spec, '-'); + + if (dash == spec) + spage = epage = 1; + else + spage = epage = atoi(spec); + + if (dash) + { + if (strlen(dash) > 1) + epage = atoi(dash+1); + else + epage = pdf_getpagecount(pages); + } + + if (spage > epage) + page = spage, spage = epage, epage = page; + + for (page = spage; page <= epage; page++) + { + if (page < 1 || page > pdf_getpagecount(pages)) + fprintf(stderr, "page out of bounds: %d\n", page); + else + { + showpage(xref, pdf_getpageobject(pages, page - 1), page); + } + } } fz_droprenderer(gc); diff --git a/apps/pdfselect.c b/apps/pdfselect.c new file mode 100644 index 00000000..f2381f94 --- /dev/null +++ b/apps/pdfselect.c @@ -0,0 +1,299 @@ +#include <fitz.h> +#include <mupdf.h> + +void usage() +{ + fprintf(stderr, + "usage: pdfselect [options] infile.pdf outfile.pdf pageranges\n" + " -d -\tpassword for decryption\n" + " -e \tselect only even pages\n" + " -o \tselect only odd pages\n" + " -r \toutput in reverse order\n" + " -v \tverbose\n" + ); + exit(1); +} + +void preloadobjstms(pdf_xref *xref) +{ + fz_error *error; + fz_obj *obj; + int i; + + for (i = 0; i < xref->len; i++) + { + if (xref->table[i].type == 'o') + { + error = pdf_loadobject(&obj, xref, i, 0); + if (error) fz_abort(error); + fz_dropobj(obj); + } + } +} + +int main(int argc, char **argv) +{ + fz_error *error; + char *password = ""; + char *infile; + char *outfile; + pdf_xref *xref; + pdf_pagetree *pages; + fz_obj *pagesref; + fz_obj *kids; + int i, k; + int c; + int page; + int rootoid; + int rootgid; + int kidsoid; + int kidsgid; + int pagesoid; + int pagesgid; + fz_obj *obj; + + int verbose = 0; + int even = 0; + int odd = 0; + int reverse = 0; + int all = 0; + + while ((c = getopt(argc, argv, "d:eorv")) != -1) + { + switch (c) + { + case 'd': password = optarg; break; + case 'e': ++ even; break; + case 'o': ++ odd; break; + case 'r': ++ reverse; break; + case 'v': ++ verbose; break; + default: usage(); + } + } + + if (argc - optind < 2) + usage(); + + if (argc - optind < 3 && !even && !odd && !reverse) + usage(); + + if (argc - optind == 2) + all = 1; + + infile = argv[optind++]; + outfile = argv[optind++]; + + if (verbose) + printf("loading pdf '%s'\n", infile); + + error = pdf_newxref(&xref); + if (error) + fz_abort(error); + + error = pdf_loadxref(xref, infile); + if (error) + fz_abort(error); + + error = pdf_decryptxref(xref); + if (error) + fz_abort(error); + + if (xref->crypt) + { + error = pdf_setpassword(xref->crypt, password); + if (error) + fz_abort(error); + } + + error = pdf_loadpagetree(&pages, xref); + if (error) + fz_abort(error); + + /* + * Kill annotations on all pages + */ + + if (verbose) + printf("killing time\n"); + + for (k = 0; k < pages->count; k++) + { + fz_dictdels(pages->pobj[k], "Parent"); + fz_dictdels(pages->pobj[k], "B"); + fz_dictdels(pages->pobj[k], "PieceInfo"); + fz_dictdels(pages->pobj[k], "Metadata"); + fz_dictdels(pages->pobj[k], "Annots"); + fz_dictdels(pages->pobj[k], "Tabs"); + pdf_updateobject(xref, + fz_tonum(pages->pref[k]), + fz_togen(pages->pref[k]), + pages->pobj[k]); + } + + /* + * Save the pages we want to keep, in the order specified + */ + + error = fz_newarray(&kids, 100); + if (error) + fz_abort(error); + + for ( ; optind < argc; optind++) + { + int spage, epage; + char *spec = argv[optind]; + char *dash = strchr(spec, '-'); + + if (dash == spec) + spage = epage = 1; + else + spage = epage = atoi(spec); + + if (dash) + { + if (strlen(dash) > 1) + epage = atoi(dash+1); + else + epage = pdf_getpagecount(pages); + } + + if (spage > epage) + page = spage, spage = epage, epage = page; + + for (page = spage; page <= epage; page++) + { + if (page < 1 || page > pdf_getpagecount(pages)) + continue; + if (odd && (page & 1) != 1) + continue; + if (even && (page & 1) != 0) + continue; + error = fz_arraypush(kids, pages->pref[page-1]); + if (error) + fz_abort(error); + } + } + + if (all) + { + for (page = 1; page <= pdf_getpagecount(pages); page++) + { + if (odd && (page & 1) != 1) + continue; + if (even && (page & 1) != 0) + continue; + error = fz_arraypush(kids, pages->pref[page-1]); + if (error) + fz_abort(error); + } + } + + if (reverse) + { + fz_obj *o1, *o2; + int len = fz_arraylen(kids); + for (i = 0; i < len / 2; i++) + { + o1 = fz_keepobj(fz_arrayget(kids, i)); + o2 = fz_keepobj(fz_arrayget(kids, len - i - 1)); + fz_arrayput(kids, i, o2); + fz_arrayput(kids, len - i - 1, o1); + } + } + + /* + * Save the new kids array + */ + + error = pdf_allocobject(xref, &kidsoid, &kidsgid); + if (error) + fz_abort(error); + + pdf_updateobject(xref, kidsoid, kidsgid, kids); + + /* + * Save the new pages object + */ + + error = pdf_allocobject(xref, &pagesoid, &pagesgid); + if (error) + fz_abort(error); + + error = fz_packobj(&obj, + "<</Type/Pages/Count %i/Kids %r>>", + fz_arraylen(kids), kidsoid, kidsgid); + if (error) + fz_abort(error); + + pdf_updateobject(xref, pagesoid, pagesgid, obj); + + fz_dropobj(obj); + + /* + * Relink parents to point to new pages object + */ + + error = fz_newindirect(&pagesref, pagesoid, pagesgid); + if (error) + fz_abort(error); + + for (i = 0; i < fz_arraylen(kids); i++) + { + int oid = fz_tonum(fz_arrayget(kids, i)); + int gid = fz_togen(fz_arrayget(kids, i)); + error = pdf_loadobject(&obj, xref, oid, gid); + if (error) + fz_abort(error); + error = fz_dictputs(obj, "Parent", pagesref); + if (error) + fz_abort(error); + pdf_updateobject(xref, oid, gid, obj); + fz_dropobj(obj); + } + + fz_dropobj(pagesref); + + /* + * Create new catalog and trailer + */ + + error = pdf_allocobject(xref, &rootoid, &rootgid); + if (error) + fz_abort(error); + + error = fz_packobj(&obj, + "<</Type/Catalog/Pages %r>>", + pagesoid, pagesgid); + if (error) + fz_abort(error); + + pdf_updateobject(xref, rootoid, rootgid, obj); + + fz_dropobj(obj); + + error = fz_packobj(&xref->trailer, "<</Root %r>>", rootoid, rootgid); + if (error) + fz_abort(error); + + /* + * Write out the new PDF + */ + + if (verbose) + printf("garbage collecting\n"); + + preloadobjstms(xref); + pdf_garbagecollect(xref); + + if (verbose) + printf("saving pdf '%s'\n", outfile); + + error = pdf_savexref(xref, outfile, nil); + if (error) + fz_abort(error); + + pdf_closexref(xref); + + return 0; +} + diff --git a/include/mupdf.h b/include/mupdf.h index 94d47812..7556484a 100644 --- a/include/mupdf.h +++ b/include/mupdf.h @@ -14,6 +14,7 @@ void pdf_logimage(char *fmt, ...); void pdf_logshade(char *fmt, ...); void pdf_logpage(char *fmt, ...); +#include "mupdf/version.h" #include "mupdf/syntax.h" #include "mupdf/xref.h" #include "mupdf/rsrc.h" diff --git a/include/mupdf/version.h b/include/mupdf/version.h new file mode 100644 index 00000000..af218190 --- /dev/null +++ b/include/mupdf/version.h @@ -0,0 +1,8 @@ +/* + * Version strings + */ + +#define PDF_NAME "GhostPDF" +#define PDF_VERSION (1) +#define PDF_COPYRIGHT "Copyright (C) 2005 artofcode LLC" + diff --git a/mupdf/pagetree.c b/mupdf/pagetree.c index eafc0599..315c2bd2 100644 --- a/mupdf/pagetree.c +++ b/mupdf/pagetree.c @@ -72,6 +72,9 @@ loadpagetree(pdf_xref *xref, pdf_pagetree *pages, if (inh) inherit.rotate = inh; kids = fz_dictgets(obj, "Kids"); + error = pdf_resolve(&kids, xref); + if (error) + return error; pdf_logpage("subtree %d {\n", fz_arraylen(kids)); @@ -80,13 +83,15 @@ loadpagetree(pdf_xref *xref, pdf_pagetree *pages, kref = fz_arrayget(kids, i); error = pdf_loadindirect(&kobj, xref, kref); - if (error) return error; + if (error) { fz_dropobj(kids); return error; } error = loadpagetree(xref, pages, inherit, kobj, kref); fz_dropobj(kobj); - if (error) return error; + if (error) { fz_dropobj(kids); return error; } } + fz_dropobj(kids); + pdf_logpage("}\n"); } diff --git a/mupdf/xref.c b/mupdf/xref.c index c5305b28..01b3dbf8 100644 --- a/mupdf/xref.c +++ b/mupdf/xref.c @@ -20,7 +20,7 @@ pdf_newxref(pdf_xref **xrefp) pdf_logxref("newxref %p\n", xref); xref->file = nil; - xref->version = 1.0; + xref->version = 1.3; xref->startxref = 0; xref->crypt = nil; |