rendering with pdf.js working
This commit is contained in:
parent
34d2c05f21
commit
0e1e7c359b
@ -7,8 +7,15 @@
|
||||
import ContentModel from "../models/ContentModel.svelte";
|
||||
|
||||
let {fState, height}: {fState: FileViewState, height: number} = $props();
|
||||
let state: PageViewState | undefined = $derived(fState.pageViewState);
|
||||
let state: PageViewState = new PageViewState(fState, render);
|
||||
let contents: ContentModel | undefined = $derived(state?.contents)
|
||||
let display: HTMLCanvasElement;
|
||||
fState.pageViewState = state;
|
||||
|
||||
function render() {
|
||||
console.log("render", display)
|
||||
state.loadImage(display);
|
||||
}
|
||||
|
||||
</script>
|
||||
{#if state}
|
||||
@ -19,7 +26,7 @@
|
||||
</div>
|
||||
</Pane>
|
||||
<Pane minSize={1}>
|
||||
<RenderedPageView img={state.img_data} {height}></RenderedPageView>
|
||||
<RenderedPageView bind:img={display} {height}></RenderedPageView>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
{:else }
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
<script lang="ts">
|
||||
import ZoomableContainer from "./ZoomableContainer.svelte";
|
||||
import { RefreshOutline } from "flowbite-svelte-icons";
|
||||
import {RawImageData} from "../models/RawImageData";
|
||||
|
||||
let { img, height }: { img: RawImageData | undefined; height: number } =
|
||||
let { img = $bindable(), height }: { img: HTMLCanvasElement; height: number } =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<div class="page-container" style:height={height + "px"}>
|
||||
{#if !img}
|
||||
<div class="loading-container">
|
||||
<RefreshOutline class="animate-spin" size="xl" />
|
||||
</div>
|
||||
{:else}
|
||||
<ZoomableContainer {img} {height} />
|
||||
{/if}
|
||||
<!--{#if !img}-->
|
||||
<!-- <div class="loading-container">-->
|
||||
<!-- <RefreshOutline class="animate-spin" size="xl" />-->
|
||||
<!-- </div>-->
|
||||
<!--{:else}-->
|
||||
<canvas bind:this={img} class="bg-forge-dark">
|
||||
|
||||
</canvas>
|
||||
<!-- <ZoomableContainer imgUrl={img} {height} />-->
|
||||
<!-- {/if}-->
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
zoomStep?: number;
|
||||
img: RawImageData;
|
||||
imgUrl: string;
|
||||
height?: string | number;
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
minZoom = 0.5,
|
||||
maxZoom = 10,
|
||||
zoomStep = 0.1,
|
||||
img,
|
||||
imgUrl,
|
||||
height,
|
||||
}: ZoomableProps = $props();
|
||||
|
||||
@ -134,7 +134,7 @@
|
||||
>
|
||||
<img
|
||||
alt="rendered-page"
|
||||
src={img.toObjectUrl()}
|
||||
src={imgUrl}
|
||||
style:max-height={height + "px"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,40 @@
|
||||
import {fromBase64Util} from "pdfjs-dist/types/src/shared/util";
|
||||
import {getDocument} from "pdfjs-dist";
|
||||
import {getDocument, GlobalWorkerOptions, type PDFDocumentProxy} from "pdfjs-dist";
|
||||
import type {RenderParameters} from "pdfjs-dist/types/src/display/api";
|
||||
|
||||
export default class Document {
|
||||
|
||||
|
||||
doc: Promise<PDFDocumentProxy>;
|
||||
constructor(bytesB64: string) {
|
||||
let bytes = fromBase64Util(bytesB64)
|
||||
let loadingTask = getDocument({ data: bytes });
|
||||
let doc = await loadingTask.promise
|
||||
let dict: Dict = new Dict()
|
||||
GlobalWorkerOptions.workerSrc = new URL('/pdf.worker.min.mjs', import.meta.url).toString();
|
||||
let loadingTask = getDocument({ data: atob(bytesB64) });
|
||||
this.doc = loadingTask.promise
|
||||
}
|
||||
|
||||
public async render_page(num: number, canvas: HTMLCanvasElement) {
|
||||
console.log("render");
|
||||
let doc = await this.doc;
|
||||
let page = await doc.getPage(num);
|
||||
const outputScale = window.devicePixelRatio || 1;
|
||||
const scale = 1.5;
|
||||
let viewport = page.getViewport({ scale: scale, });
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
canvas.style.width = Math.floor(canvas.width) + "px";
|
||||
canvas.style.height = Math.floor(canvas.height) + "px";
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) return "";
|
||||
const transform = [1, 0, 0, 1, 0, 0];
|
||||
// viewport = page.getViewport({ scale: 0.7, offsetX: canvas.width / 4, offsetY: canvas.height / 4});
|
||||
|
||||
const renderContext: RenderParameters = {
|
||||
canvasContext: context,
|
||||
transform: transform,
|
||||
viewport: viewport,
|
||||
};
|
||||
let renderTask = page.render(renderContext);
|
||||
context.fillStyle = '#ffffff';
|
||||
context.fillRect(viewport.offsetX, viewport.offsetY, viewport.width, viewport.height);
|
||||
await renderTask.promise
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,7 +33,6 @@ export default class FileViewState {
|
||||
public xref_entries: XRefEntry[] = $state([]);
|
||||
|
||||
public treeState: TreeViewState | undefined = $state();
|
||||
pageViewStates: Map<number, PageViewState> = new Map();
|
||||
public pageViewState: PageViewState | undefined = $state();
|
||||
|
||||
public notifications: ForgeNotification[] = $state([]);
|
||||
@ -84,11 +83,6 @@ export default class FileViewState {
|
||||
this.loadXrefEntries();
|
||||
this.treeState?.reload();
|
||||
await this.selectPath(this.path);
|
||||
this.pageViewStates.clear();
|
||||
if (this.pageViewState) {
|
||||
this.pageViewState.reload()
|
||||
this.pageViewStates.set(this.pageViewState.page_num, this.pageViewState);
|
||||
}
|
||||
}
|
||||
|
||||
public logError(message: string) {
|
||||
@ -256,21 +250,15 @@ export default class FileViewState {
|
||||
}
|
||||
|
||||
|
||||
private async reloadPageState(page_num: number) {
|
||||
|
||||
this.pageViewState = new PageViewState(page_num, this);
|
||||
this.pageViewStates.set(page_num, this.pageViewState);
|
||||
public getCurrentPageNumber() : number | undefined {
|
||||
if (!(this.container_prim && this.container_prim.isPage())) return undefined;
|
||||
return this.container_prim.getPageNumber();
|
||||
}
|
||||
|
||||
private async loadPageState() {
|
||||
if (!(this.container_prim && this.container_prim.isPage())) return;
|
||||
let page_num = this.container_prim.getPageNumber();
|
||||
|
||||
if (this.pageViewState && this.pageViewState.page_num === page_num) return;
|
||||
this.pageViewState = this.pageViewStates.get(page_num);
|
||||
|
||||
if (!this.pageViewState) {
|
||||
await this.reloadPageState(page_num);
|
||||
let page_num = this.getCurrentPageNumber();
|
||||
if (page_num) {
|
||||
await this.pageViewState?.load(page_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,64 +1,67 @@
|
||||
import ContentModel from "./ContentModel.svelte";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {RawImageData} from "./RawImageData";
|
||||
import type FileViewState from "./FileViewState.svelte";
|
||||
import {Mutex} from "async-mutex";
|
||||
|
||||
export class PageViewState {
|
||||
file_id: string;
|
||||
page_num: number;
|
||||
fState: FileViewState;
|
||||
|
||||
img_data: RawImageData | undefined = $state();
|
||||
|
||||
display_lock = new Mutex();
|
||||
contents: ContentModel | undefined = $state(undefined);
|
||||
|
||||
constructor(page_num: number, fState: FileViewState) {
|
||||
render: () => void;
|
||||
constructor(fState: FileViewState, render: () => void) {
|
||||
this.file_id = fState.file.id;
|
||||
this.page_num = page_num;
|
||||
this.fState = fState;
|
||||
|
||||
this.load();
|
||||
this.render = render;
|
||||
const num = fState.getCurrentPageNumber();
|
||||
if (num) {
|
||||
this.load(num);
|
||||
}
|
||||
}
|
||||
|
||||
private logError(err: string) {
|
||||
this.fState.logError(err);
|
||||
}
|
||||
|
||||
public async reload() {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
public async load() {
|
||||
invoke<ContentModel>("get_contents", {id: this.file_id, path: "Page" + this.page_num})
|
||||
public async load(page_num: number) {
|
||||
console.log("load", page_num)
|
||||
this.render();
|
||||
|
||||
invoke<ContentModel>("get_contents", {id: this.file_id, path: "Page" + page_num})
|
||||
.then((result) => {
|
||||
this.contents = undefined;
|
||||
this.contents = new ContentModel(result.parts);
|
||||
this.loadImage();
|
||||
})
|
||||
.catch(this.logError);
|
||||
}
|
||||
|
||||
public async loadImage() {
|
||||
this.img_data?.dispose();
|
||||
this.img_data = undefined;
|
||||
let result = await invoke<ArrayBuffer>("get_page_by_num", {
|
||||
id: this.file_id,
|
||||
num: this.page_num,
|
||||
})
|
||||
.catch(this.logError);
|
||||
if (result) {
|
||||
this.img_data = new RawImageData(result);
|
||||
}
|
||||
public async loadImage(display: HTMLCanvasElement) {
|
||||
console.log("load", display)
|
||||
this.display_lock.acquire().then(release => {
|
||||
try {
|
||||
let page_num = this.fState.getCurrentPageNumber();
|
||||
if (!page_num) return;
|
||||
this.fState.renderer.render_page(page_num, display)
|
||||
} finally {
|
||||
release()
|
||||
}
|
||||
}
|
||||
)
|
||||
this.display_lock.release();
|
||||
}
|
||||
|
||||
public handleSave(newData: string) {
|
||||
let page_num = this.fState.getCurrentPageNumber();
|
||||
if (!page_num) return;
|
||||
let contents = ContentModel.fromDisplay(newData);
|
||||
console.log(contents);
|
||||
invoke<ContentModel>("update_contents", {id: this.file_id, path: "Page" + this.page_num, contents: contents})
|
||||
invoke<ContentModel>("update_contents", {id: this.file_id, path: "Page" + page_num, contents: contents})
|
||||
.then((result) => {
|
||||
this.contents = undefined;
|
||||
this.fState.reload();
|
||||
})
|
||||
.catch(this.logError);
|
||||
}
|
||||
|
||||
}
|
||||
@ -54,7 +54,7 @@ export default class TreeViewState {
|
||||
result.push(entry);
|
||||
}
|
||||
totalIndex += 1;
|
||||
if (totalIndex >= end) {
|
||||
if (totalIndex >= end + 5) {
|
||||
return [result, stickies];
|
||||
}
|
||||
}
|
||||
|
||||
21
static/pdf.min.mjs
Normal file
21
static/pdf.min.mjs
Normal file
File diff suppressed because one or more lines are too long
21
static/pdf.worker.min.mjs
Normal file
21
static/pdf.worker.min.mjs
Normal file
File diff suppressed because one or more lines are too long
@ -3,7 +3,6 @@ import { sveltekit } from "@sveltejs/kit/vite";
|
||||
// import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
||||
// @ts-expect-error process is a nodejs global
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user