# PDF Viewer Standalone PDF viewer built on Mozilla's **pdf.js** library (loaded through the SGApps `thirdparty/convert/pdf/reader` module from `application-prototype-thirdparty`). Opens a PDF from any URL (public or SGApps `fs://`) and renders each page onto its own canvas at device-pixel-ratio resolution, with a simple toolbar for zoom and fit-to-width. Internally this is the **Code Editor** webapp launched with a chrome-less preset: no sidebar, no top toolbars, no tab bar, light theme, and the PDF tab type pre-selected. Each page renders eagerly on open; large documents may take a moment to finish. ## Live Demo ## Embed ```html ``` ### Open with a PDF URL Encode the PDF URL in base64 and append it as `/url/`: ```js var pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; var src = "https://sgapps.io/online/webapp/pdf-browser/url/" + btoa(pdfUrl); // → opens that PDF in the viewer on first load ``` ## API Events PDF Viewer inherits the full Code Editor API surface — the active tab is a PDF-type tab, so the Monaco-specific calls (`active-file:codeEditor:value`, `:syntax`, etc.) return "No active editor" for a pdf tab. The following work: ### `files:open` -- Open a PDF in a New Tab ```js socket.fire("webapp::instance::request", "files:open", "/files/my-document.pdf", "pdf", function (err, tabId) { if (err) console.error(err); else console.log("Opened as tab", tabId); } ); ``` Equivalent object form: ```js socket.fire("webapp::instance::request", "files:open", { type: "pdf", path: "/files/my-document.pdf" }, function (err, tabId) { /* ... */ } ); ``` ### `files:list` -- List Open Tabs ```js socket.fire("webapp::instance::request", "files:list", function (err, tabs) { console.log(tabs); // [{ index, name, path, type: "pdf", title }, ...] } ); ``` ### `files:active:set` -- Switch Active Tab ```js socket.fire("webapp::instance::request", "files:active:set", 1, function (err) { /* ... */ }); ``` ### `files:close:all` -- Close All Tabs ```js socket.fire("webapp::instance::request", "files:close:all", function () { console.log("All tabs closed"); }); ``` ### `pdf:sidebar` -- Show / Hide the Page Thumbnails The sidebar lists a thumbnail per page (drawn when it scrolls into view); a click goes to that page and the current page is highlighted. It is shown by default when the viewer is at least 600 px wide. Pass `true` / `false` to show / hide it, or only a callback to read its state. Answers `"The active tab is not a PDF"` when another kind of tab is active. | Arg | Type | Description | |-----|------|-------------| | `show` | boolean (optional) | `true` = show, `false` = hide | | `callback` | function | `(err, shown: boolean)` | ```js socket.fire("webapp::instance::request", "pdf:sidebar", false, function (err, shown) { console.log("thumbnails shown:", shown); }); ``` ### `fs:write` -- Write a PDF to SGApps fs Combine with `files:open` to open an in-memory PDF without a public URL: ```js // `bytes` must be ArrayBuffer -- WindowSocket's encoder doesn't carry Blob/File. fetch("local-doc.pdf").then(function (r) { return r.arrayBuffer(); }).then(function (bytes) { socket.fire("webapp::instance::request", "fs:write", "input://memory/doc.pdf", bytes, function (err) { if (err) return console.error(err); socket.fire("webapp::instance::request", "files:open", "input://memory/doc.pdf", "pdf"); } ); }); ``` See [Code Editor](/apps/code-editor.md) for the full `fs:*` API, and [Embedding](/embedding.md) for the ArrayBuffer caveat. ## Window Chrome Controls Hide the chrome entirely for a fullscreen viewer: ```js socket.fire("webapp::instance::embed-mode", true); ``` Or granular (see [Embedding](/embedding.md)): ```js socket.fire("webapp::instance::window:controls", false); socket.fire("webapp::instance::window:maximize", true); ``` ## Supported Mime Types Registered for: `application/pdf`, `application/x-pdf`, `application/acrobat`, `applications/vnd.pdf`, `text/pdf`, `text/x-pdf`. Opens from the SGApps window manager when a PDF is double-clicked in File Manager. ## Features - Canvas-based pdfjs render (device-pixel-ratio aware, re-fits on container resize) - Toolbar: page thumbnails on / off, current page (`Page 3 / 14`), zoom in / zoom out / fit-width, Download - Sidebar with page thumbnails: click to go to a page, the current page is highlighted while scrolling (`pdf:sidebar` to show / hide it) - Download saves the PDF under its own file name, from the copy already loaded (no second request, so PDFs from other sites work too) - Opens from any URL (public web or SGApps `fs://` paths through `fs.readStreamUrl`) - Multiple PDFs in parallel tabs (same code-editor tab bar — hidden by default, re-enable by removing `--no-files`) - Launch via URL argument: `/online/webapp/pdf-browser/url/` - Full inherited `files:*` and `fs:*` API from Code Editor - pdfjsLib loaded via `thirdparty/convert/pdf/reader` (version-pinned with the rest of the SGApps third-party stack, no raw CDN pulls)