mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
1 parent
98d9effc8c
commit
affc84459e
9 files changed
+103
-37
No files matched your search
@@ -4085,8 +4085,18 @@ async function loadDocumentDetails(openSeq) {
|
||||
const info = await callWASM("ofdgoDocumentInfo", true);
|
||||
if (openSeq !== state.openSeq) return;
|
||||
if (!info.detailsPending) {
|
||||
const knownFonts = new Set((state.doc.fonts || []).map(font => font.id));
|
||||
const missingFonts = info.fonts?.some(font => font.used > 0 && font.status === "missing" && !knownFonts.has(font.id));
|
||||
Object.assign(state.doc, info, { detailsPending: false, detailsError: "" });
|
||||
renderMeta();
|
||||
if (missingFonts && fontManager.permission === "granted" && !state.editorInfo && !document.body.hasAttribute("aria-busy")) {
|
||||
setBusy(true);
|
||||
try {
|
||||
if (await autoLoadDocumentLocalFonts(openSeq) && openSeq === state.openSeq) await applyFontChange();
|
||||
} finally {
|
||||
if (openSeq === state.openSeq) setBusy(false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ require (
|
||||
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||
github.com/xiaoqidun/jbig2 v1.1.1
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20260926152052-3a4cb2e9266a
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927050705-f09ba3f6aa16
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/image v0.46.0
|
||||
golang.org/x/text v0.42.0
|
||||
|
||||
@@ -62,8 +62,8 @@ github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
||||
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20260926152052-3a4cb2e9266a h1:BT4VQisXVY8OlUdZFE6WTOIktGk8fCQOfkWlSxvL+cc=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20260926152052-3a4cb2e9266a/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927050705-f09ba3f6aa16 h1:yzUX5/pLl38Nczw+yQXck/QZ7sPblNvKu5+55cH6ONM=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927050705-f09ba3f6aa16/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||
|
||||
@@ -921,7 +921,13 @@ func matchFontFiles(args []js.Value) (any, error) {
|
||||
if currentSession == nil {
|
||||
return nil, fmt.Errorf("ofd document is not opened")
|
||||
}
|
||||
return currentSession.Reader.MatchFontFiles(stringsFromJS(args[0]))
|
||||
ids := make([]string, 0, len(currentSession.fontInfos))
|
||||
for _, info := range currentSession.fontInfos {
|
||||
if info.Used > 0 {
|
||||
ids = append(ids, info.ID)
|
||||
}
|
||||
}
|
||||
return currentSession.Reader.MatchFontFiles(stringsFromJS(args[0]), ids...)
|
||||
}
|
||||
|
||||
// fontFaces 读取上传字体的名称和集合索引
|
||||
|
||||
+8
-3
@@ -182,14 +182,19 @@ func FontFileMatches(files []string, bold, italic bool, names ...string) []strin
|
||||
return fontFileMatchNames(fontFileMatches(fontFileCandidates(files, path.Base), names, bold, italic))
|
||||
}
|
||||
|
||||
// MatchFontFiles 按文档字体名称和样式选择首选文件,跳过内嵌字体
|
||||
// 入参: files 可用字体文件名
|
||||
// MatchFontFiles 按文档字体名称和样式选择首选文件,未声明的使用编号按默认规则回退
|
||||
// 入参: files 可用字体文件名, ids 已发现的使用编号,省略时仅匹配声明字体
|
||||
// 返回: []string 所需字体文件名, error 错误信息
|
||||
func (r *Reader) MatchFontFiles(files []string) ([]string, error) {
|
||||
func (r *Reader) MatchFontFiles(files []string, ids ...string) ([]string, error) {
|
||||
fonts, err := r.Fonts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, id := range ids {
|
||||
if r.fontCache[id] == nil {
|
||||
fonts = append(fonts, Font{ID: id})
|
||||
}
|
||||
}
|
||||
candidates := fontFileCandidates(files, path.Base)
|
||||
names := make([]string, 0, len(fonts))
|
||||
seen := make(map[string]bool)
|
||||
|
||||
+7
-7
@@ -262,13 +262,13 @@ func (r *Renderer) fontInfos(usage map[string]int) ([]FontInfo, error) {
|
||||
if seen[id] {
|
||||
continue
|
||||
}
|
||||
infos = append(infos, FontInfo{
|
||||
ID: id,
|
||||
FontName: id,
|
||||
Status: FontStatusMissing,
|
||||
Detail: "字体资源未被声明",
|
||||
Used: used,
|
||||
})
|
||||
info := r.fontInfo(Font{ID: id})
|
||||
info.FontName, info.Used = id, used
|
||||
info.Detail = "字体资源未被声明"
|
||||
if info.Status == FontStatusFallback {
|
||||
info.Detail += ",使用外部字体回退"
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
sort.SliceStable(infos, func(i, j int) bool {
|
||||
if infos[i].Used == 0 && infos[j].Used > 0 {
|
||||
|
||||
+26
-20
@@ -152,28 +152,29 @@ func (r *Renderer) resolveFontSource(backend FontBackend, id string, exact bool)
|
||||
}
|
||||
definition = r.Reader.fontCache[id]
|
||||
}
|
||||
if definition != nil {
|
||||
if definition.FontFile != "" {
|
||||
data, err := r.Reader.FontData(id)
|
||||
if err != nil {
|
||||
return ResolvedFont{}, err
|
||||
}
|
||||
face, err := r.Reader.embeddedFontFace(*definition)
|
||||
if err != nil {
|
||||
return ResolvedFont{}, err
|
||||
}
|
||||
return ResolvedFont{Data: data, Source: path.Base(definition.FontFile), Face: face, Exact: true}, nil
|
||||
if definition == nil {
|
||||
definition = &Font{ID: id}
|
||||
}
|
||||
if definition.FontFile != "" {
|
||||
data, err := r.Reader.FontData(id)
|
||||
if err != nil {
|
||||
return ResolvedFont{}, err
|
||||
}
|
||||
if source, metrics := r.fontSourceMatch(backend, id, definition, exact); metrics != nil {
|
||||
data := metrics.Write()
|
||||
faces, err := (FontFile{Data: data}).Faces()
|
||||
if err != nil {
|
||||
return ResolvedFont{}, err
|
||||
}
|
||||
face := faces[0]
|
||||
face.Index = source.face
|
||||
return ResolvedFont{Data: data, Source: source.name, Face: &face, Exact: source.exact}, nil
|
||||
face, err := r.Reader.embeddedFontFace(*definition)
|
||||
if err != nil {
|
||||
return ResolvedFont{}, err
|
||||
}
|
||||
return ResolvedFont{Data: data, Source: path.Base(definition.FontFile), Face: face, Exact: true}, nil
|
||||
}
|
||||
if source, metrics := r.fontSourceMatch(backend, id, definition, exact); metrics != nil {
|
||||
data := metrics.Write()
|
||||
faces, err := (FontFile{Data: data}).Faces()
|
||||
if err != nil {
|
||||
return ResolvedFont{}, err
|
||||
}
|
||||
face := faces[0]
|
||||
face.Index = source.face
|
||||
return ResolvedFont{Data: data, Source: source.name, Face: &face, Exact: source.exact}, nil
|
||||
}
|
||||
if exact {
|
||||
return ResolvedFont{}, fmt.Errorf("font %q is unavailable", id)
|
||||
@@ -238,6 +239,11 @@ func (r *Renderer) fontSources(fontID string, font *Font) []fontSource {
|
||||
}
|
||||
}
|
||||
sortFontSources(sources[systemStart:])
|
||||
for _, dir := range r.fontDirs {
|
||||
for _, match := range r.matchFontFiles(dir, nil, bold, italic) {
|
||||
sources = appendFontSource(sources, seen, fontSource{kind: fontSourceFile, name: match.name, face: match.face})
|
||||
}
|
||||
}
|
||||
for index := range r.fontFS {
|
||||
for _, match := range r.matchFontFS(index, nil, bold, italic) {
|
||||
sources = appendFontSource(sources, seen, fontSource{kind: fontSourceFS, index: index, name: match.name, face: match.face})
|
||||
|
||||
+41
-2
@@ -21,7 +21,9 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"maps"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
@@ -409,6 +411,7 @@ func (p *pdfImporter) text(mark pdfgo.TextMark) error {
|
||||
}
|
||||
|
||||
// pdfFontProgram 为PDF子集字体补齐封装表,保留字形轮廓和编号
|
||||
// 复合字体的重复映射区段按PDF字符映射重建,不改变CID对应的字形
|
||||
// 入参: source PDF字体, type1 已解析的Type1程序,nil时按需解析
|
||||
// 返回: []byte 封装后的字体数据, uint16 缺失度量前的完整字形数, error 错误信息
|
||||
func pdfFontProgram(source *pdfgo.Font, type1 *type1Program) ([]byte, uint16, error) {
|
||||
@@ -470,9 +473,9 @@ func pdfFontProgram(source *pdfgo.Font, type1 *type1Program) ([]byte, uint16, er
|
||||
tables["hmtx"] = tables["hmtx"][:metricLength]
|
||||
changed = true
|
||||
}
|
||||
if len(tables["cmap"]) == 0 {
|
||||
if len(tables["cmap"]) == 0 || source.Subtype == "Type0" && pdfCmapOverlaps(tables["cmap"]) {
|
||||
mapping := map[rune]uint16{}
|
||||
for code := range source.Unicode {
|
||||
for _, code := range slices.Sorted(maps.Keys(source.Unicode)) {
|
||||
glyphs, err := source.Decode([]byte(code))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -536,6 +539,42 @@ func pdfFontProgram(source *pdfgo.Font, type1 *type1Program) ([]byte, uint16, er
|
||||
return result, repairedLimit, err
|
||||
}
|
||||
|
||||
// pdfCmapOverlaps 检查格式4字符映射是否包含交叠区段
|
||||
// 入参: data cmap表数据
|
||||
// 返回: bool 是否存在交叠
|
||||
func pdfCmapOverlaps(data []byte) bool {
|
||||
if len(data) < 4 {
|
||||
return false
|
||||
}
|
||||
count := int(binary.BigEndian.Uint16(data[2:]))
|
||||
if count > (len(data)-4)/8 {
|
||||
return false
|
||||
}
|
||||
for index := range count {
|
||||
offset := uint64(binary.BigEndian.Uint32(data[8+8*index:]))
|
||||
if offset > uint64(len(data)) || uint64(len(data))-offset < 16 {
|
||||
continue
|
||||
}
|
||||
sub := data[int(offset):]
|
||||
if binary.BigEndian.Uint16(sub) != 4 {
|
||||
continue
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(sub[2:]))
|
||||
segments := int(binary.BigEndian.Uint16(sub[6:])) / 2
|
||||
if length > len(sub) || 16+8*segments > length {
|
||||
continue
|
||||
}
|
||||
for i := 1; i < segments; i++ {
|
||||
previous := binary.BigEndian.Uint16(sub[14+2*(i-1):])
|
||||
start := binary.BigEndian.Uint16(sub[16+2*segments+2*i:])
|
||||
if start <= previous {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// pdfSFNTMissingPadding 判断字体表目录是否引用了未写入的末尾对齐字节
|
||||
// 入参: program OpenType字体数据
|
||||
// 返回: bool 是否需要重新封装
|
||||
|
||||
@@ -44,7 +44,7 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
||||
return nil
|
||||
}
|
||||
if of == nil {
|
||||
of = &Font{ID: fontID, FontName: "default"}
|
||||
of = &Font{ID: fontID, FontName: "OFDGoFallback"}
|
||||
}
|
||||
fontData := resolved.Data
|
||||
fontStyle := canvasFontStyle(of)
|
||||
|
||||
Reference in new issue
Block a user