mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
1 parent
32ab7fe732
commit
60b8b44eca
6 files changed
+206
-133
No files matched your search
@@ -10,7 +10,7 @@ require (
|
||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893
|
||||
github.com/xiaoqidun/jbig2 v1.1.3
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009094649-718bb9e8a149
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009121346-abd17a2aa440
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/image v0.46.0
|
||||
golang.org/x/text v0.42.0
|
||||
|
||||
@@ -62,8 +62,8 @@ github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893 h1:jmQlBO7R+bcimOM
|
||||
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/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009094649-718bb9e8a149 h1:E5g5tl7ms0DTBYnccc33VoQOpGQUnQQPKd4UlIh0LCM=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009094649-718bb9e8a149/go.mod h1:swzoqA5+wSHsRPesJS1VOMLhSyWkLCSQ+IgHNYXbcKo=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009121346-abd17a2aa440 h1:auHwW058jOQGHVB6FXNfT6kdLh5DU6KByu4DGDSLJ7U=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261009121346-abd17a2aa440/go.mod h1:swzoqA5+wSHsRPesJS1VOMLhSyWkLCSQ+IgHNYXbcKo=
|
||||
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=
|
||||
|
||||
@@ -444,22 +444,3 @@ func resolveActionURI(action URI) string {
|
||||
}
|
||||
return base.ResolveReference(target).String()
|
||||
}
|
||||
|
||||
// outlineDest 获取大纲跳转目标
|
||||
// 入参: outline 大纲节点, bookmarks 书签
|
||||
// 返回: *Dest 跳转目标
|
||||
func outlineDest(outline OutlineElem, bookmarks map[string]Dest) *Dest {
|
||||
for _, action := range outline.Actions {
|
||||
if action.Goto != nil {
|
||||
if dest := gotoDest(action.Goto, bookmarks); dest != nil {
|
||||
return dest
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, child := range outline.OutlineElem {
|
||||
if dest := outlineDest(child, bookmarks); dest != nil {
|
||||
return dest
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -165,38 +165,30 @@ func (CanvasBackend) RenderPDF(r *Renderer, pages []RenderDocumentPage, writer i
|
||||
return err
|
||||
}
|
||||
data := replacePDFProducer(buf.Bytes()[start:])
|
||||
data, err = renderer.preserveImages(r.outputContext(), data)
|
||||
if err != nil {
|
||||
buf.Truncate(start)
|
||||
return err
|
||||
options := pdfgo.OptimizeOptions{Compression: r.Compression}
|
||||
if r.Compression.Mode != CompressionUnchanged && progress != nil {
|
||||
options.OnProgress = func(_ string, _, _ int) error {
|
||||
return progress(len(pages), len(pages))
|
||||
}
|
||||
}
|
||||
if direct && (renderer.exactImages || navigation.exactLinks) {
|
||||
if direct {
|
||||
if !renderer.exactImages && !navigation.exactLinks {
|
||||
if err := r.outputContext().Err(); err != nil {
|
||||
buf.Truncate(start)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var result bytes.Buffer
|
||||
err := renderer.writePDF(r.outputContext(), data, &result, options)
|
||||
buf.Truncate(start)
|
||||
_, err = buf.Write(data)
|
||||
return err
|
||||
}
|
||||
if r.Compression.Mode != CompressionUnchanged {
|
||||
reader, err := pdfgo.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
_, err = reader.OptimizeTo(r.outputContext(), writer, pdfgo.OptimizeOptions{Compression: r.Compression, OnProgress: func(_ string, _, _ int) error {
|
||||
if progress != nil {
|
||||
return progress(len(pages), len(pages))
|
||||
}
|
||||
return nil
|
||||
}})
|
||||
_, err = buf.Write(result.Bytes())
|
||||
return err
|
||||
}
|
||||
if direct {
|
||||
return nil
|
||||
}
|
||||
n, err := writer.Write(data)
|
||||
if err == nil && n != len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
return err
|
||||
return renderer.writePDF(r.outputContext(), data, writer, options)
|
||||
}
|
||||
|
||||
// replacePDFProducer 替换PDF的Producer属性
|
||||
|
||||
+151
-68
@@ -15,9 +15,11 @@
|
||||
package ofdgo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"strconv"
|
||||
|
||||
"github.com/tdewolff/canvas"
|
||||
cimage "github.com/tdewolff/canvas/image"
|
||||
@@ -37,31 +39,36 @@ type pdfRenderer struct {
|
||||
|
||||
// pdfNavigation PDF导航信息
|
||||
type pdfNavigation struct {
|
||||
Anchor map[int][]pdfAnchor
|
||||
Link map[int][]pdfLink
|
||||
Outline map[int][]pdfOutline
|
||||
nextID int
|
||||
Outline []pdfOutline
|
||||
exactLinks bool
|
||||
}
|
||||
|
||||
// pdfAnchor PDF跳转目标
|
||||
type pdfAnchor struct {
|
||||
Name string
|
||||
Rect canvas.Rect
|
||||
}
|
||||
|
||||
// pdfLink PDF链接
|
||||
type pdfLink struct {
|
||||
URI string
|
||||
Rect canvas.Rect
|
||||
Region *pdfgo.LinkRegion
|
||||
Target *pdfTarget
|
||||
}
|
||||
|
||||
// pdfTarget 保存输出页索引及默认用户空间中的跳转参数
|
||||
type pdfTarget struct {
|
||||
Page int
|
||||
Destination pdfgo.Destination
|
||||
}
|
||||
|
||||
// pdfOutline PDF大纲
|
||||
type pdfOutline struct {
|
||||
Name string
|
||||
Level int
|
||||
Y float64
|
||||
Name string
|
||||
Level int
|
||||
Actions []pdfOutlineAction
|
||||
}
|
||||
|
||||
// pdfOutlineAction 保存目录的导航动作,输出时关联实际页面引用
|
||||
type pdfOutlineAction struct {
|
||||
URI string
|
||||
Target *pdfTarget
|
||||
}
|
||||
|
||||
// RenderImage 校验惰性图片并同步不透明画笔,保留解码错误及后端透明度状态
|
||||
@@ -126,12 +133,10 @@ func (r *pdfRenderer) glyphPath(path *canvas.Path, matrix canvas.Matrix) *canvas
|
||||
|
||||
// newPDFNavigation 创建PDF导航信息
|
||||
// 入参: renderer 渲染器, doc 文档结构, pages 页面数据
|
||||
// 返回: *pdfNavigation PDF导航信息, error 几何错误
|
||||
// 返回: *pdfNavigation PDF导航信息, error 区域或目标错误
|
||||
func newPDFNavigation(renderer *Renderer, doc *Document, pages []RenderDocumentPage) (*pdfNavigation, error) {
|
||||
navigation := &pdfNavigation{
|
||||
Anchor: make(map[int][]pdfAnchor),
|
||||
Link: make(map[int][]pdfLink),
|
||||
Outline: make(map[int][]pdfOutline),
|
||||
Link: make(map[int][]pdfLink),
|
||||
}
|
||||
pageIndex := make(map[string]int, len(pages))
|
||||
for i, page := range pages {
|
||||
@@ -162,7 +167,9 @@ func newPDFNavigation(renderer *Renderer, doc *Document, pages []RenderDocumentP
|
||||
}
|
||||
rect := pdfSourceRect(box, page.Box.H)
|
||||
count := len(navigation.Link[i])
|
||||
navigation.addAction(i, rect, action, bookmarks, pageIndex, pages)
|
||||
if err := navigation.addAction(i, rect, action, bookmarks, pageIndex, pages); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(navigation.Link[i]) == count || outline == "" {
|
||||
continue
|
||||
}
|
||||
@@ -182,71 +189,154 @@ func newPDFNavigation(renderer *Renderer, doc *Document, pages []RenderDocumentP
|
||||
}
|
||||
}
|
||||
if doc != nil {
|
||||
navigation.addOutlines(doc.Outlines.OutlineElem, 0, bookmarks, pageIndex, pages)
|
||||
if err := navigation.addOutlines(doc.Outlines.OutlineElem, 0, bookmarks, pageIndex, pages); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return navigation, nil
|
||||
}
|
||||
|
||||
// addAction 添加PDF动作
|
||||
// 入参: page 页面索引, rect 动作区域, action 动作, bookmarks 书签, pageIndex 页面索引表, pages 页面数据
|
||||
func (n *pdfNavigation) addAction(page int, rect canvas.Rect, action Action, bookmarks map[string]Dest, pageIndex map[string]int, pages []RenderDocumentPage) {
|
||||
// 返回: error 目标参数错误
|
||||
func (n *pdfNavigation) addAction(page int, rect canvas.Rect, action Action, bookmarks map[string]Dest, pageIndex map[string]int, pages []RenderDocumentPage) error {
|
||||
if action.Goto != nil {
|
||||
dest := gotoDest(action.Goto, bookmarks)
|
||||
if dest == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
target, ok := pageIndex[dest.PageID]
|
||||
if !ok {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
targetRect, ok := pdfDestRect(*dest, pages[target].Box.H)
|
||||
if !ok {
|
||||
return
|
||||
destination, err := pdfDestination(*dest, pages[target].Box.H)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := fmt.Sprintf("ofdgo-dest-%d", n.nextID)
|
||||
n.nextID++
|
||||
n.Anchor[target] = append(n.Anchor[target], pdfAnchor{Name: name, Rect: targetRect})
|
||||
n.Link[page] = append(n.Link[page], pdfLink{URI: "#" + name, Rect: rect})
|
||||
return
|
||||
n.Link[page] = append(n.Link[page], pdfLink{Rect: rect, Target: &pdfTarget{Page: target, Destination: destination}})
|
||||
n.exactLinks = true
|
||||
return nil
|
||||
}
|
||||
if action.URI != nil && action.URI.URI != "" {
|
||||
n.Link[page] = append(n.Link[page], pdfLink{URI: resolveActionURI(*action.URI), Rect: rect})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addOutlines 添加PDF大纲
|
||||
// addOutlines 保留目录层级及节点自身的导航动作,选页时移除无保留子项的失效跳转节点
|
||||
// 入参: outlines 大纲节点, level 节点层级, bookmarks 书签, pageIndex 页面索引表, pages 页面数据
|
||||
func (n *pdfNavigation) addOutlines(outlines []OutlineElem, level int, bookmarks map[string]Dest, pageIndex map[string]int, pages []RenderDocumentPage) {
|
||||
// 返回: error 目标参数错误
|
||||
func (n *pdfNavigation) addOutlines(outlines []OutlineElem, level int, bookmarks map[string]Dest, pageIndex map[string]int, pages []RenderDocumentPage) error {
|
||||
for _, outline := range outlines {
|
||||
nextLevel := level
|
||||
if dest := outlineDest(outline, bookmarks); dest != nil {
|
||||
if page, ok := pageIndex[dest.PageID]; ok {
|
||||
n.Outline[page] = append(n.Outline[page], pdfOutline{
|
||||
Name: outline.Title,
|
||||
Level: level,
|
||||
Y: pdfDestY(*dest, pages[page].Box.H),
|
||||
})
|
||||
nextLevel++
|
||||
entry := pdfOutline{Name: outline.Title, Level: level}
|
||||
onlyMissingTargets := len(outline.Actions) != 0
|
||||
for _, action := range outline.Actions {
|
||||
if action.Goto != nil {
|
||||
dest := gotoDest(action.Goto, bookmarks)
|
||||
if dest == nil {
|
||||
continue
|
||||
}
|
||||
page, ok := pageIndex[dest.PageID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
destination, err := pdfDestination(*dest, pages[page].Box.H)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.Actions = append(entry.Actions, pdfOutlineAction{Target: &pdfTarget{Page: page, Destination: destination}})
|
||||
} else {
|
||||
onlyMissingTargets = false
|
||||
if action.URI != nil && action.URI.URI != "" {
|
||||
entry.Actions = append(entry.Actions, pdfOutlineAction{URI: resolveActionURI(*action.URI)})
|
||||
}
|
||||
}
|
||||
}
|
||||
n.addOutlines(outline.OutlineElem, nextLevel, bookmarks, pageIndex, pages)
|
||||
index := len(n.Outline)
|
||||
n.Outline = append(n.Outline, entry)
|
||||
if err := n.addOutlines(outline.OutlineElem, level+1, bookmarks, pageIndex, pages); err != nil {
|
||||
return err
|
||||
}
|
||||
if onlyMissingTargets && len(entry.Actions) == 0 && len(n.Outline) == index+1 {
|
||||
n.Outline = n.Outline[:index]
|
||||
}
|
||||
}
|
||||
n.exactLinks = n.exactLinks || len(n.Outline) != 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// apply 应用PDF导航信息
|
||||
// apply 生成链接及大纲结构,内部目标在原生写入时关联
|
||||
// 入参: renderer PDF渲染器, page 页面索引
|
||||
func (n *pdfNavigation) apply(renderer *pdf.PDF, page int) {
|
||||
for _, anchor := range n.Anchor[page] {
|
||||
renderer.AddAnchor(anchor.Name, anchor.Rect)
|
||||
}
|
||||
for _, link := range n.Link[page] {
|
||||
renderer.AddLink(link.URI, link.Rect)
|
||||
}
|
||||
for _, outline := range n.Outline[page] {
|
||||
renderer.AddOutline(outline.Name, outline.Level, outline.Y)
|
||||
if page == 0 {
|
||||
for index, outline := range n.Outline {
|
||||
renderer.AddOutline(strconv.Itoa(index), outline.Level, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// destinations 将输出页引用关联到原有链接和先序大纲,不按目标页重新排序
|
||||
// 入参: ctx 取消上下文, reader 输出PDF, pages 输出页引用, options 替换配置
|
||||
// 返回: error 目录结构或取消错误
|
||||
func (n *pdfNavigation) destinations(ctx context.Context, reader *pdfgo.Reader, pages []pdfgo.Reference, options *pdfgo.RewriteOptions) error {
|
||||
options.LinkDestinations = make(map[pdfgo.AnnotationLocation]pdfgo.Destination)
|
||||
for page, links := range n.Link {
|
||||
for index, link := range links {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if link.Target != nil {
|
||||
dest := link.Target.Destination
|
||||
dest.Page = pages[link.Target.Page]
|
||||
options.LinkDestinations[pdfgo.AnnotationLocation{Page: pages[page], Index: index}] = dest
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(n.Outline) == 0 {
|
||||
return nil
|
||||
}
|
||||
options.OutlineDestinations = make(map[pdfgo.Reference]pdfgo.Destination, len(n.Outline))
|
||||
options.OutlineTitles = make(map[pdfgo.Reference]string, len(n.Outline))
|
||||
options.OutlineActions = make(map[pdfgo.Reference][]pdfgo.NavigationAction)
|
||||
count := 0
|
||||
if err := reader.WalkOutlines(ctx, func(level int, item pdfgo.OutlineItem) error {
|
||||
if count >= len(n.Outline) || n.Outline[count].Level != level || strconv.Itoa(count) != item.Title {
|
||||
return fmt.Errorf("PDF output outline differs")
|
||||
}
|
||||
actions := n.Outline[count].Actions
|
||||
if len(actions) == 1 && actions[0].Target != nil {
|
||||
target := actions[0].Target
|
||||
dest := target.Destination
|
||||
dest.Page = pages[target.Page]
|
||||
options.OutlineDestinations[item.Reference] = dest
|
||||
} else {
|
||||
converted := make([]pdfgo.NavigationAction, 0, len(actions))
|
||||
for _, action := range actions {
|
||||
value := pdfgo.NavigationAction{URI: action.URI}
|
||||
if action.Target != nil {
|
||||
dest := action.Target.Destination
|
||||
dest.Page = pages[action.Target.Page]
|
||||
value.Destination = &dest
|
||||
}
|
||||
converted = append(converted, value)
|
||||
}
|
||||
options.OutlineActions[item.Reference] = converted
|
||||
}
|
||||
options.OutlineTitles[item.Reference] = n.Outline[count].Name
|
||||
count++
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if count != len(n.Outline) {
|
||||
return fmt.Errorf("PDF output outline missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pdfSourceRect 转换PDF动作区域
|
||||
// 入参: box OFD区域, pageH 页面高度
|
||||
// 返回: canvas.Rect PDF区域
|
||||
@@ -254,32 +344,25 @@ func pdfSourceRect(box Box, pageH float64) canvas.Rect {
|
||||
return canvas.RectFromSize(box.X, pageH-box.Y-box.H, box.W, box.H)
|
||||
}
|
||||
|
||||
// pdfDestRect 转换PDF跳转目标
|
||||
// pdfDestination 按OFD目标语义转换PDF坐标、显示模式及缩放比例
|
||||
// 入参: dest OFD跳转目标, pageH 页面高度
|
||||
// 返回: canvas.Rect PDF目标区域, bool 是否支持
|
||||
func pdfDestRect(dest Dest, pageH float64) (canvas.Rect, bool) {
|
||||
// 返回: pdfgo.Destination 待关联输出页的目标, error 目标参数错误
|
||||
func pdfDestination(dest Dest, pageH float64) (pdfgo.Destination, error) {
|
||||
dest = dest.effective()
|
||||
if _, err := dest.attributes(); err != nil {
|
||||
return pdfgo.Destination{}, err
|
||||
}
|
||||
const scale = 72.0 / 25.4
|
||||
result := pdfgo.Destination{Mode: pdfgo.Name(dest.Type)}
|
||||
switch dest.Type {
|
||||
case "XYZ":
|
||||
return canvas.Rect{X0: dest.Left, Y0: pageH - dest.Top, X1: dest.Left, Y1: pageH - dest.Top}, true
|
||||
case "Fit":
|
||||
return canvas.Rect{}, true
|
||||
result.Parameters = pdfgo.Array{pdfgo.Real(dest.Left * scale), pdfgo.Real((pageH - dest.Top) * scale), pdfgo.Real(dest.Zoom)}
|
||||
case "FitH":
|
||||
return canvas.Rect{Y0: pageH - dest.Top, Y1: pageH - dest.Top}, true
|
||||
result.Parameters = pdfgo.Array{pdfgo.Real((pageH - dest.Top) * scale)}
|
||||
case "FitV":
|
||||
return canvas.Rect{X0: dest.Left, X1: dest.Left}, true
|
||||
result.Parameters = pdfgo.Array{pdfgo.Real(dest.Left * scale)}
|
||||
case "FitR":
|
||||
return canvas.Rect{X0: dest.Left, Y0: pageH - dest.Bottom, X1: dest.Right, Y1: pageH - dest.Top}, true
|
||||
result.Parameters = pdfgo.Array{pdfgo.Real(dest.Left * scale), pdfgo.Real((pageH - dest.Bottom) * scale), pdfgo.Real(dest.Right * scale), pdfgo.Real((pageH - dest.Top) * scale)}
|
||||
}
|
||||
return canvas.Rect{}, false
|
||||
}
|
||||
|
||||
// pdfDestY 获取PDF大纲目标位置
|
||||
// 入参: dest OFD跳转目标, pageH 页面高度
|
||||
// 返回: float64 PDF纵坐标
|
||||
func pdfDestY(dest Dest, pageH float64) float64 {
|
||||
switch dest.Type {
|
||||
case "XYZ", "FitH", "FitR":
|
||||
return pageH - dest.Top
|
||||
}
|
||||
return 0
|
||||
return result, nil
|
||||
}
|
||||
@@ -19,34 +19,48 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
|
||||
"github.com/xiaoqidun/pdfgo"
|
||||
)
|
||||
|
||||
// preserveImages 一次恢复图像精度和链接区域,二值透明采用标准Matte预混合
|
||||
// 入参: ctx 取消上下文, data 后端PDF数据
|
||||
// 返回: []byte 保留图像及导航信息的PDF数据, error 资源或写入错误
|
||||
func (r *pdfRenderer) preserveImages(ctx context.Context, data []byte) ([]byte, error) {
|
||||
// writePDF 合并图像、导航修正及压缩,直接写出最终PDF
|
||||
// 入参: ctx 取消上下文, data 后端PDF数据, writer 输出流, optimization 优化配置
|
||||
// 返回: error 资源、压缩或写入错误
|
||||
func (r *pdfRenderer) writePDF(ctx context.Context, data []byte, writer io.Writer, optimization pdfgo.OptimizeOptions) error {
|
||||
if r.imageError != nil {
|
||||
return nil, r.imageError
|
||||
return r.imageError
|
||||
}
|
||||
exactLinks := r.navigation != nil && r.navigation.exactLinks
|
||||
if !r.exactImages && !exactLinks {
|
||||
return data, nil
|
||||
if !r.exactImages && !exactLinks && optimization.Compression.Mode == CompressionUnchanged {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := writer.Write(data)
|
||||
if err == nil && n != len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
return err
|
||||
}
|
||||
reader, err := pdfgo.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
if !r.exactImages && !exactLinks {
|
||||
_, err := reader.OptimizeTo(ctx, writer, optimization)
|
||||
return err
|
||||
}
|
||||
images := make(map[pdfgo.Reference]image.Image)
|
||||
links := make(map[pdfgo.AnnotationLocation]pdfgo.LinkRegion)
|
||||
references := make([]pdfgo.Reference, len(r.images))
|
||||
pages := 0
|
||||
err = reader.WalkPages(ctx, func(index int, page *pdfgo.Page) error {
|
||||
if index >= len(r.images) {
|
||||
return fmt.Errorf("unexpected PDF output page")
|
||||
}
|
||||
pages++
|
||||
references[index] = page.Reference
|
||||
if exactLinks {
|
||||
annotations, err := page.AnnotationsContext(ctx)
|
||||
if err != nil {
|
||||
@@ -119,27 +133,30 @@ func (r *pdfRenderer) preserveImages(ctx context.Context, data []byte) ([]byte,
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if pages != len(r.images) {
|
||||
return nil, fmt.Errorf("PDF output page missing")
|
||||
return fmt.Errorf("PDF output page missing")
|
||||
}
|
||||
options := pdfgo.RewriteOptions{Images: images, ImageOptions: pdfgo.ImageWriteOptions{PreblendBinary: true}, LinkRegions: links, Optimization: optimization}
|
||||
if exactLinks {
|
||||
if err := r.navigation.destinations(ctx, reader, references, &options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for ref, original := range images {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
pixels, err := imagePixelData(original)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if pixels == nil || pixels.Bounds() != original.Bounds() {
|
||||
return nil, fmt.Errorf("PDF output image pixel bounds differ")
|
||||
return fmt.Errorf("PDF output image pixel bounds differ")
|
||||
}
|
||||
images[ref] = pixels
|
||||
}
|
||||
var result bytes.Buffer
|
||||
if _, err := reader.RewriteTo(ctx, &result, pdfgo.RewriteOptions{Images: images, ImageOptions: pdfgo.ImageWriteOptions{PreblendBinary: true}, LinkRegions: links}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.Bytes(), nil
|
||||
_, err = reader.RewriteTo(ctx, writer, options)
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user