| import solara, ipyreact |
| from pathlib import Path |
| import base64 |
| import requests |
|
|
| def pdf_url_to_base64(url): |
| response = requests.get(url) |
| if response.status_code == 200: |
| pdf_bytes = response.content |
| base64_encoded = base64.b64encode(pdf_bytes).decode("utf-8") |
| return base64_encoded |
| else: |
| print("Failed to fetch PDF from URL:", url) |
| return None |
|
|
|
|
| ipyreact.define_module("ts-pdf", "./tp.mjs") |
| ipyreact.define_import_map({ |
| "pdfjs-dist": "https://esm.sh/pdfjs-dist@2.16.105/build/pdf.worker.js", |
| }) |
|
|
| @solara.component |
| def PDFViewer(type,pdf): |
| if type=="bytes": |
| pdf_bytes=pdf |
| else: |
| pdf_bytes=pdf_url_to_base64(pdf) |
| |
| app = ipyreact.ValueWidget(_esm= |
| """ |
| import React, { useEffect } from 'react'; |
| import { AnnotEventDetail, CustomStampEventDetail, TsPdfViewer, TsPdfViewerOptions } from 'ts-pdf'; |
| |
| const PdfViewerComponent = () => { |
| useEffect(() => { |
| const mobileVhHack = () => { |
| const vh = window.innerHeight * 0.01; |
| document.documentElement.style.setProperty('--vh', `${vh}px`); |
| }; |
| |
| mobileVhHack(); |
| window.addEventListener("resize", mobileVhHack); |
| |
| return () => { |
| window.removeEventListener("resize", mobileVhHack); |
| }; |
| }, []); |
| |
| var arrayBuffer = base64ToArrayBuffer('"""+pdf_bytes+"""'); |
| |
| function base64ToArrayBuffer(base64) { |
| var binaryString = window.atob(base64); |
| var binaryLen = binaryString.length; |
| var bytes = new Uint8Array(binaryLen); |
| for (var i = 0; i < binaryLen; i++) { |
| var ascii = binaryString.charCodeAt(i); |
| bytes[i] = ascii; |
| } |
| return bytes; |
| } |
| var blob = new Blob([arrayBuffer], {type: "application/pdf"}); |
| var link = window.URL.createObjectURL(blob); |
| |
| useEffect(() => { |
| const run = async () => { |
| const options: TsPdfViewerOptions = { |
| containerSelector: "#pdf-main-container", |
| workerSource: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/build/pdf.worker.js", |
| userName: "corran", |
| fileButtons: ["close", "save"], |
| comparableFileButtons: ["open", "close"], |
| annotChangeCallback: (detail: AnnotEventDetail) => { |
| if (detail.type === "focus" || detail.type === "select" || detail.type === "render") { |
| return; |
| } |
| console.log(detail); |
| }, |
| customStampChangeCallback: (detail: CustomStampEventDetail) => { |
| // console.log(JSON.stringify(detail.stamp)); |
| }, |
| }; |
| const viewer = new TsPdfViewer(options); |
| // viewer.importAnnotationsFromJson(dtos); |
| // }, 5000); |
| // for debug |
| window["pdfViewer"] = viewer; |
| await viewer.openPdfAsync(link); |
| }; |
| |
| run(); |
| |
| // Clean up function |
| return () => { |
| // Perform any cleanup of the effect here |
| }; |
| }, []); |
| |
| return ( |
| <div style={{ |
| position: "relative", |
| minWidth: "320px", |
| width: "100vw", |
| height: "calc(var(--vh, 1vh) * 100)", |
| margin: "0", |
| backgroundColor: "var(--tspdf-color-bg)", |
| transition: "height .25s ease" |
| }}> |
| <style> |
| {` |
| .abs-stretch { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| } |
| `} |
| </style> |
| <div className="test-container abs-stretch"> |
| <div id="pdf-main-container" className="abs-stretch"> |
| {/* Your PDF content will be rendered here */} |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
| |
| export default PdfViewerComponent; |
| """) |
| solara.display(app) |
|
|
|
|
| @solara.component |
| def Reader(): |
| solara.Style(""" |
| #app > div > div:nth-child(2) > |
| div:nth-child(2){ |
| display: none; |
| } |
| """) |
| router = solara.use_router() |
| path = router.path |
| if "bytes=" in path: |
| data=path.split("bytes=")[-1] |
| PDFViewer("bytes",data) |
| elif "url=" in path: |
| data=path.split("url=")[-1] |
| PDFViewer("url",data) |
| else: |
| solara.Markdown("Invalid PDF") |
| solara.Markdown(path) |
|
|
| routes = [ |
| solara.Route( |
| "reader", |
| component=Reader, |
| ), |
| ] |
| Reader() |
|
|