Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,102 +1,57 @@
|
|
| 1 |
-
#
|
| 2 |
import base64
|
| 3 |
-
import
|
|
|
|
| 4 |
import gradio as gr
|
| 5 |
|
| 6 |
|
| 7 |
-
def
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
b64 = base64.b64encode(raw).decode()
|
| 22 |
-
out.append(f"data:image/{suffix};base64,{b64}")
|
| 23 |
-
return out
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def render_img(b64_list, idx, scale):
|
| 27 |
-
"""根据当前索引 idx 和缩放倍数 scale 渲染 HTML。"""
|
| 28 |
-
if not b64_list:
|
| 29 |
-
return "<p style='color:gray'>请先上传图片</p>"
|
| 30 |
-
idx %= len(b64_list) # 防止越界
|
| 31 |
-
src = b64_list[idx]
|
| 32 |
-
return (
|
| 33 |
-
f'<div style="overflow:auto;border:1px solid #ccc;'
|
| 34 |
-
f'width:100%;height:800px;text-align:center">'
|
| 35 |
-
f'<img src="{src}" '
|
| 36 |
-
f'style="transform:scale({scale});transform-origin:0 0;" />'
|
| 37 |
-
f'</div>'
|
| 38 |
-
)
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
with gr.Blocks(title="多图预览 + 缩放 + 翻页") as demo:
|
| 42 |
-
gr.Markdown("## 🖼️ 多张图片翻页预览 Demo")
|
| 43 |
-
|
| 44 |
-
# 用户上传
|
| 45 |
-
uploader = gr.File(
|
| 46 |
-
file_types=[".png", ".jpg", ".jpeg"],
|
| 47 |
-
file_count="multiple",
|
| 48 |
-
label="上传多张图片",
|
| 49 |
-
)
|
| 50 |
-
|
| 51 |
-
# 缩放 & 翻页 控件
|
| 52 |
with gr.Row():
|
| 53 |
-
|
| 54 |
-
next_btn = gr.Button("下一张 ➡️")
|
| 55 |
zoom = gr.Slider(0.5, 3, value=1, step=0.1, label="缩放倍数")
|
| 56 |
|
| 57 |
-
|
| 58 |
-
viewer = gr.HTML()
|
| 59 |
-
|
| 60 |
-
# ---- 状态变量 ----
|
| 61 |
-
img_list_state = gr.State([]) # base64 列表
|
| 62 |
-
idx_state = gr.State(0) # 当前索引
|
| 63 |
-
|
| 64 |
-
# ---- 事件逻辑 ----
|
| 65 |
-
def upload_handler(files):
|
| 66 |
-
b64s = files_to_b64(files)
|
| 67 |
-
return b64s, 0, render_img(b64s, 0, 1)
|
| 68 |
-
|
| 69 |
-
uploader.change(
|
| 70 |
-
upload_handler,
|
| 71 |
-
inputs=uploader,
|
| 72 |
-
outputs=[img_list_state, idx_state, viewer],
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
def show_prev(b64s, idx, scale):
|
| 76 |
-
idx -= 1
|
| 77 |
-
return idx, render_img(b64s, idx, scale)
|
| 78 |
-
|
| 79 |
-
prev_btn.click(
|
| 80 |
-
show_prev,
|
| 81 |
-
inputs=[img_list_state, idx_state, zoom],
|
| 82 |
-
outputs=[idx_state, viewer],
|
| 83 |
-
)
|
| 84 |
-
|
| 85 |
-
def show_next(b64s, idx, scale):
|
| 86 |
-
idx += 1
|
| 87 |
-
return idx, render_img(b64s, idx, scale)
|
| 88 |
-
|
| 89 |
-
next_btn.click(
|
| 90 |
-
show_next,
|
| 91 |
-
inputs=[img_list_state, idx_state, zoom],
|
| 92 |
-
outputs=[idx_state, viewer],
|
| 93 |
-
)
|
| 94 |
|
| 95 |
-
#
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
inputs=[img_list_state, idx_state, zoom],
|
| 99 |
-
outputs=viewer,
|
| 100 |
-
)
|
| 101 |
|
| 102 |
-
demo.launch(
|
|
|
|
| 1 |
+
# pdf_viewer_app.py
|
| 2 |
import base64
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from typing import Union, BinaryIO
|
| 5 |
import gradio as gr
|
| 6 |
|
| 7 |
|
| 8 |
+
def pdf_to_html(
|
| 9 |
+
pdf: Union[str, Path, bytes, BinaryIO],
|
| 10 |
+
scale: float = 1.0,
|
| 11 |
+
height: int = 800,
|
| 12 |
+
) -> str:
|
| 13 |
+
"""把 PDF 转成可嵌入浏览器的 HTML 片段(<iframe> + Blob URL)"""
|
| 14 |
+
# 读取二进制
|
| 15 |
+
if isinstance(pdf, (str, Path)):
|
| 16 |
+
raw = Path(pdf).read_bytes()
|
| 17 |
+
elif isinstance(pdf, bytes):
|
| 18 |
+
raw = pdf
|
| 19 |
+
else: # file-like
|
| 20 |
+
raw = pdf.read()
|
| 21 |
+
|
| 22 |
+
b64 = base64.b64encode(raw).decode()
|
| 23 |
+
zoom = int(scale * 100) # 转百分比
|
| 24 |
+
|
| 25 |
+
return f"""
|
| 26 |
+
<div style="width:100%;height:{height}px;">
|
| 27 |
+
<iframe id="pdf_view" style="width:100%;height:100%;border:none;"></iframe>
|
| 28 |
+
</div>
|
| 29 |
+
<script>
|
| 30 |
+
const bytes = Uint8Array.from(atob("{b64}"), c => c.charCodeAt(0));
|
| 31 |
+
const blob = new Blob([bytes], {{type:"application/pdf"}});
|
| 32 |
+
const url = URL.createObjectURL(blob);
|
| 33 |
+
document.getElementById("pdf_view").src = url + "#zoom={zoom}";
|
| 34 |
+
</script>
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def show_pdf(file_obj, scale):
|
| 39 |
+
if file_obj is None:
|
| 40 |
+
return "<p style='color:gray'>请先上传 PDF 文件</p>"
|
| 41 |
+
return pdf_to_html(file_obj, scale)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
with gr.Blocks(title="自定义 PDF 预览") as demo:
|
| 45 |
+
gr.Markdown("## 📄 PDF 预览 Demo(Blob URL + iframe)")
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
with gr.Row():
|
| 48 |
+
uploader = gr.File(label="上传 PDF", file_types=[".pdf"])
|
|
|
|
| 49 |
zoom = gr.Slider(0.5, 3, value=1, step=0.1, label="缩放倍数")
|
| 50 |
|
| 51 |
+
html_out = gr.HTML(sanitize=False) # 关闭净化,允许 <script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
# 事件绑定
|
| 54 |
+
uploader.change(show_pdf, [uploader, zoom], html_out)
|
| 55 |
+
zoom.change(show_pdf, [uploader, zoom], html_out)
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
+
demo.launch()
|