diff options
-rw-r--r-- | Jamfile | 1 | ||||
-rw-r--r-- | TODO | 7 | ||||
-rw-r--r-- | include/mupdf.h | 1 | ||||
-rw-r--r-- | include/mupdf/annot.h | 53 | ||||
-rw-r--r-- | include/mupdf/page.h | 2 | ||||
-rw-r--r-- | include/mupdf/rsrc.h | 2 | ||||
-rw-r--r-- | include/mupdf/xref.h | 5 | ||||
-rw-r--r-- | mupdf/annot.c | 194 | ||||
-rw-r--r-- | mupdf/nametree.c | 30 | ||||
-rw-r--r-- | mupdf/open.c | 2 | ||||
-rw-r--r-- | mupdf/page.c | 51 | ||||
-rw-r--r-- | mupdf/repair.c | 2 | ||||
-rw-r--r-- | test/x11pdf.c | 132 |
13 files changed, 477 insertions, 5 deletions
@@ -116,6 +116,7 @@ Library libmupdf : mupdf/nametree.c mupdf/outline.c + mupdf/annot.c mupdf/pagetree.c mupdf/store.c @@ -1,5 +1,12 @@ immediate plan: + + refactor xref init + + loadlowlevel + + loadhighlevel + - load name trees + - load page tree + - load outline + - altivec optimize - design gui for editor - go through spec and check all features! diff --git a/include/mupdf.h b/include/mupdf.h index 5ee5fe91..94d47812 100644 --- a/include/mupdf.h +++ b/include/mupdf.h @@ -18,5 +18,6 @@ void pdf_logpage(char *fmt, ...); #include "mupdf/xref.h" #include "mupdf/rsrc.h" #include "mupdf/content.h" +#include "mupdf/annot.h" #include "mupdf/page.h" diff --git a/include/mupdf/annot.h b/include/mupdf/annot.h new file mode 100644 index 00000000..ac017735 --- /dev/null +++ b/include/mupdf/annot.h @@ -0,0 +1,53 @@ +/* + * Interactive features + */ + +typedef struct pdf_link_s pdf_link; +typedef struct pdf_comment_s pdf_comment; +typedef struct pdf_widget_s pdf_widget; + +typedef enum pdf_linkkind_e +{ + PDF_LGOTO, + PDF_LURI +} pdf_linkkind; + +struct pdf_link_s +{ + pdf_linkkind kind; + fz_rect rect; + fz_obj *page; + fz_obj *uri; + int ismap; + pdf_link *next; +}; + +typedef enum pdf_commentkind_e +{ + PDF_CTEXT, + PDF_CFREETEXT, + PDF_CLINE, + PDF_CSQUARE, + PDF_CCIRCLE, + PDF_CPOLYGON, + PDF_CPOLYLINE, + PDF_CMARKUP, + PDF_CCARET, + PDF_CSTAMP, + PDF_CINK +} pdf_commentkind; + +struct pdf_comment_s +{ + pdf_commentkind kind; + fz_rect rect; + fz_rect popup; + fz_obj *contents; + pdf_comment *next; +}; + +fz_error * pdf_newlink(pdf_link**, fz_rect rect, int ismap, fz_obj *page, fz_obj *uri); +void pdf_droplink(pdf_link *link); + +fz_error *pdf_loadannots(pdf_comment **, pdf_link **, pdf_xref *, fz_obj *annots); + diff --git a/include/mupdf/page.h b/include/mupdf/page.h index 3bb27db6..89bad060 100644 --- a/include/mupdf/page.h +++ b/include/mupdf/page.h @@ -51,6 +51,8 @@ struct pdf_page_s fz_obj *resources; fz_tree *tree; pdf_textline *text; + pdf_comment *comments; + pdf_link *links; }; struct pdf_textchar_s diff --git a/include/mupdf/rsrc.h b/include/mupdf/rsrc.h index 328a483b..3c263c08 100644 --- a/include/mupdf/rsrc.h +++ b/include/mupdf/rsrc.h @@ -2,6 +2,8 @@ * Resource store */ +typedef struct pdf_store_s pdf_store; + typedef enum pdf_itemkind_e { PDF_KCOLORSPACE, diff --git a/include/mupdf/xref.h b/include/mupdf/xref.h index c1307e45..3721e09a 100644 --- a/include/mupdf/xref.h +++ b/include/mupdf/xref.h @@ -2,8 +2,6 @@ * xref and object / stream api */ -typedef struct pdf_store_s pdf_store; /* parsed resource store */ - typedef struct pdf_xrefentry_s pdf_xrefentry; typedef struct pdf_xref_s pdf_xref; @@ -20,7 +18,8 @@ struct pdf_xref_s int cap; pdf_xrefentry *table; - pdf_store *store; + struct pdf_nametree_s *dests; + struct pdf_store_s *store; }; struct pdf_xrefentry_s diff --git a/mupdf/annot.c b/mupdf/annot.c new file mode 100644 index 00000000..0f8acf6f --- /dev/null +++ b/mupdf/annot.c @@ -0,0 +1,194 @@ +#include <fitz.h> +#include <mupdf.h> + +static fz_error * +loadcomment(pdf_comment **commentp, pdf_xref *xref, fz_obj *dict) +{ + return nil; +} + +fz_error * +pdf_newlink(pdf_link **linkp, fz_rect bbox, int ismap, fz_obj *page, fz_obj *uri) +{ + pdf_link *link; + + link = fz_malloc(sizeof(pdf_link)); + if (!link) + return fz_outofmem; + + link->rect = bbox; + link->ismap = ismap; + link->page = page ? fz_keepobj(page) : nil; + link->uri = uri ? fz_keepobj(uri) : nil; + link->next = nil; + + *linkp = link; + return nil; +} + +void +pdf_droplink(pdf_link *link) +{ + if (link->next) + pdf_droplink(link->next); + if (link->page) + fz_dropobj(link->page); + if (link->uri) + fz_dropobj(link->uri); + fz_free(link); +} + +static fz_obj * +resolvedest(pdf_xref *xref, fz_obj *dest) +{ + if (fz_isname(dest) && xref->dests) + { + dest = pdf_lookupname(xref->dests, dest); + pdf_resolve(&dest, xref); /* XXX */ + return resolvedest(xref, dest); + } + + else if (fz_isstring(dest) && xref->dests) + { + dest = pdf_lookupname(xref->dests, dest); + pdf_resolve(&dest, xref); /* XXX */ + return resolvedest(xref, dest); + } + + else if (fz_isarray(dest)) + { + return fz_arrayget(dest, 0); + } + + else if (fz_isdict(dest)) + { + dest = fz_dictgets(dest, "D"); + return resolvedest(xref, dest); + } + + else if (fz_isindirect(dest)) + return dest; + + return nil; +} + +static fz_error * +loadlink(pdf_link **linkp, pdf_xref *xref, fz_obj *dict) +{ + fz_error *error; + pdf_link *link; + fz_obj *page; + fz_obj *uri; + fz_obj *action; + fz_obj *obj; + fz_rect bbox; + int ismap; + + pdf_logpage("load link {\n"); + + link = nil; + page = nil; + uri = nil; + ismap = 0; + + obj = fz_dictgets(dict, "Rect"); + bbox = pdf_torect(obj); + pdf_logpage("rect [%g %g %g %g]\n", + bbox.min.x, bbox.min.y, + bbox.max.x, bbox.max.y); + + obj = fz_dictgets(dict, "Dest"); + if (obj) + { + error = pdf_resolve(&obj, xref); + if (error) + return error; + page = resolvedest(xref, obj); + pdf_logpage("dest %d %d R\n", fz_tonum(page), fz_togen(page)); + fz_dropobj(obj); + } + + action = fz_dictgets(dict, "A"); + if (action) + { + error = pdf_resolve(&action, xref); + if (error) + return error; + + obj = fz_dictgets(action, "S"); + if (!strcmp(fz_toname(obj), "GoTo")) + { + page = resolvedest(xref, fz_dictgets(action, "D")); + pdf_logpage("action goto %d %d R\n", fz_tonum(page), fz_togen(page)); + } + else if (!strcmp(fz_toname(obj), "URI")) + { + uri = fz_dictgets(action, "URI"); + ismap = fz_tobool(fz_dictgets(action, "IsMap")); + pdf_logpage("action uri ismap=%d\n", ismap); + } + else + pdf_logpage("action ... ?\n"); + + fz_dropobj(action); + } + + pdf_logpage("}\n"); + + if (page || uri) + { + error = pdf_newlink(&link, bbox, ismap, page, uri); + if (error) + return error; + link->next = *linkp; + *linkp = link; + } + + return nil; +} + +fz_error * +pdf_loadannots(pdf_comment **cp, pdf_link **lp, pdf_xref *xref, fz_obj *annots) +{ + fz_error *error; + pdf_comment *comment; + pdf_link *link; + fz_obj *subtype; + fz_obj *obj; + int i; + + comment = nil; + link = nil; + + pdf_logpage("load annotations {\n"); + + for (i = 0; i < fz_arraylen(annots); i++) + { + obj = fz_arrayget(annots, i); + error = pdf_resolve(&obj, xref); + if (error) + goto cleanup; + + subtype = fz_dictgets(obj, "Subtype"); + if (!strcmp(fz_toname(subtype), "Link")) + error = loadlink(&link, xref, obj); + else + error = loadcomment(&comment, xref, obj); + + fz_dropobj(obj); + + if (error) + goto cleanup; + } + + pdf_logpage("}\n"); + + *cp = comment; + *lp = link; + return nil; + +cleanup: + pdf_droplink(link); + return error; +} + diff --git a/mupdf/nametree.c b/mupdf/nametree.c index b338cbbb..2dd0d76d 100644 --- a/mupdf/nametree.c +++ b/mupdf/nametree.c @@ -178,6 +178,35 @@ pdf_loadnametree(pdf_nametree **pnt, pdf_xref *xref, char* key) error = pdf_loadindirect(&catalog, xref, ref); if (error) goto cleanup; +#if 1 // XXX XXX + names = fz_dictgets(catalog, "Names"); + if (!names) + { + nt = *pnt = fz_malloc(sizeof(pdf_nametree)); + if (!nt) { error = fz_outofmem; goto cleanup; } + nt->cap = 0; + nt->len = 0; + nt->items = 0; + return nil; + } + + error = pdf_resolve(&names, xref); + if (error) goto cleanup; + + root = fz_dictgets(names, key); + if (!root) + { + nt = *pnt = fz_malloc(sizeof(pdf_nametree)); + if (!nt) { error = fz_outofmem; goto cleanup; } + nt->cap = 0; + nt->len = 0; + nt->items = 0; + return nil; + } + error = pdf_resolve(&root, xref); + if (error) goto cleanup; + +#else /*XXX create empty nametree instead of failing */ names = fz_dictgets(catalog, "Names"); /*XXX never resolve something that can be null */ @@ -188,6 +217,7 @@ pdf_loadnametree(pdf_nametree **pnt, pdf_xref *xref, char* key) /*XXX never resolve something that can be null */ error = pdf_resolve(&root, xref); if (error) goto cleanup; +#endif nt = *pnt = fz_malloc(sizeof(pdf_nametree)); if (!nt) { error = fz_outofmem; goto cleanup; } diff --git a/mupdf/open.c b/mupdf/open.c index 1be868c9..840690b4 100644 --- a/mupdf/open.c +++ b/mupdf/open.c @@ -551,6 +551,8 @@ pdf_openpdf(pdf_xref **xrefp, char *filename) if (error) goto cleanup; + xref->dests = nil; + *xrefp = xref; return nil; diff --git a/mupdf/page.c b/mupdf/page.c index 3d09c269..994657e3 100644 --- a/mupdf/page.c +++ b/mupdf/page.c @@ -144,12 +144,18 @@ pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict) fz_obj *obj; pdf_page *page; fz_obj *rdb; + pdf_comment *comments = nil; + pdf_link *links = nil; fz_tree *tree; fz_rect bbox; int rotate; pdf_logpage("load page {\n"); + /* + * Sort out page media + */ + obj = fz_dictgets(dict, "CropBox"); if (!obj) obj = fz_dictgets(dict, "MediaBox"); @@ -169,6 +175,22 @@ pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict) pdf_logpage("rotate %d\n", rotate); /* + * Load annotations + */ + + obj = fz_dictgets(dict, "Annots"); + if (obj) + { + error = pdf_resolve(&obj, xref); + if (error) + return error; + error = pdf_loadannots(&comments, &links, xref, obj); + fz_dropobj(obj); + if (error) + return error; + } + + /* * Load resources */ @@ -176,10 +198,12 @@ pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict) if (!obj) return fz_throw("syntaxerror: Page missing Resources"); error = pdf_resolve(&obj, xref); - if (error) return error; + if (error) + return error; error = pdf_loadresources(&rdb, xref, obj); fz_dropobj(obj); - if (error) return error; + if (error) + return error; /* * Interpret content stream to build display tree @@ -219,6 +243,21 @@ pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict) page->resources = rdb; page->tree = tree; + page->comments = comments; + page->links = links; + page->text = nil; + + /* + * Extract unicode text lines + */ + + error = pdf_loadtextfromtree(&page->text, page->tree); + if (error) + { + pdf_droppage(page); + return error; + } + pdf_logpage("} %p\n", page); return nil; @@ -228,6 +267,14 @@ void pdf_droppage(pdf_page *page) { pdf_logpage("drop page %p\n", page); +/* + if (page->comments) + pdf_dropcomment(page->comments); +*/ + if (page->links) + pdf_droplink(page->links); + if (page->text) + pdf_droptextline(page->text); fz_dropobj(page->resources); fz_droptree(page->tree); fz_free(page); diff --git a/mupdf/repair.c b/mupdf/repair.c index f342b083..bacdaead 100644 --- a/mupdf/repair.c +++ b/mupdf/repair.c @@ -298,6 +298,8 @@ pdf_repairpdf(pdf_xref **xrefp, char *filename) if (error) goto cleanup; + xref->dests = nil; + *xrefp = xref; return nil; diff --git a/test/x11pdf.c b/test/x11pdf.c index 02aeb8c0..5c525f33 100644 --- a/test/x11pdf.c +++ b/test/x11pdf.c @@ -217,6 +217,10 @@ static void pdfopen(char *filename, char *password) count = pdf_getpagecount(pages); + error = pdf_loadnametree(&xref->dests, xref, "Dests"); + if (error) + fz_abort(error); + strlcpy(doctitle, filename, sizeof doctitle); obj = fz_dictgets(xref->trailer, "Info"); if (fz_isindirect(obj)) @@ -253,6 +257,42 @@ static void dumptext() pdf_droptextline(line); } +static void drawlinks(void) +{ + pdf_link *link; + fz_matrix ctm; + fz_point a, b, c, d; + fz_rect r; + + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.max.y)); + ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); + ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate)); + ctm = fz_concat(ctm, fz_translate(-image->x, -image->y)); + + for (link = page->links; link; link = link->next) + { + r = link->rect; + + a.x = r.min.x; a.y = r.min.y; + b.x = r.min.x; b.y = r.max.y; + c.x = r.max.x; c.y = r.max.y; + d.x = r.max.x; d.y = r.min.y; + + a = fz_transformpoint(ctm, a); + b = fz_transformpoint(ctm, b); + c = fz_transformpoint(ctm, c); + d = fz_transformpoint(ctm, d); + + XDrawLine(xdpy, xwin, xgc, a.x, a.y, b.x, b.y); + XDrawLine(xdpy, xwin, xgc, b.x, b.y, c.x, c.y); + XDrawLine(xdpy, xwin, xgc, c.x, c.y, d.x, d.y); + XDrawLine(xdpy, xwin, xgc, d.x, d.y, a.x, a.y); + } + + XFlush(xdpy); +} + static void handlekey(int c) { int oldpage = pageno; @@ -271,6 +311,7 @@ static void handlekey(int c) case 'a': rotate -= 5; break; case 's': rotate += 5; break; case 'x': dumptext(); break; + case 'o': drawlinks(); break; case '\b': case 'b': @@ -343,6 +384,89 @@ static void handlekey(int c) showpage(); } +static void handlemouse(float x, int y, int btn) +{ + pdf_link *link; + fz_matrix ctm; + fz_point p; + int oid, gen; + int i; + + p.x = x + image->x; + p.y = y + image->y; + + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.max.y)); + ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); + ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate)); + ctm = fz_invertmatrix(ctm); + + p = fz_transformpoint(ctm, p); + + for (link = page->links; link; link = link->next) + { + if (p.x >= link->rect.min.x && p.x <= link->rect.max.x) + if (p.y >= link->rect.min.y && p.y <= link->rect.max.y) + break; + } + + if (link) + { + XDefineCursor(xdpy, xwin, xchand); + + if (btn) + { + if (link->uri) + { + char cmd[2048]; + char buf[2048]; + + printf("goto uri: "); + fz_debugobj(link->uri); + printf("\n"); + + memcpy(buf, fz_tostringbuf(link->uri), fz_tostringlen(link->uri)); + buf[fz_tostringlen(link->uri)] = 0; + + if (getenv("BROWSER")) + { + sprintf(cmd, "%s %s &", getenv("BROWSER"), buf); + system(cmd); + } + } + + if (link->page) + { + oid = fz_tonum(link->page); + gen = fz_togen(link->page); + printf("goto page: %d %d R\n", oid, gen); + + for (i = 0; i < count; i++) + { + if (fz_tonum(pages->pref[i]) == oid) + { + if (histlen + 1 == 256) + { + memmove(hist, hist + 1, sizeof(int) * 255); + histlen --; + } + hist[histlen++] = pageno; + pageno = i + 1; + showpage(); + return; + } + } + } + } + } + else + { + XDefineCursor(xdpy, xwin, xcarrow); + if (btn) + printf("click empty\n"); + } +} + int main(int argc, char **argv) { char *filename; @@ -400,6 +524,14 @@ int main(int argc, char **argv) if (len) handlekey(buf[0]); break; + + case MotionNotify: + handlemouse(xevt.xbutton.x, xevt.xbutton.y, 0); + break; + + case ButtonPress: + handlemouse(xevt.xbutton.x, xevt.xbutton.y, 1); + break; } } #endif |