feat(目录压缩): 规范目录导航,优化压缩写出
build.yaml / build (push) Successful in 46s

This commit is contained in:
xiaoqidun committed 2026-10-09 20:26:39 +08:00
1 parent 32ab7fe732
commit 60b8b44eca
6 files changed
+203 -130

No files matched your search

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