File size: 4,789 Bytes
9b9424e
 
 
 
2ae8e86
9b9424e
 
 
 
 
 
 
 
 
2ae8e86
 
929115a
9b9424e
 
 
2ae8e86
 
9b9424e
 
 
c8e695f
9b9424e
2ae8e86
9b9424e
 
 
 
2ae8e86
9b9424e
 
 
 
 
 
2ae8e86
9b9424e
 
2ae8e86
9b9424e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ae8e86
 
 
9b9424e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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", "./ts-pdf.mjs")
ipyreact.define_import_map({
    "pdfjs-dist": "https://esm.sh/[email protected]/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/[email protected]/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()