summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamfile1
-rw-r--r--TODO7
-rw-r--r--include/mupdf.h1
-rw-r--r--include/mupdf/annot.h53
-rw-r--r--include/mupdf/page.h2
-rw-r--r--include/mupdf/rsrc.h2
-rw-r--r--include/mupdf/xref.h5
-rw-r--r--mupdf/annot.c194
-rw-r--r--mupdf/nametree.c30
-rw-r--r--mupdf/open.c2
-rw-r--r--mupdf/page.c51
-rw-r--r--mupdf/repair.c2
-rw-r--r--test/x11pdf.c132
13 files changed, 477 insertions, 5 deletions
diff --git a/Jamfile b/Jamfile
index 3fa6c6a0..68867637 100644
--- a/Jamfile
+++ b/Jamfile
@@ -116,6 +116,7 @@ Library libmupdf :
mupdf/nametree.c
mupdf/outline.c
+ mupdf/annot.c
mupdf/pagetree.c
mupdf/store.c
diff --git a/TODO b/TODO
index 6d41f0d9..2cb87096 100644
--- a/TODO
+++ b/TODO
@@ -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