diff --git a/ofdgo_editor.go b/ofdgo_editor.go index b161673..279f775 100644 --- a/ofdgo_editor.go +++ b/ofdgo_editor.go @@ -847,6 +847,11 @@ func (v *editorValidation) prepareObject(id string, object GraphicObject) (Graph if _, err := e.editorImage(obj.ResourceID); err != nil { return GraphicObject{}, err } + if obj.Substitution != "" { + if _, err := e.editorImage(obj.Substitution); err != nil { + return GraphicObject{}, err + } + } if obj.ImageMask != "" { if _, err := e.editorImage(obj.ImageMask); err != nil { return GraphicObject{}, err diff --git a/ofdgo_editor_color.go b/ofdgo_editor_color.go index ac2db00..f9654ee 100644 --- a/ofdgo_editor_color.go +++ b/ofdgo_editor_color.go @@ -462,6 +462,7 @@ func collectObjectReferences(object GraphicObject, used map[string]bool) { actions = object.PathObject.Actions case "ImageObject": used[object.ImageObject.ResourceID], used[object.ImageObject.ImageMask] = true, true + used[object.ImageObject.Substitution] = true if object.ImageObject.Border != nil { collectPaintReferences((*FillColor)(object.ImageObject.Border.BorderColor), used) } diff --git a/ofdgo_editor_xml.go b/ofdgo_editor_xml.go index 708536f..718d44f 100644 --- a/ofdgo_editor_xml.go +++ b/ofdgo_editor_xml.go @@ -307,7 +307,7 @@ func editorXMLSupported(node *editorXML) bool { children += " Clips" } case "ImageObject": - allowed, children = "ID Boundary CTM ResourceID ImageMask Visible Alpha", "Clips" + allowed, children = "ID Boundary CTM ResourceID Substitution ImageMask Visible Alpha", "Clips" case "TextCode": allowed = "X Y DeltaX DeltaY" case "FillColor", "StrokeColor": @@ -344,7 +344,7 @@ func editorXMLTransformable(node *editorXML) bool { case "PathObject": attrs = "ID Boundary CTM DrawParam LineWidth MiterLimit Join Cap Rule DashPattern DashOffset Visible Stroke Fill Alpha" case "ImageObject": - attrs = "ID Boundary CTM ResourceID ImageMask Visible Alpha" + attrs = "ID Boundary CTM ResourceID Substitution ImageMask Visible Alpha" case "CompositeObject", "CompositeGraphicUnit": attrs = "ID Boundary CTM ResourceID DrawParam Visible Alpha" default: diff --git a/ofdgo_image_resource.go b/ofdgo_image_resource.go index 1ffa6c1..4da8f71 100644 --- a/ofdgo_image_resource.go +++ b/ofdgo_image_resource.go @@ -94,6 +94,38 @@ func (r *Renderer) ImageResource(id string) (image.Image, error) { return r.cachedImageResource(path) } +// imageObjectResource 读取主图,无法读取或解码时使用对象声明的替换图 +// 入参: object 图像对象 +// 返回: image.Image 图片资源, error 读取或解码错误 +func (r *Renderer) imageObjectResource(object ImageObject) (image.Image, error) { + read := func(id string) (image.Image, error) { + img, err := r.ImageResource(id) + if err == nil && (r.decodeImages || object.Substitution != "") { + pixels, decodeErr := imagePixelData(img) + err = decodeErr + if r.decodeImages { + img = pixels + } + } + return img, err + } + img, err := read(object.ResourceID) + if err == nil || object.Substitution == "" || object.Substitution == object.ResourceID { + return img, err + } + if r.pageContext != nil && r.pageContext.Err() != nil { + return nil, r.pageContext.Err() + } + if r.OutputContext != nil && r.OutputContext.Err() != nil { + return nil, r.OutputContext.Err() + } + img, substituteErr := read(object.Substitution) + if substituteErr != nil { + return nil, fmt.Errorf("image resource %q: %w; substitution %q: %w", object.ResourceID, err, object.Substitution, substituteErr) + } + return img, nil +} + // newEncodedImage 接管编码数据并读取头信息 // 入参: data 原始编码 // 返回: *EncodedImage 惰性图片, error 头信息或格式错误 diff --git a/ofdgo_page.go b/ofdgo_page.go index c0bee7e..e673755 100644 --- a/ofdgo_page.go +++ b/ofdgo_page.go @@ -240,16 +240,17 @@ 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"` - Border *ImageBorder `xml:"Border"` - Clips *Clips `xml:"Clips"` - Actions []Action `xml:"Actions>Action"` + 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 图像边框 diff --git a/ofdgo_render_compile_image.go b/ofdgo_render_compile_image.go index 0b4f040..ac71141 100644 --- a/ofdgo_render_compile_image.go +++ b/ofdgo_render_compile_image.go @@ -59,7 +59,7 @@ func (c *semanticCompiler) imageObject(object ImageObject, state RenderState) er return err } } else { - img, err := c.renderer.ImageResource(object.ResourceID) + img, err := c.renderer.imageObjectResource(object) if err != nil { return err } diff --git a/ofdgo_renderer_image_canvas.go b/ofdgo_renderer_image_canvas.go index 4bfa8ed..5f6acde 100644 --- a/ofdgo_renderer_image_canvas.go +++ b/ofdgo_renderer_image_canvas.go @@ -38,12 +38,7 @@ func (r *Renderer) renderImage(ctx *canvas.Context, obj ImageObject, pageH float if obj.Visible != nil && !*obj.Visible || obj.Alpha != nil && *obj.Alpha == 0 { return } - resPath, ok := resourceValue(r.Reader.ResMap, obj.ResourceID) - if !ok { - r.renderError = fmt.Errorf("image resource %q not found", obj.ResourceID) - return - } - img, err := r.decodeImageResource(resPath) + img, err := r.imageObjectResource(obj) if err != nil { r.renderError = err return diff --git a/ofdgo_writer.go b/ofdgo_writer.go index 3a014bb..017a3ba 100644 --- a/ofdgo_writer.go +++ b/ofdgo_writer.go @@ -849,6 +849,7 @@ func (x *ofdXML) object(object GraphicObject, root bool) { attrs.add("CTM", obj.CTM) attrs.add("ResourceID", obj.ResourceID) attrs.add("ImageMask", obj.ImageMask) + attrs.add("Substitution", obj.Substitution) attrs.flag("Visible", obj.Visible) attrs.alpha(obj.Alpha) actions = obj.Actions