belinghy / PDFRefPreview

A boorkmarklet for previewing citations and other internal links in PDFs on mouse hover
91 stars 6 forks source link

Preview doesn't take x coordinate into account #2

Open MortenHannemose opened 1 year ago

MortenHannemose commented 1 year ago

Thank you for writing this code. I was looking for something exactly like this. When I mouse over a reference that's on the right side of the page, it still shows the entire page (unlike in the preview gif you have, where it only shows the right part of the page). If I zoom in and click the link, I'm taken to the correct place in both x and y. image For reference the PDF is https://arxiv.org/pdf/1711.07971.pdf and I'm on Firefox 107.0 (latest version as of this post) Another difference to your gif is that the top left corner of the preview does not coincide with the mouse location. In both cases, the gif is the more desirable behavior.

belinghy commented 1 year ago

Thanks! Glad you found it useful.

To have the behaviour you described, use the bookmarklet below. For reference, if others want to make adjust the code to their preferences, the code below is a few lines change from the default behaviour here and here.

javascript:(async () => {
  const app = window.PDFViewerApplication;
  const anchor = document.getElementById("viewer");
  if ("_previewHandler" in app) {
    anchor.removeEventListener("mouseover", app._previewHandler);
    delete app._previewHandler;
    delete app._previewing;
    return;
  }

  const box = anchor.getBoundingClientRect();
  const halfWidth = (box.left + box.right) / 2;
  const destinations = await app.pdfDocument.getDestinations();
  app._previewing = false;

  async function mouseoverHandler(event) {
    if (event.target.className != "internalLink" || app._previewing) return;

    const hash = event.target.hash;
    const parent = event.target.parentElement;

    const preview = document.createElement("canvas");
    const previewStyle = preview.style;
    previewStyle.border = "1px solid black";
    previewStyle.direction = "ltr";
    previewStyle.position = "fixed";
    previewStyle.zIndex = "2";
    previewStyle.top = `${event.clientY + 4}px`;
    previewStyle.boxShadow = "5px 5px 5px black, -5px 5px 5px black";

    const namedDest = decodeURIComponent(hash.substring(1));
    const explicitDest =
      namedDest in destinations
        ? destinations[namedDest]
        : JSON.parse(namedDest);
    const pageNumber = app.pdfLinkService._cachedPageNumber(explicitDest[0]);

    app.pdfDocument.getPage(pageNumber).then(function (page) {
      const tempViewport = page.getViewport({ scale: 1.0 });
      const height = tempViewport.height * 1.2 * app.pdfViewer.currentScale;
      const width = tempViewport.width * 1.2 * app.pdfViewer.currentScale;
      previewStyle.height = `${height}px`;
      previewStyle.width = `${width}px`;
      previewStyle.left = `${event.clientX - 4}px`;

      let offsetX, offsetY;
      switch (explicitDest[1].name) {
        case "XYZ":
          offsetX = -explicitDest[2];
          offsetY = explicitDest[3];
          break;
        case "FitH":
        case "FitBH":
        case "FitV":
        case "FitBV":
          offsetY = explicitDest[2];
          break;
        default:
          console.log(`Oops, link ${explicitDest[1].name} is not supported.`);
      }

      const scale = 4;
      const viewport = page.getViewport({
        scale: scale,
        offsetX: offsetX * scale,
        offsetY: (offsetY - tempViewport.height) * scale,
      });

      preview.height = viewport.height;
      preview.width = viewport.width;

      const renderContext = {
        canvasContext: preview.getContext("2d"),
        viewport: viewport,
      };
      page.render(renderContext);
    });

    anchor.prepend(preview);
    app._previewing = true;

    parent.addEventListener("mouseleave", function (event) {
      preview.remove();
      app._previewing = false;
    });
  }
  anchor.addEventListener("mouseover", mouseoverHandler);
  app._previewHandler = mouseoverHandler;
})();
MortenHannemose commented 1 year ago

Ah, perfect. Thanks so much for sharing this. It's also much easier to edit the not minified code 😄 I also have a minor comment/something I was wondering. It seems that FitH and FitBH are the only modes that can specify a y-offset, and that if FitV and FitBV specify an offset, it's an offset in the x-direction. However, I've not encountered them in a real PDF, so this is only based on what I've read in the documentation.

MortenHannemose commented 1 year ago

And increasing the zIndex to a large value such as 99 is necessary for some PDFs