feat(前端界面): OFDGo WebUI可选是否渲染注解
build.yaml / build (push) Successful in 35s

This commit is contained in:
2026-07-07 13:10:55 +08:00
parent 98244f2bb7
commit f1d1f5ed2d
5 changed files with 35 additions and 25 deletions
+1
View File
@@ -32,6 +32,7 @@
<button id="zoomInButton" class="icon-button" type="button" title="放大" disabled>+</button> <button id="zoomInButton" class="icon-button" type="button" title="放大" disabled>+</button>
<button id="fitButton" class="button" type="button" data-short="宽" disabled>适宽</button> <button id="fitButton" class="button" type="button" data-short="宽" disabled>适宽</button>
<button id="fitHeightButton" class="button" type="button" data-short="高" disabled>适高</button> <button id="fitHeightButton" class="button" type="button" data-short="高" disabled>适高</button>
<button id="annotationButton" class="button" type="button" data-short="注" title="渲染注解" aria-label="渲染注解" aria-pressed="true">注解</button>
</div> </div>
<div class="tool-group"> <div class="tool-group">
<select id="pageExportFormat" class="format-select" title="单页格式" aria-label="单页格式" disabled></select> <select id="pageExportFormat" class="format-select" title="单页格式" aria-label="单页格式" disabled></select>
-4
View File
@@ -739,10 +739,6 @@ body[data-hide-meta] .meta-panel {
text-align: center; text-align: center;
} }
body[aria-busy="true"] .viewer-panel {
cursor: progress;
}
.app-footer { .app-footer {
justify-content: space-between; justify-content: space-between;
border-top: 1px solid var(--line); border-top: 1px solid var(--line);
+24 -14
View File
@@ -59,6 +59,7 @@ const state = {
pageIndex: 0, pageIndex: 0,
scale: 1, scale: 1,
fitMode: "width", fitMode: "width",
renderAnnotations: true,
pageCache: new Map(), pageCache: new Map(),
pageInFlight: new Set(), pageInFlight: new Set(),
pageObserver: null, pageObserver: null,
@@ -88,6 +89,7 @@ const el = {
zoomLabel: document.querySelector("#zoomLabel"), zoomLabel: document.querySelector("#zoomLabel"),
fitButton: document.querySelector("#fitButton"), fitButton: document.querySelector("#fitButton"),
fitHeightButton: document.querySelector("#fitHeightButton"), fitHeightButton: document.querySelector("#fitHeightButton"),
annotationButton: document.querySelector("#annotationButton"),
pageExportFormat: document.querySelector("#pageExportFormat"), pageExportFormat: document.querySelector("#pageExportFormat"),
exportPageButton: document.querySelector("#exportPageButton"), exportPageButton: document.querySelector("#exportPageButton"),
exportButton: document.querySelector("#exportButton"), exportButton: document.querySelector("#exportButton"),
@@ -125,6 +127,7 @@ el.zoomOutButton.addEventListener("click", () => setScale(state.scale - 0.1));
el.zoomInButton.addEventListener("click", () => setScale(state.scale + 0.1)); el.zoomInButton.addEventListener("click", () => setScale(state.scale + 0.1));
el.fitButton.addEventListener("click", fitWidth); el.fitButton.addEventListener("click", fitWidth);
el.fitHeightButton.addEventListener("click", fitHeight); el.fitHeightButton.addEventListener("click", fitHeight);
el.annotationButton.addEventListener("click", toggleAnnotations);
el.exportPageButton.addEventListener("click", exportCurrentPage); el.exportPageButton.addEventListener("click", exportCurrentPage);
el.exportButton.addEventListener("click", exportPDF); el.exportButton.addEventListener("click", exportPDF);
el.pageInput.addEventListener("change", () => { el.pageInput.addEventListener("change", () => {
@@ -524,9 +527,7 @@ function fontData(fonts) {
function updateFontSummary() { function updateFontSummary() {
const total = fontRecords().length; const total = fontRecords().length;
const enabled = fontRecords().filter((font) => font.enabled).length; const enabled = fontRecords().filter((font) => font.enabled).length;
if (el.availableFontSummary) {
el.availableFontSummary.textContent = total ? `${enabled}/${total}` : "0"; el.availableFontSummary.textContent = total ? `${enabled}/${total}` : "0";
}
} }
function createFontRecord(name, data, source) { function createFontRecord(name, data, source) {
@@ -660,15 +661,25 @@ async function applyFontChange(options = {}) {
}); });
} }
async function toggleAnnotations() {
state.renderAnnotations = !state.renderAnnotations;
updateAnnotationButton();
if (!state.ofdBytes || !state.doc) {
return;
}
await openDocument({
pageIndex: state.pageIndex,
fitMode: state.fitMode,
skipAutoFonts: true,
});
}
function removeFont(id) { function removeFont(id) {
state.localFonts = state.localFonts.filter((font) => font.id !== id); state.localFonts = state.localFonts.filter((font) => font.id !== id);
state.userFonts = state.userFonts.filter((font) => font.id !== id); state.userFonts = state.userFonts.filter((font) => font.id !== id);
} }
function renderFontList() { function renderFontList() {
if (!el.fontList) {
return;
}
el.fontList.replaceChildren(); el.fontList.replaceChildren();
const fonts = fontRecords(); const fonts = fontRecords();
if (!fonts.length) { if (!fonts.length) {
@@ -753,9 +764,6 @@ function fontSourceText(source) {
} }
function updateLocalFontButton() { function updateLocalFontButton() {
if (!el.localFontButton) {
return;
}
const supported = canReadLocalFonts(); const supported = canReadLocalFonts();
el.localFontButton.disabled = !supported; el.localFontButton.disabled = !supported;
el.localFontButton.title = supported ? "授权读取浏览器可访问的系统字体" : "当前浏览器不支持读取系统字体"; el.localFontButton.title = supported ? "授权读取浏览器可访问的系统字体" : "当前浏览器不支持读取系统字体";
@@ -786,7 +794,7 @@ async function openDocument(options = {}) {
if (openSeq !== state.openSeq) { if (openSeq !== state.openSeq) {
return; return;
} }
const doc = callWASM("ofdgoOpen", state.ofdBytes, resetLocalFonts ? uploadedFonts() : allFonts()); const doc = callWASM("ofdgoOpen", state.ofdBytes, resetLocalFonts ? uploadedFonts() : allFonts(), state.renderAnnotations);
if (openSeq !== state.openSeq) { if (openSeq !== state.openSeq) {
return; return;
} }
@@ -1436,14 +1444,9 @@ function renderMeta() {
} }
function renderDocumentFonts() { function renderDocumentFonts() {
if (!el.docFontList) {
return;
}
const fonts = state.doc?.fonts || []; const fonts = state.doc?.fonts || [];
el.docFontList.replaceChildren(); el.docFontList.replaceChildren();
if (el.docFontSummary) {
el.docFontSummary.textContent = fontSummary(fonts); el.docFontSummary.textContent = fontSummary(fonts);
}
if (!fonts.length) { if (!fonts.length) {
const empty = document.createElement("div"); const empty = document.createElement("div");
empty.className = "font-empty"; empty.className = "font-empty";
@@ -1594,11 +1597,18 @@ function updateControls() {
el.fitHeightButton.disabled = !hasDoc; el.fitHeightButton.disabled = !hasDoc;
el.fitButton.toggleAttribute("aria-pressed", hasDoc && state.fitMode === "width"); el.fitButton.toggleAttribute("aria-pressed", hasDoc && state.fitMode === "width");
el.fitHeightButton.toggleAttribute("aria-pressed", hasDoc && state.fitMode === "height"); el.fitHeightButton.toggleAttribute("aria-pressed", hasDoc && state.fitMode === "height");
updateAnnotationButton();
el.pageExportFormat.disabled = !hasDoc || !state.exportFormats.length; el.pageExportFormat.disabled = !hasDoc || !state.exportFormats.length;
el.exportPageButton.disabled = !hasDoc || !state.exportFormats.length; el.exportPageButton.disabled = !hasDoc || !state.exportFormats.length;
el.exportButton.disabled = !hasDoc; el.exportButton.disabled = !hasDoc;
} }
function updateAnnotationButton() {
el.annotationButton.setAttribute("aria-pressed", String(state.renderAnnotations));
el.annotationButton.title = state.renderAnnotations ? "关闭注解渲染" : "开启注解渲染";
el.annotationButton.setAttribute("aria-label", el.annotationButton.title);
}
function setExportControlsDisabled(disabled) { function setExportControlsDisabled(disabled) {
el.exportButton.disabled = disabled; el.exportButton.disabled = disabled;
el.exportPageButton.disabled = disabled; el.exportPageButton.disabled = disabled;
+5 -4
View File
@@ -79,22 +79,23 @@ func safeCall(fn func([]js.Value) (any, error), args []js.Value) (data any, err
// 入参: args 浏览器参数 // 入参: args 浏览器参数
// 返回: any 文档信息, error 错误信息 // 返回: any 文档信息, error 错误信息
func openDocument(args []js.Value) (any, error) { func openDocument(args []js.Value) (any, error) {
if len(args) == 0 { if len(args) < 3 {
return nil, fmt.Errorf("missing ofd data") return nil, fmt.Errorf("missing open arguments")
} }
data, err := bytesFromJS(args[0]) data, err := bytesFromJS(args[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
fonts, err := fontsFromJS(jsArg(args, 1)) fonts, err := fontsFromJS(args[1])
if err != nil { if err != nil {
return nil, err return nil, err
} }
renderAnnotations := args[2].Bool()
if currentSession != nil { if currentSession != nil {
_ = currentSession.Close() _ = currentSession.Close()
currentSession = nil currentSession = nil
} }
session, err := Open(data, OpenOptions{Fonts: fonts}) session, err := Open(data, OpenOptions{Fonts: fonts, RenderAnnotations: renderAnnotations})
if err != nil { if err != nil {
return nil, err return nil, err
} }
+2
View File
@@ -30,6 +30,7 @@ import (
// OpenOptions 打开OFD文档选项 // OpenOptions 打开OFD文档选项
type OpenOptions struct { type OpenOptions struct {
Fonts []FontFile Fonts []FontFile
RenderAnnotations bool
} }
// FontFile 字体文件 // FontFile 字体文件
@@ -144,6 +145,7 @@ func Open(data []byte, opts OpenOptions) (*Session, error) {
} }
rendererOptions = append(rendererOptions, ofdgo.WithFontFS(fontFS)) rendererOptions = append(rendererOptions, ofdgo.WithFontFS(fontFS))
} }
rendererOptions = append(rendererOptions, ofdgo.WithAnnotations(opts.RenderAnnotations))
return &Session{Reader: reader, Renderer: ofdgo.NewRenderer(reader, rendererOptions...), doc: doc}, nil return &Session{Reader: reader, Renderer: ofdgo.NewRenderer(reader, rendererOptions...), doc: doc}, nil
} }