From 7dbf88f221f278075d4ff9e153b21236d0826c33 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 29 Jul 2021 00:03:57 +0800 Subject: [PATCH] This closes #971, closes #972 and closes #974 - Escape XML character in the drop list - Fix incorrect character count limit in the drop list - Fix Excel time parse issue in some case - Fix custom number format month parse issue in some case - Fix corrupted file generated caused by concurrency adding pictures --- col.go | 4 ++-- datavalidation.go | 13 +++++++------ date.go | 27 ++++++++++----------------- sheet.go | 3 ++- styles.go | 2 ++ xmlWorksheet.go | 4 ++-- 6 files changed, 25 insertions(+), 28 deletions(-) diff --git a/col.go b/col.go index 5171f34..088fac9 100644 --- a/col.go +++ b/col.go @@ -439,10 +439,10 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error { for col := start; col <= end; col++ { from, _ := CoordinatesToCellName(col, 1) to, _ := CoordinatesToCellName(col, rows) - f.SetCellStyle(sheet, from, to, styleID) + err = f.SetCellStyle(sheet, from, to, styleID) } } - return nil + return err } // SetColWidth provides a function to set the width of a single column or diff --git a/datavalidation.go b/datavalidation.go index 0f8508b..a95f4d0 100644 --- a/datavalidation.go +++ b/datavalidation.go @@ -14,6 +14,7 @@ package excelize import ( "fmt" "strings" + "unicode/utf16" ) // DataValidationType defined the type of data validation. @@ -111,10 +112,10 @@ func (dd *DataValidation) SetInput(title, msg string) { // SetDropList data validation list. func (dd *DataValidation) SetDropList(keys []string) error { formula := "\"" + strings.Join(keys, ",") + "\"" - if dataValidationFormulaStrLen < len(formula) { + if dataValidationFormulaStrLen < len(utf16.Encode([]rune(formula))) { return fmt.Errorf(dataValidationFormulaStrLenErr) } - dd.Formula1 = fmt.Sprintf("%s", formula) + dd.Formula1 = formula dd.Type = convDataValidationType(typeList) return nil } @@ -123,12 +124,12 @@ func (dd *DataValidation) SetDropList(keys []string) error { func (dd *DataValidation) SetRange(f1, f2 float64, t DataValidationType, o DataValidationOperator) error { formula1 := fmt.Sprintf("%f", f1) formula2 := fmt.Sprintf("%f", f2) - if dataValidationFormulaStrLen+21 < len(dd.Formula1) || dataValidationFormulaStrLen+21 < len(dd.Formula2) { + if dataValidationFormulaStrLen < len(utf16.Encode([]rune(dd.Formula1))) || dataValidationFormulaStrLen < len(utf16.Encode([]rune(dd.Formula2))) { return fmt.Errorf(dataValidationFormulaStrLenErr) } - dd.Formula1 = fmt.Sprintf("%s", formula1) - dd.Formula2 = fmt.Sprintf("%s", formula2) + dd.Formula1 = formula1 + dd.Formula2 = formula2 dd.Type = convDataValidationType(t) dd.Operator = convDataValidationOperatior(o) return nil @@ -148,7 +149,7 @@ func (dd *DataValidation) SetRange(f1, f2 float64, t DataValidationType, o DataV // func (dd *DataValidation) SetSqrefDropList(sqref string, isCurrentSheet bool) error { if isCurrentSheet { - dd.Formula1 = fmt.Sprintf("%s", sqref) + dd.Formula1 = sqref dd.Type = convDataValidationType(typeList) return nil } diff --git a/date.go b/date.go index 0531b6c..a5edcf8 100644 --- a/date.go +++ b/date.go @@ -17,11 +17,14 @@ import ( ) const ( + nanosInADay = float64((24 * time.Hour) / time.Nanosecond) dayNanoseconds = 24 * time.Hour maxDuration = 290 * 364 * dayNanoseconds ) var ( + excel1900Epoc = time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC) + excel1904Epoc = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC) excelMinTime1900 = time.Date(1899, time.December, 31, 0, 0, 0, 0, time.UTC) excelBuggyPeriodStart = time.Date(1900, time.March, 1, 0, 0, 0, 0, time.UTC).Add(-time.Nanosecond) ) @@ -131,12 +134,11 @@ func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) { // timeFromExcelTime provides a function to convert an excelTime // representation (stored as a floating point number) to a time.Time. func timeFromExcelTime(excelTime float64, date1904 bool) time.Time { - const MDD int64 = 106750 // Max time.Duration Days, aprox. 290 years var date time.Time - var intPart = int64(excelTime) + var wholeDaysPart = int(excelTime) // Excel uses Julian dates prior to March 1st 1900, and Gregorian // thereafter. - if intPart <= 61 { + if wholeDaysPart <= 61 { const OFFSET1900 = 15018.0 const OFFSET1904 = 16480.0 const MJD0 float64 = 2400000.5 @@ -148,23 +150,14 @@ func timeFromExcelTime(excelTime float64, date1904 bool) time.Time { } return date } - var floatPart = excelTime - float64(intPart) - var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000 + var floatPart = excelTime - float64(wholeDaysPart) if date1904 { - date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC) + date = excel1904Epoc } else { - date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC) + date = excel1900Epoc } - - // Duration is limited to aprox. 290 years - for intPart > MDD { - durationDays := time.Duration(MDD) * time.Hour * 24 - date = date.Add(durationDays) - intPart = intPart - MDD - } - durationDays := time.Duration(intPart) * time.Hour * 24 - durationPart := time.Duration(dayNanoSeconds * floatPart) - return date.Add(durationDays).Add(durationPart) + durationPart := time.Duration(nanosInADay * floatPart) + return date.AddDate(0, 0, wholeDaysPart).Add(durationPart) } // ExcelDateToTime converts a float-based excel date representation to a time.Time. diff --git a/sheet.go b/sheet.go index 7a1fff3..756eb81 100644 --- a/sheet.go +++ b/sheet.go @@ -72,12 +72,13 @@ func (f *File) contentTypesReader() *xlsxTypes { if f.ContentTypes == nil { f.ContentTypes = new(xlsxTypes) + f.ContentTypes.Lock() + defer f.ContentTypes.Unlock() if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))). Decode(f.ContentTypes); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } - return f.ContentTypes } diff --git a/styles.go b/styles.go index 07ccab1..2a99a4d 100644 --- a/styles.go +++ b/styles.go @@ -996,6 +996,7 @@ func parseTime(v string, format string) string { {"mm", "01"}, {"am/pm", "pm"}, {"m/", "1/"}, + {"m", "1"}, {"%%%%", "January"}, {"&&&&", "Monday"}, } @@ -1005,6 +1006,7 @@ func parseTime(v string, format string) string { {"\\ ", " "}, {"\\.", "."}, {"\\", ""}, + {"\"", ""}, } // It is the presence of the "am/pm" indicator that determines if this is // a 12 hour or 24 hours time format, not the number of 'h' characters. diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 4499546..a54d51b 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -436,8 +436,8 @@ type DataValidation struct { ShowInputMessage bool `xml:"showInputMessage,attr,omitempty"` Sqref string `xml:"sqref,attr"` Type string `xml:"type,attr,omitempty"` - Formula1 string `xml:",innerxml"` - Formula2 string `xml:",innerxml"` + Formula1 string `xml:"formula1,omitempty"` + Formula2 string `xml:"formula2,omitempty"` } // xlsxC collection represents a cell in the worksheet. Information about the