mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
This commit is contained in:
1 parent
bed08b2c4b
commit
0d83cabecc
8 files changed
+133
-32
No files matched your search
@@ -41,6 +41,7 @@ type Annotation struct {
|
||||
Subtype string `xml:"Subtype,attr"`
|
||||
Creator string `xml:"Creator,attr"`
|
||||
LastModDate string `xml:"LastModDate,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Appearance Appearance
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -103,8 +103,8 @@ type CustomTag struct {
|
||||
type CommonData struct {
|
||||
MaxUnitID int `xml:"MaxUnitID"`
|
||||
PageArea PageArea `xml:"PageArea"`
|
||||
PublicRes string `xml:"PublicRes"`
|
||||
DocumentRes string `xml:"DocumentRes"`
|
||||
PublicRes []string `xml:"PublicRes"`
|
||||
DocumentRes []string `xml:"DocumentRes"`
|
||||
TemplatePage []TemplatePage `xml:"TemplatePage"`
|
||||
DefaultCS int `xml:"DefaultCS"`
|
||||
}
|
||||
|
||||
+21
-10
@@ -22,7 +22,7 @@ type PageContent struct {
|
||||
ID string `xml:"-"`
|
||||
Area PageArea `xml:"Area"`
|
||||
Template []Template `xml:"Template"`
|
||||
PageRes string `xml:"PageRes"`
|
||||
PageRes []string `xml:"PageRes"`
|
||||
Content Content `xml:"Content"`
|
||||
Actions []Action `xml:"Actions>Action"`
|
||||
}
|
||||
@@ -225,13 +225,24 @@ type ShdColor struct {
|
||||
|
||||
// ImageObject 图片对象
|
||||
type ImageObject struct {
|
||||
ID string `xml:"ID,attr"`
|
||||
Boundary string `xml:"Boundary,attr"`
|
||||
ResourceID string `xml:"ResourceID,attr"`
|
||||
ImageMask string `xml:"ImageMask,attr"`
|
||||
CTM string `xml:"CTM,attr"`
|
||||
Alpha *int `xml:"Alpha,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Clips *Clips `xml:"Clips"`
|
||||
Actions []Action `xml:"Actions>Action"`
|
||||
ID string `xml:"ID,attr"`
|
||||
Boundary string `xml:"Boundary,attr"`
|
||||
ResourceID string `xml:"ResourceID,attr"`
|
||||
ImageMask string `xml:"ImageMask,attr"`
|
||||
CTM string `xml:"CTM,attr"`
|
||||
Alpha *int `xml:"Alpha,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Border *ImageBorder `xml:"Border"`
|
||||
Clips *Clips `xml:"Clips"`
|
||||
Actions []Action `xml:"Actions>Action"`
|
||||
}
|
||||
|
||||
// ImageBorder 图像边框
|
||||
type ImageBorder struct {
|
||||
LineWidth *float64 `xml:"LineWidth,attr"`
|
||||
HorizonalCornerRadius float64 `xml:"HorizonalCornerRadius,attr"`
|
||||
VerticalCornerRadius float64 `xml:"VerticalCornerRadius,attr"`
|
||||
DashOffset float64 `xml:"DashOffset,attr"`
|
||||
DashPattern string `xml:"DashPattern,attr"`
|
||||
BorderColor *StrokeColor `xml:"BorderColor"`
|
||||
}
|
||||
+65
-13
@@ -160,11 +160,11 @@ func (r *Reader) Doc() (*Document, error) {
|
||||
if doc.Signatures == "" {
|
||||
doc.Signatures = docAttr.Signatures
|
||||
}
|
||||
if doc.CommonData.DocumentRes != "" {
|
||||
r.loadRes(doc.CommonData.DocumentRes)
|
||||
for _, res := range doc.CommonData.DocumentRes {
|
||||
r.loadRes(res)
|
||||
}
|
||||
if doc.CommonData.PublicRes != "" {
|
||||
r.loadRes(doc.CommonData.PublicRes)
|
||||
for _, res := range doc.CommonData.PublicRes {
|
||||
r.loadRes(res)
|
||||
}
|
||||
r.doc = &doc
|
||||
_ = r.parseAnnotations(&doc)
|
||||
@@ -251,8 +251,12 @@ func (r *Reader) PageContent(page Page) (*PageContent, error) {
|
||||
return nil, fmt.Errorf("failed to unmarshal page content: %w", err)
|
||||
}
|
||||
content.ID = page.ID
|
||||
if content.PageRes != "" {
|
||||
r.loadRes(resolveResourcePath(page.BaseLoc, "", content.PageRes))
|
||||
for _, res := range content.PageRes {
|
||||
r.loadRes(resolveResourcePath(page.BaseLoc, "", res))
|
||||
}
|
||||
content.Area, err = r.resolvePageArea(content.Area, content.Template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &content, nil
|
||||
}
|
||||
@@ -261,33 +265,81 @@ func (r *Reader) PageContent(page Page) (*PageContent, error) {
|
||||
// 入参: page 页面对象
|
||||
// 返回: PageArea 页面区域, error 错误信息
|
||||
func (r *Reader) PageArea(page Page) (PageArea, error) {
|
||||
f, err := r.openFile(r.ResPath(page.BaseLoc))
|
||||
content, err := r.readPageHeader(page)
|
||||
if err != nil {
|
||||
return PageArea{}, err
|
||||
}
|
||||
return r.resolvePageArea(content.Area, content.Template)
|
||||
}
|
||||
|
||||
// readPageHeader 读取页面区域和模板引用
|
||||
// 入参: page 页面对象
|
||||
// 返回: PageContent 页面头部, error 错误信息
|
||||
func (r *Reader) readPageHeader(page Page) (PageContent, error) {
|
||||
f, err := r.openFile(r.ResPath(page.BaseLoc))
|
||||
if err != nil {
|
||||
return PageContent{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
var content PageContent
|
||||
d := xml.NewDecoder(f)
|
||||
for {
|
||||
token, err := d.Token()
|
||||
if err == io.EOF {
|
||||
return PageArea{}, nil
|
||||
return content, nil
|
||||
}
|
||||
if err != nil {
|
||||
return PageArea{}, err
|
||||
return PageContent{}, err
|
||||
}
|
||||
if start, ok := token.(xml.StartElement); ok {
|
||||
switch start.Name.Local {
|
||||
case "Page":
|
||||
continue
|
||||
case "Template":
|
||||
var template Template
|
||||
if err := d.DecodeElement(&template, &start); err != nil {
|
||||
return PageContent{}, err
|
||||
}
|
||||
content.Template = append(content.Template, template)
|
||||
case "Area":
|
||||
var area PageArea
|
||||
err := d.DecodeElement(&area, &start)
|
||||
return area, err
|
||||
err := d.DecodeElement(&content.Area, &start)
|
||||
return content, err
|
||||
case "Content":
|
||||
return PageArea{}, nil
|
||||
return content, nil
|
||||
default:
|
||||
if err := d.Skip(); err != nil {
|
||||
return PageContent{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolvePageArea 解析页面区域的继承关系
|
||||
// 入参: area 页面区域, templates 模板引用
|
||||
// 返回: PageArea 页面区域, error 错误信息
|
||||
func (r *Reader) resolvePageArea(area PageArea, templates []Template) (PageArea, error) {
|
||||
if area != (PageArea{}) || r.doc == nil {
|
||||
return area, nil
|
||||
}
|
||||
for _, ref := range templates {
|
||||
for _, template := range r.doc.CommonData.TemplatePage {
|
||||
if template.ID != ref.TemplateID {
|
||||
continue
|
||||
}
|
||||
content, err := r.readPageHeader(Page{BaseLoc: template.BaseLoc})
|
||||
if err != nil {
|
||||
return PageArea{}, err
|
||||
}
|
||||
if content.Area != (PageArea{}) {
|
||||
return content.Area, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return r.doc.CommonData.PageArea, nil
|
||||
}
|
||||
|
||||
// ResPath 获取资源的完整路径
|
||||
// 入参: resLink 资源链接
|
||||
// 返回: string 完整路径
|
||||
|
||||
+7
-6
@@ -79,15 +79,16 @@ func (r *Renderer) renderPage(page *PageContent) (*canvas.Canvas, error) {
|
||||
// 入参: page 页面内容
|
||||
// 返回: Box 区域, error 错误信息
|
||||
func (r *Renderer) GetPageBox(page *PageContent) (Box, error) {
|
||||
boxStr := page.Area.PhysicalBox
|
||||
area, err := r.Reader.resolvePageArea(page.Area, page.Template)
|
||||
if err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
boxStr := area.PhysicalBox
|
||||
if boxStr == "" {
|
||||
boxStr = page.Area.ApplicationBox
|
||||
boxStr = area.ApplicationBox
|
||||
}
|
||||
if boxStr == "" {
|
||||
boxStr = page.Area.ContentBox
|
||||
}
|
||||
if boxStr == "" && r.Reader.doc != nil {
|
||||
boxStr = r.Reader.doc.CommonData.PageArea.PhysicalBox
|
||||
boxStr = area.ContentBox
|
||||
}
|
||||
if boxStr == "" {
|
||||
boxStr = "0 0 210 297"
|
||||
|
||||
@@ -16,6 +16,7 @@ package ofdgo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
@@ -83,6 +84,38 @@ func (r *Renderer) renderImage(ctx *canvas.Context, obj ImageObject, pageH float
|
||||
m[1][2] -= m[1][0]*p + m[1][1]*p
|
||||
}
|
||||
ctx.RenderImage(img, ctx.CoordSystemView().Mul(ctx.View()).Mul(m))
|
||||
if obj.Border != nil {
|
||||
r.renderImageBorder(ctx, obj, box, pageH, parentCTM, boundaryInCTM, clipPath)
|
||||
}
|
||||
}
|
||||
|
||||
// renderImageBorder 渲染图像边框
|
||||
// 入参: ctx 画布上下文, obj 图片对象, box 图像边界, pageH 页面高度, parentCTM 父级CTM, boundaryInCTM 边界是否参与父级CTM, clipPath 裁剪路径
|
||||
func (r *Renderer) renderImageBorder(ctx *canvas.Context, obj ImageObject, box Box, pageH float64, parentCTM *Matrix, boundaryInCTM bool, clipPath *canvas.Path) {
|
||||
border := obj.Border
|
||||
width := defaultPathLineWidth
|
||||
if border.LineWidth != nil {
|
||||
width = *border.LineWidth
|
||||
}
|
||||
if width == 0 {
|
||||
return
|
||||
}
|
||||
w, h := box.W, box.H
|
||||
data := fmt.Sprintf("M 0 0 L %g 0 L %g %g L 0 %g C", w, w, h, h)
|
||||
rx, ry := min(border.HorizonalCornerRadius, w/2), min(border.VerticalCornerRadius, h/2)
|
||||
if rx > 0 && ry > 0 {
|
||||
data = fmt.Sprintf("M 0 %g A %g %g 0 0 1 %g 0 L %g 0 A %g %g 0 0 1 %g %g L %g %g A %g %g 0 0 1 %g %g L %g %g A %g %g 0 0 1 0 %g C",
|
||||
ry, rx, ry, rx, w-rx, rx, ry, w, ry, w, h-ry, rx, ry, w-rx, h, rx, h, rx, ry, h-ry)
|
||||
}
|
||||
r.renderPath(ctx, PathObject{
|
||||
Boundary: obj.Boundary,
|
||||
LineWidth: width,
|
||||
DashOffset: border.DashOffset,
|
||||
DashPattern: border.DashPattern,
|
||||
Alpha: obj.Alpha,
|
||||
StrokeColor: border.BorderColor,
|
||||
AbbreviatedData: data,
|
||||
}, pageH, nil, parentCTM, boundaryInCTM, clipPath)
|
||||
}
|
||||
|
||||
// imageWithMask 应用图片蒙版
|
||||
|
||||
@@ -20,7 +20,7 @@ import "github.com/tdewolff/canvas"
|
||||
// 入参: ctx 画布上下文, pageID 页面ID, pageH 页面高度
|
||||
func (r *Renderer) renderAnnotations(ctx *canvas.Context, pageID string, pageH float64) {
|
||||
for _, annot := range r.Reader.Annots[pageID] {
|
||||
if len(annot.Appearance.Objects) == 0 {
|
||||
if annot.Visible != nil && !*annot.Visible {
|
||||
continue
|
||||
}
|
||||
box, _ := ParseBox(annot.Appearance.Boundary)
|
||||
|
||||
@@ -184,6 +184,9 @@ func pageActionSources(page pdfPage) []pdfActionSource {
|
||||
func annotationActionSources(annotations []Annotation) []pdfActionSource {
|
||||
sources := make([]pdfActionSource, 0)
|
||||
for _, annotation := range annotations {
|
||||
if annotation.Visible != nil && !*annotation.Visible {
|
||||
continue
|
||||
}
|
||||
box, err := ParseBox(annotation.Appearance.Boundary)
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user