Skip to content

Commit 8fdc26e

Browse files
authored
This fixes qax-os#2001 and fixes qax-os#2002. Breaking changes: offsets no longer affect the image size (qax-os#2123)
- Change default column width from 9.140625 to 10.5 - Change default column width in pixels from 64 to 84 - Change default row height from 15 to 15.6 - Change default row height in pixels from 20 to 20.8 - Change column width and row height to pixels conversion calculation logic - Support to insert one cell anchor image when specified the positioning as "oneCell"
1 parent efdfc52 commit 8fdc26e

12 files changed

+133
-92
lines changed

adjust.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ var adjustHelperFunc = [9]func(*File, *xlsxWorksheet, string, adjustDirection, i
3838
return f.adjustDataValidations(ws, sheet, dir, num, offset, sheetID)
3939
},
4040
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
41-
return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
41+
return f.adjustDefinedNames(sheet, dir, num, offset)
4242
},
4343
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
44-
return f.adjustDrawings(ws, sheet, dir, num, offset, sheetID)
44+
return f.adjustDrawings(ws, sheet, dir, num, offset)
4545
},
4646
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
4747
return f.adjustMergeCells(ws, sheet, dir, num, offset, sheetID)
@@ -1016,7 +1016,7 @@ func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editA
10161016

10171017
// adjustDrawings updates the ending anchor of the two cell anchor pictures
10181018
// and charts object when inserting or deleting rows or columns.
1019-
func (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, editAs string, ok bool) error {
1019+
func (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, ok bool) error {
10201020
if dir == columns && to.Col+1 >= num && to.Col+offset >= 0 && ok {
10211021
if to.Col+offset >= MaxColumns {
10221022
return ErrColumnNumber
@@ -1036,32 +1036,38 @@ func (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, editAs st
10361036
// inserting or deleting rows or columns.
10371037
func (a *xdrCellAnchor) adjustDrawings(dir adjustDirection, num, offset int) error {
10381038
editAs := a.EditAs
1039-
if a.From == nil || a.To == nil || editAs == "absolute" {
1039+
if (a.From == nil && (a.To == nil || a.Ext == nil)) || editAs == "absolute" {
10401040
return nil
10411041
}
10421042
ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
10431043
if err != nil {
10441044
return err
10451045
}
1046-
return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
1046+
if a.To != nil {
1047+
return a.To.adjustDrawings(dir, num, offset, ok || editAs == "")
1048+
}
1049+
return err
10471050
}
10481051

10491052
// adjustDrawings updates the existing two cell anchor pictures and charts
10501053
// object when inserting or deleting rows or columns.
10511054
func (a *xlsxCellAnchorPos) adjustDrawings(dir adjustDirection, num, offset int, editAs string) error {
1052-
if a.From == nil || a.To == nil || editAs == "absolute" {
1055+
if (a.From == nil && (a.To == nil || a.Ext == nil)) || editAs == "absolute" {
10531056
return nil
10541057
}
10551058
ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
10561059
if err != nil {
10571060
return err
10581061
}
1059-
return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
1062+
if a.To != nil {
1063+
return a.To.adjustDrawings(dir, num, offset, ok || editAs == "")
1064+
}
1065+
return err
10601066
}
10611067

10621068
// adjustDrawings updates the pictures and charts object when inserting or
10631069
// deleting rows or columns.
1064-
func (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
1070+
func (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) error {
10651071
if ws.Drawing == nil {
10661072
return nil
10671073
}
@@ -1110,12 +1116,17 @@ func (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirecti
11101116
return err
11111117
}
11121118
}
1119+
for _, anchor := range wsDr.OneCellAnchor {
1120+
if err = anchorCb(anchor); err != nil {
1121+
return err
1122+
}
1123+
}
11131124
return nil
11141125
}
11151126

11161127
// adjustDefinedNames updates the cell reference of the defined names when
11171128
// inserting or deleting rows or columns.
1118-
func (f *File) adjustDefinedNames(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
1129+
func (f *File) adjustDefinedNames(sheet string, dir adjustDirection, num, offset int) error {
11191130
wb, err := f.workbookReader()
11201131
if err != nil {
11211132
return err

adjust_test.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ func TestAdjustDrawings(t *testing.T) {
12061206
assert.NoError(t, f.InsertRows("Sheet1", 15, 1))
12071207
cells, err := f.GetPictureCells("Sheet1")
12081208
assert.NoError(t, err)
1209-
assert.Equal(t, []string{"D3", "D13", "B21"}, cells)
1209+
assert.Equal(t, []string{"D3", "B21", "D13"}, cells)
12101210
wb := filepath.Join("test", "TestAdjustDrawings.xlsx")
12111211
assert.NoError(t, f.SaveAs(wb))
12121212

@@ -1215,7 +1215,7 @@ func TestAdjustDrawings(t *testing.T) {
12151215
assert.NoError(t, f.RemoveRow("Sheet1", 1))
12161216
cells, err = f.GetPictureCells("Sheet1")
12171217
assert.NoError(t, err)
1218-
assert.Equal(t, []string{"C2", "C12", "B21"}, cells)
1218+
assert.Equal(t, []string{"C2", "B21", "C12"}, cells)
12191219

12201220
// Test adjust existing pictures on inserting columns and rows
12211221
f, err = OpenFile(wb)
@@ -1227,7 +1227,7 @@ func TestAdjustDrawings(t *testing.T) {
12271227
assert.NoError(t, f.InsertRows("Sheet1", 16, 1))
12281228
cells, err = f.GetPictureCells("Sheet1")
12291229
assert.NoError(t, err)
1230-
assert.Equal(t, []string{"F4", "F15", "B21"}, cells)
1230+
assert.Equal(t, []string{"F4", "B21", "F15"}, cells)
12311231

12321232
// Test adjust drawings with unsupported charset
12331233
f, err = OpenFile(wb)
@@ -1267,6 +1267,11 @@ func TestAdjustDrawings(t *testing.T) {
12671267
assert.NoError(t, err)
12681268
f.Pkg.Store("xl/drawings/drawing1.xml", []byte(xml.Header+`<wsDr xmlns="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><twoCellAnchor><from><col>0</col><colOff>0</colOff><row>0</row><rowOff>0</rowOff></from><to><col>1</col><colOff>0</colOff><row>1</row><rowOff>0</rowOff></to><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"></mc:AlternateContent><clientData/></twoCellAnchor></wsDr>`))
12691269
assert.NoError(t, f.InsertCols("Sheet1", "A", 1))
1270+
1271+
f, err = OpenFile(wb)
1272+
assert.NoError(t, err)
1273+
f.Pkg.Store("xl/drawings/drawing1.xml", []byte(xml.Header+fmt.Sprintf(`<wsDr xmlns="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><oneCellAnchor><from><col>%d</col><row>0</row></from><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"></mc:AlternateContent><clientData/></oneCellAnchor></wsDr>`, MaxColumns)))
1274+
assert.EqualError(t, f.InsertCols("Sheet1", "A", 1), "the column number must be greater than or equal to 1 and less than or equal to 16384")
12701275
}
12711276

12721277
func TestAdjustDefinedNames(t *testing.T) {
@@ -1330,5 +1335,5 @@ func TestAdjustDefinedNames(t *testing.T) {
13301335
f = NewFile()
13311336
f.WorkBook = nil
13321337
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
1333-
assert.EqualError(t, f.adjustDefinedNames(nil, "Sheet1", columns, 0, 0, 1), "XML syntax error on line 1: invalid UTF-8")
1338+
assert.EqualError(t, f.adjustDefinedNames("Sheet1", columns, 0, 0), "XML syntax error on line 1: invalid UTF-8")
13341339
}

chart_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ func TestChartSize(t *testing.T) {
9393
t.FailNow()
9494
}
9595

96-
if !assert.Equal(t, 14, anchor.To.Col, "Expected 'to' column 14") ||
97-
!assert.Equal(t, 29, anchor.To.Row, "Expected 'to' row 29") {
96+
if !assert.Equal(t, 11, anchor.To.Col, "Expected 'to' column 11") ||
97+
!assert.Equal(t, 27, anchor.To.Row, "Expected 'to' row 27") {
9898

9999
t.FailNow()
100100
}

col.go

+34-40
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ package excelize
1414
import (
1515
"bytes"
1616
"encoding/xml"
17-
"math"
1817
"strconv"
1918
"strings"
2019

@@ -23,10 +22,10 @@ import (
2322

2423
// Define the default cell size and EMU unit of measurement.
2524
const (
26-
defaultColWidth float64 = 9.140625
27-
defaultColWidthPixels float64 = 64
28-
defaultRowHeight float64 = 15
29-
defaultRowHeightPixels float64 = 20
25+
defaultColWidth float64 = 10.5
26+
defaultColWidthPixels float64 = 84.0
27+
defaultRowHeight float64 = 15.6
28+
defaultRowHeightPixels float64 = 20.8
3029
EMU int = 9525
3130
)
3231

@@ -621,40 +620,35 @@ func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol)
621620
//
622621
// width # Width of object frame.
623622
// height # Height of object frame.
624-
func (f *File) positionObjectPixels(sheet string, col, row, x1, y1, width, height int) (int, int, int, int, int, int) {
623+
func (f *File) positionObjectPixels(sheet string, col, row, width, height int, opts *GraphicOptions) (int, int, int, int, int, int, int, int) {
625624
colIdx, rowIdx := col-1, row-1
626-
// Adjust start column for offsets that are greater than the col width.
627-
for x1 >= f.getColWidth(sheet, colIdx+1) {
628-
colIdx++
629-
x1 -= f.getColWidth(sheet, colIdx)
630-
}
631-
632-
// Adjust start row for offsets that are greater than the row height.
633-
for y1 >= f.getRowHeight(sheet, rowIdx+1) {
634-
rowIdx++
635-
y1 -= f.getRowHeight(sheet, rowIdx)
636-
}
637-
638625
// Initialized end cell to the same as the start cell.
639626
colEnd, rowEnd := colIdx, rowIdx
627+
x1, y1, x2, y2 := opts.OffsetX, opts.OffsetY, width, height
628+
if opts.Positioning == "" || opts.Positioning == "twoCell" {
629+
// Using a twoCellAnchor, the maximum possible offset is limited by the
630+
// "from" cell dimensions. If these were to be exceeded the "toPoint" would
631+
// be calculated incorrectly, since the requested "fromPoint" is not possible
632+
633+
x1 = min(x1, f.getColWidth(sheet, col))
634+
y1 = min(y1, f.getRowHeight(sheet, row))
635+
636+
x2 += x1
637+
y2 += y1
638+
// Subtract the underlying cell widths to find end cell of the object.
639+
for x2 >= f.getColWidth(sheet, colEnd+1) {
640+
colEnd++
641+
x2 -= f.getColWidth(sheet, colEnd)
642+
}
640643

641-
width += x1
642-
height += y1
643-
644-
// Subtract the underlying cell widths to find end cell of the object.
645-
for width >= f.getColWidth(sheet, colEnd+1) {
646-
colEnd++
647-
width -= f.getColWidth(sheet, colEnd)
648-
}
649-
650-
// Subtract the underlying cell heights to find end cell of the object.
651-
for height >= f.getRowHeight(sheet, rowEnd+1) {
652-
rowEnd++
653-
height -= f.getRowHeight(sheet, rowEnd)
644+
// Subtract the underlying cell heights to find end cell of the object.
645+
for y2 >= f.getRowHeight(sheet, rowEnd+1) {
646+
rowEnd++
647+
y2 -= f.getRowHeight(sheet, rowEnd)
648+
}
654649
}
655-
656650
// The end vertices are whatever is left from the width and height.
657-
return colIdx, rowIdx, colEnd, rowEnd, width, height
651+
return colIdx, rowIdx, colEnd, rowEnd, x1, y1, x2, y2
658652
}
659653

660654
// getColWidth provides a function to get column width in pixels by given
@@ -664,13 +658,14 @@ func (f *File) getColWidth(sheet string, col int) int {
664658
ws.mu.Lock()
665659
defer ws.mu.Unlock()
666660
if ws.Cols != nil {
667-
var width float64
661+
width := -1.0
668662
for _, v := range ws.Cols.Col {
669663
if v.Min <= col && col <= v.Max && v.Width != nil {
670664
width = *v.Width
665+
break
671666
}
672667
}
673-
if width != 0 {
668+
if width != -1.0 {
674669
return int(convertColWidthToPixels(width))
675670
}
676671
}
@@ -801,16 +796,15 @@ func (f *File) RemoveCol(sheet, col string) error {
801796
// pixel. If the width hasn't been set by the user we use the default value.
802797
// If the column is hidden it has a value of zero.
803798
func convertColWidthToPixels(width float64) float64 {
804-
var padding float64 = 5
805799
var pixels float64
806-
var maxDigitWidth float64 = 7
800+
var maxDigitWidth float64 = 8
807801
if width == 0 {
808802
return pixels
809803
}
810804
if width < 1 {
811805
pixels = (width * 12) + 0.5
812-
return math.Ceil(pixels)
806+
return float64(int(pixels))
813807
}
814-
pixels = (width*maxDigitWidth + 0.5) + padding
815-
return math.Ceil(pixels)
808+
pixels = (width*maxDigitWidth + 0.5)
809+
return float64(int(pixels))
816810
}

col_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ func TestColWidth(t *testing.T) {
396396
width, err = f.GetColWidth("Sheet1", "A")
397397
assert.NoError(t, err)
398398
assert.Equal(t, 10.0, width)
399-
assert.Equal(t, 76, f.getColWidth("Sheet1", 1))
399+
assert.Equal(t, 80, f.getColWidth("Sheet1", 1))
400400

401401
// Test set and get column width with illegal cell reference
402402
width, err = f.GetColWidth("Sheet1", "*")

drawing.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
14061406
}
14071407
width = int(float64(width) * opts.ScaleX)
14081408
height = int(float64(height) * opts.ScaleY)
1409-
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, opts.OffsetX, opts.OffsetY, width, height)
1409+
colStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2 := f.positionObjectPixels(sheet, col, row, width, height, opts)
14101410
content, cNvPrID, err := f.drawingParser(drawingXML)
14111411
if err != nil {
14121412
return err
@@ -1415,9 +1415,9 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
14151415
twoCellAnchor.EditAs = opts.Positioning
14161416
from := xlsxFrom{}
14171417
from.Col = colStart
1418-
from.ColOff = opts.OffsetX * EMU
1418+
from.ColOff = x1 * EMU
14191419
from.Row = rowStart
1420-
from.RowOff = opts.OffsetY * EMU
1420+
from.RowOff = y1 * EMU
14211421
to := xlsxTo{}
14221422
to.Col = colEnd
14231423
to.ColOff = x2 * EMU

picture.go

+39-23
Original file line numberDiff line numberDiff line change
@@ -366,25 +366,29 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyper
366366
width = int(float64(width) * opts.ScaleX)
367367
height = int(float64(height) * opts.ScaleY)
368368
}
369-
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, opts.OffsetX, opts.OffsetY, width, height)
369+
colStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2 := f.positionObjectPixels(sheet, col, row, width, height, opts)
370370
content, cNvPrID, err := f.drawingParser(drawingXML)
371371
if err != nil {
372372
return err
373373
}
374-
twoCellAnchor := xdrCellAnchor{}
375-
twoCellAnchor.EditAs = opts.Positioning
374+
cellAnchor := xdrCellAnchor{}
375+
cellAnchor.EditAs = opts.Positioning
376376
from := xlsxFrom{}
377377
from.Col = colStart
378-
from.ColOff = opts.OffsetX * EMU
378+
from.ColOff = x1 * EMU
379379
from.Row = rowStart
380-
from.RowOff = opts.OffsetY * EMU
381-
to := xlsxTo{}
382-
to.Col = colEnd
383-
to.ColOff = x2 * EMU
384-
to.Row = rowEnd
385-
to.RowOff = y2 * EMU
386-
twoCellAnchor.From = &from
387-
twoCellAnchor.To = &to
380+
from.RowOff = y1 * EMU
381+
cellAnchor.From = &from
382+
383+
if opts.Positioning != "oneCell" {
384+
to := xlsxTo{}
385+
to.Col = colEnd
386+
to.ColOff = x2 * EMU
387+
to.Row = rowEnd
388+
to.RowOff = y2 * EMU
389+
cellAnchor.To = &to
390+
}
391+
388392
pic := xlsxPic{}
389393
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.LockAspectRatio
390394
pic.NvPicPr.CNvPr.ID = cNvPrID
@@ -413,14 +417,29 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyper
413417
}
414418
pic.SpPr.PrstGeom.Prst = "rect"
415419

416-
twoCellAnchor.Pic = &pic
417-
twoCellAnchor.ClientData = &xdrClientData{
420+
if opts.Positioning == "oneCell" {
421+
cx := x2 * EMU
422+
cy := y2 * EMU
423+
cellAnchor.Ext = &aExt{
424+
Cx: cx,
425+
Cy: cy,
426+
}
427+
pic.SpPr.Xfrm.Ext.Cx = cx
428+
pic.SpPr.Xfrm.Ext.Cy = cy
429+
}
430+
431+
cellAnchor.Pic = &pic
432+
cellAnchor.ClientData = &xdrClientData{
418433
FLocksWithSheet: *opts.Locked,
419434
FPrintsWithSheet: *opts.PrintObject,
420435
}
421436
content.mu.Lock()
422437
defer content.mu.Unlock()
423-
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
438+
if opts.Positioning == "oneCell" {
439+
content.OneCellAnchor = append(content.OneCellAnchor, &cellAnchor)
440+
} else {
441+
content.TwoCellAnchor = append(content.TwoCellAnchor, &cellAnchor)
442+
}
424443
f.Drawings.Store(drawingXML, content)
425444
return err
426445
}
@@ -753,18 +772,15 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, opts *Gr
753772
cellHeight += f.getRowHeight(sheet, row)
754773
}
755774
}
756-
if float64(cellWidth) < width {
757-
asp := float64(cellWidth) / width
758-
width, height = float64(cellWidth), height*asp
759-
}
760-
if float64(cellHeight) < height {
761-
asp := float64(cellHeight) / height
762-
height, width = float64(cellHeight), width*asp
775+
if float64(cellWidth) < width || float64(cellHeight) < height {
776+
aspWidth := float64(cellWidth) / width
777+
aspHeight := float64(cellHeight) / height
778+
asp := min(aspWidth, aspHeight)
779+
width, height = width*asp, height*asp
763780
}
764781
if opts.AutoFitIgnoreAspect {
765782
width, height = float64(cellWidth), float64(cellHeight)
766783
}
767-
width, height = width-float64(opts.OffsetX), height-float64(opts.OffsetY)
768784
w, h = int(width*opts.ScaleX), int(height*opts.ScaleY)
769785
return
770786
}

0 commit comments

Comments
 (0)