|
<script lang="ts"> |
|
import { tick } from "svelte"; |
|
import PdfUploadText from "./PdfUploadText.svelte"; |
|
import type { Gradio } from "@gradio/utils"; |
|
import { Block, BlockLabel } from "@gradio/atoms"; |
|
import { BaseButton } from "@gradio/button"; |
|
import { File } from "@gradio/icons"; |
|
import { StatusTracker } from "@gradio/statustracker"; |
|
import type { LoadingStatus } from "@gradio/statustracker"; |
|
import type { FileData } from "@gradio/client"; |
|
import { normalise_file } from "@gradio/client"; |
|
import { Upload, ModifyUpload } from "@gradio/upload"; |
|
import pdfjsLib from "pdfjs-dist"; |
|
|
|
export let elem_id = ""; |
|
export let elem_classes: string[] = []; |
|
export let visible = true; |
|
export let value: FileData | null = null; |
|
export let container = true; |
|
export let scale: number | null = null; |
|
export let root: string; |
|
export let height: number | null = 500; |
|
export let label: string; |
|
export let proxy_url: string; |
|
export let min_width: number | undefined = undefined; |
|
export let loading_status: LoadingStatus; |
|
export let gradio: Gradio<{ |
|
change: never; |
|
upload: never; |
|
}>; |
|
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdn.bootcss.com/pdf.js/3.11.174/pdf.worker.js"; |
|
|
|
let _value = value; |
|
let old_value = _value; |
|
let pdfDoc; |
|
let numPages = 1; |
|
let currentPage = 1; |
|
let canvasRef; |
|
|
|
async function handle_clear() { |
|
_value = null; |
|
await tick(); |
|
gradio.dispatch("change"); |
|
} |
|
|
|
async function handle_upload({detail}: CustomEvent<FileData>): Promise<void> { |
|
value = detail; |
|
await tick(); |
|
gradio.dispatch("change"); |
|
gradio.dispatch("upload"); |
|
} |
|
|
|
|
|
async function get_doc(value: FileData) { |
|
const loadingTask = pdfjsLib.getDocument(value.url); |
|
pdfDoc = await loadingTask.promise; |
|
numPages = pdfDoc.numPages; |
|
render_page(); |
|
} |
|
|
|
function render_page() { |
|
|
|
pdfDoc.getPage(currentPage).then(page => { |
|
const ctx = canvasRef.getContext('2d') |
|
ctx.clearRect(0, 0, canvasRef.width, canvasRef.height); |
|
let viewport = page.getViewport({ scale: 1 }); |
|
let scale = height / viewport.height; |
|
viewport = page.getViewport({ scale: scale }); |
|
|
|
const renderContext = { |
|
canvasContext: ctx, |
|
viewport, |
|
}; |
|
canvasRef.width = viewport.width; |
|
canvasRef.height = viewport.height; |
|
page.render(renderContext); |
|
}); |
|
} |
|
|
|
function next_page() { |
|
if (currentPage >= numPages) { |
|
return; |
|
} |
|
currentPage++; |
|
render_page(); |
|
} |
|
|
|
function prev_page() { |
|
if (currentPage == 1) { |
|
return; |
|
} |
|
currentPage--; |
|
render_page(); |
|
} |
|
|
|
$: height = height || 500; |
|
|
|
|
|
|
|
$: _value = normalise_file(value, root, proxy_url); |
|
|
|
|
|
$: if(JSON.stringify(old_value) != JSON.stringify(_value)) { |
|
if (_value){ |
|
get_doc(_value); |
|
} |
|
old_value = _value; |
|
gradio.dispatch("change"); |
|
} |
|
</script> |
|
|
|
<Block {visible} {elem_id} {elem_classes} {container} {scale} {min_width}> |
|
{#if loading_status} |
|
<StatusTracker |
|
autoscroll={gradio.autoscroll} |
|
i18n={gradio.i18n} |
|
{...loading_status} |
|
/> |
|
{/if} |
|
<BlockLabel |
|
show_label={label !== null} |
|
Icon={File} |
|
float={value === null} |
|
label={label || "File"} |
|
/> |
|
{#if _value} |
|
<ModifyUpload i18n={gradio.i18n} on:clear={handle_clear} absolute /> |
|
<div class="pdf-canvas" style="height: {height}px"> |
|
<canvas bind:this={canvasRef}></canvas> |
|
</div> |
|
<div class="button-row"> |
|
<BaseButton on:click={prev_page}> |
|
⬅️ |
|
</BaseButton> |
|
<span class="page-count"> {currentPage} / {numPages} </span> |
|
<BaseButton on:click={next_page}> |
|
➡️ |
|
</BaseButton> |
|
</div> |
|
{:else} |
|
<Upload |
|
on:load={handle_upload} |
|
filetype={"application/pdf"} |
|
file_count="single" |
|
{root} |
|
> |
|
<PdfUploadText/> |
|
</Upload> |
|
{/if} |
|
</Block> |
|
|
|
<style> |
|
.pdf-canvas { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
overflow-y: auto; |
|
} |
|
|
|
.button-row { |
|
display: flex; |
|
flex-direction: row; |
|
width: 100%; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
|
|
.page-count { |
|
margin: 0 10px; |
|
font-family: var(--font-mono); |
|
} |
|
|
|
</style> |