mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
This commit is contained in:
1 parent
c2899deff1
commit
299c2cbfdc
6 files changed
+178
-18
No files matched your search
@@ -118,6 +118,10 @@
|
|||||||
<dd id="metaFonts">0</dd>
|
<dd id="metaFonts">0</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
<section id="attachmentPanel" class="attachment-panel" aria-labelledby="attachmentPanelTitle" hidden>
|
||||||
|
<div id="attachmentPanelTitle" class="panel-title">附件</div>
|
||||||
|
<div id="attachmentList" class="attachment-list"></div>
|
||||||
|
</section>
|
||||||
<section id="signaturePanel" class="signature-panel" aria-labelledby="signaturePanelTitle" hidden>
|
<section id="signaturePanel" class="signature-panel" aria-labelledby="signaturePanelTitle" hidden>
|
||||||
<div class="panel-row">
|
<div class="panel-row">
|
||||||
<div id="signaturePanelTitle" class="panel-title compact">签名</div>
|
<div id="signaturePanelTitle" class="panel-title compact">签名</div>
|
||||||
|
|||||||
+32
-1
@@ -411,7 +411,7 @@ body[data-hide-meta] .meta-panel {
|
|||||||
list-style-position: outside;
|
list-style-position: outside;
|
||||||
}
|
}
|
||||||
|
|
||||||
.outline-leaf {
|
.outline-list:has(> li > details) .outline-leaf {
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,12 +731,43 @@ button.outline-link:hover {
|
|||||||
|
|
||||||
.font-panel,
|
.font-panel,
|
||||||
.doc-font-panel,
|
.doc-font-panel,
|
||||||
|
.attachment-panel,
|
||||||
.signature-panel {
|
.signature-panel {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
border-top: 1px solid var(--line);
|
border-top: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.attachment-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-item {
|
||||||
|
width: 100%;
|
||||||
|
padding: 4px 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--accent-strong);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-item:not(:disabled):hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-item:hover .attachment-name {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-detail {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
.count-pill {
|
.count-pill {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ const el = {
|
|||||||
metaType: document.querySelector("#metaType"),
|
metaType: document.querySelector("#metaType"),
|
||||||
metaFonts: document.querySelector("#metaFonts"),
|
metaFonts: document.querySelector("#metaFonts"),
|
||||||
metaSignatures: document.querySelector("#metaSignatures"),
|
metaSignatures: document.querySelector("#metaSignatures"),
|
||||||
|
attachmentPanel: document.querySelector("#attachmentPanel"),
|
||||||
|
attachmentList: document.querySelector("#attachmentList"),
|
||||||
signaturePanel: document.querySelector("#signaturePanel"),
|
signaturePanel: document.querySelector("#signaturePanel"),
|
||||||
signatureSummary: document.querySelector("#signatureSummary"),
|
signatureSummary: document.querySelector("#signatureSummary"),
|
||||||
signatureList: document.querySelector("#signatureList"),
|
signatureList: document.querySelector("#signatureList"),
|
||||||
@@ -1288,6 +1290,34 @@ async function renderPage(index, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function downloadAttachment(attachment) {
|
||||||
|
if (!state.doc || document.body.hasAttribute("aria-busy")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const openSeq = state.openSeq;
|
||||||
|
state.exporting = true;
|
||||||
|
updateControls();
|
||||||
|
setBusy(true, "正在读取附件", null, "正在读取附件");
|
||||||
|
try {
|
||||||
|
const result = await callWASM("ofdgoAttachmentData", attachment.id);
|
||||||
|
if (openSeq !== state.openSeq) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadBytes(result.bytes, "application/octet-stream", attachment.fileName);
|
||||||
|
setStatus(`附件已下载 ${formatBytes(result.bytes.length, "0 KB")}`);
|
||||||
|
} catch (err) {
|
||||||
|
if (openSeq === state.openSeq) {
|
||||||
|
showError(err, false);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
state.exporting = false;
|
||||||
|
if (openSeq === state.openSeq) {
|
||||||
|
setBusy(false);
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function exportPDF() {
|
async function exportPDF() {
|
||||||
if (!state.doc || document.body.hasAttribute("aria-busy")) {
|
if (!state.doc || document.body.hasAttribute("aria-busy")) {
|
||||||
return;
|
return;
|
||||||
@@ -2016,12 +2046,47 @@ function renderMeta() {
|
|||||||
el.metaFonts.textContent = String(doc.fontCount || 0);
|
el.metaFonts.textContent = String(doc.fontCount || 0);
|
||||||
el.metaSignatures.textContent = doc.detailsPending ? "正在检查" : String(doc.signatureCount || 0);
|
el.metaSignatures.textContent = doc.detailsPending ? "正在检查" : String(doc.signatureCount || 0);
|
||||||
el.pageTotal.textContent = String(doc.pageCount || 0);
|
el.pageTotal.textContent = String(doc.pageCount || 0);
|
||||||
|
renderAttachments();
|
||||||
renderSignatures();
|
renderSignatures();
|
||||||
renderDocumentFonts();
|
renderDocumentFonts();
|
||||||
renderFontList();
|
renderFontList();
|
||||||
updateLocalFontButton();
|
updateLocalFontButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAttachments() {
|
||||||
|
const attachments = state.doc?.attachments || [];
|
||||||
|
el.attachmentPanel.hidden = !attachments.length && !state.doc?.attachmentError;
|
||||||
|
el.attachmentList.replaceChildren();
|
||||||
|
if (state.doc?.attachmentError) {
|
||||||
|
const error = document.createElement("div");
|
||||||
|
error.className = "attachment-detail";
|
||||||
|
error.textContent = "附件读取失败";
|
||||||
|
error.title = state.doc.attachmentError;
|
||||||
|
el.attachmentList.append(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const attachment of attachments) {
|
||||||
|
const item = document.createElement("button");
|
||||||
|
item.type = "button";
|
||||||
|
item.className = "attachment-item";
|
||||||
|
item.title = `下载 ${attachment.fileName}`;
|
||||||
|
item.setAttribute("aria-label", `下载 ${attachment.name}`);
|
||||||
|
const name = document.createElement("span");
|
||||||
|
name.className = "attachment-name";
|
||||||
|
name.textContent = attachment.name;
|
||||||
|
item.append(name);
|
||||||
|
const detail = [attachment.format, attachment.size == null ? "" : formatBytes(attachment.size * 1024, "0 KB")].filter(Boolean).join(" · ");
|
||||||
|
if (detail) {
|
||||||
|
const meta = document.createElement("span");
|
||||||
|
meta.className = "attachment-detail";
|
||||||
|
meta.textContent = ` · ${detail}`;
|
||||||
|
item.append(meta);
|
||||||
|
}
|
||||||
|
item.addEventListener("click", () => downloadAttachment(attachment));
|
||||||
|
el.attachmentList.append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderSignatures() {
|
function renderSignatures() {
|
||||||
const signatures = state.doc?.signatures || [];
|
const signatures = state.doc?.signatures || [];
|
||||||
const showPanel = Boolean(state.doc?.signatureError || signatures.length);
|
const showPanel = Boolean(state.doc?.signatureError || signatures.length);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ func RunWASM() {
|
|||||||
registerCallback("ofdgoOpen", openDocument)
|
registerCallback("ofdgoOpen", openDocument)
|
||||||
registerCallback("ofdgoConfigure", configureDocument)
|
registerCallback("ofdgoConfigure", configureDocument)
|
||||||
registerCallback("ofdgoDocumentInfo", documentInfo)
|
registerCallback("ofdgoDocumentInfo", documentInfo)
|
||||||
|
registerCallback("ofdgoAttachmentData", attachmentData)
|
||||||
registerCallback("ofdgoRenderPage", renderPage)
|
registerCallback("ofdgoRenderPage", renderPage)
|
||||||
registerCallback("ofdgoExportFormats", exportFormats)
|
registerCallback("ofdgoExportFormats", exportFormats)
|
||||||
registerCallback("ofdgoExportPage", exportPage)
|
registerCallback("ofdgoExportPage", exportPage)
|
||||||
@@ -131,6 +132,20 @@ func documentInfo(args []js.Value) (any, error) {
|
|||||||
return currentSession.Info(), nil
|
return currentSession.Info(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attachmentData 读取附件数据
|
||||||
|
// 入参: args 浏览器参数
|
||||||
|
// 返回: any 附件数据, error 错误信息
|
||||||
|
func attachmentData(args []js.Value) (any, error) {
|
||||||
|
if currentSession == nil {
|
||||||
|
return nil, fmt.Errorf("ofd document is not opened")
|
||||||
|
}
|
||||||
|
data, err := currentSession.Reader.AttachmentData(args[0].String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return successResult(map[string]any{"bytes": bytesToJS(data)}), nil
|
||||||
|
}
|
||||||
|
|
||||||
// renderPage 渲染OFD页面
|
// renderPage 渲染OFD页面
|
||||||
// 入参: args 浏览器参数
|
// 入参: args 浏览器参数
|
||||||
// 返回: any 页面SVG结果, error 错误信息
|
// 返回: any 页面SVG结果, error 错误信息
|
||||||
|
|||||||
+43
-16
@@ -22,6 +22,7 @@ import (
|
|||||||
"image/draw"
|
"image/draw"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -113,27 +114,38 @@ type Session struct {
|
|||||||
|
|
||||||
// DocumentInfo 文档信息
|
// DocumentInfo 文档信息
|
||||||
type DocumentInfo struct {
|
type DocumentInfo struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
DocType string `json:"docType"`
|
DocType string `json:"docType"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Author string `json:"author"`
|
Author string `json:"author"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
CreationDate string `json:"creationDate"`
|
CreationDate string `json:"creationDate"`
|
||||||
ModDate string `json:"modDate"`
|
ModDate string `json:"modDate"`
|
||||||
PageCount int `json:"pageCount"`
|
PageCount int `json:"pageCount"`
|
||||||
FontCount int `json:"fontCount"`
|
FontCount int `json:"fontCount"`
|
||||||
SignatureCount int `json:"signatureCount"`
|
SignatureCount int `json:"signatureCount"`
|
||||||
SignatureError string `json:"signatureError,omitempty"`
|
SignatureError string `json:"signatureError,omitempty"`
|
||||||
Fonts []FontInfo `json:"fonts"`
|
AttachmentError string `json:"attachmentError,omitempty"`
|
||||||
Signatures []SignatureInfo `json:"signatures"`
|
Attachments []AttachmentInfo `json:"attachments,omitempty"`
|
||||||
Pages []PageInfo `json:"pages"`
|
Fonts []FontInfo `json:"fonts"`
|
||||||
Outlines []OutlineInfo `json:"outlines,omitempty"`
|
Signatures []SignatureInfo `json:"signatures"`
|
||||||
DetailsPending bool `json:"detailsPending,omitempty"`
|
Pages []PageInfo `json:"pages"`
|
||||||
|
Outlines []OutlineInfo `json:"outlines,omitempty"`
|
||||||
|
DetailsPending bool `json:"detailsPending,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutlineInfo 目录节点信息
|
// OutlineInfo 目录节点信息
|
||||||
type OutlineInfo = ofdgo.OutlineInfo
|
type OutlineInfo = ofdgo.OutlineInfo
|
||||||
|
|
||||||
|
// AttachmentInfo 可见附件信息,Size单位为KB
|
||||||
|
type AttachmentInfo struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Format string `json:"format,omitempty"`
|
||||||
|
Size *float64 `json:"size,omitempty"`
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
}
|
||||||
|
|
||||||
// PageInfo 页面信息
|
// PageInfo 页面信息
|
||||||
type PageInfo struct {
|
type PageInfo struct {
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
@@ -307,6 +319,21 @@ func (s *Session) Info() DocumentInfo {
|
|||||||
Pages: make([]PageInfo, 0, len(s.doc.Pages.Page)),
|
Pages: make([]PageInfo, 0, len(s.doc.Pages.Page)),
|
||||||
Outlines: s.doc.OutlineInfos(),
|
Outlines: s.doc.OutlineInfos(),
|
||||||
}
|
}
|
||||||
|
if attachments, err := s.Reader.Attachments(); err == nil {
|
||||||
|
for _, attachment := range attachments {
|
||||||
|
if attachment.Visible {
|
||||||
|
info.Attachments = append(info.Attachments, AttachmentInfo{
|
||||||
|
ID: attachment.ID,
|
||||||
|
Name: attachment.Name,
|
||||||
|
Format: attachment.Format,
|
||||||
|
Size: attachment.Size,
|
||||||
|
FileName: path.Base(s.Reader.ResPath(attachment.FileLoc)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info.AttachmentError = err.Error()
|
||||||
|
}
|
||||||
if signatures, err := s.signatureInfos(); err == nil {
|
if signatures, err := s.signatureInfos(); err == nil {
|
||||||
info.Signatures = signatures
|
info.Signatures = signatures
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+19
-1
@@ -505,7 +505,9 @@ func (r *Reader) Attachments() ([]Attachment, error) {
|
|||||||
}
|
}
|
||||||
for i := range attachments.Attachment {
|
for i := range attachments.Attachment {
|
||||||
attachment := &attachments.Attachment[i]
|
attachment := &attachments.Attachment[i]
|
||||||
attachment.FileLoc = resolveResourcePath(partPath, "", attachment.FileLoc)
|
if fileLoc := resolveResourcePath(partPath, "", attachment.FileLoc); fileLoc != "" {
|
||||||
|
attachment.FileLoc = "/" + fileLoc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if attachments.Attachment == nil {
|
if attachments.Attachment == nil {
|
||||||
attachments.Attachment = []Attachment{}
|
attachments.Attachment = []Attachment{}
|
||||||
@@ -515,6 +517,22 @@ func (r *Reader) Attachments() ([]Attachment, error) {
|
|||||||
return doc.Attachments.Attachment, nil
|
return doc.Attachments.Attachment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AttachmentData 获取附件文件数据
|
||||||
|
// 入参: id 附件标识
|
||||||
|
// 返回: []byte 附件数据, error 错误信息
|
||||||
|
func (r *Reader) AttachmentData(id string) ([]byte, error) {
|
||||||
|
attachments, err := r.Attachments()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, attachment := range attachments {
|
||||||
|
if attachment.ID == id {
|
||||||
|
return r.ResData(attachment.FileLoc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("attachment not found: %s", id)
|
||||||
|
}
|
||||||
|
|
||||||
// CustomTags 获取自定义标引
|
// CustomTags 获取自定义标引
|
||||||
// 返回: []CustomTag 自定义标引列表, error 错误信息
|
// 返回: []CustomTag 自定义标引列表, error 错误信息
|
||||||
func (r *Reader) CustomTags() ([]CustomTag, error) {
|
func (r *Reader) CustomTags() ([]CustomTag, error) {
|
||||||
|
|||||||
Reference in new issue
Block a user