feat(注解转换): 修正注解转换与图像导出
build.yaml / build (push) Successful in 52s

This commit is contained in:
xiaoqidun committed 2026-10-04 23:24:49 +08:00
1 parent d09c57f226
commit 5dcd7a4e8d
7 files changed
+61 -21

No files matched your search

+1 -1
View File
@@ -10,7 +10,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-20261004122813-dd48c1539fca
github.com/xiaoqidun/pdfgo v0.0.0-20261004151751-bff53238d953
golang.org/x/crypto v0.57.0
golang.org/x/image v0.46.0
golang.org/x/text v0.42.0
+2 -2
View File
@@ -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-20261004122813-dd48c1539fca h1:lpsew7G6nhHcEm3Pzg1VKObMqPYZv4anQDMVRoVnelg=
github.com/xiaoqidun/pdfgo v0.0.0-20261004122813-dd48c1539fca/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
github.com/xiaoqidun/pdfgo v0.0.0-20261004151751-bff53238d953 h1:4fz7oPfhMZr8N2fWjv7Gn6+L2TInWGarQm3UGaqV+54=
github.com/xiaoqidun/pdfgo v0.0.0-20261004151751-bff53238d953/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=
+9 -13
View File
@@ -48,18 +48,11 @@ func (p *pdfImporter) appearanceAnnotation(ctx context.Context, page *pdfgo.Page
annotation.Dictionary = maps.Clone(annotation.Dictionary)
annotation.Dictionary["Contents"] = pdfgo.String("\xef\xbb\xbf" + remark)
}
flags := int64(0)
if value := annotation.Dictionary["F"]; value != nil {
resolved, err := p.reader.Resolve(value)
if err != nil {
return err
}
number, ok := resolved.(pdfgo.Integer)
if !ok {
return fmt.Errorf("invalid PDF annotation flags")
}
flags = int64(number)
flags, err := p.reader.ReadAnnotationFlags(annotation)
if err != nil {
return err
}
invisible := flags&1 != 0 && !annotation.IsStandard()
if flags&256 != 0 {
return &pdfgo.UnsupportedError{Feature: "annotation ToggleNoView flag"}
}
@@ -101,7 +94,7 @@ func (p *pdfImporter) appearanceAnnotation(ctx context.Context, page *pdfgo.Page
if err := stamp.compositeObjects(nodes); err != nil {
return err
}
if flags&(2|32) == 0 {
if flags&(2|32) == 0 && !invisible {
p.compositeNodes = stamp.compositeNodes
p.transferBackdrop = stamp.transferBackdrop
}
@@ -142,7 +135,7 @@ func (p *pdfImporter) appearanceAnnotation(ctx context.Context, page *pdfgo.Page
region.Fill, region.Stroke, region.Actions = &no, &no, actions
converted.Appearance.Objects = append(converted.Appearance.Objects, GraphicObject{Type: "PathObject", PathObject: region})
}
if flags&(2|32) != 0 {
if flags&(2|32) != 0 || invisible {
visible := false
converted.Visible = &visible
}
@@ -194,6 +187,9 @@ func pdfAnnotationType(subtype pdfgo.Name) string {
case "Text", "FreeText", "Line", "Square", "Circle", "Polygon", "PolyLine", "Caret", "Ink", "FileAttachment", "Sound", "Movie", "Screen", "Link", "Popup", "Widget", "PrinterMark", "TrapNet", "3D", "RichMedia", "Redact":
return "Path"
}
if !(pdfgo.Annotation{Subtype: subtype}).IsStandard() {
return "Path"
}
return ""
}
+6 -2
View File
@@ -21,8 +21,12 @@ import "github.com/xiaoqidun/pdfgo"
// 返回: error 严格模式无法保留设备网屏
func (p *pdfImporter) halftone(style pdfgo.Style) error {
h := style.Halftone
message := "PDF continuous-tone appearance retained; device halftone not transferred to OFD"
if h == nil || h.Type == 0 {
return nil
if style.HalftoneOrigin == nil {
return nil
}
message = "PDF continuous-tone appearance retained; device halftone origin not transferred to OFD"
}
if p.warning == nil {
return &pdfgo.UnsupportedError{Feature: "device halftone conversion"}
@@ -32,7 +36,7 @@ func (p *pdfImporter) halftone(style pdfgo.Style) error {
}
if !p.halftoneWarnings[h] {
p.halftoneWarnings[h] = true
p.warning(pdfgo.Diagnostic{Message: "PDF continuous-tone appearance retained; device halftone not transferred to OFD"})
p.warning(pdfgo.Diagnostic{Message: message})
}
return nil
}
+9
View File
@@ -128,6 +128,11 @@ func (CanvasBackend) RenderPDF(r *Renderer, pages []RenderDocumentPage, writer i
start := buf.Len()
p := pdf.New(buf, pages[0].Box.W, pages[0].Box.H, nil)
renderer := &pdfRenderer{PDF: p, glyphPaths: make(map[*canvas.Path]*canvas.Path), images: make([][]image.Image, 1)}
defer func() {
if renderer.imageError != nil {
r.canvasState().images = renderCache[*EncodedImage, image.Image]{limit: imageCacheLimit}
}
}()
var info DocInfo
if docInfo, err := r.Reader.DocInfo(); err == nil {
info = *docInfo
@@ -143,6 +148,10 @@ func (CanvasBackend) RenderPDF(r *Renderer, pages []RenderDocumentPage, writer i
buf.Truncate(start)
return fmt.Errorf("failed to render page %d: %w", i+1, err)
}
if renderer.imageError != nil {
buf.Truncate(start)
return fmt.Errorf("failed to render page %d: %w", i+1, renderer.imageError)
}
pages[i].Content = nil
if progress != nil {
if err := progress(i+1, len(pages)); err != nil {
+16 -1
View File
@@ -30,6 +30,7 @@ type pdfRenderer struct {
glyphPaths map[*canvas.Path]*canvas.Path
images [][]image.Image
exactImages bool
imageError error
}
// pdfNavigation PDF导航信息
@@ -59,9 +60,23 @@ type pdfOutline struct {
Y float64
}
// RenderImage 在图像局部保存状态前同步不透明画笔,避免恢复后的透明度与后端缓存不一致
// RenderImage 校验惰性图片并同步不透明画笔,保留解码错误及后端透明度状态
// 入参: img 图像, matrix 图像变换
func (r *pdfRenderer) RenderImage(img image.Image, matrix canvas.Matrix) {
if r.imageError != nil {
return
}
if source, ok := img.(interface{ Image() (image.Image, error) }); ok {
pixels, err := source.Image()
if err != nil {
r.imageError = err
return
}
if pixels == nil || pixels.Bounds() != img.Bounds() {
r.imageError = fmt.Errorf("PDF output image pixel bounds differ")
return
}
}
if len(r.images) != 0 {
var original image.Image
opaque := false
+18 -2
View File
@@ -23,10 +23,13 @@ import (
"github.com/xiaoqidun/pdfgo"
)
// preserveImages 按实际绘制顺序恢复颜色及透明度精度,不依赖后端资源命名或对象编号
// preserveImages 按绘制顺序恢复颜色及透明度精度,二值透明采用标准Matte预混合
// 入参: ctx 取消上下文, data 后端PDF数据
// 返回: []byte 保留图像精度的PDF数据, error 资源或写入错误
func (r *pdfRenderer) preserveImages(ctx context.Context, data []byte) ([]byte, error) {
if r.imageError != nil {
return nil, r.imageError
}
if !r.exactImages {
return data, nil
}
@@ -98,8 +101,21 @@ func (r *pdfRenderer) preserveImages(ctx context.Context, data []byte) ([]byte,
if pages != len(r.images) {
return nil, fmt.Errorf("PDF output page missing")
}
for ref, original := range images {
if err := ctx.Err(); err != nil {
return nil, err
}
pixels, err := imagePixelData(original)
if err != nil {
return nil, err
}
if pixels == nil || pixels.Bounds() != original.Bounds() {
return nil, fmt.Errorf("PDF output image pixel bounds differ")
}
images[ref] = pixels
}
var result bytes.Buffer
if _, err := reader.ReplaceImagesTo(ctx, &result, images); err != nil {
if _, err := reader.ReplaceImagesTo(ctx, &result, images, pdfgo.ImageWriteOptions{PreblendBinary: true}); err != nil {
return nil, err
}
return result.Bytes(), nil