diff --git a/ofdgo_annot.go b/ofdgo_annot.go index e4cf8e8..c1ea9e1 100644 --- a/ofdgo_annot.go +++ b/ofdgo_annot.go @@ -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 } diff --git a/ofdgo_doc.go b/ofdgo_doc.go index f607fee..fe64103 100644 --- a/ofdgo_doc.go +++ b/ofdgo_doc.go @@ -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"` } diff --git a/ofdgo_page.go b/ofdgo_page.go index 4e6a2d2..af1c13a 100644 --- a/ofdgo_page.go +++ b/ofdgo_page.go @@ -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"` } diff --git a/ofdgo_reader.go b/ofdgo_reader.go index 7ec0cd7..28fa547 100644 --- a/ofdgo_reader.go +++ b/ofdgo_reader.go @@ -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 完整路径 diff --git a/ofdgo_renderer.go b/ofdgo_renderer.go index 480c1c6..bcc105f 100644 --- a/ofdgo_renderer.go +++ b/ofdgo_renderer.go @@ -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" diff --git a/ofdgo_renderer_image.go b/ofdgo_renderer_image.go index 5ccd08d..329794e 100644 --- a/ofdgo_renderer_image.go +++ b/ofdgo_renderer_image.go @@ -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 应用图片蒙版 diff --git a/ofdgo_renderer_layer.go b/ofdgo_renderer_layer.go index c1e4545..f6cfb64 100644 --- a/ofdgo_renderer_layer.go +++ b/ofdgo_renderer_layer.go @@ -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) diff --git a/ofdgo_renderer_pdf.go b/ofdgo_renderer_pdf.go index 80566bd..0c06a38 100644 --- a/ofdgo_renderer_pdf.go +++ b/ofdgo_renderer_pdf.go @@ -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