mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:53:44 +08:00
1 parent
ad099a099b
commit
f73b67c729
6 files changed
+148
-14
No files matched your search
@@ -8,9 +8,9 @@ require (
|
|||||||
github.com/go-text/typesetting v0.3.5
|
github.com/go-text/typesetting v0.3.5
|
||||||
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
||||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||||
github.com/xiaoqidun/j2kgo v0.0.0-20261009050019-d6d8c42b70bd
|
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893
|
||||||
github.com/xiaoqidun/jbig2 v1.1.3
|
github.com/xiaoqidun/jbig2 v1.1.3
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009054335-bd8a5e721992
|
github.com/xiaoqidun/pdfgo v0.0.0-20261009064143-9a945dbdcbb6
|
||||||
golang.org/x/crypto v0.57.0
|
golang.org/x/crypto v0.57.0
|
||||||
golang.org/x/image v0.46.0
|
golang.org/x/image v0.46.0
|
||||||
golang.org/x/text v0.42.0
|
golang.org/x/text v0.42.0
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ github.com/tdewolff/parse/v2 v2.8.16 h1:bLk5svUOQRkW/Y2SJ+DeENSIkZBcTIkq+Atyv5D8
|
|||||||
github.com/tdewolff/parse/v2 v2.8.16/go.mod h1:XdsoSFThlVIRIajAuqz1evNY7bagZS8LBOPA3aVopwQ=
|
github.com/tdewolff/parse/v2 v2.8.16/go.mod h1:XdsoSFThlVIRIajAuqz1evNY7bagZS8LBOPA3aVopwQ=
|
||||||
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
||||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||||
github.com/xiaoqidun/j2kgo v0.0.0-20261009050019-d6d8c42b70bd h1:8aJ5cOOwA68CKp0FwcFgS27zSdcSJnYbmu8TJSn/HVQ=
|
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893 h1:jmQlBO7R+bcimOMs9jjyDWR1FqiPNGtZqA/V1WiYfPA=
|
||||||
github.com/xiaoqidun/j2kgo v0.0.0-20261009050019-d6d8c42b70bd/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA=
|
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.3 h1:p0d8FMLiLfzQuX278T4VTfTz8tIJEw87u4v63lH4CBc=
|
github.com/xiaoqidun/jbig2 v1.1.3 h1:p0d8FMLiLfzQuX278T4VTfTz8tIJEw87u4v63lH4CBc=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.3/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
github.com/xiaoqidun/jbig2 v1.1.3/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009054335-bd8a5e721992 h1:W5ukWZe5ru3A2Mlq1sqXw1hnW9e2ViK1LYvxG63baz0=
|
github.com/xiaoqidun/pdfgo v0.0.0-20261009064143-9a945dbdcbb6 h1:r1G1wTBO32uPvfT3iMl1S96+yj4294bNuFiHHGA+vM0=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009054335-bd8a5e721992/go.mod h1:n0s/KSvZRp3IGc2fGCkrg0shh2KPj++HYCzNDJj/ydg=
|
github.com/xiaoqidun/pdfgo v0.0.0-20261009064143-9a945dbdcbb6/go.mod h1:swzoqA5+wSHsRPesJS1VOMLhSyWkLCSQ+IgHNYXbcKo=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ type pdfImporter struct {
|
|||||||
pageActionPage int
|
pageActionPage int
|
||||||
fontIDs map[*pdfgo.Font]string
|
fontIDs map[*pdfgo.Font]string
|
||||||
fonts map[*pdfgo.Font]*pdfImportedFont
|
fonts map[*pdfgo.Font]*pdfImportedFont
|
||||||
|
symbolFonts map[*pdfgo.Font]*pdfImportedFont
|
||||||
fontWarnings map[string]bool
|
fontWarnings map[string]bool
|
||||||
clipTexts map[*pdfgo.TextClip][]TextObject
|
clipTexts map[*pdfgo.TextClip][]TextObject
|
||||||
clipPaths *renderCache[[32]byte, pdfClipPath]
|
clipPaths *renderCache[[32]byte, pdfClipPath]
|
||||||
|
|||||||
@@ -143,6 +143,120 @@ func (p *pdfImporter) importedFont(source *pdfgo.Font) (*pdfImportedFont, error)
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// symbolFont 将标准符号字体的旧式字符表转换为Unicode映射,保留原字形编号
|
||||||
|
// 入参: source 未嵌入的PDF字体
|
||||||
|
// 返回: *pdfImportedFont 可用的符号字体,无匹配时为空, error 字体或取消错误
|
||||||
|
func (p *pdfImporter) symbolFont(source *pdfgo.Font) (*pdfImportedFont, error) {
|
||||||
|
name := pdfEmbeddedFontName(source.Name)
|
||||||
|
if source.Subtype != "Type1" || name != "Symbol" && name != "ZapfDingbats" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if imported, ok := p.symbolFonts[source]; ok {
|
||||||
|
return imported, nil
|
||||||
|
}
|
||||||
|
if p.symbolFonts == nil {
|
||||||
|
p.symbolFonts = make(map[*pdfgo.Font]*pdfImportedFont)
|
||||||
|
}
|
||||||
|
if err := p.ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
definition := &Font{ID: name, FontName: name, FamilyName: name}
|
||||||
|
reader := &Reader{doc: &Document{}, fontResourcesRead: true, fontCache: map[string]*Font{name: definition}}
|
||||||
|
resolved, err := p.editor.newRenderer(reader).ResolveFont(name, true)
|
||||||
|
if err != nil || len(resolved.Data) == 0 || p.editor.backends.FontResources == nil {
|
||||||
|
p.symbolFonts[source] = nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
tables, err := fontFileTables(resolved.Data, 0)
|
||||||
|
if err != nil || !pdfSymbolCmap(tables["cmap"]) {
|
||||||
|
p.symbolFonts[source] = nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
standard, err := p.reader.ReadFontContext(p.ctx, pdfgo.Dictionary{"Type": pdfgo.Name("Font"), "Subtype": pdfgo.Name("Type1"), "BaseFont": pdfgo.Name(name)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
codes := make([]byte, 256)
|
||||||
|
for index := range codes {
|
||||||
|
codes[index] = byte(index)
|
||||||
|
}
|
||||||
|
glyphs, err := standard.DecodeContext(p.ctx, codes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
original := parseCmapMappings(tables["cmap"])
|
||||||
|
mapping := make(map[rune]uint16)
|
||||||
|
names := make(map[string]uint16)
|
||||||
|
for _, glyph := range glyphs {
|
||||||
|
if glyph.Name == ".notdef" || glyph.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id := original[rune(glyph.Code)]
|
||||||
|
for _, base := range [...]rune{0xf000, 0xf100, 0xf200} {
|
||||||
|
if candidate := original[base+rune(glyph.Code)]; candidate != 0 {
|
||||||
|
if id != 0 && id != candidate {
|
||||||
|
return nil, fmt.Errorf("ambiguous PDF symbol font mapping")
|
||||||
|
}
|
||||||
|
id = candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
names[glyph.Name] = id
|
||||||
|
if char, size := utf8.DecodeRuneInString(glyph.Text); size != 0 && size == len(glyph.Text) {
|
||||||
|
mapping[char] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mapping) == 0 {
|
||||||
|
p.symbolFonts[source] = nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
tables["cmap"] = buildCmapTable(0, mapping)
|
||||||
|
program, err := serializeOTF(tables)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resource, err := p.editor.backends.FontResources.OpenFontResource(FontFile{Name: name + ".ttf", Data: program}, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metrics, err := p.editor.backends.Fonts.OpenFont(resource.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := metrics.(FontOutlines); !ok {
|
||||||
|
p.symbolFonts[source] = nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
result := &pdfImportedFont{resource: resource, checksum: sha256.Sum256(resource.Data), metrics: metrics, type1Glyphs: names}
|
||||||
|
p.symbolFonts[source] = result
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pdfSymbolCmap 识别不含Unicode字符表的Windows符号字体
|
||||||
|
// 入参: data 字符映射表
|
||||||
|
// 返回: bool 是否为旧式符号字体
|
||||||
|
func pdfSymbolCmap(data []byte) bool {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
count := int(binary.BigEndian.Uint16(data[2:]))
|
||||||
|
if count > (len(data)-4)/8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
symbol := false
|
||||||
|
for i := range count {
|
||||||
|
pos := 4 + i*8
|
||||||
|
platform, encoding := binary.BigEndian.Uint16(data[pos:]), binary.BigEndian.Uint16(data[pos+2:])
|
||||||
|
if platform == 0 || platform == 3 && (encoding == 1 || encoding == 10) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
symbol = symbol || platform == 3 && encoding == 0
|
||||||
|
}
|
||||||
|
return symbol
|
||||||
|
}
|
||||||
|
|
||||||
// pdfFontProgram 为PDF子集字体补齐封装表,保留字形轮廓和编号
|
// pdfFontProgram 为PDF子集字体补齐封装表,保留字形轮廓和编号
|
||||||
// 无效字符表按PDF已解码字形重建,不改变字符对应的字形编号
|
// 无效字符表按PDF已解码字形重建,不改变字符对应的字形编号
|
||||||
// 入参: source PDF字体, type1 已解析的Type1程序,nil时按需解析
|
// 入参: source PDF字体, type1 已解析的Type1程序,nil时按需解析
|
||||||
|
|||||||
+4
-1
@@ -370,10 +370,13 @@ func (p *pdfImporter) compileGroup(build func(*pdfImporter) error) (*RasterPage,
|
|||||||
if p.fonts == nil {
|
if p.fonts == nil {
|
||||||
p.fonts = make(map[*pdfgo.Font]*pdfImportedFont)
|
p.fonts = make(map[*pdfgo.Font]*pdfImportedFont)
|
||||||
}
|
}
|
||||||
|
if p.symbolFonts == nil {
|
||||||
|
p.symbolFonts = make(map[*pdfgo.Font]*pdfImportedFont)
|
||||||
|
}
|
||||||
if p.clipPaths == nil {
|
if p.clipPaths == nil {
|
||||||
p.clipPaths = &renderCache[[32]byte, pdfClipPath]{limit: 8 << 20}
|
p.clipPaths = &renderCache[[32]byte, pdfClipPath]{limit: 8 << 20}
|
||||||
}
|
}
|
||||||
local := pdfImporter{ctx: p.ctx, reader: p.reader, editor: editor, renderer: p.renderer, matrix: p.matrix, pageBox: p.pageBox, pageWidth: p.pageWidth, pageHeight: p.pageHeight, rasterDPI: p.rasterDPI, warning: p.warning, rasterWarned: true, fontIDs: map[*pdfgo.Font]string{}, fonts: p.fonts, clipPaths: p.clipPaths}
|
local := pdfImporter{ctx: p.ctx, reader: p.reader, editor: editor, renderer: p.renderer, matrix: p.matrix, pageBox: p.pageBox, pageWidth: p.pageWidth, pageHeight: p.pageHeight, rasterDPI: p.rasterDPI, warning: p.warning, rasterWarned: true, fontIDs: map[*pdfgo.Font]string{}, fonts: p.fonts, symbolFonts: p.symbolFonts, clipPaths: p.clipPaths}
|
||||||
if err := build(&local); err != nil {
|
if err := build(&local); err != nil {
|
||||||
return nil, box, err
|
return nil, box, err
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-7
@@ -326,13 +326,29 @@ func (p *pdfImporter) text(mark pdfgo.TextMark) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if imported = p.embeddedTextFont(font, mark.Glyphs); imported != nil {
|
} else {
|
||||||
reused, embedded = true, true
|
imported = p.embeddedTextFont(font, mark.Glyphs)
|
||||||
mark.Glyphs = slices.Clone(mark.Glyphs)
|
symbol := false
|
||||||
for index := range mark.Glyphs {
|
if imported == nil {
|
||||||
char, _ := utf8.DecodeRuneInString(mark.Glyphs[index].Text)
|
imported, err = p.symbolFont(font)
|
||||||
mark.Glyphs[index].ID = imported.metrics.GlyphIndex(char)
|
if err != nil {
|
||||||
mark.Glyphs[index].HasID = true
|
return err
|
||||||
|
}
|
||||||
|
symbol = imported != nil
|
||||||
|
}
|
||||||
|
if imported != nil {
|
||||||
|
reused, embedded = true, true
|
||||||
|
mark.Glyphs = slices.Clone(mark.Glyphs)
|
||||||
|
for index := range mark.Glyphs {
|
||||||
|
glyph := &mark.Glyphs[index]
|
||||||
|
if symbol {
|
||||||
|
glyph.ID = imported.type1Glyphs[glyph.Name]
|
||||||
|
} else {
|
||||||
|
char, _ := utf8.DecodeRuneInString(glyph.Text)
|
||||||
|
glyph.ID = imported.metrics.GlyphIndex(char)
|
||||||
|
}
|
||||||
|
glyph.HasID = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
id := p.fontIDs[font]
|
id := p.fontIDs[font]
|
||||||
|
|||||||
Reference in new issue
Block a user