Skip to content

Commit b7375bc

Browse files
authored
This closes #1395, add new function SetLegacyDrawingHF support to set graphics in a header/footer (#2018)
1 parent 0d5d1c5 commit b7375bc

File tree

5 files changed

+171
-8
lines changed

5 files changed

+171
-8
lines changed

picture.go

+10
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,16 @@ func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
295295
}
296296
}
297297

298+
// addSheetLegacyDrawingHF provides a function to add legacy drawing
299+
// header/footer element to xl/worksheets/sheet%d.xml by given
300+
// worksheet name and relationship index.
301+
func (f *File) addSheetLegacyDrawingHF(sheet string, rID int) {
302+
ws, _ := f.workSheetReader(sheet)
303+
ws.LegacyDrawingHF = &xlsxLegacyDrawingHF{
304+
RID: "rId" + strconv.Itoa(rID),
305+
}
306+
}
307+
298308
// addSheetDrawing provides a function to add drawing element to
299309
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
300310
func (f *File) addSheetDrawing(sheet string, rID int) {

sheet.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,7 @@ func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) {
12391239
// |
12401240
// &F | Current workbook's file name
12411241
// |
1242-
// &G | Drawing object as background (Not support currently)
1242+
// &G | Drawing object as background (Use SetLegacyDrawingHF)
12431243
// |
12441244
// &H | Shadow text format
12451245
// |

vml.go

+79
Original file line numberDiff line numberDiff line change
@@ -1070,3 +1070,82 @@ func extractVMLFont(font []decodeVMLFont) []RichTextRun {
10701070
}
10711071
return runs
10721072
}
1073+
1074+
// SetLegacyDrawingHF provides a mechanism to set the graphics that
1075+
// can be referenced in the Header/Footer defitions via &G.
1076+
//
1077+
// The extension should be provided with a "." in front, e.g. ".png".
1078+
// The width/height should have units in them, e.g. "100pt".
1079+
func (f *File) SetLegacyDrawingHF(sheet string, g *HeaderFooterGraphics) error {
1080+
vmlID := f.countVMLDrawing() + 1
1081+
1082+
vml := &vmlDrawing{
1083+
XMLNSv: "urn:schemas-microsoft-com:vml",
1084+
XMLNSo: "urn:schemas-microsoft-com:office:office",
1085+
XMLNSx: "urn:schemas-microsoft-com:office:excel",
1086+
ShapeLayout: &xlsxShapeLayout{
1087+
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: vmlID},
1088+
},
1089+
ShapeType: &xlsxShapeType{
1090+
ID: "_x0000_t75",
1091+
CoordSize: "21600,21600",
1092+
Spt: 75,
1093+
PreferRelative: "t",
1094+
Path: "m@4@5l@4@11@9@11@9@5xe",
1095+
Filled: "f",
1096+
Stroked: "f",
1097+
Stroke: &xlsxStroke{JoinStyle: "miter"},
1098+
VFormulas: &vFormulas{
1099+
Formulas: []vFormula{
1100+
{Equation: "if lineDrawn pixelLineWidth 0"},
1101+
{Equation: "sum @0 1 0"},
1102+
{Equation: "sum 0 0 @1"},
1103+
{Equation: "prod @2 1 2"},
1104+
{Equation: "prod @3 21600 pixelWidth"},
1105+
{Equation: "prod @3 21600 pixelHeight"},
1106+
{Equation: "sum @0 0 1"},
1107+
{Equation: "prod @6 1 2"},
1108+
{Equation: "prod @7 21600 pixelWidth"},
1109+
{Equation: "sum @8 21600 0"},
1110+
{Equation: "prod @7 21600 pixelHeight"},
1111+
{Equation: "sum @10 21600 0"},
1112+
},
1113+
},
1114+
VPath: &vPath{ExtrusionOK: "f", GradientShapeOK: "t", ConnectType: "rect"},
1115+
Lock: &oLock{Ext: "edit", AspectRatio: "t"},
1116+
},
1117+
}
1118+
1119+
style := fmt.Sprintf("position:absolute;margin-left:0;margin-top:0;width:%s;height:%s;z-index:1", g.Width, g.Height)
1120+
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
1121+
drawingVMLRels := "xl/drawings/_rels/vmlDrawing" + strconv.Itoa(vmlID) + ".vml.rels"
1122+
1123+
mediaStr := ".." + strings.TrimPrefix(f.addMedia(g.File, g.Extension), "xl")
1124+
imageID := f.addRels(drawingVMLRels, SourceRelationshipImage, mediaStr, "")
1125+
1126+
shape := xlsxShape{
1127+
ID: "RH",
1128+
Spid: "_x0000_s1025",
1129+
Type: "#_x0000_t75",
1130+
Style: style,
1131+
}
1132+
s, _ := xml.Marshal(encodeShape{
1133+
ImageData: &vImageData{RelID: "rId" + strconv.Itoa(imageID)},
1134+
Lock: &oLock{Ext: "edit", Rotation: "t"},
1135+
})
1136+
shape.Val = string(s[13 : len(s)-14])
1137+
vml.Shape = append(vml.Shape, shape)
1138+
f.VMLDrawing[drawingVML] = vml
1139+
1140+
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
1141+
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
1142+
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
1143+
1144+
drawingID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
1145+
f.addSheetNameSpace(sheet, SourceRelationship)
1146+
f.addSheetLegacyDrawingHF(sheet, drawingID)
1147+
if err := f.setContentTypePartImageExtensions(); err != nil {
1148+
return err
1149+
}
1150+
return f.setContentTypePartVMLExtensions()
1151+
}

vmlDrawing.go

+49-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type vmlDrawing struct {
2020
XMLNSv string `xml:"xmlns:v,attr"`
2121
XMLNSo string `xml:"xmlns:o,attr"`
2222
XMLNSx string `xml:"xmlns:x,attr"`
23-
XMLNSmv string `xml:"xmlns:mv,attr"`
23+
XMLNSmv string `xml:"xmlns:mv,attr,omitempty"`
2424
ShapeLayout *xlsxShapeLayout `xml:"o:shapelayout"`
2525
ShapeType *xlsxShapeType `xml:"v:shapetype"`
2626
Shape []xlsxShape `xml:"v:shape"`
@@ -44,6 +44,7 @@ type xlsxIDmap struct {
4444
type xlsxShape struct {
4545
XMLName xml.Name `xml:"v:shape"`
4646
ID string `xml:"id,attr"`
47+
Spid string `xml:"o:spid,attr,omitempty"`
4748
Type string `xml:"type,attr"`
4849
Style string `xml:"style,attr"`
4950
Button string `xml:"o:button,attr,omitempty"`
@@ -57,12 +58,17 @@ type xlsxShape struct {
5758

5859
// xlsxShapeType directly maps the shapetype element.
5960
type xlsxShapeType struct {
60-
ID string `xml:"id,attr"`
61-
CoordSize string `xml:"coordsize,attr"`
62-
Spt int `xml:"o:spt,attr"`
63-
Path string `xml:"path,attr"`
64-
Stroke *xlsxStroke `xml:"v:stroke"`
65-
VPath *vPath `xml:"v:path"`
61+
ID string `xml:"id,attr"`
62+
CoordSize string `xml:"coordsize,attr"`
63+
Spt int `xml:"o:spt,attr"`
64+
PreferRelative string `xml:"o:preferrelative,attr,omitempty"`
65+
Path string `xml:"path,attr"`
66+
Filled string `xml:"filled,attr,omitempty"`
67+
Stroked string `xml:"stroked,attr,omitempty"`
68+
Stroke *xlsxStroke `xml:"v:stroke"`
69+
VFormulas *vFormulas `xml:"v:formulas"`
70+
VPath *vPath `xml:"v:path"`
71+
Lock *oLock `xml:"o:lock"`
6672
}
6773

6874
// xlsxStroke directly maps the stroke element.
@@ -72,10 +78,28 @@ type xlsxStroke struct {
7278

7379
// vPath directly maps the v:path element.
7480
type vPath struct {
81+
ExtrusionOK string `xml:"o:extrusionok,attr,omitempty"`
7582
GradientShapeOK string `xml:"gradientshapeok,attr,omitempty"`
7683
ConnectType string `xml:"o:connecttype,attr"`
7784
}
7885

86+
// oLock directly maps the o:lock element.
87+
type oLock struct {
88+
Ext string `xml:"v:ext,attr"`
89+
Rotation string `xml:"rotation,attr,omitempty"`
90+
AspectRatio string `xml:"aspectratio,attr,omitempty"`
91+
}
92+
93+
// vFormulas directly maps to the v:formulas element
94+
type vFormulas struct {
95+
Formulas []vFormula `xml:"v:f"`
96+
}
97+
98+
// vFormula directly maps to the v:f element
99+
type vFormula struct {
100+
Equation string `xml:"eqn,attr"`
101+
}
102+
79103
// vFill directly maps the v:fill element. This element must be defined within a
80104
// Shape element.
81105
type vFill struct {
@@ -106,6 +130,13 @@ type vTextBox struct {
106130
Div *xlsxDiv `xml:"div"`
107131
}
108132

133+
// vImageData directly maps the v:imagedata element. This element must be
134+
// defined within a Shape element.
135+
type vImageData struct {
136+
RelID string `xml:"o:relid,attr"`
137+
Title string `xml:"o:title,attr,omitempty"`
138+
}
139+
109140
// xlsxDiv directly maps the div element.
110141
type xlsxDiv struct {
111142
Style string `xml:"style,attr"`
@@ -254,7 +285,9 @@ type encodeShape struct {
254285
Shadow *vShadow `xml:"v:shadow"`
255286
Path *vPath `xml:"v:path"`
256287
TextBox *vTextBox `xml:"v:textbox"`
288+
ImageData *vImageData `xml:"v:imagedata"`
257289
ClientData *xClientData `xml:"x:ClientData"`
290+
Lock *oLock `xml:"o:lock"`
258291
}
259292

260293
// formCtrlPreset defines the structure used to form control presets.
@@ -301,3 +334,12 @@ type FormControl struct {
301334
Type FormControlType
302335
Format GraphicOptions
303336
}
337+
338+
// HeaderFooterGraphics defines the settings for an image to be
339+
// accessible from the header/footer options.
340+
type HeaderFooterGraphics struct {
341+
File []byte
342+
Extension string
343+
Width string
344+
Height string
345+
}

vml_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -412,3 +412,35 @@ func TestExtractFormControl(t *testing.T) {
412412
_, err := extractFormControl(string(MacintoshCyrillicCharset))
413413
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
414414
}
415+
416+
func TestSetLegacyDrawingHF(t *testing.T) {
417+
f := NewFile()
418+
sheet := "Sheet1"
419+
headerFooterOptions := HeaderFooterOptions{
420+
OddHeader: "&LExcelize&R&G",
421+
}
422+
assert.NoError(t, f.SetHeaderFooter(sheet, &headerFooterOptions))
423+
file, err := os.ReadFile(filepath.Join("test", "images", "excel.png"))
424+
assert.NoError(t, err)
425+
assert.NoError(t, f.SetLegacyDrawingHF(sheet, &HeaderFooterGraphics{
426+
Extension: ".png",
427+
File: file,
428+
Width: "50pt",
429+
Height: "32pt",
430+
}))
431+
assert.NoError(t, f.SetCellValue(sheet, "A1", "Example"))
432+
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetLegacyDrawingHF.xlsx")))
433+
assert.NoError(t, f.Close())
434+
435+
// Test set legacy drawing header/footer with unsupported charset content types
436+
f = NewFile()
437+
f.ContentTypes = nil
438+
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
439+
assert.EqualError(t, f.SetLegacyDrawingHF(sheet, &HeaderFooterGraphics{
440+
Extension: ".png",
441+
File: file,
442+
Width: "50pt",
443+
Height: "32pt",
444+
}), "XML syntax error on line 1: invalid UTF-8")
445+
assert.NoError(t, f.Close())
446+
}

0 commit comments

Comments
 (0)