package ora import ( "archive/zip" "encoding/xml" "os" ) type ORALayer struct { Name string Filename string // relative to data/ Visible bool Opacity float32 // 0..1 Blend string // svg:src-over, svg:multiply, etc } type imageXML struct { XMLName xml.Name `xml:"image"` W int `xml:"w,attr"` H int `xml:"h,attr"` Stack stackXML `xml:"stack"` } type stackXML struct { Layers []layerXML `xml:"layer"` } type layerXML struct { Name string `xml:"name,attr"` Src string `xml:"src,attr"` Opacity float32 `xml:"opacity,attr"` Visible bool `xml:"visible,attr"` Composite string `xml:"composite-op,attr"` } func WriteORA( outPath string, width, height int, layers []ORALayer, pngLoader func(name string) ([]byte, error), ) error { f, err := os.Create(outPath) if err != nil { return err } defer f.Close() zw := zip.NewWriter(f) defer zw.Close() // 1. mimetype (must be first, uncompressed) h := &zip.FileHeader{ Name: "mimetype", Method: zip.Store, } w, _ := zw.CreateHeader(h) w.Write([]byte("image/openraster")) // 2. stack.xml var stack []layerXML for _, l := range layers { stack = append(stack, layerXML{ Name: l.Name, Src: "data/" + l.Filename, Opacity: l.Opacity, Visible: l.Visible, Composite: l.Blend, }) } img := imageXML{ W: width, H: height, Stack: stackXML{ Layers: stack, }, } xmlBytes, _ := xml.MarshalIndent(img, "", " ") xmlBuf := append([]byte(xml.Header), xmlBytes...) w, _ = zw.Create("stack.xml") w.Write(xmlBuf) // 3. layer PNGs for _, l := range layers { data, err := pngLoader(l.Filename) if err != nil { return err } w, _ = zw.Create("data/" + l.Filename) w.Write(data) } return nil }