mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 15:23:01 +08:00
1 parent
035bbfb079
commit
73105646aa
4 files changed
+214
-159
No files matched your search
@@ -9,7 +9,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-20260928103444-c1e05032dc3a
|
github.com/xiaoqidun/pdfgo v0.0.0-20260928111239-49265df4c04c
|
||||||
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-20260928103444-c1e05032dc3a h1:ROTJKhSKiF4LHufA5UjBWwGQimvWQV8hU9SxsrkeTWE=
|
github.com/xiaoqidun/pdfgo v0.0.0-20260928111239-49265df4c04c h1:AkN9yw2b5LXinOBkWy/Wppdu4AtqQ2j6M4AYxlThL/A=
|
||||||
github.com/xiaoqidun/pdfgo v0.0.0-20260928103444-c1e05032dc3a/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
github.com/xiaoqidun/pdfgo v0.0.0-20260928111239-49265df4c04c/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=
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ type pdfImporter struct {
|
|||||||
objects []GraphicObject
|
objects []GraphicObject
|
||||||
pages map[pdfgo.Reference]*pdfgo.Page
|
pages map[pdfgo.Reference]*pdfgo.Page
|
||||||
pageIDs map[pdfgo.Reference]string
|
pageIDs map[pdfgo.Reference]string
|
||||||
|
pageIndexes map[pdfgo.Reference]int
|
||||||
imageIDs map[pdfImageKey]string
|
imageIDs map[pdfImageKey]string
|
||||||
maskClips map[*pdfgo.SoftMask]pdfgo.Path
|
maskClips map[*pdfgo.SoftMask]pdfgo.Path
|
||||||
pageBox pdfgo.Rectangle
|
pageBox pdfgo.Rectangle
|
||||||
@@ -128,6 +129,7 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
editor.fontDirs, editor.fontFS = renderer.FontSources()
|
editor.fontDirs, editor.fontFS = renderer.FontSources()
|
||||||
importer := pdfImporter{ctx: ctx, reader: reader, editor: editor, renderer: renderer, rasterDPI: renderer.DPI, fontIDs: map[*pdfgo.Font]string{}, fontMetrics: map[string]FontMetrics{}, pages: map[pdfgo.Reference]*pdfgo.Page{}, pageIDs: map[pdfgo.Reference]string{}}
|
importer := pdfImporter{ctx: ctx, reader: reader, editor: editor, renderer: renderer, rasterDPI: renderer.DPI, fontIDs: map[*pdfgo.Font]string{}, fontMetrics: map[string]FontMetrics{}, pages: map[pdfgo.Reference]*pdfgo.Page{}, pageIDs: map[pdfgo.Reference]string{}}
|
||||||
importer.report.Warnings = diagnostics
|
importer.report.Warnings = diagnostics
|
||||||
|
importer.pageIndexes = make(map[pdfgo.Reference]int)
|
||||||
if security := reader.Encryption(); security != nil && !security.Owner && security.Permissions&0xf3c != 0xf3c {
|
if security := reader.Encryption(); security != nil && !security.Owner && security.Permissions&0xf3c != 0xf3c {
|
||||||
if options.Strict {
|
if options.Strict {
|
||||||
return nil, PDFImportReport{}, &pdfgo.UnsupportedError{Feature: "PDF access permission conversion"}
|
return nil, PDFImportReport{}, &pdfgo.UnsupportedError{Feature: "PDF access permission conversion"}
|
||||||
@@ -152,6 +154,7 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
|||||||
}
|
}
|
||||||
importer.pages[page.Reference] = page
|
importer.pages[page.Reference] = page
|
||||||
importer.pageIDs[page.Reference] = editor.pages[index].ID
|
importer.pageIDs[page.Reference] = editor.pages[index].ID
|
||||||
|
importer.pageIndexes[page.Reference] = index
|
||||||
if options.OnProgress != nil {
|
if options.OnProgress != nil {
|
||||||
return options.OnProgress("pages", index+1, 0)
|
return options.OnProgress("pages", index+1, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
+208
-156
@@ -65,6 +65,22 @@ func (p *pdfImporter) annotations(ctx context.Context, page *pdfgo.Page, strict
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
dict := annotation.Dictionary
|
dict := annotation.Dictionary
|
||||||
|
if dict["OC"] != nil {
|
||||||
|
if strict {
|
||||||
|
return &pdfgo.UnsupportedError{Feature: "annotation optional content conversion"}
|
||||||
|
}
|
||||||
|
visible, err := p.reader.OptionalContentVisible(dict["OC"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.warning(pdfgo.Diagnostic{Page: p.page + 1, Message: "PDF annotation optional content default visibility applied; layer switching not transferred"})
|
||||||
|
if !visible {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dict = maps.Clone(dict)
|
||||||
|
delete(dict, "OC")
|
||||||
|
annotation.Dictionary = dict
|
||||||
|
}
|
||||||
if annotation.Subtype == "Text" {
|
if annotation.Subtype == "Text" {
|
||||||
value, err := p.reader.Resolve(dict["Open"])
|
value, err := p.reader.Resolve(dict["Open"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -169,178 +185,50 @@ func (p *pdfImporter) annotations(ctx context.Context, page *pdfgo.Page, strict
|
|||||||
if annotation.Subtype != "Link" {
|
if annotation.Subtype != "Link" {
|
||||||
return &pdfgo.UnsupportedError{Feature: "annotation " + string(annotation.Subtype)}
|
return &pdfgo.UnsupportedError{Feature: "annotation " + string(annotation.Subtype)}
|
||||||
}
|
}
|
||||||
for _, key := range []pdfgo.Name{"AA", "OC"} {
|
if dict["AA"] != nil {
|
||||||
if dict[key] != nil {
|
return &pdfgo.UnsupportedError{Feature: "link annotation field \"AA\""}
|
||||||
return &pdfgo.UnsupportedError{Feature: fmt.Sprintf("link annotation field %q", key)}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
action := Action{Event: "CLICK"}
|
var actions []Action
|
||||||
target := dict["Dest"]
|
currentPage := p.page
|
||||||
if dict["A"] != nil {
|
appendAction := func(dict pdfgo.Dictionary) error {
|
||||||
value, err := p.reader.Resolve(dict["A"])
|
current := annotation
|
||||||
|
current.Dictionary = dict
|
||||||
|
action, err := p.linkAction(current, strict, ¤tPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a, ok := value.(pdfgo.Dictionary)
|
if action != nil {
|
||||||
if !ok {
|
actions = append(actions, *action)
|
||||||
return fmt.Errorf("invalid PDF link action")
|
|
||||||
}
|
|
||||||
if a["Next"] != nil {
|
|
||||||
return &pdfgo.UnsupportedError{Feature: "chained link actions"}
|
|
||||||
}
|
|
||||||
switch a["S"] {
|
|
||||||
case pdfgo.Name("GoTo"):
|
|
||||||
target = a["D"]
|
|
||||||
case pdfgo.Name("Named"):
|
|
||||||
value, err := p.reader.Resolve(a["N"])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
index := p.page
|
|
||||||
switch value {
|
|
||||||
case pdfgo.Name("FirstPage"):
|
|
||||||
index = 0
|
|
||||||
case pdfgo.Name("LastPage"):
|
|
||||||
index = len(p.editor.pages) - 1
|
|
||||||
case pdfgo.Name("NextPage"):
|
|
||||||
index = min(index+1, len(p.editor.pages)-1)
|
|
||||||
case pdfgo.Name("PrevPage"):
|
|
||||||
index = max(index-1, 0)
|
|
||||||
default:
|
|
||||||
return &pdfgo.UnsupportedError{Feature: "named link action"}
|
|
||||||
}
|
|
||||||
action.Goto = &Goto{Dest: &Dest{Type: "XYZ", PageID: p.editor.pages[index].ID, OmitLeft: true, OmitTop: true, OmitZoom: true}}
|
|
||||||
case pdfgo.Name("URI"):
|
|
||||||
value, err := p.reader.Resolve(a["URI"])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
uri, ok := value.(pdfgo.String)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid PDF URI")
|
|
||||||
}
|
|
||||||
isMap, err := p.reader.Resolve(a["IsMap"])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if isMap != nil && isMap != pdfgo.Boolean(false) && isMap != pdfgo.Boolean(true) {
|
|
||||||
return fmt.Errorf("invalid PDF URI image map flag")
|
|
||||||
}
|
|
||||||
if isMap == pdfgo.Boolean(true) {
|
|
||||||
return &pdfgo.UnsupportedError{Feature: "URI image map"}
|
|
||||||
}
|
|
||||||
base, err := p.reader.BaseURI()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
action.URI = &URI{URI: string(uri), Base: base}
|
|
||||||
default:
|
|
||||||
return &pdfgo.UnsupportedError{Feature: "link action"}
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if action.URI == nil && action.Goto == nil {
|
if dict["A"] == nil {
|
||||||
destination, err := p.reader.ReadDestination(target)
|
if err := appendAction(dict); err != nil {
|
||||||
if err != nil {
|
|
||||||
if !strict && errors.Is(err, pdfgo.ErrDestinationNotFound) {
|
|
||||||
p.report.Warnings = append(p.report.Warnings, pdfgo.Diagnostic{Page: p.page + 1, Message: fmt.Sprintf("%v; link omitted", err)})
|
|
||||||
if err := p.appearanceAnnotation(ctx, page, annotation); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
targetPage := p.pages[destination.Page]
|
} else if err := p.reader.WalkActions(ctx, dict["A"], func(info pdfgo.ActionInfo) error {
|
||||||
if targetPage == nil {
|
current := maps.Clone(dict)
|
||||||
if !strict {
|
current["A"] = info.Dictionary
|
||||||
p.report.Warnings = append(p.report.Warnings, pdfgo.Diagnostic{Page: p.page + 1, Message: "PDF link targets a page outside the document page tree; link omitted"})
|
return appendAction(current)
|
||||||
if err := p.appearanceAnnotation(ctx, page, annotation); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return fmt.Errorf("PDF destination page missing")
|
|
||||||
}
|
|
||||||
matrix, _, _ := pdfPageMatrix(targetPage)
|
|
||||||
dest := &Dest{Type: string(destination.Mode), PageID: p.pageIDs[destination.Page]}
|
|
||||||
if destination.Mode == "XYZ" {
|
|
||||||
values := [3]float64{}
|
|
||||||
retained := [3]bool{}
|
|
||||||
for n, v := range destination.Parameters {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case pdfgo.Integer:
|
|
||||||
values[n] = float64(v)
|
|
||||||
case pdfgo.Real:
|
|
||||||
values[n] = float64(v)
|
|
||||||
case nil:
|
|
||||||
retained[n] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
point := matrix.Apply(pdfgo.Point{X: values[0], Y: values[1]})
|
|
||||||
dest.Left, dest.Top, dest.Zoom = point.X, point.Y, values[2]
|
|
||||||
dest.OmitLeft, dest.OmitTop = retained[0], retained[1]
|
|
||||||
if targetPage.Rotation == 90 || targetPage.Rotation == 270 {
|
|
||||||
dest.OmitLeft, dest.OmitTop = retained[1], retained[0]
|
|
||||||
}
|
|
||||||
dest.OmitZoom = retained[2] || values[2] == 0
|
|
||||||
} else if destination.Mode == "FitH" || destination.Mode == "FitV" {
|
|
||||||
var coordinate float64
|
|
||||||
switch value := destination.Parameters[0].(type) {
|
|
||||||
case pdfgo.Integer:
|
|
||||||
coordinate = float64(value)
|
|
||||||
case pdfgo.Real:
|
|
||||||
coordinate = float64(value)
|
|
||||||
}
|
|
||||||
point := pdfgo.Point{X: coordinate}
|
|
||||||
if destination.Mode == "FitH" {
|
|
||||||
point = pdfgo.Point{Y: coordinate}
|
|
||||||
}
|
|
||||||
point = matrix.Apply(point)
|
|
||||||
if targetPage.Rotation == 90 || targetPage.Rotation == 270 {
|
|
||||||
if dest.Type == "FitH" {
|
|
||||||
dest.Type = "FitV"
|
|
||||||
} else {
|
|
||||||
dest.Type = "FitH"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dest.Type == "FitH" {
|
|
||||||
dest.Top, dest.OmitTop = point.Y, destination.Parameters[0] == nil
|
|
||||||
} else {
|
|
||||||
dest.Left, dest.OmitLeft = point.X, destination.Parameters[0] == nil
|
|
||||||
}
|
|
||||||
} else if destination.Mode == "FitR" {
|
|
||||||
values := [4]float64{}
|
|
||||||
for n, v := range destination.Parameters {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case pdfgo.Integer:
|
|
||||||
values[n] = float64(v)
|
|
||||||
case pdfgo.Real:
|
|
||||||
values[n] = float64(v)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid PDF FitR destination coordinate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bounds := pdfBounds([]pdfgo.Point{
|
|
||||||
matrix.Apply(pdfgo.Point{X: values[0], Y: values[1]}),
|
|
||||||
matrix.Apply(pdfgo.Point{X: values[2], Y: values[3]}),
|
|
||||||
})
|
|
||||||
dest.Left, dest.Top = bounds.X, bounds.Y
|
|
||||||
dest.Right, dest.Bottom = bounds.X+bounds.W, bounds.Y+bounds.H
|
|
||||||
} else if destination.Mode != "Fit" {
|
|
||||||
return &pdfgo.UnsupportedError{Feature: "destination mode " + string(destination.Mode)}
|
|
||||||
}
|
|
||||||
action.Goto = &Goto{Dest: dest}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b := annotation.Rect
|
b := annotation.Rect
|
||||||
box := pdfBounds([]pdfgo.Point{p.matrix.Apply(pdfgo.Point{X: b.XMin, Y: b.YMin}), p.matrix.Apply(pdfgo.Point{X: b.XMax, Y: b.YMax})})
|
box := pdfBounds([]pdfgo.Point{p.matrix.Apply(pdfgo.Point{X: b.XMin, Y: b.YMin}), p.matrix.Apply(pdfgo.Point{X: b.XMax, Y: b.YMax})})
|
||||||
action.Region, err = p.linkRegion(annotation, box)
|
region, err := p.linkRegion(annotation, box)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.appearanceAnnotation(ctx, page, annotation, action); err != nil {
|
for i := range actions {
|
||||||
|
actions[i].Region = region
|
||||||
|
}
|
||||||
|
if err := p.appearanceAnnotation(ctx, page, annotation, actions...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.report.Links++
|
if len(actions) != 0 {
|
||||||
|
p.report.Links++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -390,3 +278,167 @@ func (p *pdfImporter) linkRegion(annotation pdfgo.Annotation, box Box) (*Region,
|
|||||||
}
|
}
|
||||||
return region, nil
|
return region, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// linkAction 将单个链接动作转换为OFD动作,失效目标按导入策略报告
|
||||||
|
// 入参: annotation 链接注解, strict 严格检查开关, currentPage 动作链当前页
|
||||||
|
// 返回: *Action 动作,失效目标为空, error 转换错误
|
||||||
|
func (p *pdfImporter) linkAction(annotation pdfgo.Annotation, strict bool, currentPage *int) (*Action, error) {
|
||||||
|
dict := annotation.Dictionary
|
||||||
|
action := Action{Event: "CLICK"}
|
||||||
|
target, err := p.reader.Resolve(dict["Dest"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if target == nil && dict["A"] == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if dict["A"] != nil {
|
||||||
|
value, err := p.reader.Resolve(dict["A"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a, ok := value.(pdfgo.Dictionary)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid PDF link action")
|
||||||
|
}
|
||||||
|
switch a["S"] {
|
||||||
|
case pdfgo.Name("GoTo"):
|
||||||
|
target = a["D"]
|
||||||
|
case pdfgo.Name("Named"):
|
||||||
|
value, err := p.reader.Resolve(a["N"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
index := *currentPage
|
||||||
|
switch value {
|
||||||
|
case pdfgo.Name("FirstPage"):
|
||||||
|
index = 0
|
||||||
|
case pdfgo.Name("LastPage"):
|
||||||
|
index = len(p.editor.pages) - 1
|
||||||
|
case pdfgo.Name("NextPage"):
|
||||||
|
index = min(index+1, len(p.editor.pages)-1)
|
||||||
|
case pdfgo.Name("PrevPage"):
|
||||||
|
index = max(index-1, 0)
|
||||||
|
default:
|
||||||
|
return nil, &pdfgo.UnsupportedError{Feature: "named link action"}
|
||||||
|
}
|
||||||
|
action.Goto = &Goto{Dest: &Dest{Type: "XYZ", PageID: p.editor.pages[index].ID, OmitLeft: true, OmitTop: true, OmitZoom: true}}
|
||||||
|
*currentPage = index
|
||||||
|
case pdfgo.Name("URI"):
|
||||||
|
value, err := p.reader.Resolve(a["URI"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
uri, ok := value.(pdfgo.String)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid PDF URI")
|
||||||
|
}
|
||||||
|
isMap, err := p.reader.Resolve(a["IsMap"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isMap != nil && isMap != pdfgo.Boolean(false) && isMap != pdfgo.Boolean(true) {
|
||||||
|
return nil, fmt.Errorf("invalid PDF URI image map flag")
|
||||||
|
}
|
||||||
|
if isMap == pdfgo.Boolean(true) {
|
||||||
|
return nil, &pdfgo.UnsupportedError{Feature: "URI image map"}
|
||||||
|
}
|
||||||
|
base, err := p.reader.BaseURI()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
action.URI = &URI{URI: string(uri), Base: base}
|
||||||
|
default:
|
||||||
|
return nil, &pdfgo.UnsupportedError{Feature: "link action"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if action.URI == nil && action.Goto == nil {
|
||||||
|
destination, err := p.reader.ReadDestination(target)
|
||||||
|
if err != nil {
|
||||||
|
if !strict && errors.Is(err, pdfgo.ErrDestinationNotFound) {
|
||||||
|
p.report.Warnings = append(p.report.Warnings, pdfgo.Diagnostic{Page: p.page + 1, Message: fmt.Sprintf("%v; link omitted", err)})
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
targetPage := p.pages[destination.Page]
|
||||||
|
if targetPage == nil {
|
||||||
|
if !strict {
|
||||||
|
p.report.Warnings = append(p.report.Warnings, pdfgo.Diagnostic{Page: p.page + 1, Message: "PDF link targets a page outside the document page tree; link omitted"})
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("PDF destination page missing")
|
||||||
|
}
|
||||||
|
matrix, _, _ := pdfPageMatrix(targetPage)
|
||||||
|
dest := &Dest{Type: string(destination.Mode), PageID: p.pageIDs[destination.Page]}
|
||||||
|
if destination.Mode == "XYZ" {
|
||||||
|
values := [3]float64{}
|
||||||
|
retained := [3]bool{}
|
||||||
|
for n, v := range destination.Parameters {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case pdfgo.Integer:
|
||||||
|
values[n] = float64(v)
|
||||||
|
case pdfgo.Real:
|
||||||
|
values[n] = float64(v)
|
||||||
|
case nil:
|
||||||
|
retained[n] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
point := matrix.Apply(pdfgo.Point{X: values[0], Y: values[1]})
|
||||||
|
dest.Left, dest.Top, dest.Zoom = point.X, point.Y, values[2]
|
||||||
|
dest.OmitLeft, dest.OmitTop = retained[0], retained[1]
|
||||||
|
if targetPage.Rotation == 90 || targetPage.Rotation == 270 {
|
||||||
|
dest.OmitLeft, dest.OmitTop = retained[1], retained[0]
|
||||||
|
}
|
||||||
|
dest.OmitZoom = retained[2] || values[2] == 0
|
||||||
|
} else if destination.Mode == "FitH" || destination.Mode == "FitV" {
|
||||||
|
var coordinate float64
|
||||||
|
switch value := destination.Parameters[0].(type) {
|
||||||
|
case pdfgo.Integer:
|
||||||
|
coordinate = float64(value)
|
||||||
|
case pdfgo.Real:
|
||||||
|
coordinate = float64(value)
|
||||||
|
}
|
||||||
|
point := pdfgo.Point{X: coordinate}
|
||||||
|
if destination.Mode == "FitH" {
|
||||||
|
point = pdfgo.Point{Y: coordinate}
|
||||||
|
}
|
||||||
|
point = matrix.Apply(point)
|
||||||
|
if targetPage.Rotation == 90 || targetPage.Rotation == 270 {
|
||||||
|
if dest.Type == "FitH" {
|
||||||
|
dest.Type = "FitV"
|
||||||
|
} else {
|
||||||
|
dest.Type = "FitH"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dest.Type == "FitH" {
|
||||||
|
dest.Top, dest.OmitTop = point.Y, destination.Parameters[0] == nil
|
||||||
|
} else {
|
||||||
|
dest.Left, dest.OmitLeft = point.X, destination.Parameters[0] == nil
|
||||||
|
}
|
||||||
|
} else if destination.Mode == "FitR" {
|
||||||
|
values := [4]float64{}
|
||||||
|
for n, v := range destination.Parameters {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case pdfgo.Integer:
|
||||||
|
values[n] = float64(v)
|
||||||
|
case pdfgo.Real:
|
||||||
|
values[n] = float64(v)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid PDF FitR destination coordinate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bounds := pdfBounds([]pdfgo.Point{
|
||||||
|
matrix.Apply(pdfgo.Point{X: values[0], Y: values[1]}),
|
||||||
|
matrix.Apply(pdfgo.Point{X: values[2], Y: values[3]}),
|
||||||
|
})
|
||||||
|
dest.Left, dest.Top = bounds.X, bounds.Y
|
||||||
|
dest.Right, dest.Bottom = bounds.X+bounds.W, bounds.Y+bounds.H
|
||||||
|
} else if destination.Mode != "Fit" {
|
||||||
|
return nil, &pdfgo.UnsupportedError{Feature: "destination mode " + string(destination.Mode)}
|
||||||
|
}
|
||||||
|
action.Goto = &Goto{Dest: dest}
|
||||||
|
*currentPage = p.pageIndexes[destination.Page]
|
||||||
|
}
|
||||||
|
return &action, nil
|
||||||
|
}
|
||||||
Reference in new issue
Block a user