Skip to content

Commit 47b5c30

Browse files
committed
Support set one cell anchor positioning type for shape and slicer
- Update unit tests - Simplify code with check graphic options in one utility function - Support get one cell anchor positioning slicers
1 parent 16306a8 commit 47b5c30

File tree

8 files changed

+226
-142
lines changed

8 files changed

+226
-142
lines changed

chart.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -583,18 +583,6 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
583583
if opts.Dimension.Height == 0 {
584584
opts.Dimension.Height = defaultChartDimensionHeight
585585
}
586-
if opts.Format.PrintObject == nil {
587-
opts.Format.PrintObject = boolPtr(true)
588-
}
589-
if opts.Format.Locked == nil {
590-
opts.Format.Locked = boolPtr(false)
591-
}
592-
if opts.Format.ScaleX == 0 {
593-
opts.Format.ScaleX = defaultDrawingScale
594-
}
595-
if opts.Format.ScaleY == 0 {
596-
opts.Format.ScaleY = defaultDrawingScale
597-
}
598586
if opts.Legend.Position == "" {
599587
opts.Legend.Position = defaultChartLegendPosition
600588
}
@@ -611,6 +599,12 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
611599
if opts.ShowBlanksAs == "" {
612600
opts.ShowBlanksAs = defaultChartShowBlanksAs
613601
}
602+
format := opts.Format
603+
graphicOptions, err := format.parseGraphicOptions(nil)
604+
if err != nil {
605+
return opts, err
606+
}
607+
opts.Format = *graphicOptions
614608
return opts, opts.parseSeries()
615609
}
616610

