Skip to content

Commit 12645e7

Browse files
committed
This fixes qax-os#1461, supports 0 row height and column width
- Increase max cell styles to 65430 - Add new exported error variable `ErrCellStyles` - Update unit tests, support test under Go 1.20.x
1 parent 85e0b6c commit 12645e7

10 files changed

+60
-46
lines changed

.github/workflows/go.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jobs:
55
test:
66
strategy:
77
matrix:
8-
go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x]
8+
go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x]
99
os: [ubuntu-latest, macos-latest, windows-latest]
1010
targetplatform: [x86, x64]
1111

col.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ func (f *File) SetColVisible(sheet, columns string, visible bool) error {
300300
colData := xlsxCol{
301301
Min: min,
302302
Max: max,
303-
Width: defaultColWidth, // default width
303+
Width: float64Ptr(defaultColWidth),
304304
Hidden: !visible,
305305
CustomWidth: true,
306306
}
@@ -449,7 +449,7 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error {
449449
ws.Cols.Col = flatCols(xlsxCol{
450450
Min: min,
451451
Max: max,
452-
Width: defaultColWidth,
452+
Width: float64Ptr(defaultColWidth),
453453
Style: styleID,
454454
}, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {
455455
fc.BestFit = c.BestFit
@@ -493,7 +493,7 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
493493
col := xlsxCol{
494494
Min: min,
495495
Max: max,
496-
Width: width,
496+
Width: float64Ptr(width),
497497
CustomWidth: true,
498498
}
499499
if ws.Cols == nil {
@@ -639,8 +639,8 @@ func (f *File) getColWidth(sheet string, col int) int {
639639
if ws.Cols != nil {
640640
var width float64
641641
for _, v := range ws.Cols.Col {
642-
if v.Min <= col && col <= v.Max {
643-
width = v.Width
642+
if v.Min <= col && col <= v.Max && v.Width != nil {
643+
width = *v.Width
644644
}
645645
}
646646
if width != 0 {
@@ -691,8 +691,8 @@ func (f *File) GetColWidth(sheet, col string) (float64, error) {
691691
if ws.Cols != nil {
692692
var width float64
693693
for _, v := range ws.Cols.Col {
694-
if v.Min <= colNum && colNum <= v.Max {
695-
width = v.Width
694+
if v.Min <= colNum && colNum <= v.Max && v.Width != nil {
695+
width = *v.Width
696696
}
697697
}
698698
if width != 0 {

errors.go

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ var (
230230
// ErrSheetNameLength defined the error message on receiving the sheet
231231
// name length exceeds the limit.
232232
ErrSheetNameLength = fmt.Errorf("the sheet name length exceeds the %d characters limit", MaxSheetNameLength)
233+
// ErrCellStyles defined the error message on cell styles exceeds the limit.
234+
ErrCellStyles = fmt.Errorf("the cell styles exceeds the %d limit", MaxCellStyles)
233235
// ErrUnprotectWorkbook defined the error message on workbook has set no
234236
// protection.
235237
ErrUnprotectWorkbook = errors.New("workbook has set no protect")

lib_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,10 @@ func TestReadBytes(t *testing.T) {
342342
}
343343

344344
func TestUnzipToTemp(t *testing.T) {
345-
if strings.HasPrefix(runtime.Version(), "go1.19") {
346-
t.Skip()
345+
for _, v := range []string{"go1.19", "go1.20"} {
346+
if strings.HasPrefix(runtime.Version(), v) {
347+
t.Skip()
348+
}
347349
}
348350
os.Setenv("TMPDIR", "test")
349351
defer os.Unsetenv("TMPDIR")

rows.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ func (f *File) SetRowHeight(sheet string, row int, height float64) error {
363363
prepareSheetXML(ws, 0, row)
364364

365365
rowIdx := row - 1
366-
ws.SheetData.Row[rowIdx].Ht = height
366+
ws.SheetData.Row[rowIdx].Ht = float64Ptr(height)
367367
ws.SheetData.Row[rowIdx].CustomHeight = true
368368
return nil
369369
}
@@ -376,8 +376,8 @@ func (f *File) getRowHeight(sheet string, row int) int {
376376
defer ws.Unlock()
377377
for i := range ws.SheetData.Row {
378378
v := &ws.SheetData.Row[i]
379-
if v.R == row && v.Ht != 0 {
380-
return int(convertRowHeightToPixels(v.Ht))
379+
if v.R == row && v.Ht != nil {
380+
return int(convertRowHeightToPixels(*v.Ht))
381381
}
382382
}
383383
// Optimization for when the row heights haven't changed.
@@ -404,8 +404,8 @@ func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
404404
return ht, nil // it will be better to use 0, but we take care with BC
405405
}
406406
for _, v := range ws.SheetData.Row {
407-
if v.R == row && v.Ht != 0 {
408-
return v.Ht, nil
407+
if v.R == row && v.Ht != nil {
408+
return *v.Ht, nil
409409
}
410410
}
411411
// Optimization for when the row heights haven't changed.
@@ -784,7 +784,7 @@ func checkRow(ws *xlsxWorksheet) error {
784784

785785
// hasAttr determine if row non-default attributes.
786786
func (r *xlsxRow) hasAttr() bool {
787-
return r.Spans != "" || r.S != 0 || r.CustomFormat || r.Ht != 0 ||
787+
return r.Spans != "" || r.S != 0 || r.CustomFormat || r.Ht != nil ||
788788
r.Hidden || r.CustomHeight || r.OutlineLevel != 0 || r.Collapsed ||
789789
r.ThickTop || r.ThickBot || r.Ph
790790
}

sheet.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1844,10 +1844,10 @@ func prepareSheetXML(ws *xlsxWorksheet, col int, row int) {
18441844
defer ws.Unlock()
18451845
rowCount := len(ws.SheetData.Row)
18461846
sizeHint := 0
1847-
var ht float64
1847+
var ht *float64
18481848
var customHeight bool
18491849
if ws.SheetFormatPr != nil && ws.SheetFormatPr.CustomHeight {
1850-
ht = ws.SheetFormatPr.DefaultRowHeight
1850+
ht = float64Ptr(ws.SheetFormatPr.DefaultRowHeight)
18511851
customHeight = true
18521852
}
18531853
if rowCount > 0 {

styles.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,7 @@ func parseFormatStyleSet(style *Style) (*Style, error) {
11931193
//
11941194
// Style
11951195
// ------------------
1196+
// none
11961197
// single
11971198
// double
11981199
//
@@ -2047,8 +2048,7 @@ func (f *File) NewStyle(style *Style) (int, error) {
20472048

20482049
applyAlignment, alignment := fs.Alignment != nil, newAlignment(fs)
20492050
applyProtection, protection := fs.Protection != nil, newProtection(fs)
2050-
cellXfsID = setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, applyProtection, alignment, protection)
2051-
return cellXfsID, nil
2051+
return setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, applyProtection, alignment, protection)
20522052
}
20532053

20542054
var getXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{
@@ -2620,7 +2620,7 @@ func newBorders(style *Style) *xlsxBorder {
26202620

26212621
// setCellXfs provides a function to set describes all of the formatting for a
26222622
// cell.
2623-
func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) int {
2623+
func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) (int, error) {
26242624
var xf xlsxXf
26252625
xf.FontID = intPtr(fontID)
26262626
if fontID != 0 {
@@ -2638,6 +2638,9 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a
26382638
if borderID != 0 {
26392639
xf.ApplyBorder = boolPtr(true)
26402640
}
2641+
if len(style.CellXfs.Xf) == MaxCellStyles {
2642+
return 0, ErrCellStyles
2643+
}
26412644
style.CellXfs.Count = len(style.CellXfs.Xf) + 1
26422645
xf.Alignment = alignment
26432646
if alignment != nil {
@@ -2650,7 +2653,7 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a
26502653
xfID := 0
26512654
xf.XfID = &xfID
26522655
style.CellXfs.Xf = append(style.CellXfs.Xf, xf)
2653-
return style.CellXfs.Count - 1
2656+
return style.CellXfs.Count - 1, nil
26542657
}
26552658

26562659
// GetCellStyle provides a function to get cell style index by given worksheet

styles_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,13 @@ func TestNewStyle(t *testing.T) {
325325
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
326326
_, err = f.NewStyle(&Style{NumFmt: 165})
327327
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
328+
329+
// Test create cell styles reach maximum
330+
f = NewFile()
331+
f.Styles.CellXfs.Xf = make([]xlsxXf, MaxCellStyles)
332+
f.Styles.CellXfs.Count = MaxCellStyles
333+
_, err = f.NewStyle(&Style{NumFmt: 0})
334+
assert.Equal(t, ErrCellStyles, err)
328335
}
329336

330337
func TestNewConditionalStyle(t *testing.T) {

xmlDrawing.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ const (
108108

109109
// Excel specifications and limits
110110
const (
111-
MaxCellStyles = 64000
111+
MaxCellStyles = 65430
112112
MaxColumns = 16384
113113
MaxColumnWidth = 255
114114
MaxFieldLength = 255

xmlWorksheet.go

+23-23
Original file line numberDiff line numberDiff line change
@@ -280,16 +280,16 @@ type xlsxCols struct {
280280
// xlsxCol directly maps the col (Column Width & Formatting). Defines column
281281
// width and column formatting for one or more columns of the worksheet.
282282
type xlsxCol struct {
283-
BestFit bool `xml:"bestFit,attr,omitempty"`
284-
Collapsed bool `xml:"collapsed,attr,omitempty"`
285-
CustomWidth bool `xml:"customWidth,attr,omitempty"`
286-
Hidden bool `xml:"hidden,attr,omitempty"`
287-
Max int `xml:"max,attr"`
288-
Min int `xml:"min,attr"`
289-
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
290-
Phonetic bool `xml:"phonetic,attr,omitempty"`
291-
Style int `xml:"style,attr,omitempty"`
292-
Width float64 `xml:"width,attr,omitempty"`
283+
BestFit bool `xml:"bestFit,attr,omitempty"`
284+
Collapsed bool `xml:"collapsed,attr,omitempty"`
285+
CustomWidth bool `xml:"customWidth,attr,omitempty"`
286+
Hidden bool `xml:"hidden,attr,omitempty"`
287+
Max int `xml:"max,attr"`
288+
Min int `xml:"min,attr"`
289+
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
290+
Phonetic bool `xml:"phonetic,attr,omitempty"`
291+
Style int `xml:"style,attr,omitempty"`
292+
Width *float64 `xml:"width,attr"`
293293
}
294294

295295
// xlsxDimension directly maps the dimension element in the namespace
@@ -316,19 +316,19 @@ type xlsxSheetData struct {
316316
// about an entire row of a worksheet, and contains all cell definitions for a
317317
// particular row in the worksheet.
318318
type xlsxRow struct {
319-
C []xlsxC `xml:"c"`
320-
R int `xml:"r,attr,omitempty"`
321-
Spans string `xml:"spans,attr,omitempty"`
322-
S int `xml:"s,attr,omitempty"`
323-
CustomFormat bool `xml:"customFormat,attr,omitempty"`
324-
Ht float64 `xml:"ht,attr,omitempty"`
325-
Hidden bool `xml:"hidden,attr,omitempty"`
326-
CustomHeight bool `xml:"customHeight,attr,omitempty"`
327-
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
328-
Collapsed bool `xml:"collapsed,attr,omitempty"`
329-
ThickTop bool `xml:"thickTop,attr,omitempty"`
330-
ThickBot bool `xml:"thickBot,attr,omitempty"`
331-
Ph bool `xml:"ph,attr,omitempty"`
319+
C []xlsxC `xml:"c"`
320+
R int `xml:"r,attr,omitempty"`
321+
Spans string `xml:"spans,attr,omitempty"`
322+
S int `xml:"s,attr,omitempty"`
323+
CustomFormat bool `xml:"customFormat,attr,omitempty"`
324+
Ht *float64 `xml:"ht,attr"`
325+
Hidden bool `xml:"hidden,attr,omitempty"`
326+
CustomHeight bool `xml:"customHeight,attr,omitempty"`
327+
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
328+
Collapsed bool `xml:"collapsed,attr,omitempty"`
329+
ThickTop bool `xml:"thickTop,attr,omitempty"`
330+
ThickBot bool `xml:"thickBot,attr,omitempty"`
331+
Ph bool `xml:"ph,attr,omitempty"`
332332
}
333333

334334
// xlsxSortState directly maps the sortState element. This collection

0 commit comments

Comments
 (0)