feat(转换能力): 有限兼容日期,保留严格校验
build.yaml / build (push) Successful in 44s

This commit is contained in:
xiaoqidun committed 2026-10-20 18:45:00 +08:00
1 parent 6ca44cd9a5
commit 3227134bfc
3 files changed
+45 -5

No files matched your search

+27
View File
@@ -22,6 +22,33 @@ import (
"github.com/xiaoqidun/pdfgo" "github.com/xiaoqidun/pdfgo"
) )
// NormalizeDate 校验PDF日期,非严格模式补齐完整时区偏移的分隔符
// 入参: text PDF日期字符串, strict 是否严格校验
// 返回: string 标准日期字符串, error 日期错误
func NormalizeDate(text string, strict bool) (string, error) {
if text == "" {
return text, nil
}
_, err := pdfgo.ParseDate(text)
if err == nil || strict {
return text, err
}
offset := len(text) - 5
if offset < 6 || text[offset] != '+' && text[offset] != '-' {
return text, err
}
for _, digit := range text[offset+1:] {
if digit < '0' || digit > '9' {
return text, err
}
}
normalized := text[:offset+3] + "'" + text[offset+3:] + "'"
if _, parseErr := pdfgo.ParseDate(normalized); parseErr != nil {
return text, err
}
return normalized, nil
}
// MetadataDate 保留当地日历日期,PDF省略时区按GMT,XMP省略日或时区时不推算 // MetadataDate 保留当地日历日期,PDF省略时区按GMT,XMP省略日或时区时不推算
// 入参: text 来源日期, xmp 是否为XMP日期 // 入参: text 来源日期, xmp 是否为XMP日期
// 返回: time.Time 解析值, bool 是否精确到日, bool 是否可比较绝对时间, error 日期错误 // 返回: time.Time 解析值, bool 是否精确到日, bool 是否可比较绝对时间, error 日期错误
+2 -2
View File
@@ -33,7 +33,7 @@ var ErrPDFPassword = pdfgo.ErrPassword
// PDFImportOptions 指定PDF转换的密码、运行时后端、进度通知与检查策略 // PDFImportOptions 指定PDF转换的密码、运行时后端、进度通知与检查策略
// Password默认使用PDF安全处理器编码,PasswordUTF8启用UTF-8输入及标准密码准备,空值尝试空密码 // Password默认使用PDF安全处理器编码,PasswordUTF8启用UTF-8输入及标准密码准备,空值尝试空密码
// Strict禁止恢复缺失资源或丢失内容语义,默认在报告中记录警告 // Strict禁止日期兼容、恢复缺失资源或丢失内容语义,默认在报告中记录警告
// RendererOptions复用渲染器字体来源和后端配置,Backends优先于其中的后端设置 // RendererOptions复用渲染器字体来源和后端配置,Backends优先于其中的后端设置
// RasterDPI指定局部合成与三维绘制精度,0沿用渲染器DPI,默认300dpi,Strict禁用局部合成 // RasterDPI指定局部合成与三维绘制精度,0沿用渲染器DPI,默认300dpi,Strict禁用局部合成
// OnProgress按open、pages、convert及write.*阶段报告进度,total为0表示总量未知 // OnProgress按open、pages、convert及write.*阶段报告进度,total为0表示总量未知
@@ -307,7 +307,7 @@ func ImportPDF(ctx context.Context, input *Input, options PDFImportOptions) (*Ed
} }
if len(editor.pages) != 0 { if len(editor.pages) != 0 {
importer.page = -1 importer.page = -1
if err := importer.documentMetadata(ctx); err != nil { if err := importer.documentMetadata(ctx, options.Strict); err != nil {
return nil, PDFImportReport{}, fmt.Errorf("import PDF metadata: %w", err) return nil, PDFImportReport{}, fmt.Errorf("import PDF metadata: %w", err)
} }
if err := importer.documentAttachments(ctx); err != nil { if err := importer.documentAttachments(ctx); err != nil {
+16 -3
View File
@@ -908,9 +908,9 @@ func (b *pdfStructureBindings) reference(pageID string, sequence *pdfgo.ContentS
} }
// documentMetadata 保留PDF元数据原文并映射文档描述,来源软件与扩展字段保存在独立标引中 // documentMetadata 保留PDF元数据原文并映射文档描述,来源软件与扩展字段保存在独立标引中
// 入参: ctx 取消上下文 // 入参: ctx 取消上下文, strict 是否严格校验
// 返回: error 读取、解析、日期或提交错误 // 返回: error 读取、解析、日期或提交错误
func (p *pdfImporter) documentMetadata(ctx context.Context) error { func (p *pdfImporter) documentMetadata(ctx context.Context, strict bool) error {
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return err return err
} }
@@ -918,6 +918,19 @@ func (p *pdfImporter) documentMetadata(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
for _, field := range []struct {
name string
text *string
}{{"CreationDate", &info.CreationDate}, {"ModDate", &info.ModDate}} {
text, err := codecpdf.NormalizeDate(*field.text, strict)
if err != nil {
return fmt.Errorf("PDF metadata %s: %w", field.name, err)
}
if text != *field.text {
p.report.Warnings = append(p.report.Warnings, pdfgo.Diagnostic{Message: "PDF metadata " + field.name + ": time zone separators normalized"})
*field.text = text
}
}
values, original, err := p.informationMetadata(ctx, info) values, original, err := p.informationMetadata(ctx, info)
if err != nil { if err != nil {
return err return err
@@ -1006,7 +1019,7 @@ func (p *pdfImporter) documentMetadata(ctx context.Context) error {
return ctx.Err() return ctx.Err()
} }
// informationMetadata 收集已声明字段并保存原始文本,XML无法表示的字符使用Base64保留 // informationMetadata 收集已声明字段并保存文本,日期使用校验结果,XML无法表示的字符使用Base64保留
// 入参: ctx 取消上下文, info 已解码的信息字典 // 入参: ctx 取消上下文, info 已解码的信息字典
// 返回: map[string]pdfMetadataValue 可映射字段, StructureNode 原始字段, error 读取或取消错误 // 返回: map[string]pdfMetadataValue 可映射字段, StructureNode 原始字段, error 读取或取消错误
func (p *pdfImporter) informationMetadata(ctx context.Context, info pdfgo.DocumentInfo) (map[string]pdfMetadataValue, StructureNode, error) { func (p *pdfImporter) informationMetadata(ctx context.Context, info pdfgo.DocumentInfo) (map[string]pdfMetadataValue, StructureNode, error) {