Files
ofdgo/ofdgo_page.go
T
2026-10-27 11:54:18 +08:00

330 lines
11 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"encoding/xml"
"fmt"
)
// PageContent 页面内容
type PageContent struct {
XMLName xml.Name `xml:"Page"`
ID string `xml:"-"`
Area PageArea `xml:"Area"`
Template []Template `xml:"Template"`
PageRes []string `xml:"PageRes"`
Content Content `xml:"Content"`
Actions []Action `xml:"Actions>Action"`
}
// Template 页面模板引用
type Template struct {
TemplateID string `xml:"TemplateID,attr"`
ZOrder string `xml:"ZOrder,attr"`
}
// Content 页面内容节点
type Content struct {
Layer []Layer `xml:"Layer"`
}
// Layer 图层
// Objects非空时按其顺序渲染,图元修改应作用于Objects中的对象
type Layer struct {
ID string `xml:"ID,attr"`
Type string `xml:"Type,attr"`
DrawParam string `xml:"DrawParam,attr"`
Objects []GraphicObject `xml:"-"`
TextObject []TextObject `xml:"TextObject"`
PathObject []PathObject `xml:"PathObject"`
ImageObject []ImageObject `xml:"ImageObject"`
CompositeGraphicUnit []CompositeGraphicUnit `xml:"CompositeGraphicUnit"`
}
// GraphicObject 图形对象
type GraphicObject struct {
Type string
TextObject TextObject
PathObject PathObject
ImageObject ImageObject
CompositeGraphicUnit CompositeGraphicUnit
origin *editorObjectOrigin
state editorCompositeState
richText *richTextSnapshot
}
// Clips 相对于对象外接矩形的裁剪集合
// TransFlag仅兼容旧式扩展,写入时折算为Area的CTM
type Clips struct {
TransFlag *bool `xml:"TransFlag,attr"`
Clip []Clip `xml:"Clip"`
}
// Clip 裁剪
type Clip struct {
Area []ClipArea `xml:"Area"`
}
// ClipArea 裁剪区域
type ClipArea struct {
DrawParam string `xml:"DrawParam,attr"`
CTM string `xml:"CTM,attr"`
Path []PathObject `xml:"Path"`
Text []TextObject `xml:"Text"`
}
// TextObject 文本对象
// MiterLimitSet为true时,零截断值覆盖继承值,否则零值表示未指定
type TextObject struct {
ID string `xml:"ID,attr"`
Boundary string `xml:"Boundary,attr"`
DrawParam string `xml:"DrawParam,attr"`
LineWidth float64 `xml:"LineWidth,attr"`
Cap string `xml:"Cap,attr"`
DashPattern string `xml:"DashPattern,attr"`
DashOffset *float64 `xml:"DashOffset,attr"`
Join string `xml:"Join,attr"`
MiterLimit float64 `xml:"MiterLimit,attr"`
Font string `xml:"Font,attr"`
Size float64 `xml:"Size,attr"`
Weight int `xml:"Weight,attr"`
Italic bool `xml:"Italic,attr"`
Decoration string `xml:"Decoration,attr"`
HScale float64 `xml:"HScale,attr"`
VScale float64 `xml:"VScale,attr"`
ReadDirection int `xml:"ReadDirection,attr"`
CharDirection int `xml:"CharDirection,attr"`
CTM string `xml:"CTM,attr"`
Alpha *int `xml:"Alpha,attr"`
Visible *bool `xml:"Visible,attr"`
Fill *bool `xml:"Fill,attr"`
Stroke *bool `xml:"Stroke,attr"`
StrokeColor *StrokeColor `xml:"StrokeColor"`
FillColor *FillColor `xml:"FillColor"`
CGTransform []CGTransform `xml:"CGTransform"`
TextCode []TextCode `xml:"TextCode"`
Clips *Clips `xml:"Clips"`
Actions []Action `xml:"Actions>Action"`
LineWidthSet bool `xml:"-"`
MiterLimitSet bool `xml:"-"`
dashPatternSet bool
layout *textLayout
}
// FillColor 填充颜色
type FillColor struct {
Value string `xml:"Value,attr"`
Index *int `xml:"Index,attr"`
ColorSpace string `xml:"ColorSpace,attr"`
Alpha *int `xml:"Alpha,attr"`
Pattern *Pattern `xml:"Pattern"`
AxialShd *AxialShd `xml:"AxialShd"`
RadialShd *RadialShd `xml:"RadialShd"`
GouraudShd *GouraudShd `xml:"GouraudShd"`
LaGouraudShd *LaGouraudShd `xml:"LaGouraudShd"`
unsupported bool
}
// Pattern 图案填充
type Pattern struct {
Width float64 `xml:"Width,attr"`
Height float64 `xml:"Height,attr"`
XStep float64 `xml:"XStep,attr"`
YStep float64 `xml:"YStep,attr"`
ReflectMethod string `xml:"ReflectMethod,attr"`
RelativeTo string `xml:"RelativeTo,attr"`
CTM string `xml:"CTM,attr"`
CellContent PatternContent `xml:"CellContent"`
}
// PatternContent 图案单元内容
// Objects非空时保留绘制顺序,否则依次使用文字、路径、图片和复合图元数组
type PatternContent struct {
Thumbnail string `xml:"Thumbnail,attr"`
Objects []GraphicObject `xml:"-"`
TextObject []TextObject `xml:"TextObject"`
PathObject []PathObject `xml:"PathObject"`
ImageObject []ImageObject `xml:"ImageObject"`
CompositeGraphicUnit []CompositeGraphicUnit `xml:"CompositeGraphicUnit"`
}
// TextCode 文本内容节点
type TextCode struct {
X string `xml:"X,attr"`
Y string `xml:"Y,attr"`
DeltaX string `xml:"DeltaX,attr"`
DeltaY string `xml:"DeltaY,attr"`
Index string `xml:"Index,attr"`
Value string `xml:",chardata"`
}
// CGTransform 字符到字形的映射
// CodePosition在内存中为整个TextObject的字符索引,保存时转换为所在TextCode的局部索引
type CGTransform struct {
CodePosition int `xml:"CodePosition,attr"`
CodeCount int `xml:"CodeCount,attr"`
GlyphCount int `xml:"GlyphCount,attr"`
Glyphs string `xml:"Glyphs"`
}
// PathObject 路径对象
// MiterLimitSet为true时,零截断值覆盖继承值,否则零值表示未指定
type PathObject struct {
ID string `xml:"ID,attr"`
Boundary string `xml:"Boundary,attr"`
DrawParam string `xml:"DrawParam,attr"`
LineWidth float64 `xml:"LineWidth,attr"`
Join string `xml:"Join,attr"`
Cap string `xml:"Cap,attr"`
DashOffset *float64 `xml:"DashOffset,attr"`
DashPattern string `xml:"DashPattern,attr"`
MiterLimit float64 `xml:"MiterLimit,attr"`
CTM string `xml:"CTM,attr"`
Alpha *int `xml:"Alpha,attr"`
Visible *bool `xml:"Visible,attr"`
Stroke *bool `xml:"Stroke,attr"`
Fill *bool `xml:"Fill,attr"`
Rule string `xml:"Rule,attr"`
StrokeColor *StrokeColor `xml:"StrokeColor"`
FillColor *FillColor `xml:"FillColor"`
AbbreviatedData string `xml:"AbbreviatedData"`
Clips *Clips `xml:"Clips"`
Actions []Action `xml:"Actions>Action"`
LineWidthSet bool `xml:"-"`
MiterLimitSet bool `xml:"-"`
dashPatternSet bool
}
// StrokeColor 勾边颜色
type StrokeColor FillColor
// AxialShd 轴向渐变
type AxialShd struct {
MapType string `xml:"MapType,attr"`
MapUnit float64 `xml:"MapUnit,attr"`
Extend string `xml:"Extend,attr"`
StartPoint string `xml:"StartPoint,attr"`
EndPoint string `xml:"EndPoint,attr"`
Segment []ShdSegment `xml:"Segment"`
}
// RadialShd 径向渐变
type RadialShd struct {
MapType string `xml:"MapType,attr"`
MapUnit float64 `xml:"MapUnit,attr"`
Eccentricity float64 `xml:"Eccentricity,attr"`
Angle float64 `xml:"Angle,attr"`
Extend string `xml:"Extend,attr"`
StartPoint string `xml:"StartPoint,attr"`
StartRadius float64 `xml:"StartRadius,attr"`
EndPoint string `xml:"EndPoint,attr"`
EndRadius float64 `xml:"EndRadius,attr"`
Segment []ShdSegment `xml:"Segment"`
}
// ShdSegment 渐变分段
type ShdSegment struct {
Position float64 `xml:"Position,attr"`
Color ShdColor `xml:"Color"`
positionMissing bool
}
// ShdColor 渐变颜色
type ShdColor struct {
Value string `xml:"Value,attr"`
Index *int `xml:"Index,attr"`
ColorSpace string `xml:"ColorSpace,attr"`
Alpha *int `xml:"Alpha,attr"`
}
// ImageObject 图片对象
type ImageObject struct {
ID string `xml:"ID,attr"`
Boundary string `xml:"Boundary,attr"`
ResourceID string `xml:"ResourceID,attr"`
Substitution string `xml:"Substitution,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"`
}
// UnmarshalXML 读取页面直接子节点,扩展内容不作为页面区域、资源或图层使用
// 入参: d XML解码器, start 起始节点
// 返回: error 解析错误
func (page *PageContent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if start.Name.Local != "Page" {
return fmt.Errorf("expected Page root, got %s", start.Name.Local)
}
*page = PageContent{XMLName: start.Name}
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
switch node.Name.Local {
case "Area":
return d.DecodeElement(&page.Area, &node)
case "Template":
var template Template
if err := d.DecodeElement(&template, &node); err != nil {
return err
}
page.Template = append(page.Template, template)
case "PageRes":
var location string
if err := d.DecodeElement(&location, &node); err != nil {
return err
}
page.PageRes = append(page.PageRes, location)
case "Content":
return d.DecodeElement(&page.Content, &node)
case "Actions":
return decodeOFDActions(d, node, &page.Actions)
default:
return d.Skip()
}
return nil
})
}
// UnmarshalXML 读取页面图层,不将外部命名空间中的Layer作为图层
// 入参: d XML解码器, start 起始节点
// 返回: error 解析错误
func (content *Content) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
*content = Content{}
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
if node.Name.Local != "Layer" {
return d.Skip()
}
var layer Layer
if err := d.DecodeElement(&layer, &node); err != nil {
return err
}
content.Layer = append(content.Layer, layer)
return nil
})
}