mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
This commit is contained in:
1 parent
bed08b2c4b
commit
0d83cabecc
8 files changed
+124
-23
No files matched your search
@@ -41,6 +41,7 @@ type Annotation struct {
|
|||||||
Subtype string `xml:"Subtype,attr"`
|
Subtype string `xml:"Subtype,attr"`
|
||||||
Creator string `xml:"Creator,attr"`
|
Creator string `xml:"Creator,attr"`
|
||||||
LastModDate string `xml:"LastModDate,attr"`
|
LastModDate string `xml:"LastModDate,attr"`
|
||||||
|
Visible *bool `xml:"Visible,attr"`
|
||||||
Appearance Appearance
|
Appearance Appearance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -103,8 +103,8 @@ type CustomTag struct {
|
|||||||
type CommonData struct {
|
type CommonData struct {
|
||||||
MaxUnitID int `xml:"MaxUnitID"`
|
MaxUnitID int `xml:"MaxUnitID"`
|
||||||
PageArea PageArea `xml:"PageArea"`
|
PageArea PageArea `xml:"PageArea"`
|
||||||
PublicRes string `xml:"PublicRes"`
|
PublicRes []string `xml:"PublicRes"`
|
||||||
DocumentRes string `xml:"DocumentRes"`
|
DocumentRes []string `xml:"DocumentRes"`
|
||||||
TemplatePage []TemplatePage `xml:"TemplatePage"`
|
TemplatePage []TemplatePage `xml:"TemplatePage"`
|
||||||
DefaultCS int `xml:"DefaultCS"`
|
DefaultCS int `xml:"DefaultCS"`
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-1
@@ -22,7 +22,7 @@ type PageContent struct {
|
|||||||
ID string `xml:"-"`
|
ID string `xml:"-"`
|
||||||
Area PageArea `xml:"Area"`
|
Area PageArea `xml:"Area"`
|
||||||
Template []Template `xml:"Template"`
|
Template []Template `xml:"Template"`
|
||||||
PageRes string `xml:"PageRes"`
|
PageRes []string `xml:"PageRes"`
|
||||||
Content Content `xml:"Content"`
|
Content Content `xml:"Content"`
|
||||||
Actions []Action `xml:"Actions>Action"`
|
Actions []Action `xml:"Actions>Action"`
|
||||||
}
|
}
|
||||||
@@ -232,6 +232,17 @@ type ImageObject struct {
|
|||||||
CTM string `xml:"CTM,attr"`
|
CTM string `xml:"CTM,attr"`
|
||||||
Alpha *int `xml:"Alpha,attr"`
|
Alpha *int `xml:"Alpha,attr"`
|
||||||
Visible *bool `xml:"Visible,attr"`
|
Visible *bool `xml:"Visible,attr"`
|
||||||
|
Border *ImageBorder `xml:"Border"`
|
||||||
Clips *Clips `xml:"Clips"`
|
Clips *Clips `xml:"Clips"`
|
||||||
Actions []Action `xml:"Actions>Action"`
|
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 == "" {
|
if doc.Signatures == "" {
|
||||||
doc.Signatures = docAttr.Signatures
|
doc.Signatures = docAttr.Signatures
|
||||||
}
|
}
|
||||||
if doc.CommonData.DocumentRes != "" {
|
for _, res := range doc.CommonData.DocumentRes {
|
||||||
r.loadRes(doc.CommonData.DocumentRes)
|
r.loadRes(res)
|
||||||
}
|
}
|
||||||
if doc.CommonData.PublicRes != "" {
|
for _, res := range doc.CommonData.PublicRes {
|
||||||
r.loadRes(doc.CommonData.PublicRes)
|
r.loadRes(res)
|
||||||
}
|
}
|
||||||
r.doc = &doc
|
r.doc = &doc
|
||||||
_ = r.parseAnnotations(&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)
|
return nil, fmt.Errorf("failed to unmarshal page content: %w", err)
|
||||||
}
|
}
|
||||||
content.ID = page.ID
|
content.ID = page.ID
|
||||||
if content.PageRes != "" {
|
for _, res := range content.PageRes {
|
||||||
r.loadRes(resolveResourcePath(page.BaseLoc, "", 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
|
return &content, nil
|
||||||
}
|
}
|
||||||
@@ -261,32 +265,80 @@ func (r *Reader) PageContent(page Page) (*PageContent, error) {
|
|||||||
// 入参: page 页面对象
|
// 入参: page 页面对象
|
||||||
// 返回: PageArea 页面区域, error 错误信息
|
// 返回: PageArea 页面区域, error 错误信息
|
||||||
func (r *Reader) PageArea(page 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 {
|
if err != nil {
|
||||||
return PageArea{}, err
|
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()
|
defer f.Close()
|
||||||
|
var content PageContent
|
||||||
d := xml.NewDecoder(f)
|
d := xml.NewDecoder(f)
|
||||||
for {
|
for {
|
||||||
token, err := d.Token()
|
token, err := d.Token()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return PageArea{}, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PageArea{}, err
|
return PageContent{}, err
|
||||||
}
|
}
|
||||||
if start, ok := token.(xml.StartElement); ok {
|
if start, ok := token.(xml.StartElement); ok {
|
||||||
switch start.Name.Local {
|
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":
|
case "Area":
|
||||||
var area PageArea
|
err := d.DecodeElement(&content.Area, &start)
|
||||||
err := d.DecodeElement(&area, &start)
|
return content, err
|
||||||
return area, err
|
|
||||||
case "Content":
|
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 获取资源的完整路径
|
// ResPath 获取资源的完整路径
|
||||||
// 入参: resLink 资源链接
|
// 入参: resLink 资源链接
|
||||||
|
|||||||
+7
-6
@@ -79,15 +79,16 @@ func (r *Renderer) renderPage(page *PageContent) (*canvas.Canvas, error) {
|
|||||||
// 入参: page 页面内容
|
// 入参: page 页面内容
|
||||||
// 返回: Box 区域, error 错误信息
|
// 返回: Box 区域, error 错误信息
|
||||||
func (r *Renderer) GetPageBox(page *PageContent) (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 == "" {
|
if boxStr == "" {
|
||||||
boxStr = page.Area.ApplicationBox
|
boxStr = area.ApplicationBox
|
||||||
}
|
}
|
||||||
if boxStr == "" {
|
if boxStr == "" {
|
||||||
boxStr = page.Area.ContentBox
|
boxStr = area.ContentBox
|
||||||
}
|
|
||||||
if boxStr == "" && r.Reader.doc != nil {
|
|
||||||
boxStr = r.Reader.doc.CommonData.PageArea.PhysicalBox
|
|
||||||
}
|
}
|
||||||
if boxStr == "" {
|
if boxStr == "" {
|
||||||
boxStr = "0 0 210 297"
|
boxStr = "0 0 210 297"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package ofdgo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"io"
|
"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
|
m[1][2] -= m[1][0]*p + m[1][1]*p
|
||||||
}
|
}
|
||||||
ctx.RenderImage(img, ctx.CoordSystemView().Mul(ctx.View()).Mul(m))
|
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 应用图片蒙版
|
// imageWithMask 应用图片蒙版
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import "github.com/tdewolff/canvas"
|
|||||||
// 入参: ctx 画布上下文, pageID 页面ID, pageH 页面高度
|
// 入参: ctx 画布上下文, pageID 页面ID, pageH 页面高度
|
||||||
func (r *Renderer) renderAnnotations(ctx *canvas.Context, pageID string, pageH float64) {
|
func (r *Renderer) renderAnnotations(ctx *canvas.Context, pageID string, pageH float64) {
|
||||||
for _, annot := range r.Reader.Annots[pageID] {
|
for _, annot := range r.Reader.Annots[pageID] {
|
||||||
if len(annot.Appearance.Objects) == 0 {
|
if annot.Visible != nil && !*annot.Visible {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
box, _ := ParseBox(annot.Appearance.Boundary)
|
box, _ := ParseBox(annot.Appearance.Boundary)
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ func pageActionSources(page pdfPage) []pdfActionSource {
|
|||||||
func annotationActionSources(annotations []Annotation) []pdfActionSource {
|
func annotationActionSources(annotations []Annotation) []pdfActionSource {
|
||||||
sources := make([]pdfActionSource, 0)
|
sources := make([]pdfActionSource, 0)
|
||||||
for _, annotation := range annotations {
|
for _, annotation := range annotations {
|
||||||
|
if annotation.Visible != nil && !*annotation.Visible {
|
||||||
|
continue
|
||||||
|
}
|
||||||
box, err := ParseBox(annotation.Appearance.Boundary)
|
box, err := ParseBox(annotation.Appearance.Boundary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in new issue
Block a user