LaTeX-Workshop/viewer
2023-06-05 02:10:12 +12:00
..
components Update viewer to pdf.js v3.6.172 2023-05-24 12:45:11 +08:00
images Update viewer to pdf.js v3.6.172 2023-05-24 12:45:11 +08:00
locale Update viewer to pdf.js v3.6.172 2023-05-24 12:45:11 +08:00
favicon.ico Update favicon of web pdf viewer 2019-03-06 14:06:14 +08:00
latexworkshop.css Fix #3902 Tweak viewer button sizes 2023-06-05 02:10:12 +12:00
latexworkshop.ts Fix #3749 Viewer pages do not overflow 2023-03-07 21:26:32 +08:00
README.md Update to PDF.js v2.15.349 2022-08-31 07:52:28 +09:00
tsconfig.json Support pageColorsForeground and pageColorsBackground for the internal PDF viewer 2022-05-29 09:58:48 +09:00
viewer.css Update viewer to pdf.js v3.6.172 2023-05-24 12:45:11 +08:00
viewer.html Update viewer to pdf.js v3.6.172 2023-05-24 12:45:11 +08:00
viewer.js Update viewer to pdf.js v3.6.172 2023-05-24 12:45:11 +08:00

Overview

The pdf viewer is based on PDF.js by Mozilla Foundation. Files are from the prebuilt download version. See a related issue.

We load viewer.html of PDF.js in iframe in Webview of VS Code through a local Web server. The Webview's content including the iframe tag is generated by Viewer.getPDFViewerContent. On the browser, we directly load viewer.html. This architecture enables us to load the same viewer.html both on VS Code and on the browser.

We control the PDF viewers on both the tab and the browser through WebSockets in the same way. The handler on the server side is defined as Viewer.handler.

We provide additional features by setting up new event listeners in latexworkshop.ts for DOM objects in viewer.html. We do not and should not override functions defined by PDF.js.

We can see all the changes, diff, we have made to viewer.js. We had better find a way to achieve this without modifying viewer.js.

Restoring the PDF viewer

To restore the PDF viewer at the startup of VS Code, we use the serialization of Webview. The state of the PDF viewer is sent with window.parent.postMessage in latexworkshop.ts. We can see the handling of saving the state in HTML generated by Viewer.getPDFViewerContent.

Since it is difficult to map WebSockets to tabs of VS Code, we do not use WebSockets to send the state of the PDF viewer.

Building

JavaScript files, latexworkshop.js, and others, are generated in ../out/viewer/ from TypeScript files for ES2015 modules.

Limitation

We cannot use Node.js API and VS Code API in latexworkshop.ts since it is executed in the context of the browser. We cannot import TypeScript files under ../src either.

Misc

Mozilla asks web developers to reskin viewer.html because Firefox users would think bugs of the viewer on the web site are ones of Firefox and would report them to the PDF.js team. See link. Our usage does not cause such a problem.

PDFWorker

By default, PDFWorker is recreated every time reopening a PDF file. By setting workerPort, we can reuse the PDFWorker:

  workerPort: {
    value: new Worker('/build/pdf.worker.js'),
    kind: OptionKind.WORKER
  },

See mozilla/pdf.js/pull/8107 for the details of the setting.

Architecture

flowchart TB
  subgraph ExtensionHost["VS Code Extension Host"]
    LW["LaTeX Workshop"]
    Server["Server for PDF viewer \n(Files and WebSocket)\n127.0.0.1"]
    LW --- Server
  end
  subgraph VSCode["VS Code"]
    subgraph WebView["WebView (parent iframe)"]
      PDFViewer["PDF viewer (viewer.html)"]
    end
  end
  Server <--> PDFViewer
  Server <--> Browser
  subgraph Browser
    PDFViewerB["PDF viewer (viewer.html)"]
  end

VS Code Remote Development

flowchart TB
  subgraph Remote["Remote machine or container"]
    subgraph ExtensionHost["VS Code Server (Extension Host)"]
      LW["LaTeX Workshop"]
      Server["Server for PDF viewer\n(Files and WebSocket)\n127.0.0.1 at remote"]
      LW --- Server
    end
  end
  subgraph Local["Local machine"]
    subgraph VSCode["VS Code"]
      PortForwarder["Port forwarder\n127.0.0.1 at local"]
      subgraph WebView["WebView (parent iframe)"]
        PDFViewer["PDF viewer (viewer.html)"]
      end
    end
    subgraph Browser
      PDFViewerB["PDF viewer (viewer.html)"]
    end
  end
  PortForwarder <--> PDFViewer
  PortForwarder <--> Browser
  Server <--> PortForwarder

