mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
feat(写入功能): 新增页面复制与资源按需写出
This commit is contained in:
1 parent
79d566b2e7
commit
dcf1ae8b3a
2 files changed
+88
-21
No files matched your search
+42
-2
@@ -127,6 +127,46 @@ func (e *Editor) AddPage(width, height float64) (int, error) {
|
|||||||
return len(e.pages) - 1, nil
|
return len(e.pages) - 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyPage 复制页面并追加到文档末尾,分配新标识,复用字体和图片资源
|
||||||
|
// 副本可独立修改,位置通过MovePage调整
|
||||||
|
// 入参: index 原页面索引
|
||||||
|
// 返回: int 副本页面索引, error 错误信息
|
||||||
|
func (e *Editor) CopyPage(index int) (int, error) {
|
||||||
|
source, err := e.page(index)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
page := PageContent{Area: source.Area, Content: Content{Layer: make([]Layer, len(source.Content.Layer))}}
|
||||||
|
for i, layer := range source.Content.Layer {
|
||||||
|
page.Content.Layer[i] = Layer{Type: layer.Type, Objects: make([]GraphicObject, len(layer.Objects))}
|
||||||
|
for j, object := range layer.Objects {
|
||||||
|
page.Content.Layer[i].Objects[j], err = cloneEditorObject(object)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.ID = e.nextID()
|
||||||
|
for i := range page.Content.Layer {
|
||||||
|
layer := &page.Content.Layer[i]
|
||||||
|
layer.ID = e.nextID()
|
||||||
|
for j := range layer.Objects {
|
||||||
|
object := &layer.Objects[j]
|
||||||
|
id := e.nextID()
|
||||||
|
switch object.Type {
|
||||||
|
case "TextObject":
|
||||||
|
object.TextObject.ID = id
|
||||||
|
case "PathObject":
|
||||||
|
object.PathObject.ID = id
|
||||||
|
case "ImageObject":
|
||||||
|
object.ImageObject.ID = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.pages = append(e.pages, page)
|
||||||
|
return len(e.pages) - 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeletePage 删除页面,不回收资源或复用标识,删除后页面索引随之变化
|
// DeletePage 删除页面,不回收资源或复用标识,删除后页面索引随之变化
|
||||||
// 入参: index 页面索引
|
// 入参: index 页面索引
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
@@ -180,7 +220,7 @@ func (e *Editor) nextID() string {
|
|||||||
return strconv.Itoa(e.maxID)
|
return strconv.Itoa(e.maxID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFont 嵌入OpenType字体,集合字体按索引提取,重复资源复用标识
|
// AddFont 注册OpenType字体,集合字体按索引提取,重复资源复用标识,引用后写入文档
|
||||||
// 入参: file 字体文件, index 集合内字体索引,单字体为0
|
// 入参: file 字体文件, index 集合内字体索引,单字体为0
|
||||||
// 返回: string 字体资源标识, error 错误信息
|
// 返回: string 字体资源标识, error 错误信息
|
||||||
func (e *Editor) AddFont(file FontFile, index int) (string, error) {
|
func (e *Editor) AddFont(file FontFile, index int) (string, error) {
|
||||||
@@ -250,7 +290,7 @@ func editorFontName(sfnt *font.SFNT, names ...font.NameID) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddImage 嵌入PNG或JPEG图片,保留原始编码,重复资源复用标识
|
// AddImage 注册PNG或JPEG图片,保留原始编码,重复资源复用标识,引用后写入文档
|
||||||
// 入参: data 图片数据
|
// 入参: data 图片数据
|
||||||
// 返回: string 图片资源标识, error 错误信息
|
// 返回: string 图片资源标识, error 错误信息
|
||||||
func (e *Editor) AddImage(data []byte) (string, error) {
|
func (e *Editor) AddImage(data []byte) (string, error) {
|
||||||
|
|||||||
+46
-19
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
const ofdNamespace = "http://www.ofdspec.org/2016"
|
const ofdNamespace = "http://www.ofdspec.org/2016"
|
||||||
|
|
||||||
// WriteTo 逐个条目写出OFD,不关闭调用方输出流,出错时应丢弃本次输出
|
// WriteTo 逐个条目写出OFD,仅包含实际引用的资源,不关闭调用方输出流,出错时应丢弃本次输出
|
||||||
// 入参: writer 输出流
|
// 入参: writer 输出流
|
||||||
// 返回: int64 已写入字节数, error 错误信息
|
// 返回: int64 已写入字节数, error 错误信息
|
||||||
func (e *Editor) WriteTo(writer io.Writer) (int64, error) {
|
func (e *Editor) WriteTo(writer io.Writer) (int64, error) {
|
||||||
@@ -55,7 +55,7 @@ func (e *Editor) WriteTo(writer io.Writer) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reader 获取当前文档的独立内存快照,不进行ZIP压缩,后续修改不影响已有快照
|
// Reader 获取当前文档的独立内存快照,不进行ZIP压缩,后续修改不影响已有快照
|
||||||
// 返回的Reader可用于现有渲染、搜索和导出接口,二进制资源内部共享只读数据
|
// 返回的Reader可用于现有渲染、搜索和导出接口,仅包含实际引用的资源,二进制数据内部共享只读
|
||||||
// 返回: *Reader 阅读器, error 错误信息
|
// 返回: *Reader 阅读器, error 错误信息
|
||||||
func (e *Editor) Reader() (*Reader, error) {
|
func (e *Editor) Reader() (*Reader, error) {
|
||||||
if err := e.validate(); err != nil {
|
if err := e.validate(); err != nil {
|
||||||
@@ -100,6 +100,7 @@ func (e *Editor) validate() error {
|
|||||||
// 入参: write 条目写入方法
|
// 入参: write 条目写入方法
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
func (e *Editor) writeParts(write func(string, []byte, bool) error) error {
|
func (e *Editor) writeParts(write func(string, []byte, bool) error) error {
|
||||||
|
fonts, images := e.usedResources()
|
||||||
writeXML := func(name string, encode func(*ofdXML)) error {
|
writeXML := func(name string, encode func(*ofdXML)) error {
|
||||||
data, err := encodeOFDXML(encode)
|
data, err := encodeOFDXML(encode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -145,7 +146,7 @@ func (e *Editor) writeParts(write func(string, []byte, bool) error) error {
|
|||||||
x.start("PageArea", nil)
|
x.start("PageArea", nil)
|
||||||
x.text("PhysicalBox", e.pages[0].Area.PhysicalBox)
|
x.text("PhysicalBox", e.pages[0].Area.PhysicalBox)
|
||||||
x.end("PageArea")
|
x.end("PageArea")
|
||||||
if len(e.resources) != 0 {
|
if len(fonts)+len(images) != 0 {
|
||||||
x.text("DocumentRes", "DocumentRes.xml")
|
x.text("DocumentRes", "DocumentRes.xml")
|
||||||
}
|
}
|
||||||
x.end("CommonData")
|
x.end("CommonData")
|
||||||
@@ -162,8 +163,8 @@ func (e *Editor) writeParts(write func(string, []byte, bool) error) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(e.resources) != 0 {
|
if len(fonts)+len(images) != 0 {
|
||||||
if err := writeXML("Doc_0/DocumentRes.xml", e.writeResources); err != nil {
|
if err := writeXML("Doc_0/DocumentRes.xml", func(x *ofdXML) { x.resources(fonts, images) }); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,14 +175,46 @@ func (e *Editor) writeParts(write func(string, []byte, bool) error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, resource := range e.resources {
|
for _, resources := range [][]editorResource{fonts, images} {
|
||||||
|
for _, resource := range resources {
|
||||||
if err := write(resource.name, resource.data, resource.image != nil); err != nil {
|
if err := write(resource.name, resource.data, resource.image != nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usedResources 按注册顺序筛选当前页面引用的字体和图片,不修改资源池
|
||||||
|
// 返回: []editorResource 字体资源, []editorResource 图片资源
|
||||||
|
func (e *Editor) usedResources() (fonts, images []editorResource) {
|
||||||
|
used := make(map[string]bool)
|
||||||
|
for _, page := range e.pages {
|
||||||
|
for _, layer := range page.Content.Layer {
|
||||||
|
for _, object := range layer.Objects {
|
||||||
|
switch object.Type {
|
||||||
|
case "TextObject":
|
||||||
|
used[object.TextObject.Font] = true
|
||||||
|
case "ImageObject":
|
||||||
|
used[object.ImageObject.ResourceID] = true
|
||||||
|
if object.ImageObject.ImageMask != "" {
|
||||||
|
used[object.ImageObject.ImageMask] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, resource := range e.resources {
|
||||||
|
if resource.font != nil && used[resource.font.ID] {
|
||||||
|
fonts = append(fonts, resource)
|
||||||
|
}
|
||||||
|
if resource.image != nil && used[resource.image.ID] {
|
||||||
|
images = append(images, resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fonts, images
|
||||||
|
}
|
||||||
|
|
||||||
// page 写出页面尺寸、图层和对象
|
// page 写出页面尺寸、图层和对象
|
||||||
// 入参: page 页面内容
|
// 入参: page 页面内容
|
||||||
func (x *ofdXML) page(page PageContent) {
|
func (x *ofdXML) page(page PageContent) {
|
||||||
@@ -204,16 +237,13 @@ func (x *ofdXML) page(page PageContent) {
|
|||||||
x.end("Page")
|
x.end("Page")
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeResources 写出文档资源索引
|
// resources 写出文档资源索引
|
||||||
// 入参: x XML编码器
|
// 入参: fonts 字体资源, images 图片资源
|
||||||
func (e *Editor) writeResources(x *ofdXML) {
|
func (x *ofdXML) resources(fonts, images []editorResource) {
|
||||||
x.root("Res", ofdAttrs{{Name: xml.Name{Local: "BaseLoc"}, Value: "Res"}})
|
x.root("Res", ofdAttrs{{Name: xml.Name{Local: "BaseLoc"}, Value: "Res"}})
|
||||||
if len(e.fonts) != 0 {
|
if len(fonts) != 0 {
|
||||||
x.start("Fonts", nil)
|
x.start("Fonts", nil)
|
||||||
for _, resource := range e.resources {
|
for _, resource := range fonts {
|
||||||
if resource.font == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
font := resource.font
|
font := resource.font
|
||||||
var attrs ofdAttrs
|
var attrs ofdAttrs
|
||||||
attrs.add("ID", font.ID)
|
attrs.add("ID", font.ID)
|
||||||
@@ -235,12 +265,9 @@ func (e *Editor) writeResources(x *ofdXML) {
|
|||||||
}
|
}
|
||||||
x.end("Fonts")
|
x.end("Fonts")
|
||||||
}
|
}
|
||||||
if len(e.images) != 0 {
|
if len(images) != 0 {
|
||||||
x.start("MultiMedias", nil)
|
x.start("MultiMedias", nil)
|
||||||
for _, resource := range e.resources {
|
for _, resource := range images {
|
||||||
if resource.image == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
image := resource.image
|
image := resource.image
|
||||||
var attrs ofdAttrs
|
var attrs ofdAttrs
|
||||||
attrs.add("ID", image.ID)
|
attrs.add("ID", image.ID)
|
||||||
|
|||||||
Reference in new issue
Block a user