Fix imported PDFs not rendering in the Docker image
nginx has no MIME type for .mjs, so the pdf.js worker was served as application/octet-stream and browsers refused to load it. Every imported PDF page then showed "Source PDF ... is not available", which looked like missing files. Serve .mjs as text/javascript. The page background now also tells the two failures apart: "not stored on this device" when the source bytes are missing, and "could not display" (with the error logged) when pdf.js fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VsorPV3JeJRoZ1mD1pdvtL
This commit is contained in:
parent
24ebbd9056
commit
5d5d86c533
3 changed files with 24 additions and 9 deletions
|
|
@ -1,3 +1,9 @@
|
||||||
|
# This file is included at http level, so this adds to nginx's MIME table.
|
||||||
|
# Browsers refuse to run module scripts (the pdf.js worker) served as octet-stream.
|
||||||
|
types {
|
||||||
|
text/javascript mjs;
|
||||||
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { loadSource, renderPage } from '$lib/pdf/render';
|
import { MissingSourceError, loadSource, renderPage } from '$lib/pdf/render';
|
||||||
import { vault } from '$lib/storage/vault.svelte';
|
import { vault } from '$lib/storage/vault.svelte';
|
||||||
import type { Origin } from '$lib/model/types';
|
import type { Origin } from '$lib/model/types';
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
let { origin, width, height, scale }: Props = $props();
|
let { origin, width, height, scale }: Props = $props();
|
||||||
let canvas: HTMLCanvasElement;
|
let canvas: HTMLCanvasElement;
|
||||||
let failed = $state(false);
|
let failure = $state<'missing' | 'error' | null>(null);
|
||||||
|
|
||||||
// Render at a stepped resolution so zooming doesn't re-render constantly.
|
// Render at a stepped resolution so zooming doesn't re-render constantly.
|
||||||
const STEPS = [0.25, 0.5, 1, 1.5, 2, 3, 4];
|
const STEPS = [0.25, 0.5, 1, 1.5, 2, 3, 4];
|
||||||
|
|
@ -35,9 +35,13 @@
|
||||||
const doc = await loadSource(sourceId, () => vault.getSource(sourceId));
|
const doc = await loadSource(sourceId, () => vault.getSource(sourceId));
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
job = renderPage(doc, pageIndex, canvas, res);
|
job = renderPage(doc, pageIndex, canvas, res);
|
||||||
failed = false;
|
failure = null;
|
||||||
} catch {
|
} catch (e) {
|
||||||
failed = true;
|
if (e instanceof MissingSourceError) failure = 'missing';
|
||||||
|
else {
|
||||||
|
failure = 'error';
|
||||||
|
console.error(`Could not render source PDF “${origin.name}”`, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 120);
|
}, 120);
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -48,9 +52,11 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<canvas bind:this={canvas} class="bg" class:failed></canvas>
|
<canvas bind:this={canvas} class="bg"></canvas>
|
||||||
{#if failed}
|
{#if failure === 'missing'}
|
||||||
<div class="missing">Source PDF “{origin.name}” is not available</div>
|
<div class="missing">Source PDF “{origin.name}” is not stored on this device</div>
|
||||||
|
{:else if failure === 'error'}
|
||||||
|
<div class="missing">Could not display “{origin.name}” (see the browser console)</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,15 @@ pdfjs.GlobalWorkerOptions.workerSrc = workerUrl;
|
||||||
|
|
||||||
const docs = new Map<string, Promise<PDFDocumentProxy>>();
|
const docs = new Map<string, Promise<PDFDocumentProxy>>();
|
||||||
|
|
||||||
|
/** The source PDF's bytes aren't stored on this device (as opposed to pdf.js failing on them). */
|
||||||
|
export class MissingSourceError extends Error {}
|
||||||
|
|
||||||
export function loadSource(sourceId: string, getBytes: () => Promise<Uint8Array | undefined>) {
|
export function loadSource(sourceId: string, getBytes: () => Promise<Uint8Array | undefined>) {
|
||||||
let p = docs.get(sourceId);
|
let p = docs.get(sourceId);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
p = (async () => {
|
p = (async () => {
|
||||||
const bytes = await getBytes();
|
const bytes = await getBytes();
|
||||||
if (!bytes) throw new Error(`Missing source PDF ${sourceId}`);
|
if (!bytes) throw new MissingSourceError(`Missing source PDF ${sourceId}`);
|
||||||
// pdf.js transfers the buffer to its worker, so hand it a copy.
|
// pdf.js transfers the buffer to its worker, so hand it a copy.
|
||||||
return pdfjs.getDocument({ data: bytes.slice() }).promise;
|
return pdfjs.getDocument({ data: bytes.slice() }).promise;
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue