This closes #1395, add new function SetLegacyDrawingHF support to set graphics in a header/footer (#2018)
This commit is contained in:
parent
0d5d1c53b2
commit
b7375bc6d4
10
picture.go
10
picture.go
|
@ -295,6 +295,16 @@ func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
|||
}
|
||||
}
|
||||
|
||||
// addSheetLegacyDrawingHF provides a function to add legacy drawing
|
||||
// header/footer element to xl/worksheets/sheet%d.xml by given
|
||||
// worksheet name and relationship index.
|
||||
func (f *File) addSheetLegacyDrawingHF(sheet string, rID int) {
|
||||
ws, _ := f.workSheetReader(sheet)
|
||||
ws.LegacyDrawingHF = &xlsxLegacyDrawingHF{
|
||||
RID: "rId" + strconv.Itoa(rID),
|
||||
}
|
||||
}
|
||||
|
||||
// addSheetDrawing provides a function to add drawing element to
|
||||
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||
func (f *File) addSheetDrawing(sheet string, rID int) {
|
||||
|
|
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
|
||||
// |
|
||||
// &G | Drawing object as background (Not support currently)
|
||||
// &G | Drawing object as background (Use SetLegacyDrawingHF)
|
||||
// |
|
||||
// &H | Shadow text format
|
||||
// |
|
||||
|
|
79
vml.go
79
vml.go
|
@ -1070,3 +1070,82 @@ func extractVMLFont(font []decodeVMLFont) []RichTextRun {
|
|||
}
|
||||
return runs
|
||||
}
|
||||
|
||||
// SetLegacyDrawingHF provides a mechanism to set the graphics that
|
||||
// can be referenced in the Header/Footer defitions via &G.
|
||||
//
|
||||
// The extension should be provided with a "." in front, e.g. ".png".
|
||||
// The width/height should have units in them, e.g. "100pt".
|
||||
func (f *File) SetLegacyDrawingHF(sheet string, g *HeaderFooterGraphics) error {
|
||||
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"
|
||||
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"
|
||||
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
|
||||
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
|
||||
|
||||
drawingID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
|
||||
f.addSheetNameSpace(sheet, SourceRelationship)
|
||||
f.addSheetLegacyDrawingHF(sheet, drawingID)
|
||||
if err := f.setContentTypePartImageExtensions(); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.setContentTypePartVMLExtensions()
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ type vmlDrawing struct {
|
|||
XMLNSv string `xml:"xmlns:v,attr"`
|
||||
XMLNSo string `xml:"xmlns:o,attr"`
|
||||
XMLNSx string `xml:"xmlns:x,attr"`
|
||||
XMLNSmv string `xml:"xmlns:mv,attr"`
|
||||
XMLNSmv string `xml:"xmlns:mv,attr,omitempty"`
|
||||
ShapeLayout *xlsxShapeLayout `xml:"o:shapelayout"`
|
||||
ShapeType *xlsxShapeType `xml:"v:shapetype"`
|
||||
Shape []xlsxShape `xml:"v:shape"`
|
||||
|
@ -44,6 +44,7 @@ type xlsxIDmap struct {
|
|||
type xlsxShape struct {
|
||||
XMLName xml.Name `xml:"v:shape"`
|
||||
ID string `xml:"id,attr"`
|
||||
Spid string `xml:"o:spid,attr,omitempty"`
|
||||
Type string `xml:"type,attr"`
|
||||
Style string `xml:"style,attr"`
|
||||
Button string `xml:"o:button,attr,omitempty"`
|
||||
|
@ -57,12 +58,17 @@ type xlsxShape struct {
|
|||
|
||||
// xlsxShapeType directly maps the shapetype element.
|
||||
type xlsxShapeType struct {
|
||||
ID string `xml:"id,attr"`
|
||||
CoordSize string `xml:"coordsize,attr"`
|
||||
Spt int `xml:"o:spt,attr"`
|
||||
Path string `xml:"path,attr"`
|
||||
Stroke *xlsxStroke `xml:"v:stroke"`
|
||||
VPath *vPath `xml:"v:path"`
|
||||
ID string `xml:"id,attr"`
|
||||
CoordSize string `xml:"coordsize,attr"`
|
||||
Spt int `xml:"o:spt,attr"`
|
||||
PreferRelative string `xml:"o:preferrelative,attr,omitempty"`
|
||||
Path string `xml:"path,attr"`
|
||||
Filled string `xml:"filled,attr,omitempty"`
|
||||
Stroked string `xml:"stroked,attr,omitempty"`
|
||||
Stroke *xlsxStroke `xml:"v:stroke"`
|
||||
VFormulas *vFormulas `xml:"v:formulas"`
|
||||
VPath *vPath `xml:"v:path"`
|
||||
Lock *oLock `xml:"o:lock"`
|
||||
}
|
||||
|
||||
// xlsxStroke directly maps the stroke element.
|
||||
|
@ -72,10 +78,28 @@ type xlsxStroke struct {
|
|||
|
||||
// vPath directly maps the v:path element.
|
||||
type vPath struct {
|
||||
ExtrusionOK string `xml:"o:extrusionok,attr,omitempty"`
|
||||
GradientShapeOK string `xml:"gradientshapeok,attr,omitempty"`
|
||||
ConnectType string `xml:"o:connecttype,attr"`
|
||||
}
|
||||
|
||||
// oLock directly maps the o:lock element.
|
||||
type oLock struct {
|
||||
Ext string `xml:"v:ext,attr"`
|
||||
Rotation string `xml:"rotation,attr,omitempty"`
|
||||
AspectRatio string `xml:"aspectratio,attr,omitempty"`
|
||||
}
|
||||
|
||||
// vFormulas directly maps to the v:formulas element
|
||||
type vFormulas struct {
|
||||
Formulas []vFormula `xml:"v:f"`
|
||||
}
|
||||
|
||||
// vFormula directly maps to the v:f element
|
||||
type vFormula struct {
|
||||
Equation string `xml:"eqn,attr"`
|
||||
}
|
||||
|
||||
// vFill directly maps the v:fill element. This element must be defined within a
|
||||
// Shape element.
|
||||
type vFill struct {
|
||||
|
@ -106,6 +130,13 @@ type vTextBox struct {
|
|||
Div *xlsxDiv `xml:"div"`
|
||||
}
|
||||
|
||||
// vImageData directly maps the v:imagedata element. This element must be
|
||||
// defined within a Shape element.
|
||||
type vImageData struct {
|
||||
RelID string `xml:"o:relid,attr"`
|
||||
Title string `xml:"o:title,attr,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxDiv directly maps the div element.
|
||||
type xlsxDiv struct {
|
||||
Style string `xml:"style,attr"`
|
||||
|
@ -254,7 +285,9 @@ type encodeShape struct {
|
|||
Shadow *vShadow `xml:"v:shadow"`
|
||||
Path *vPath `xml:"v:path"`
|
||||
TextBox *vTextBox `xml:"v:textbox"`
|
||||
ImageData *vImageData `xml:"v:imagedata"`
|
||||
ClientData *xClientData `xml:"x:ClientData"`
|
||||
Lock *oLock `xml:"o:lock"`
|
||||
}
|
||||
|
||||
// formCtrlPreset defines the structure used to form control presets.
|
||||
|
@ -301,3 +334,12 @@ type FormControl struct {
|
|||
Type FormControlType
|
||||
Format GraphicOptions
|
||||
}
|
||||
|
||||
// HeaderFooterGraphics defines the settings for an image to be
|
||||
// accessible from the header/footer options.
|
||||
type HeaderFooterGraphics struct {
|
||||
File []byte
|
||||
Extension string
|
||||
Width string
|
||||
Height string
|
||||
}
|
||||
|
|
32
vml_test.go
32
vml_test.go
|
@ -412,3 +412,35 @@ func TestExtractFormControl(t *testing.T) {
|
|||
_, err := extractFormControl(string(MacintoshCyrillicCharset))
|
||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
func TestSetLegacyDrawingHF(t *testing.T) {
|
||||
f := NewFile()
|
||||
sheet := "Sheet1"
|
||||
headerFooterOptions := HeaderFooterOptions{
|
||||
OddHeader: "&LExcelize&R&G",
|
||||
}
|
||||
assert.NoError(t, f.SetHeaderFooter(sheet, &headerFooterOptions))
|
||||
file, err := os.ReadFile(filepath.Join("test", "images", "excel.png"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.SetLegacyDrawingHF(sheet, &HeaderFooterGraphics{
|
||||
Extension: ".png",
|
||||
File: file,
|
||||
Width: "50pt",
|
||||
Height: "32pt",
|
||||
}))
|
||||
assert.NoError(t, f.SetCellValue(sheet, "A1", "Example"))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetLegacyDrawingHF.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test set legacy drawing header/footer with unsupported charset content types
|
||||
f = NewFile()
|
||||
f.ContentTypes = nil
|
||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.SetLegacyDrawingHF(sheet, &HeaderFooterGraphics{
|
||||
Extension: ".png",
|
||||
File: file,
|
||||
Width: "50pt",
|
||||
Height: "32pt",
|
||||
}), "XML syntax error on line 1: invalid UTF-8")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue