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