Rename SetLegacyDrawingHF to AddHeaderFooterImage (#2023)
- Add new exported HeaderFooterImagePositionType enumeration - An error will be return if the image format is unsupported - Rename exported data type HeaderFooterGraphics to HeaderFooterImageOptions - Support add and update exist header and footer images - Changes the VML data ID to sheet ID - Update unit tests - Update dependencies modules
This commit is contained in:
parent
d2be5cdf8e
commit
30d3561d0e
6
go.mod
6
go.mod
|
@ -8,10 +8,10 @@ require (
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d
|
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d
|
||||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7
|
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7
|
||||||
golang.org/x/crypto v0.28.0
|
golang.org/x/crypto v0.29.0
|
||||||
golang.org/x/image v0.18.0
|
golang.org/x/image v0.18.0
|
||||||
golang.org/x/net v0.30.0
|
golang.org/x/net v0.31.0
|
||||||
golang.org/x/text v0.19.0
|
golang.org/x/text v0.20.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -15,14 +15,14 @@ github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7
|
||||||
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
|
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
|
||||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|
2
sheet.go
2
sheet.go
|
@ -1239,7 +1239,7 @@ func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) {
|
||||||
// |
|
// |
|
||||||
// &F | Current workbook's file name
|
// &F | Current workbook's file name
|
||||||
// |
|
// |
|
||||||
// &G | Drawing object as background (Use SetLegacyDrawingHF)
|
// &G | Drawing object as background (Use AddHeaderFooterImage)
|
||||||
// |
|
// |
|
||||||
// &H | Shadow text format
|
// &H | Shadow text format
|
||||||
// |
|
// |
|
||||||
|
|
206
vml.go
206
vml.go
|
@ -36,6 +36,16 @@ const (
|
||||||
FormControlScrollBar
|
FormControlScrollBar
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HeaderFooterImagePositionType is the type of header and footer image position.
|
||||||
|
type HeaderFooterImagePositionType byte
|
||||||
|
|
||||||
|
// Worksheet header and footer image position types enumeration.
|
||||||
|
const (
|
||||||
|
HeaderFooterImagePositionLeft HeaderFooterImagePositionType = iota
|
||||||
|
HeaderFooterImagePositionCenter
|
||||||
|
HeaderFooterImagePositionRight
|
||||||
|
)
|
||||||
|
|
||||||
// GetComments retrieves all comments in a worksheet by given worksheet name.
|
// GetComments retrieves all comments in a worksheet by given worksheet name.
|
||||||
func (f *File) GetComments(sheet string) ([]Comment, error) {
|
func (f *File) GetComments(sheet string) ([]Comment, error) {
|
||||||
var comments []Comment
|
var comments []Comment
|
||||||
|
@ -519,6 +529,7 @@ func (f *File) addVMLObject(opts vmlOptions) error {
|
||||||
}
|
}
|
||||||
vmlID = f.countVMLDrawing() + 1
|
vmlID = f.countVMLDrawing() + 1
|
||||||
}
|
}
|
||||||
|
sheetID := f.getSheetID(opts.sheet)
|
||||||
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
||||||
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
||||||
sheetXMLPath, _ := f.getSheetXMLPath(opts.sheet)
|
sheetXMLPath, _ := f.getSheetXMLPath(opts.sheet)
|
||||||
|
@ -534,7 +545,7 @@ func (f *File) addVMLObject(opts vmlOptions) error {
|
||||||
f.addSheetNameSpace(opts.sheet, SourceRelationship)
|
f.addSheetNameSpace(opts.sheet, SourceRelationship)
|
||||||
f.addSheetLegacyDrawing(opts.sheet, rID)
|
f.addSheetLegacyDrawing(opts.sheet, rID)
|
||||||
}
|
}
|
||||||
if err = f.addDrawingVML(vmlID, drawingVML, prepareFormCtrlOptions(&opts)); err != nil {
|
if err = f.addDrawingVML(sheetID, drawingVML, prepareFormCtrlOptions(&opts)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !opts.formCtrl {
|
if !opts.formCtrl {
|
||||||
|
@ -823,7 +834,7 @@ func (f *File) addFormCtrlShape(preset formCtrlPreset, col, row int, anchor stri
|
||||||
// anchor value is a comma-separated list of data written out as: LeftColumn,
|
// anchor value is a comma-separated list of data written out as: LeftColumn,
|
||||||
// LeftOffset, TopRow, TopOffset, RightColumn, RightOffset, BottomRow,
|
// LeftOffset, TopRow, TopOffset, RightColumn, RightOffset, BottomRow,
|
||||||
// BottomOffset.
|
// BottomOffset.
|
||||||
func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) error {
|
func (f *File) addDrawingVML(sheetID int, drawingVML string, opts *vmlOptions) error {
|
||||||
col, row, err := CellNameToCoordinates(opts.FormControl.Cell)
|
col, row, err := CellNameToCoordinates(opts.FormControl.Cell)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -843,7 +854,7 @@ func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) er
|
||||||
XMLNSx: "urn:schemas-microsoft-com:office:excel",
|
XMLNSx: "urn:schemas-microsoft-com:office:excel",
|
||||||
XMLNSmv: "http://macVmlSchemaUri",
|
XMLNSmv: "http://macVmlSchemaUri",
|
||||||
ShapeLayout: &xlsxShapeLayout{
|
ShapeLayout: &xlsxShapeLayout{
|
||||||
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: dataID},
|
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: sheetID},
|
||||||
},
|
},
|
||||||
ShapeType: &xlsxShapeType{
|
ShapeType: &xlsxShapeType{
|
||||||
ID: fmt.Sprintf("_x0000_t%d", vmlID),
|
ID: fmt.Sprintf("_x0000_t%d", vmlID),
|
||||||
|
@ -1071,79 +1082,138 @@ func extractVMLFont(font []decodeVMLFont) []RichTextRun {
|
||||||
return runs
|
return runs
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLegacyDrawingHF provides a mechanism to set the graphics that
|
// AddHeaderFooterImage provides a mechanism to set the graphics that can be
|
||||||
// can be referenced in the Header/Footer defitions via &G.
|
// referenced in the header and footer definitions via &G, file base name,
|
||||||
|
// extension name and file bytes, supported image types: EMF, EMZ, GIF, JPEG,
|
||||||
|
// JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.
|
||||||
//
|
//
|
||||||
// The extension should be provided with a "." in front, e.g. ".png".
|
// The extension should be provided with a "." in front, e.g. ".png".
|
||||||
// The width/height should have units in them, e.g. "100pt".
|
// The width and height should have units in them, e.g. "100pt".
|
||||||
func (f *File) SetLegacyDrawingHF(sheet string, g *HeaderFooterGraphics) error {
|
func (f *File) AddHeaderFooterImage(sheet string, opts *HeaderFooterImageOptions) error {
|
||||||
|
ws, err := f.workSheetReader(sheet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ext, ok := supportedImageTypes[strings.ToLower(opts.Extension)]
|
||||||
|
if !ok {
|
||||||
|
return ErrImgExt
|
||||||
|
}
|
||||||
|
sheetID := f.getSheetID(sheet)
|
||||||
vmlID := f.countVMLDrawing() + 1
|
vmlID := f.countVMLDrawing() + 1
|
||||||
|
|
||||||
vml := &vmlDrawing{
|
|
||||||
XMLNSv: "urn:schemas-microsoft-com:vml",
|
|
||||||
XMLNSo: "urn:schemas-microsoft-com:office:office",
|
|
||||||
XMLNSx: "urn:schemas-microsoft-com:office:excel",
|
|
||||||
ShapeLayout: &xlsxShapeLayout{
|
|
||||||
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: vmlID},
|
|
||||||
},
|
|
||||||
ShapeType: &xlsxShapeType{
|
|
||||||
ID: "_x0000_t75",
|
|
||||||
CoordSize: "21600,21600",
|
|
||||||
Spt: 75,
|
|
||||||
PreferRelative: "t",
|
|
||||||
Path: "m@4@5l@4@11@9@11@9@5xe",
|
|
||||||
Filled: "f",
|
|
||||||
Stroked: "f",
|
|
||||||
Stroke: &xlsxStroke{JoinStyle: "miter"},
|
|
||||||
VFormulas: &vFormulas{
|
|
||||||
Formulas: []vFormula{
|
|
||||||
{Equation: "if lineDrawn pixelLineWidth 0"},
|
|
||||||
{Equation: "sum @0 1 0"},
|
|
||||||
{Equation: "sum 0 0 @1"},
|
|
||||||
{Equation: "prod @2 1 2"},
|
|
||||||
{Equation: "prod @3 21600 pixelWidth"},
|
|
||||||
{Equation: "prod @3 21600 pixelHeight"},
|
|
||||||
{Equation: "sum @0 0 1"},
|
|
||||||
{Equation: "prod @6 1 2"},
|
|
||||||
{Equation: "prod @7 21600 pixelWidth"},
|
|
||||||
{Equation: "sum @8 21600 0"},
|
|
||||||
{Equation: "prod @7 21600 pixelHeight"},
|
|
||||||
{Equation: "sum @10 21600 0"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VPath: &vPath{ExtrusionOK: "f", GradientShapeOK: "t", ConnectType: "rect"},
|
|
||||||
Lock: &oLock{Ext: "edit", AspectRatio: "t"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
style := fmt.Sprintf("position:absolute;margin-left:0;margin-top:0;width:%s;height:%s;z-index:1", g.Width, g.Height)
|
|
||||||
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
||||||
drawingVMLRels := "xl/drawings/_rels/vmlDrawing" + strconv.Itoa(vmlID) + ".vml.rels"
|
|
||||||
|
|
||||||
mediaStr := ".." + strings.TrimPrefix(f.addMedia(g.File, g.Extension), "xl")
|
|
||||||
imageID := f.addRels(drawingVMLRels, SourceRelationshipImage, mediaStr, "")
|
|
||||||
|
|
||||||
shape := xlsxShape{
|
|
||||||
ID: "RH",
|
|
||||||
Spid: "_x0000_s1025",
|
|
||||||
Type: "#_x0000_t75",
|
|
||||||
Style: style,
|
|
||||||
}
|
|
||||||
s, _ := xml.Marshal(encodeShape{
|
|
||||||
ImageData: &vImageData{RelID: "rId" + strconv.Itoa(imageID)},
|
|
||||||
Lock: &oLock{Ext: "edit", Rotation: "t"},
|
|
||||||
})
|
|
||||||
shape.Val = string(s[13 : len(s)-14])
|
|
||||||
vml.Shape = append(vml.Shape, shape)
|
|
||||||
f.VMLDrawing[drawingVML] = vml
|
|
||||||
|
|
||||||
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
|
||||||
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
|
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
|
||||||
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
|
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
|
||||||
|
if ws.LegacyDrawingHF != nil {
|
||||||
|
// The worksheet already has a VML relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.
|
||||||
|
sheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawingHF.RID)
|
||||||
|
vmlID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, "../drawings/vmlDrawing"), ".vml"))
|
||||||
|
drawingVML = strings.ReplaceAll(sheetRelationshipsDrawingVML, "..", "xl")
|
||||||
|
} else {
|
||||||
|
// Add first VML drawing for given sheet.
|
||||||
|
rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
|
||||||
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
||||||
|
f.addSheetLegacyDrawingHF(sheet, rID)
|
||||||
|
}
|
||||||
|
|
||||||
|
shapeID := map[HeaderFooterImagePositionType]string{
|
||||||
|
HeaderFooterImagePositionLeft: "L",
|
||||||
|
HeaderFooterImagePositionCenter: "C",
|
||||||
|
HeaderFooterImagePositionRight: "R",
|
||||||
|
}[opts.Position] +
|
||||||
|
map[bool]string{false: "H", true: "F"}[opts.IsFooter] +
|
||||||
|
map[bool]string{false: "", true: "FIRST"}[opts.FirstPage]
|
||||||
|
vml := f.VMLDrawing[drawingVML]
|
||||||
|
if vml == nil {
|
||||||
|
vml = &vmlDrawing{
|
||||||
|
XMLNSv: "urn:schemas-microsoft-com:vml",
|
||||||
|
XMLNSo: "urn:schemas-microsoft-com:office:office",
|
||||||
|
XMLNSx: "urn:schemas-microsoft-com:office:excel",
|
||||||
|
ShapeLayout: &xlsxShapeLayout{
|
||||||
|
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: sheetID},
|
||||||
|
},
|
||||||
|
ShapeType: &xlsxShapeType{
|
||||||
|
ID: "_x0000_t75",
|
||||||
|
CoordSize: "21600,21600",
|
||||||
|
Spt: 75,
|
||||||
|
PreferRelative: "t",
|
||||||
|
Path: "m@4@5l@4@11@9@11@9@5xe",
|
||||||
|
Filled: "f",
|
||||||
|
Stroked: "f",
|
||||||
|
Stroke: &xlsxStroke{JoinStyle: "miter"},
|
||||||
|
VFormulas: &vFormulas{
|
||||||
|
Formulas: []vFormula{
|
||||||
|
{Equation: "if lineDrawn pixelLineWidth 0"},
|
||||||
|
{Equation: "sum @0 1 0"},
|
||||||
|
{Equation: "sum 0 0 @1"},
|
||||||
|
{Equation: "prod @2 1 2"},
|
||||||
|
{Equation: "prod @3 21600 pixelWidth"},
|
||||||
|
{Equation: "prod @3 21600 pixelHeight"},
|
||||||
|
{Equation: "sum @0 0 1"},
|
||||||
|
{Equation: "prod @6 1 2"},
|
||||||
|
{Equation: "prod @7 21600 pixelWidth"},
|
||||||
|
{Equation: "sum @8 21600 0"},
|
||||||
|
{Equation: "prod @7 21600 pixelHeight"},
|
||||||
|
{Equation: "sum @10 21600 0"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
VPath: &vPath{ExtrusionOK: "f", GradientShapeOK: "t", ConnectType: "rect"},
|
||||||
|
Lock: &oLock{Ext: "edit", AspectRatio: "t"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
|
||||||
|
d, err := f.decodeVMLDrawingReader(drawingVML)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d != nil {
|
||||||
|
vml.ShapeType.ID = d.ShapeType.ID
|
||||||
|
vml.ShapeType.CoordSize = d.ShapeType.CoordSize
|
||||||
|
vml.ShapeType.Spt = d.ShapeType.Spt
|
||||||
|
vml.ShapeType.PreferRelative = d.ShapeType.PreferRelative
|
||||||
|
vml.ShapeType.Path = d.ShapeType.Path
|
||||||
|
vml.ShapeType.Filled = d.ShapeType.Filled
|
||||||
|
vml.ShapeType.Stroked = d.ShapeType.Stroked
|
||||||
|
for _, v := range d.Shape {
|
||||||
|
s := xlsxShape{
|
||||||
|
ID: v.ID,
|
||||||
|
SpID: v.SpID,
|
||||||
|
Type: v.Type,
|
||||||
|
Style: v.Style,
|
||||||
|
Val: v.Val,
|
||||||
|
}
|
||||||
|
vml.Shape = append(vml.Shape, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, shape := range vml.Shape {
|
||||||
|
if shape.ID == shapeID {
|
||||||
|
vml.Shape = append(vml.Shape[:idx], vml.Shape[idx+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
style := fmt.Sprintf("position:absolute;margin-left:0;margin-top:0;width:%s;height:%s;z-index:1", opts.Width, opts.Height)
|
||||||
|
drawingVMLRels := "xl/drawings/_rels/vmlDrawing" + strconv.Itoa(vmlID) + ".vml.rels"
|
||||||
|
|
||||||
|
mediaStr := ".." + strings.TrimPrefix(f.addMedia(opts.File, ext), "xl")
|
||||||
|
imageID := f.addRels(drawingVMLRels, SourceRelationshipImage, mediaStr, "")
|
||||||
|
|
||||||
|
shape := xlsxShape{
|
||||||
|
ID: shapeID,
|
||||||
|
SpID: "_x0000_s1025",
|
||||||
|
Type: "#_x0000_t75",
|
||||||
|
Style: style,
|
||||||
|
}
|
||||||
|
sp, _ := xml.Marshal(encodeShape{
|
||||||
|
ImageData: &vImageData{RelID: "rId" + strconv.Itoa(imageID)},
|
||||||
|
Lock: &oLock{Ext: "edit", Rotation: "t"},
|
||||||
|
})
|
||||||
|
|
||||||
|
shape.Val = string(sp[13 : len(sp)-14])
|
||||||
|
vml.Shape = append(vml.Shape, shape)
|
||||||
|
f.VMLDrawing[drawingVML] = vml
|
||||||
|
|
||||||
drawingID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
|
|
||||||
f.addSheetNameSpace(sheet, SourceRelationship)
|
|
||||||
f.addSheetLegacyDrawingHF(sheet, drawingID)
|
|
||||||
if err := f.setContentTypePartImageExtensions(); err != nil {
|
if err := f.setContentTypePartImageExtensions(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ type xlsxIDmap struct {
|
||||||
type xlsxShape struct {
|
type xlsxShape struct {
|
||||||
XMLName xml.Name `xml:"v:shape"`
|
XMLName xml.Name `xml:"v:shape"`
|
||||||
ID string `xml:"id,attr"`
|
ID string `xml:"id,attr"`
|
||||||
Spid string `xml:"o:spid,attr,omitempty"`
|
SpID string `xml:"o:spid,attr,omitempty"`
|
||||||
Type string `xml:"type,attr"`
|
Type string `xml:"type,attr"`
|
||||||
Style string `xml:"style,attr"`
|
Style string `xml:"style,attr"`
|
||||||
Button string `xml:"o:button,attr,omitempty"`
|
Button string `xml:"o:button,attr,omitempty"`
|
||||||
|
@ -193,15 +193,19 @@ type decodeVmlDrawing struct {
|
||||||
// decodeShapeType defines the structure used to parse the shapetype element in
|
// decodeShapeType defines the structure used to parse the shapetype element in
|
||||||
// the file xl/drawings/vmlDrawing%d.vml.
|
// the file xl/drawings/vmlDrawing%d.vml.
|
||||||
type decodeShapeType struct {
|
type decodeShapeType struct {
|
||||||
ID string `xml:"id,attr"`
|
ID string `xml:"id,attr"`
|
||||||
CoordSize string `xml:"coordsize,attr"`
|
CoordSize string `xml:"coordsize,attr"`
|
||||||
Spt int `xml:"spt,attr"`
|
Spt int `xml:"spt,attr"`
|
||||||
Path string `xml:"path,attr"`
|
PreferRelative string `xml:"preferrelative,attr,omitempty"`
|
||||||
|
Path string `xml:"path,attr"`
|
||||||
|
Filled string `xml:"filled,attr,omitempty"`
|
||||||
|
Stroked string `xml:"stroked,attr,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeShape defines the structure used to parse the particular shape element.
|
// decodeShape defines the structure used to parse the particular shape element.
|
||||||
type decodeShape struct {
|
type decodeShape struct {
|
||||||
ID string `xml:"id,attr"`
|
ID string `xml:"id,attr"`
|
||||||
|
SpID string `xml:"spid,attr,omitempty"`
|
||||||
Type string `xml:"type,attr"`
|
Type string `xml:"type,attr"`
|
||||||
Style string `xml:"style,attr"`
|
Style string `xml:"style,attr"`
|
||||||
Button string `xml:"button,attr,omitempty"`
|
Button string `xml:"button,attr,omitempty"`
|
||||||
|
@ -335,10 +339,13 @@ type FormControl struct {
|
||||||
Format GraphicOptions
|
Format GraphicOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeaderFooterGraphics defines the settings for an image to be
|
// HeaderFooterImageOptions defines the settings for an image to be accessible
|
||||||
// accessible from the header/footer options.
|
// from the worksheet header and footer options.
|
||||||
type HeaderFooterGraphics struct {
|
type HeaderFooterImageOptions struct {
|
||||||
|
Position HeaderFooterImagePositionType
|
||||||
File []byte
|
File []byte
|
||||||
|
IsFooter bool
|
||||||
|
FirstPage bool
|
||||||
Extension string
|
Extension string
|
||||||
Width string
|
Width string
|
||||||
Height string
|
Height string
|
||||||
|
|
89
vml_test.go
89
vml_test.go
|
@ -413,32 +413,97 @@ func TestExtractFormControl(t *testing.T) {
|
||||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetLegacyDrawingHF(t *testing.T) {
|
func TestAddHeaderFooterImage(t *testing.T) {
|
||||||
f := NewFile()
|
f, sheet, wb := NewFile(), "Sheet1", filepath.Join("test", "TestAddHeaderFooterImage.xlsx")
|
||||||
sheet := "Sheet1"
|
|
||||||
headerFooterOptions := HeaderFooterOptions{
|
headerFooterOptions := HeaderFooterOptions{
|
||||||
OddHeader: "&LExcelize&R&G",
|
DifferentFirst: true,
|
||||||
|
OddHeader: "&L&GExcelize&C&G&R&G",
|
||||||
|
OddFooter: "&L&GExcelize&C&G&R&G",
|
||||||
|
FirstHeader: "&L&GExcelize&C&G&R&G",
|
||||||
|
FirstFooter: "&L&GExcelize&C&G&R&G",
|
||||||
}
|
}
|
||||||
assert.NoError(t, f.SetHeaderFooter(sheet, &headerFooterOptions))
|
assert.NoError(t, f.SetHeaderFooter(sheet, &headerFooterOptions))
|
||||||
file, err := os.ReadFile(filepath.Join("test", "images", "excel.png"))
|
assert.NoError(t, f.SetSheetView(sheet, -1, &ViewOptions{View: stringPtr("pageLayout")}))
|
||||||
|
images := map[string][]byte{
|
||||||
|
".wmf": nil, ".tif": nil, ".png": nil,
|
||||||
|
".jpg": nil, ".gif": nil, ".emz": nil, ".emf": nil,
|
||||||
|
}
|
||||||
|
for ext := range images {
|
||||||
|
img, err := os.ReadFile(filepath.Join("test", "images", "excel"+ext))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
images[ext] = img
|
||||||
|
}
|
||||||
|
for _, opt := range []struct {
|
||||||
|
position HeaderFooterImagePositionType
|
||||||
|
file []byte
|
||||||
|
isFooter bool
|
||||||
|
firstPage bool
|
||||||
|
ext string
|
||||||
|
}{
|
||||||
|
{position: HeaderFooterImagePositionLeft, file: images[".tif"], firstPage: true, ext: ".tif"},
|
||||||
|
{position: HeaderFooterImagePositionCenter, file: images[".gif"], firstPage: true, ext: ".gif"},
|
||||||
|
{position: HeaderFooterImagePositionRight, file: images[".png"], firstPage: true, ext: ".png"},
|
||||||
|
{position: HeaderFooterImagePositionLeft, file: images[".emf"], isFooter: true, firstPage: true, ext: ".emf"},
|
||||||
|
{position: HeaderFooterImagePositionCenter, file: images[".wmf"], isFooter: true, firstPage: true, ext: ".wmf"},
|
||||||
|
{position: HeaderFooterImagePositionRight, file: images[".emz"], isFooter: true, firstPage: true, ext: ".emz"},
|
||||||
|
{position: HeaderFooterImagePositionLeft, file: images[".png"], ext: ".png"},
|
||||||
|
{position: HeaderFooterImagePositionCenter, file: images[".png"], ext: ".png"},
|
||||||
|
{position: HeaderFooterImagePositionRight, file: images[".png"], ext: ".png"},
|
||||||
|
{position: HeaderFooterImagePositionLeft, file: images[".tif"], isFooter: true, ext: ".tif"},
|
||||||
|
{position: HeaderFooterImagePositionCenter, file: images[".tif"], isFooter: true, ext: ".tif"},
|
||||||
|
{position: HeaderFooterImagePositionRight, file: images[".tif"], isFooter: true, ext: ".tif"},
|
||||||
|
} {
|
||||||
|
assert.NoError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
|
||||||
|
Position: opt.position,
|
||||||
|
File: opt.file,
|
||||||
|
IsFooter: opt.isFooter,
|
||||||
|
FirstPage: opt.firstPage,
|
||||||
|
Extension: opt.ext,
|
||||||
|
Width: "50pt",
|
||||||
|
Height: "32pt",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
assert.NoError(t, f.SetCellValue(sheet, "A1", "Example"))
|
||||||
|
|
||||||
|
// Test add header footer image with not exist sheet
|
||||||
|
assert.EqualError(t, f.AddHeaderFooterImage("SheetN", nil), "sheet SheetN does not exist")
|
||||||
|
// Test add header footer image with unsupported file type
|
||||||
|
assert.Equal(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
|
||||||
|
Extension: "jpg",
|
||||||
|
}), ErrImgExt)
|
||||||
|
assert.NoError(t, f.SaveAs(wb))
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
// Test change already exist header image with the different image
|
||||||
|
f, err := OpenFile(wb)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetLegacyDrawingHF(sheet, &HeaderFooterGraphics{
|
assert.NoError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
|
||||||
Extension: ".png",
|
File: images[".jpg"],
|
||||||
File: file,
|
FirstPage: true,
|
||||||
|
Extension: ".jpg",
|
||||||
Width: "50pt",
|
Width: "50pt",
|
||||||
Height: "32pt",
|
Height: "32pt",
|
||||||
}))
|
}))
|
||||||
assert.NoError(t, f.SetCellValue(sheet, "A1", "Example"))
|
assert.NoError(t, f.Save())
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetLegacyDrawingHF.xlsx")))
|
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test add header image with unsupported charset VML drawing
|
||||||
|
f, err = OpenFile(wb)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
f.Pkg.Store("xl/drawings/vmlDrawing1.vml", MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
|
||||||
|
File: images[".jpg"],
|
||||||
|
Extension: ".jpg",
|
||||||
|
Width: "50pt",
|
||||||
|
Height: "32pt",
|
||||||
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
// Test set legacy drawing header/footer with unsupported charset content types
|
// Test set legacy drawing header/footer with unsupported charset content types
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.SetLegacyDrawingHF(sheet, &HeaderFooterGraphics{
|
assert.EqualError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
|
||||||
Extension: ".png",
|
Extension: ".png",
|
||||||
File: file,
|
File: images[".png"],
|
||||||
Width: "50pt",
|
Width: "50pt",
|
||||||
Height: "32pt",
|
Height: "32pt",
|
||||||
}), "XML syntax error on line 1: invalid UTF-8")
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
Loading…
Reference in New Issue