mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
f8c8259502
commit
9d6bf3e87f
4 files changed
+106
-13
No files matched your search
+37
-13
@@ -113,6 +113,8 @@ function createDocumentState() {
|
|||||||
outlineExpanded: new Map(),
|
outlineExpanded: new Map(),
|
||||||
textFonts: [],
|
textFonts: [],
|
||||||
textFontID: null,
|
textFontID: null,
|
||||||
|
textFontInitialized: false,
|
||||||
|
textFontInitializing: null,
|
||||||
textDefaults: { type: "TextObject", size: 16 * 25.4 / 72, color: "#000000", wrap: true, align: "left", paragraphHeight: 0, letterSpacing: 0 },
|
textDefaults: { type: "TextObject", size: 16 * 25.4 / 72, color: "#000000", wrap: true, align: "left", paragraphHeight: 0, letterSpacing: 0 },
|
||||||
ofdBytes: null,
|
ofdBytes: null,
|
||||||
sourcePDF: false,
|
sourcePDF: false,
|
||||||
@@ -4363,13 +4365,12 @@ function editorFonts(item) {
|
|||||||
return [...embedded.values(), ...fonts];
|
return [...embedded.values(), ...fonts];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadEditorFonts(requestPermission = true) {
|
async function loadEditorFonts(requestPermission = true, tab = documentTab(), waitPending = false) {
|
||||||
const tab = documentTab(), target = tab.state;
|
const target = tab.state;
|
||||||
const tasks = [];
|
const tasks = waitPending ? [target.fontCatalogLoading, target.fontFacesLoading].filter(Boolean) : [];
|
||||||
if (!state.fontCatalogLoading && fontManager.canReadLocal() && !fontManager.catalogLoaded && fontManager.permission !== "denied"
|
if (!target.fontCatalogLoading && fontManager.canReadLocal() && !fontManager.catalogLoaded && fontManager.permission !== "denied"
|
||||||
&& (requestPermission || fontManager.permission === "granted")) {
|
&& (requestPermission || fontManager.permission === "granted")) {
|
||||||
target.fontCatalogLoading = true;
|
target.fontCatalogLoading = (async () => {
|
||||||
tasks.push((async () => {
|
|
||||||
try {
|
try {
|
||||||
await fontManager.queryLocal();
|
await fontManager.queryLocal();
|
||||||
refreshEditorFonts();
|
refreshEditorFonts();
|
||||||
@@ -4378,13 +4379,13 @@ async function loadEditorFonts(requestPermission = true) {
|
|||||||
} finally {
|
} finally {
|
||||||
target.fontCatalogLoading = false;
|
target.fontCatalogLoading = false;
|
||||||
}
|
}
|
||||||
})());
|
})();
|
||||||
|
tasks.push(target.fontCatalogLoading);
|
||||||
}
|
}
|
||||||
if (!state.fontFacesLoading && fontManager.userFonts.some(font => font.enabled && !font.faces)) {
|
if (!target.fontFacesLoading && fontManager.userFonts.some(font => font.enabled && !font.faces)) {
|
||||||
target.fontFacesLoading = true;
|
|
||||||
const wasmSeq = target.wasmSeq;
|
const wasmSeq = target.wasmSeq;
|
||||||
const current = () => !tab.closed && !target.wasmExited && target.wasmSeq === wasmSeq;
|
const current = () => !tab.closed && !target.wasmExited && target.wasmSeq === wasmSeq;
|
||||||
tasks.push((async () => {
|
target.fontFacesLoading = (async () => {
|
||||||
try {
|
try {
|
||||||
await fontManager.loadFaces(data => callTabWASM(tab, "ofdgoFontFaces", data), current);
|
await fontManager.loadFaces(data => callTabWASM(tab, "ofdgoFontFaces", data), current);
|
||||||
refreshEditorFonts();
|
refreshEditorFonts();
|
||||||
@@ -4392,11 +4393,33 @@ async function loadEditorFonts(requestPermission = true) {
|
|||||||
target.fontFacesLoading = false;
|
target.fontFacesLoading = false;
|
||||||
if (!current() && state.ready) loadEditorFonts(false);
|
if (!current() && state.ready) loadEditorFonts(false);
|
||||||
}
|
}
|
||||||
})());
|
})();
|
||||||
|
tasks.push(target.fontFacesLoading);
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function initializeTextFont(tab) {
|
||||||
|
const target = tab.state;
|
||||||
|
if (target.textFontInitialized) return;
|
||||||
|
if (target.textFontInitializing) return target.textFontInitializing;
|
||||||
|
const wasmSeq = target.wasmSeq;
|
||||||
|
const current = () => !tab.closed && !target.wasmExited && target.wasmSeq === wasmSeq;
|
||||||
|
target.textFontInitializing = (async () => {
|
||||||
|
await loadEditorFonts(false, tab, true);
|
||||||
|
if (!current()) return;
|
||||||
|
const font = await fontManager.defaultFont((faces, families) => callTabWASM(tab, "ofdgoFontPreferenceOrder", JSON.stringify({
|
||||||
|
faces: faces.map(({ family, fullName, postscriptName, style, names }) => ({ family, fullName, postscriptName, style, names })), families
|
||||||
|
})), async (font, text) => callTabWASM(tab, "ofdgoCheckTextFont", await readTextFont(font, tab), text), current);
|
||||||
|
if (!current()) return;
|
||||||
|
target.textDefaults.fontChoice = font || (fontManager.editorFonts().length ? null : undefined);
|
||||||
|
target.textFontInitialized = true;
|
||||||
|
if (isForegroundDocument(tab)) refreshEditorFonts();
|
||||||
|
})();
|
||||||
|
try { await target.textFontInitializing; }
|
||||||
|
finally { target.textFontInitializing = null; }
|
||||||
|
}
|
||||||
|
|
||||||
function refreshEditorFonts() {
|
function refreshEditorFonts() {
|
||||||
updateTextFonts(canvasEditor.selected, true, true);
|
updateTextFonts(canvasEditor.selected, true, true);
|
||||||
}
|
}
|
||||||
@@ -4446,9 +4469,9 @@ function updateTextFonts(item, refresh = false, preserve = false) {
|
|||||||
state.textFonts.unshift(selected);
|
state.textFonts.unshift(selected);
|
||||||
}
|
}
|
||||||
const mixed = (item?.items || item?.richText) && !id;
|
const mixed = (item?.items || item?.richText) && !id;
|
||||||
fontPicker.setFonts(state.textFonts, mixed ? null : selected || state.textFonts[0], preserve);
|
fontPicker.setFonts(state.textFonts, mixed ? null : selected || (state.textDefaults.fontChoice === null ? null : state.textFonts[0]), preserve);
|
||||||
el.textFont.placeholder = mixed ? "混合" : "";
|
el.textFont.placeholder = mixed ? "混合" : "";
|
||||||
if (!id && !item?.items && !item?.richText) state.textDefaults.fontChoice = state.textFonts[Number(fontPicker.value)];
|
if (!id && !item?.items && !item?.richText) state.textDefaults.fontChoice = state.textFonts[Number(fontPicker.value)] || (state.textDefaults.fontChoice === null ? null : undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6567,6 +6590,7 @@ async function openDocument(options = {}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const pageFonts = state.sourcePDF && !nativeHost && !state.editorInfo ? await fontManager.files(fontManager.records()) : fonts;
|
const pageFonts = state.sourcePDF && !nativeHost && !state.editorInfo ? await fontManager.files(fontManager.records()) : fonts;
|
||||||
|
if (state.editing) await initializeTextFont(tab);
|
||||||
if (openSeq !== state.openSeq) return;
|
if (openSeq !== state.openSeq) return;
|
||||||
tab.worker.configurePages?.(!state.editorInfo && !doc.encrypted && state.ofdBytes ? {
|
tab.worker.configurePages?.(!state.editorInfo && !doc.encrypted && state.ofdBytes ? {
|
||||||
file: state.ofdBytes, fonts: pageFonts, pdf: state.sourcePDF, fontNames: state.sourcePDF ? [...fontManager.localEntries().keys()] : [], annotations: state.renderAnnotations, document: doc.documentIndex, version: doc.documentVersion
|
file: state.ofdBytes, fonts: pageFonts, pdf: state.sourcePDF, fontNames: state.sourcePDF ? [...fontManager.localEntries().keys()] : [], annotations: state.renderAnnotations, document: doc.documentIndex, version: doc.documentVersion
|
||||||
|
|||||||
@@ -335,6 +335,25 @@ export class FontManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async defaultFont(order, inspect, current = () => true) {
|
||||||
|
const fonts = this.editorFonts().filter(font => !font.disabled);
|
||||||
|
if (!fonts.length) return;
|
||||||
|
const families = [["DengXian", "等线"], ["Microsoft YaHei", "微软雅黑"], ["PingFang SC", "苹方-简", "苹方 简体"],
|
||||||
|
["Noto Sans SC", "Noto Sans CJK SC"], ["Source Han Sans SC", "Source Han Sans CN", "思源黑体", "思源黑体 CN"]];
|
||||||
|
const indices = await order(fonts, families);
|
||||||
|
let fallback = fonts.length;
|
||||||
|
for (const index of indices) {
|
||||||
|
if (!current()) return;
|
||||||
|
try {
|
||||||
|
await inspect(fonts[index], "中文汉字编辑水印,。!?");
|
||||||
|
if (current()) return fonts[index];
|
||||||
|
} catch (error) {
|
||||||
|
if (error.missingGlyphs) fallback = Math.min(fallback, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current()) return fonts[fallback];
|
||||||
|
}
|
||||||
|
|
||||||
async add(fonts) {
|
async add(fonts) {
|
||||||
let saved = true;
|
let saved = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -3175,6 +3175,20 @@ func (e *businessEngine) fontFaces(args []platformValue) (any, error) {
|
|||||||
return (ofdgo.FontFile{Data: data}).Faces()
|
return (ofdgo.FontFile{Data: data}).Faces()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fontPreferenceOrder 按字体族偏好排列编辑字体候选索引
|
||||||
|
// 入参: args 候选字体和字体族别名组的JSON数据
|
||||||
|
// 返回: any 候选索引列表, error 错误信息
|
||||||
|
func (e *businessEngine) fontPreferenceOrder(args []platformValue) (any, error) {
|
||||||
|
var request struct {
|
||||||
|
Faces []ofdgo.FontFace `json:"faces"`
|
||||||
|
Families [][]string `json:"families"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(args[0].String()), &request); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ofdgo.FontPreferenceOrder(request.Faces, request.Families), nil
|
||||||
|
}
|
||||||
|
|
||||||
// fontFace 提取编辑时选定的字体,供输入预览和保存共用
|
// fontFace 提取编辑时选定的字体,供输入预览和保存共用
|
||||||
// 入参: args 字体数据和零起始索引
|
// 入参: args 字体数据和零起始索引
|
||||||
// 返回: any 独立字体数据, error 错误信息
|
// 返回: any 独立字体数据, error 错误信息
|
||||||
@@ -8053,6 +8067,7 @@ func (e *businessEngine) businessCommands() map[string]func([]platformValue) (an
|
|||||||
"ofdgoSaveRichText": e.saveRichText,
|
"ofdgoSaveRichText": e.saveRichText,
|
||||||
"ofdgoPreviewRichText": e.previewRichText,
|
"ofdgoPreviewRichText": e.previewRichText,
|
||||||
"ofdgoCheckTextFont": e.checkTextFont,
|
"ofdgoCheckTextFont": e.checkTextFont,
|
||||||
|
"ofdgoFontPreferenceOrder": e.fontPreferenceOrder,
|
||||||
"ofdgoInsertImage": e.insertImage,
|
"ofdgoInsertImage": e.insertImage,
|
||||||
"ofdgoInsertShape": e.insertShape,
|
"ofdgoInsertShape": e.insertShape,
|
||||||
"ofdgoUpdatePathStyle": e.updatePathStyle,
|
"ofdgoUpdatePathStyle": e.updatePathStyle,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package ofdgo
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
basefont "github.com/xiaoqidun/ofdgo/internal/base/font"
|
basefont "github.com/xiaoqidun/ofdgo/internal/base/font"
|
||||||
@@ -25,6 +26,40 @@ import (
|
|||||||
// FontFace 字体文件中的字体名称与零起始索引,不依赖文件名
|
// FontFace 字体文件中的字体名称与零起始索引,不依赖文件名
|
||||||
type FontFace = basefont.Face
|
type FontFace = basefont.Face
|
||||||
|
|
||||||
|
// FontPreferenceOrder 按字体族偏好排列候选索引,同族优先常规样式,其余保持原顺序
|
||||||
|
// 入参: faces 候选字体, families 按优先级排列的字体族别名组
|
||||||
|
// 返回: []int 候选字体的零起始索引,不修改输入列表
|
||||||
|
func FontPreferenceOrder(faces []FontFace, families [][]string) []int {
|
||||||
|
priorities := make(map[string]int)
|
||||||
|
for priority, names := range families {
|
||||||
|
for _, name := range names {
|
||||||
|
key := FontNormalizeName(name)
|
||||||
|
if _, exists := priorities[key]; key != "" && !exists {
|
||||||
|
priorities[key] = priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
order, ranks := make([]int, len(faces)), make([]int, len(faces))
|
||||||
|
for i, face := range faces {
|
||||||
|
order[i], ranks[i] = i, len(families)*2
|
||||||
|
names := append([]string{face.Family, face.FullName, face.PostScriptName}, face.Names...)
|
||||||
|
for _, name := range names {
|
||||||
|
if priority, exists := priorities[FontNormalizeName(name)]; exists && priority*2 < ranks[i] {
|
||||||
|
ranks[i] = priority * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ranks[i] < len(families)*2 {
|
||||||
|
switch FontNormalizeName(face.Style) {
|
||||||
|
case "", "regular", "normal", "roman", "常规", "正常", "标准":
|
||||||
|
default:
|
||||||
|
ranks[i]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.SliceStable(order, func(i, j int) bool { return ranks[order[i]] < ranks[order[j]] })
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
// FixFontDataAggressive 激进修复字体数据
|
// FixFontDataAggressive 激进修复字体数据
|
||||||
// 尝试修复缺失表(OS/2, cmap等)的TrueType字体或包装CFF裸数据
|
// 尝试修复缺失表(OS/2, cmap等)的TrueType字体或包装CFF裸数据
|
||||||
// 对显式字形映射补充私有字符cmap,便于按glyph id渲染
|
// 对显式字形映射补充私有字符cmap,便于按glyph id渲染
|
||||||
|
|||||||
Reference in new issue
Block a user