wasm: Use JSON to return page links.
authorTor Andersson <[email protected]>
Fri, 7 Aug 2020 16:10:26 +0000 (18:10 +0200)
committerTor Andersson <[email protected]>
Thu, 17 Sep 2020 15:15:26 +0000 (17:15 +0200)
Avoid returning hard-wired HTML strings from the C code.
Generate the HTML from the JSON in the viewer code for more flexibility.

platform/wasm/mupdf-worker.js
platform/wasm/view.html
platform/wasm/wrap.c

index f8366ac728dbb78a61e95f3cc17314c98d3a3b9d..253ad1b677d53bc06e10c9c3c6feae285c847ddc 100644 (file)
@@ -14,8 +14,8 @@ Module.onRuntimeInitialized = function () {
        mupdf.countPages = Module.cwrap('countPages', 'number', ['number']);
        mupdf.pageWidth = Module.cwrap('pageWidth', 'number', ['number', 'number', 'number']);
        mupdf.pageHeight = Module.cwrap('pageHeight', 'number', ['number', 'number', 'number']);
-       mupdf.pageLinks = Module.cwrap('pageLinks', 'string', ['number', 'number', 'number']);
        mupdf.drawPageAsPNG = Module.cwrap('drawPageAsPNG', 'string', ['number', 'number', 'number']);
+       mupdf.pageLinksJSON = Module.cwrap('pageLinks', 'string', ['number', 'number', 'number']);
        mupdf.drawPageAsHTML = Module.cwrap('drawPageAsHTML', 'string', ['number', 'number']);
        mupdf.drawPageAsSVG = Module.cwrap('drawPageAsSVG', 'string', ['number', 'number']);
        mupdf.loadOutline = Module.cwrap('loadOutline', 'number', ['number']);
@@ -76,6 +76,10 @@ mupdf.pageSizes = function (doc, dpi) {
        return list;
 }
 
+mupdf.pageLinks = function (doc, page, dpi) {
+       return JSON.parse(mupdf.pageLinksJSON(doc, page, dpi));
+}
+
 onmessage = function (event) {
        let [ func, args, id ] = event.data;
        if (!ready) {
index b8df055525842f471eb7041b884e3550b7e208d1..f8255baca060a50a52384c9e336e50d999aaeb9d 100644 (file)
@@ -5,19 +5,36 @@
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
 <style>
+
 #outline, #outline ul { margin:0; padding-left:1.2em; }
 #outline a { text-decoration:none; color: black; }
 #outline a:hover { text-decoration:underline; }
 
-#pages img {
-       display:block;
-}
-
-#pages div {
+div.page {
        display:block;
        background-color:white;
        margin:16px auto;
        box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+       position:relative;
+}
+
+div.page img {
+       display:block;
+       position:absolute;
+       user-select:none;
+}
+
+div.links {
+       position:absolute;
+}
+
+div.links a {
+       display:block;
+       position:absolute;
+}
+
+div.links a:hover {
+       border: 1px dotted blue;
 }
 
 /* GRID */
@@ -144,8 +161,6 @@ html, body, div { margin: 0; padding: 0; }
 let doc, pageCount;
 let dirty = [];
 let pageDIV = [];
-let pageIMG = [];
-let pageMAP = [];
 let pageW = [];
 let pageH = [];
 let zoom = 96;
@@ -207,8 +222,6 @@ async function initDocument(data, magic) {
                doc = 0;
                pageCount = 0;
                pageDIV = [];
-               pageIMG = [];
-               pageMAP = [];
                dirty = [];
                emptyNode(document.getElementById("pages"));
                emptyNode(document.getElementById("outline"));
@@ -233,6 +246,7 @@ async function initDocument(data, magic) {
        pagesDiv.scrollTo(0, 0);
        for (let i = 1; i <= pageCount; ++i) {
                let div = pageDIV[i] = document.createElement("div");
+               div.classList.add("page");
                div.id = "page" + i;
                div.style.width = defaultW + 'px';
                div.style.height = defaultH + 'px';
@@ -302,8 +316,6 @@ async function setZoom(newZoom) {
                pageDIV[i].style.width = defaultW + 'px';
                pageDIV[i].style.height = defaultH + 'px';
                emptyNode(pageDIV[i]);
-               pageIMG[i] = null;
-               pageMAP[i] = null;
        }
        if (current)
                pageDIV[current].scrollIntoView();
@@ -316,22 +328,37 @@ function updateView() {
                if (dirty[i] && isVisible(pageDIV[i], 1000)) {
                        console.log("mupdf: drawing page", i);
                        let pageNumber = i;
-                       dirty[i] = false;
-                       let div = pageDIV[i];
+                       dirty[pageNumber] = false;
+                       let div = pageDIV[pageNumber];
                        emptyNode(div);
-                       let img = pageIMG[i] = new Image();
-                       img.useMap = "#map" + i;
+
+                       let img = new Image();
                        img.draggable = false;
+                       // user-select:none disables image.draggable, and we want
+                       // to keep pointer-events for the link image-map
+                       img.ondragstart = function () { return false; }
                        img.onload = function () {
                                pageDIV[pageNumber].style.width = 'min-content';
                                pageDIV[pageNumber].style.height = 'auto';
                        }
                        div.appendChild(img);
-                       let map = pageMAP[i] = document.createElement("map");
-                       map.name = "map" + i;
+
+                       let map = document.createElement("div");
+                       map.classList.add("links");
                        div.appendChild(map);
-                       mupdf.drawPageAsPNG(doc, i, zoom).then(data => pageIMG[pageNumber].src = data);
-                       mupdf.pageLinks(doc, i, zoom).then(data => pageMAP[pageNumber].innerHTML = data);
+                       mupdf.drawPageAsPNG(doc, i, zoom).then(data => img.src = data);
+
+                       mupdf.pageLinks(doc, pageNumber, zoom).then(data => {
+                               for (let link of data) {
+                                       let a = document.createElement("a");
+                                       a.href = link.href;
+                                       a.style.left = link.x + 'px';
+                                       a.style.top = link.y + 'px';
+                                       a.style.width = link.w + 'px';
+                                       a.style.height = link.h + 'px';
+                                       map.appendChild(a);
+                               }
+                       });
                }
        };
 }
index 11385865c900f7ef420d2ae34dfe2d7d7bc578eb..33fc3499ae6ff7c6e30824be0420d1de06464eb0 100644 (file)
@@ -179,26 +179,37 @@ char *pageLinks(fz_document *doc, int number, float dpi)
 
        buf = fz_new_buffer(ctx, 0);
        {
+               fz_append_string(ctx, buf, "[");
                links = fz_load_links(ctx, lastPage);
                {
                        for (link = links; link; link = link->next)
                        {
                                fz_irect bbox = fz_round_rect(fz_transform_rect(link->rect, fz_scale(dpi/72, dpi/72)));
-                               fz_append_printf(ctx, buf, "<area shape=\"rect\" coords=\"%d,%d,%d,%d\"",
-                                       bbox.x0, bbox.y0, bbox.x1, bbox.y1);
+                               fz_append_string(ctx, buf, "{");
+                               fz_append_printf(ctx, buf, "%q:%d,", "x", bbox.x0);
+                               fz_append_printf(ctx, buf, "%q:%d,", "y", bbox.y0);
+                               fz_append_printf(ctx, buf, "%q:%d,", "w", bbox.x1 - bbox.x0);
+                               fz_append_printf(ctx, buf, "%q:%d,", "h", bbox.y1 - bbox.y0);
                                if (fz_is_external_link(ctx, link->uri))
-                                       fz_append_printf(ctx, buf, " href=\"%s\">\n", link->uri);
+                               {
+                                       fz_append_printf(ctx, buf, "%q:%q", "href", link->uri);
+                               }
                                else
                                {
-                                       fz_location linkLoc = fz_resolve_link(ctx, doc, link->uri, NULL, NULL);
-                                       int linkNumber = fz_page_number_from_location(ctx, doc, linkLoc);
-                                       fz_append_printf(ctx, buf, " href=\"#page%d\">\n", linkNumber+1);
+                                       fz_location link_loc = fz_resolve_link(ctx, doc, link->uri, NULL, NULL);
+                                       int link_page = fz_page_number_from_location(ctx, doc, link_loc);
+                                       fz_append_printf(ctx, buf, "%q:\"#page%d\"", "href", link_page+1);
                                }
+                               fz_append_string(ctx, buf, "}");
+                               if (link->next)
+                                       fz_append_string(ctx, buf, ",");
                        }
                }
-               fz_append_byte(ctx, buf, 0);
+               fz_append_string(ctx, buf, "]");
                fz_drop_link(ctx, links);
        }
+
+       fz_terminate_buffer(ctx, buf);
        fz_buffer_extract(ctx, buf, &data);
        fz_drop_buffer(ctx, buf);