Sequence diagrams

Opening a PDF file

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Server as WebSocket Server
  participant WebServer as Web Server
  Note over Viewer: load viewer.html
  Note over Viewer: load latexworkshop.js
  Viewer-)Server: open
  Viewer->>WebServer: fetch /config.json
  Note over Viewer: load viewer.js
  Note over Viewer: webviewerloaded
  Viewer->>Viewer: Set PDFViewerApplicationOptions
  Note over Viewer: pagesinit
  Note over Viewer: documentloaded
  Viewer->>Viewer: Apply params
  Note over Viewer: pagesloaded
  Viewer-)Server: loaded

Reloading a PDF file

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Server as WebSocket Server
  Server-)Viewer: refresh
  Note over Viewer: pagesinit
  Note over Viewer: documentloaded
  Note over Viewer: pagesloaded
  Viewer-)Server: loaded

Restoring the internal PDF viewer

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Server as WebSocket Server
  participant WebServer as Web Server
  participant Iframe as Parent iframe (VS Code WebView)
  Note over Viewer: load viewer.html
  Note over Viewer: load latexworkshop.js
  Viewer-)Server: open
  Viewer->>WebServer: fetch /config.json
  Note over Viewer: load viewer.js
  Note over Viewer: webviewerloaded
  Viewer->>Viewer: Set PDFViewerApplicationOptions
  Note over Viewer: pagesinit
  Note over Viewer: documentloaded
  Viewer->>Viewer: Apply params
  Viewer-)+Iframe: initialized
  Iframe--)-Viewer: restore_state
  Viewer->>Viewer: Restore state
  Note over Viewer: pagesloaded
  Viewer-)Server: loaded

Forward SyncTeX

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Server as WebSocket Server
  Server-)Viewer: synctex

Backward SyncTeX

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Server as WebSocket Server
  Viewer-)Server: reverse_synctex

When the internal PDF viewer status changed

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Iframe as Parent iframe (VS Code WebView)
  participant ExtensionHost as Extension Host
  Viewer-)Iframe: state
  Iframe->>Iframe: Store the state
  Iframe-)ExtensionHost: state

KeyboardEvent

sequenceDiagram
  participant Viewer as PDF Viewer
  participant Iframe as Parent iframe (VS Code WebView)
  Viewer-)Iframe: KeyboardEvent
  Iframe-)Iframe: Dispatch KeyboardEvent

Events emitted by viewer.js

When opening a PDF file. In order.

  1. webviewerloaded
  2. DOMContentLoaded (not by viewer.js)
  3. baseviewerinit
  4. pagesinit
  5. documentloaded
  6. documentinit
  7. PDF da551cb... [...] (PDF.js: 2.2.228) (a log message is output)
  8. pagerendered
  9. pagesloaded
  10. textlayerrendered
  11. pagerendered
  12. textlayerrendered

When reloading a PDF file. In order.

  1. pagesinit
  2. documentloaded
  3. documentinit
  4. PDF da551cb... [...] (PDF.js: 2.2.228) (a log message is output)
  5. pagerendered
  6. pagesloaded
  7. textlayerrendered
  8. pagerendered
  9. textlayerrendered

GitHub Codespaces

flowchart TB
  subgraph Remote["Remote machine"]
    subgraph ExtensionHost["VS Code Server (Extension Host)"]
      LW["LaTeX Workshop"]
      Server["Server for PDF viewer\n(Files and WebSocket)\n127.0.0.1 at remote"]
      LW --- Server
    end
    PortForwarder
  end
  GitHub["github.dev"]
  GitHubPreview["githubpreview.dev"]
  subgraph Browser
    subgraph WebView["parent iframe"]
      PDFViewer["PDF viewer (viewer.html)"]
    end
  end
  ExtensionHost <--> GitHub
  Server <--> PortForwarder
  PortForwarder <--> GitHubPreview
  GitHub <--> Browser
  GitHubPreview <--> PDFViewer