|
|
@@ -162,10 +162,10 @@ func intOnlyMapF(rune rune) rune {
|
|
|
return -1
|
|
|
}
|
|
|
|
|
|
-// getCoordsFromCellIDString returns the zero based cartesian
|
|
|
+// GetCoordsFromCellIDString returns the zero based cartesian
|
|
|
// coordinates from a cell name in Excel format, e.g. the cellIDString
|
|
|
// "A1" returns 0, 0 and the "B3" return 1, 2.
|
|
|
-func getCoordsFromCellIDString(cellIDString string) (x, y int, error error) {
|
|
|
+func GetCoordsFromCellIDString(cellIDString string) (x, y int, error error) {
|
|
|
var letterPart string = strings.Map(letterOnlyMapF, cellIDString)
|
|
|
y, error = strconv.Atoi(strings.Map(intOnlyMapF, cellIDString))
|
|
|
if error != nil {
|
|
|
@@ -176,9 +176,9 @@ func getCoordsFromCellIDString(cellIDString string) (x, y int, error error) {
|
|
|
return x, y, error
|
|
|
}
|
|
|
|
|
|
-// getCellIDStringFromCoords returns the Excel format cell name that
|
|
|
+// GetCellIDStringFromCoords returns the Excel format cell name that
|
|
|
// represents a pair of zero based cartesian coordinates.
|
|
|
-func getCellIDStringFromCoords(x, y int) string {
|
|
|
+func GetCellIDStringFromCoords(x, y int) string {
|
|
|
letterPart := numericToLetters(x)
|
|
|
numericPart := y + 1
|
|
|
return fmt.Sprintf("%s%d", letterPart, numericPart)
|
|
|
@@ -191,15 +191,11 @@ func getCellIDStringFromCoords(x, y int) string {
|
|
|
func getMaxMinFromDimensionRef(ref string) (minx, miny, maxx, maxy int, err error) {
|
|
|
var parts []string
|
|
|
parts = strings.Split(ref, ":")
|
|
|
- minx, miny, err = getCoordsFromCellIDString(parts[0])
|
|
|
+ minx, miny, err = GetCoordsFromCellIDString(parts[0])
|
|
|
if err != nil {
|
|
|
return -1, -1, -1, -1, err
|
|
|
}
|
|
|
- if len(parts) == 1 {
|
|
|
- maxx, maxy = minx, miny
|
|
|
- return
|
|
|
- }
|
|
|
- maxx, maxy, err = getCoordsFromCellIDString(parts[1])
|
|
|
+ maxx, maxy, err = GetCoordsFromCellIDString(parts[1])
|
|
|
if err != nil {
|
|
|
return -1, -1, -1, -1, err
|
|
|
}
|
|
|
@@ -220,7 +216,7 @@ func calculateMaxMinFromWorksheet(worksheet *xlsxWorksheet) (minx, miny, maxx, m
|
|
|
maxx = 0
|
|
|
for _, row := range worksheet.SheetData.Row {
|
|
|
for _, cell := range row.C {
|
|
|
- x, y, err = getCoordsFromCellIDString(cell.R)
|
|
|
+ x, y, err = GetCoordsFromCellIDString(cell.R)
|
|
|
if err != nil {
|
|
|
return -1, -1, -1, -1, err
|
|
|
}
|
|
|
@@ -285,7 +281,7 @@ func makeRowFromRaw(rawrow xlsxRow, sheet *Sheet) *Row {
|
|
|
|
|
|
for _, rawcell := range rawrow.C {
|
|
|
if rawcell.R != "" {
|
|
|
- x, _, error := getCoordsFromCellIDString(rawcell.R)
|
|
|
+ x, _, error := GetCoordsFromCellIDString(rawcell.R)
|
|
|
if error != nil {
|
|
|
panic(fmt.Sprintf("Invalid Cell Coord, %s\n", rawcell.R))
|
|
|
}
|
|
|
@@ -329,7 +325,7 @@ func formulaForCell(rawcell xlsxC, sharedFormulas map[int]sharedFormula) string
|
|
|
return ""
|
|
|
}
|
|
|
if f.T == "shared" {
|
|
|
- x, y, err := getCoordsFromCellIDString(rawcell.R)
|
|
|
+ x, y, err := GetCoordsFromCellIDString(rawcell.R)
|
|
|
if err != nil {
|
|
|
res = f.Content
|
|
|
} else {
|
|
|
@@ -392,7 +388,7 @@ func formulaForCell(rawcell xlsxC, sharedFormulas map[int]sharedFormula) string
|
|
|
// shiftCell returns the cell shifted according to dx and dy taking into consideration of absolute
|
|
|
// references with dollar sign ($)
|
|
|
func shiftCell(cellID string, dx, dy int) string {
|
|
|
- fx, fy, _ := getCoordsFromCellIDString(cellID)
|
|
|
+ fx, fy, _ := GetCoordsFromCellIDString(cellID)
|
|
|
|
|
|
// Is fixed column?
|
|
|
fixedCol := strings.Index(cellID, "$") == 0
|
|
|
@@ -411,7 +407,7 @@ func shiftCell(cellID string, dx, dy int) string {
|
|
|
}
|
|
|
|
|
|
// New shifted cell
|
|
|
- shiftedCellID := getCellIDStringFromCoords(fx, fy)
|
|
|
+ shiftedCellID := GetCellIDStringFromCoords(fx, fy)
|
|
|
|
|
|
if !fixedCol && !fixedRow {
|
|
|
return shiftedCellID
|
|
|
@@ -442,7 +438,7 @@ func shiftCell(cellID string, dx, dy int) string {
|
|
|
// CSV form from the raw cell value. Note - this is not actually
|
|
|
// general enough - we should support retaining tabs and newlines.
|
|
|
func fillCellData(rawcell xlsxC, reftable *RefTable, sharedFormulas map[int]sharedFormula, cell *Cell) {
|
|
|
- var data string = rawcell.V
|
|
|
+ var data = rawcell.V
|
|
|
if len(data) > 0 {
|
|
|
vval := strings.Trim(data, " \t\n\r")
|
|
|
switch rawcell.T {
|
|
|
@@ -472,6 +468,23 @@ func fillCellData(rawcell xlsxC, reftable *RefTable, sharedFormulas map[int]shar
|
|
|
cell.cellType = CellTypeFormula
|
|
|
}
|
|
|
}
|
|
|
+ } else {
|
|
|
+ if rawcell.Is != nil {
|
|
|
+ fillCellDataFromInlineString(rawcell, cell)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// fillCellDataFromInlineString attempts to get inline string data and put it into a Cell.
|
|
|
+func fillCellDataFromInlineString(rawcell xlsxC, cell *Cell) {
|
|
|
+ if rawcell.Is.T != "" {
|
|
|
+ cell.Value = strings.Trim(rawcell.Is.T, " \t\n\r")
|
|
|
+ cell.cellType = CellTypeInline
|
|
|
+ } else {
|
|
|
+ cell.Value = ""
|
|
|
+ for _, r := range rawcell.Is.R {
|
|
|
+ cell.Value += r.T
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -493,7 +506,7 @@ func readRowsFromSheet(Worksheet *xlsxWorksheet, file *File, sheet *Sheet) ([]*R
|
|
|
return nil, nil, 0, 0
|
|
|
}
|
|
|
reftable = file.referenceTable
|
|
|
- if len(Worksheet.Dimension.Ref) > 0 {
|
|
|
+ if len(Worksheet.Dimension.Ref) > 0 && len(strings.Split(Worksheet.Dimension.Ref, ":")) == 2 {
|
|
|
minCol, minRow, maxCol, maxRow, err = getMaxMinFromDimensionRef(Worksheet.Dimension.Ref)
|
|
|
} else {
|
|
|
minCol, minRow, maxCol, maxRow, err = calculateMaxMinFromWorksheet(Worksheet)
|
|
|
@@ -575,7 +588,7 @@ func readRowsFromSheet(Worksheet *xlsxWorksheet, file *File, sheet *Sheet) ([]*R
|
|
|
if err != nil {
|
|
|
panic(err.Error())
|
|
|
}
|
|
|
- x, _, _ := getCoordsFromCellIDString(rawcell.R)
|
|
|
+ x, _, _ := GetCoordsFromCellIDString(rawcell.R)
|
|
|
|
|
|
// K1000000: Prevent panic when the range specified in the spreadsheet
|
|
|
// view exceeds the actual number of columns in the dataset.
|
|
|
@@ -646,13 +659,15 @@ func readSheetViews(xSheetViews xlsxSheetViews) []SheetView {
|
|
|
// into a Sheet struct. This work can be done in parallel and so
|
|
|
// readSheetsFromZipFile will spawn an instance of this function per
|
|
|
// sheet and get the results back on the provided channel.
|
|
|
-func readSheetFromFile(sc chan *indexedSheet, index int, rsheet xlsxSheet, fi *File, sheetXMLMap map[string]string) {
|
|
|
+func readSheetFromFile(sc chan *indexedSheet, index int, rsheet xlsxSheet, fi *File, sheetXMLMap map[string]string) (errRes error) {
|
|
|
result := &indexedSheet{Index: index, Sheet: nil, Error: nil}
|
|
|
defer func() {
|
|
|
if e := recover(); e != nil {
|
|
|
+
|
|
|
switch e.(type) {
|
|
|
case error:
|
|
|
result.Error = e.(error)
|
|
|
+ errRes = e.(error)
|
|
|
default:
|
|
|
result.Error = errors.New("unexpected error")
|
|
|
}
|
|
|
@@ -665,7 +680,7 @@ func readSheetFromFile(sc chan *indexedSheet, index int, rsheet xlsxSheet, fi *F
|
|
|
if error != nil {
|
|
|
result.Error = error
|
|
|
sc <- result
|
|
|
- return
|
|
|
+ return error
|
|
|
}
|
|
|
sheet := new(Sheet)
|
|
|
sheet.File = fi
|
|
|
@@ -680,6 +695,7 @@ func readSheetFromFile(sc chan *indexedSheet, index int, rsheet xlsxSheet, fi *F
|
|
|
|
|
|
result.Sheet = sheet
|
|
|
sc <- result
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
// readSheetsFromZipFile is an internal helper function that loops
|
|
|
@@ -719,12 +735,14 @@ func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]strin
|
|
|
sheetsByName := make(map[string]*Sheet, sheetCount)
|
|
|
sheets := make([]*Sheet, sheetCount)
|
|
|
sheetChan := make(chan *indexedSheet, sheetCount)
|
|
|
- defer close(sheetChan)
|
|
|
|
|
|
go func() {
|
|
|
+ defer close(sheetChan)
|
|
|
err = nil
|
|
|
for i, rawsheet := range workbookSheets {
|
|
|
- readSheetFromFile(sheetChan, i, rawsheet, file, sheetXMLMap)
|
|
|
+ if err := readSheetFromFile(sheetChan, i, rawsheet, file, sheetXMLMap); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
}
|
|
|
}()
|
|
|
|