mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
90cda815ef
commit
8d91752943
4 files changed
+51
-19
No files matched your search
@@ -9,7 +9,7 @@ require (
|
|||||||
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/jbig2 v1.1.1
|
github.com/xiaoqidun/jbig2 v1.1.1
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927123406-320d8b5546b5
|
github.com/xiaoqidun/pdfgo v0.0.0-20260927125911-04f5476a9051
|
||||||
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
|
||||||
|
|||||||
@@ -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/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 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
||||||
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927123406-320d8b5546b5 h1:FyW2HSyxcrf33xo3F1eKVZmlizgpxML2H+qn63yXcDI=
|
github.com/xiaoqidun/pdfgo v0.0.0-20260927125911-04f5476a9051 h1:VhoxfD1yFJMBcgThFZCo480VVBEU5dB8qAZXWCUSeqI=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260927123406-320d8b5546b5/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
github.com/xiaoqidun/pdfgo v0.0.0-20260927125911-04f5476a9051/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||||
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=
|
||||||
|
|||||||
+4
-10
@@ -166,7 +166,7 @@ func (r *Renderer) RenderToText(page *PageContent, writer io.Writer) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = io.WriteString(writer, text.String())
|
_, err = text.WriteTo(writer)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,17 +191,11 @@ func (r *Renderer) RenderToMultiPageText(writer io.Writer, indices ...int) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to extract page %d text: %w", index+1, err)
|
return fmt.Errorf("failed to extract page %d text: %w", index+1, err)
|
||||||
}
|
}
|
||||||
if value := text.String(); value != "" {
|
n, err := text.writeTo(writer, written)
|
||||||
if written {
|
if err != nil {
|
||||||
if _, err := io.WriteString(writer, "\n"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
written = written || n > 0
|
||||||
if _, err := io.WriteString(writer, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
written = true
|
|
||||||
}
|
|
||||||
if err := r.exportProgress(i+1, len(indices)); err != nil {
|
if err := r.exportProgress(i+1, len(indices)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-6
@@ -17,6 +17,7 @@ package ofdgo
|
|||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@@ -62,7 +63,7 @@ type PageText struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TextRun 文本对象原文及逐字符区域,坐标以页面左上角为原点,单位为毫米
|
// TextRun 文本对象原文及逐字符区域,坐标以页面左上角为原点,单位为毫米
|
||||||
// Boxes与Text的Unicode字符一一对应,缺少字体时使用文本对象区域
|
// Boxes与Text的Unicode字符一一对应,缺少字体时使用文本对象区域,缺省区域不影响文字搜索
|
||||||
type TextRun struct {
|
type TextRun struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
@@ -122,16 +123,47 @@ func (r *Renderer) PageTextByIndex(index int) (*PageText, error) {
|
|||||||
// 返回: string 页面原文
|
// 返回: string 页面原文
|
||||||
func (p *PageText) String() string {
|
func (p *PageText) String() string {
|
||||||
var text strings.Builder
|
var text strings.Builder
|
||||||
|
_, _ = p.WriteTo(&text)
|
||||||
|
return text.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo 按文本对象顺序输出UTF-8原文,以换行分隔非空对象,不拼接整页字符串
|
||||||
|
// 入参: writer 输出流
|
||||||
|
// 返回: int64 已写字节数, error 写入错误,出错时可能已有部分输出
|
||||||
|
func (p *PageText) WriteTo(writer io.Writer) (int64, error) {
|
||||||
|
return p.writeTo(writer, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeTo 输出非空文本对象,可在首个对象前分隔上一页
|
||||||
|
// 入参: writer 输出流, separator 是否需要前导换行
|
||||||
|
// 返回: int64 已写字节数, error 写入错误
|
||||||
|
func (p *PageText) writeTo(writer io.Writer, separator bool) (int64, error) {
|
||||||
|
var written int64
|
||||||
for _, run := range p.Runs {
|
for _, run := range p.Runs {
|
||||||
if run.Text == "" {
|
if run.Text == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if text.Len() > 0 {
|
if separator {
|
||||||
text.WriteByte('\n')
|
n, err := io.WriteString(writer, "\n")
|
||||||
|
written += int64(n)
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
}
|
}
|
||||||
text.WriteString(run.Text)
|
if n != 1 {
|
||||||
|
return written, io.ErrShortWrite
|
||||||
}
|
}
|
||||||
return text.String()
|
}
|
||||||
|
n, err := io.WriteString(writer, run.Text)
|
||||||
|
written += int64(n)
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
if n != len(run.Text) {
|
||||||
|
return written, io.ErrShortWrite
|
||||||
|
}
|
||||||
|
separator = true
|
||||||
|
}
|
||||||
|
return written, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search 按原文进行忽略大小写的字面匹配,不跨文本对象拼接或识别图像
|
// Search 按原文进行忽略大小写的字面匹配,不跨文本对象拼接或识别图像
|
||||||
@@ -166,7 +198,7 @@ func (p *PageText) Search(query string) []TextMatch {
|
|||||||
Text: string(runes[start:end]),
|
Text: string(runes[start:end]),
|
||||||
After: string(runes[end:min(len(runes), end+16)]),
|
After: string(runes[end:min(len(runes), end+16)]),
|
||||||
}
|
}
|
||||||
for _, box := range run.Boxes[start:end] {
|
for _, box := range run.Boxes[min(start, len(run.Boxes)):min(end, len(run.Boxes))] {
|
||||||
if box.W > 0 && box.H > 0 && (len(match.Boxes) == 0 || match.Boxes[len(match.Boxes)-1] != box) {
|
if box.W > 0 && box.H > 0 && (len(match.Boxes) == 0 || match.Boxes[len(match.Boxes)-1] != box) {
|
||||||
match.Boxes = append(match.Boxes, box)
|
match.Boxes = append(match.Boxes, box)
|
||||||
}
|
}
|
||||||
@@ -184,6 +216,12 @@ func (p *PageText) Search(query string) []TextMatch {
|
|||||||
// 返回: string 匹配用文字
|
// 返回: string 匹配用文字
|
||||||
func foldText(text string) string {
|
func foldText(text string) string {
|
||||||
return strings.Map(func(char rune) rune {
|
return strings.Map(func(char rune) rune {
|
||||||
|
if char < utf8.RuneSelf {
|
||||||
|
if char >= 'a' && char <= 'z' {
|
||||||
|
return char - ('a' - 'A')
|
||||||
|
}
|
||||||
|
return char
|
||||||
|
}
|
||||||
folded := char
|
folded := char
|
||||||
for next := unicode.SimpleFold(char); next != char; next = unicode.SimpleFold(next) {
|
for next := unicode.SimpleFold(char); next != char; next = unicode.SimpleFold(next) {
|
||||||
folded = min(folded, next)
|
folded = min(folded, next)
|
||||||
|
|||||||
Reference in new issue
Block a user