chart_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ func TestAddChart(t *testing.T) {
151151

152152
// Test add chart on not exists worksheet
153153
assert.EqualError(t, f.AddChart("SheetN", "P1", nil), "sheet SheetN does not exist")
154+
// Test add chart with invalid positioning types
155+
assert.Equal(t, f.AddChart("Sheet1", "P1", &Chart{
156+
Format: GraphicOptions{Positioning: "x"},
157+
}), newInvalidOptionalValue("Positioning", "x", supportedPositioning))
154158
maximum, minimum, zero := 7.5, 0.5, .0
155159
series := []ChartSeries{
156160
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"},

picture.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,9 @@ const (
3838

3939
// parseGraphicOptions provides a function to parse the format settings of
4040
// the picture with default value.
41-
func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
41+
func (opts *GraphicOptions) parseGraphicOptions(defaults *GraphicOptions) (*GraphicOptions, error) {
4242
if opts == nil {
43-
return &GraphicOptions{
44-
PrintObject: boolPtr(true),
45-
Locked: boolPtr(true),
46-
ScaleX: defaultDrawingScale,
47-
ScaleY: defaultDrawingScale,
48-
}
43+
return defaults, nil
4944
}
5045
if opts.PrintObject == nil {
5146
opts.PrintObject = boolPtr(true)
@@ -59,7 +54,24 @@ func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
5954
if opts.ScaleY == 0 {
6055
opts.ScaleY = defaultDrawingScale
6156
}
62-
return opts
57+
if opts.Positioning != "" && inStrSlice(supportedPositioning, opts.Positioning, true) == -1 {
58+
return defaults, newInvalidOptionalValue("Positioning", opts.Positioning, supportedPositioning)
59+
}
60+
return opts, nil
61+
}
62+
63+
// parsePictureOptions provides a function to parse the picture options with
64+
// default value.
65+
func parsePictureOptions(pic *Picture) (*GraphicOptions, error) {
66+
if pic.InsertType != PictureInsertTypePlaceOverCells {
67+
return nil, ErrParameterInvalid
68+
}
69+
return pic.Format.parseGraphicOptions(&GraphicOptions{
70+
PrintObject: boolPtr(true),
71+
Locked: boolPtr(true),
72+
ScaleX: defaultDrawingScale,
73+
ScaleY: defaultDrawingScale,
74+
})
6375
}
6476

6577
// AddPicture provides the method to add picture in a sheet by given picture
@@ -237,10 +249,10 @@ func (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error {
237249
if !ok {
238250
return ErrImgExt
239251
}
240-
if pic.InsertType != PictureInsertTypePlaceOverCells {
241-
return ErrParameterInvalid
252+
options, err := parsePictureOptions(pic)
253+
if err != nil {
254+
return err
242255
}
243-
options := parseGraphicOptions(pic.Format)
244256
img, _, err := image.DecodeConfig(bytes.NewReader(pic.File))
245257
if err != nil {
246258
return err

picture_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,8 @@ func TestAddPictureFromBytes(t *testing.T) {
369369
imgFile, err := os.ReadFile("logo.png")
370370
assert.NoError(t, err, "Unable to load logo for test")
371371

372-
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
373-
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
372+
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "A1", &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
373+
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "A50", &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
374374
imageCount := 0
375375
f.Pkg.Range(func(fileName, v interface{}) bool {
376376
if strings.Contains(fileName.(string), "media/image") {
@@ -379,9 +379,11 @@ func TestAddPictureFromBytes(t *testing.T) {
379379
return true
380380
})
381381
assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.")
382-
assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}), "sheet SheetN does not exist")
382+
assert.EqualError(t, f.AddPictureFromBytes("SheetN", "A1", &Picture{Extension: ".png", File: imgFile}), "sheet SheetN does not exist")
383383
// Test add picture from bytes with invalid sheet name
384-
assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", fmt.Sprint("A", 1), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}), ErrSheetNameInvalid.Error())
384+
assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", "A1", &Picture{Extension: ".png", File: imgFile}), ErrSheetNameInvalid.Error())
385+
// Test add picture from bytes with invalid positioning types
386+
assert.Equal(t, f.AddPictureFromBytes("Sheet1", "A1", &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{Positioning: "x"}}), newInvalidOptionalValue("Positioning", "x", supportedPositioning))
385387
}
386388

387389
func TestDeletePicture(t *testing.T) {

shape.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,19 @@ func parseShapeOptions(opts *Shape) (*Shape, error) {
3131
if opts.Height == 0 {
3232
opts.Height = defaultShapeSize
3333
}
34-
if opts.Format.PrintObject == nil {
35-
opts.Format.PrintObject = boolPtr(true)
36-
}
37-
if opts.Format.Locked == nil {
38-
opts.Format.Locked = boolPtr(false)
39-
}
40-
if opts.Format.ScaleX == 0 {
41-
opts.Format.ScaleX = defaultDrawingScale
42-
}
43-
if opts.Format.ScaleY == 0 {
44-
opts.Format.ScaleY = defaultDrawingScale
45-
}
4634
if opts.Line.Width == nil {
4735
opts.Line.Width = float64Ptr(defaultShapeLineWidth)
4836
}
4937
if opts.Fill.Transparency < 0 || 100 < opts.Fill.Transparency {
5038
return opts, ErrTransparency
5139
}
52-
return opts, nil
40+
format := opts.Format
41+
graphicOptions, err := format.parseGraphicOptions(nil)
42+
if err != nil {
43+
return opts, err
44+
}
45+
opts.Format = *graphicOptions
46+
return opts, err
5347
}
5448

5549
// AddShape provides the method to add shape in a sheet by given worksheet
@@ -325,9 +319,9 @@ func (f *File) AddShape(sheet string, opts *Shape) error {
325319
return f.addContentTypePart(drawingID, "drawings")
326320
}
327321

328-
// twoCellAnchorShape create a two cell anchor shape size placeholder for a
322+
// cellAnchorShape create a two cell anchor shape size placeholder for a
329323
// group, a shape, or a drawing element.
330-
func (f *File) twoCellAnchorShape(sheet, drawingXML, cell string, width, height uint, format GraphicOptions) (*xlsxWsDr, *xdrCellAnchor, int, error) {
324+
func (f *File) cellAnchorShape(sheet, drawingXML, cell string, width, height uint, format GraphicOptions) (*xlsxWsDr, *xdrCellAnchor, int, error) {
331325
fromCol, fromRow, err := CellNameToCoordinates(cell)
332326
if err != nil {
333327
return nil, nil, 0, err
@@ -339,27 +333,36 @@ func (f *File) twoCellAnchorShape(sheet, drawingXML, cell string, width, height
339333
if err != nil {
340334
return content, nil, cNvPrID, err
341335
}
342-
twoCellAnchor := xdrCellAnchor{}
343-
twoCellAnchor.EditAs = format.Positioning
336+
cellAnchor := xdrCellAnchor{}
337+
cellAnchor.EditAs = format.Positioning
344338
from := xlsxFrom{}
345339
from.Col = colStart
346340
from.ColOff = x1 * EMU
347341
from.Row = rowStart
348342
from.RowOff = y1 * EMU
349-
to := xlsxTo{}
350-
to.Col = colEnd
351-
to.ColOff = x2 * EMU
352-
to.Row = rowEnd
353-
to.RowOff = y2 * EMU
354-
twoCellAnchor.From = &from
355-
twoCellAnchor.To = &to
356-
return content, &twoCellAnchor, cNvPrID, err
343+
cellAnchor.From = &from
344+
if format.Positioning != "oneCell" {
345+
to := xlsxTo{}
346+
to.Col = colEnd
347+
to.ColOff = x2 * EMU
348+
to.Row = rowEnd
349+
to.RowOff = y2 * EMU
350+
cellAnchor.To = &to
351+
cellAnchor.EditAs = format.Positioning
352+
}
353+
if format.Positioning == "oneCell" {
354+
cellAnchor.Ext = &xlsxPositiveSize2D{
355+
Cx: x2 * EMU,
356+
Cy: y2 * EMU,
357+
}
358+
}
359+
return content, &cellAnchor, cNvPrID, err
357360
}
358361

359362
// addDrawingShape provides a function to add preset geometry by given sheet,
360363
// drawingXML and format sets.
361364
func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) error {
362-
content, twoCellAnchor, cNvPrID, err := f.twoCellAnchorShape(
365+
content, cellAnchor, cNvPrID, err := f.cellAnchorShape(
363366
sheet, drawingXML, cell, opts.Width, opts.Height, opts.Format)
364367
if err != nil {
365368
return err
@@ -380,6 +383,12 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
380383
},
381384
},
382385
SpPr: &xlsxSpPr{
386+
Xfrm: xlsxXfrm{
387+
Ext: xlsxPositiveSize2D{
388+
Cx: int(opts.Width) * EMU,
389+
Cy: int(opts.Height) * EMU,
390+
},
391+
},
383392
PrstGeom: xlsxPrstGeom{
384393
Prst: opts.Type,
385394
},
@@ -476,12 +485,16 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
476485
}
477486
shape.TxBody.P = append(shape.TxBody.P, paragraph)
478487
}
479-
twoCellAnchor.Sp = &shape
480-
twoCellAnchor.ClientData = &xdrClientData{
488+
cellAnchor.Sp = &shape
489+
cellAnchor.ClientData = &xdrClientData{
481490
FLocksWithSheet: *opts.Format.Locked,
482491
FPrintsWithSheet: *opts.Format.PrintObject,
483492
}
484-
content.TwoCellAnchor = append(content.TwoCellAnchor, twoCellAnchor)
493+
if opts.Format.Positioning == "oneCell" {
494+
content.OneCellAnchor = append(content.OneCellAnchor, cellAnchor)
495+
} else {
496+
content.TwoCellAnchor = append(content.TwoCellAnchor, cellAnchor)
497+
}
485498
f.Drawings.Store(drawingXML, content)
486499
return err
487500
}

shape_test.go

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,11 @@ func TestAddShape(t *testing.T) {
2121
},
2222
}))
2323
assert.NoError(t, f.AddShape("Sheet1", &Shape{Cell: "B30", Type: "rect", Paragraph: []RichTextRun{{Text: "Rectangle"}, {}}}))
24-
assert.NoError(t, f.AddShape("Sheet1", &Shape{Cell: "C30", Type: "rect"}))
25-
assert.EqualError(t, f.AddShape("Sheet3",
26-
&Shape{
27-
Cell: "H1",
28-
Type: "ellipseRibbon",
29-
Line: ShapeLine{Color: "4286F4"},
30-
Fill: Fill{Color: []string{"8EB9FF"}},
31-
Paragraph: []RichTextRun{
32-
{
33-
Font: &Font{
34-
Bold: true,
35-
Italic: true,
36-
Family: "Times New Roman",
37-
Size: 36,
38-
Color: "777777",
39-
Underline: "single",
40-
},
41-
},
42-
},
43-
},
44-
), "sheet Sheet3 does not exist")
24+
shape1 := Shape{Cell: "C30", Type: "rect", Width: 160, Height: 160}
25+
assert.NoError(t, f.AddShape("Sheet1", &shape1))
26+
// Test add shape with invalid positioning types
27+
assert.Equal(t, newInvalidOptionalValue("Positioning", "x", supportedPositioning), f.AddShape("Sheet1", &Shape{Cell: "C30", Type: "rect", Format: GraphicOptions{Positioning: "x"}}))
28+
assert.EqualError(t, f.AddShape("Sheet3", &Shape{Cell: "C30", Type: "rect"}), "sheet Sheet3 does not exist")
4529
assert.Equal(t, ErrParameterInvalid, f.AddShape("Sheet3", nil))
4630
assert.Equal(t, ErrParameterInvalid, f.AddShape("Sheet1", &Shape{Cell: "A1"}))
4731
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.AddShape("Sheet1", &Shape{
@@ -57,26 +41,34 @@ func TestAddShape(t *testing.T) {
5741
// Test add first shape for given sheet
5842
f = NewFile()
5943
lineWidth := 1.2
60-
assert.NoError(t, f.AddShape("Sheet1",
61-
&Shape{
62-
Cell: "A1",
63-
Type: "ellipseRibbon",
64-
Line: ShapeLine{Color: "4286F4", Width: &lineWidth},
65-
Fill: Fill{Color: []string{"8EB9FF"}, Transparency: 60},
66-
Paragraph: []RichTextRun{
67-
{
68-
Font: &Font{
69-
Bold: true,
70-
Italic: true,
71-
Family: "Times New Roman",
72-
Size: 36,
73-
Color: "777777",
74-
Underline: "single",
75-
},
44+
shape2 := Shape{
45+
Cell: "A1",
46+
Type: "ellipseRibbon",
47+
Line: ShapeLine{Color: "4286F4", Width: &lineWidth},
48+
Fill: Fill{Color: []string{"8EB9FF"}, Transparency: 60},
49+
Format: GraphicOptions{
50+
PrintObject: boolPtr(true),
51+
Locked: boolPtr(false),
52+
ScaleX: 0.8,
53+
ScaleY: 0.8,
54+
Positioning: "oneCell",
55+
},
56+
Paragraph: []RichTextRun{
57+
{
58+
Font: &Font{
59+
Bold: true,
60+
Italic: true,
61+
Family: "Times New Roman",
62+
Size: 18,
63+
Color: "777777",
64+
Underline: "sng",
7665
},
66+
Text: "Shape",
7767
},
78-
Height: 90,
79-
}))
68+
},
69+
Height: 90,
70+
}
71+
assert.NoError(t, f.AddShape("Sheet1", &shape2))
8072
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx")))
8173
// Test add shape with invalid sheet name
8274
assert.Equal(t, ErrSheetNameInvalid, f.AddShape("Sheet:1", &Shape{

0 commit comments

Comments
 (0)