2023-01-02 11:47:31 +08:00
|
|
|
// Copyright 2016 - 2023 The excelize Authors. All rights reserved. Use of
|
2018-09-14 00:44:23 +08:00
|
|
|
// this source code is governed by a BSD-style license that can be found in
|
|
|
|
// the LICENSE file.
|
|
|
|
//
|
2022-02-17 00:09:11 +08:00
|
|
|
// Package excelize providing a set of functions that allow you to write to and
|
|
|
|
// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
|
|
|
|
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
|
|
|
|
// Supports complex components by high compatibility, and provided streaming
|
|
|
|
// API for generating or reading data from a worksheet with huge amounts of
|
2023-01-02 11:47:31 +08:00
|
|
|
// data. This library needs Go version 1.16 or later.
|
2018-09-14 00:58:48 +08:00
|
|
|
|
2016-08-30 21:54:28 +08:00
|
|
|
package excelize
|
|
|
|
|
|
|
|
import (
|
2022-11-15 22:08:37 +08:00
|
|
|
"bytes"
|
2017-07-24 10:26:02 +08:00
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
2021-12-27 23:34:14 +08:00
|
|
|
"os"
|
2018-03-12 20:14:39 +08:00
|
|
|
"reflect"
|
2016-08-30 21:54:28 +08:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2017-07-24 10:26:02 +08:00
|
|
|
"time"
|
2023-04-12 08:17:10 +08:00
|
|
|
"unicode/utf8"
|
2016-08-30 21:54:28 +08:00
|
|
|
)
|
|
|
|
|
2021-09-09 23:43:16 +08:00
|
|
|
// CellType is the type of cell value type.
|
|
|
|
type CellType byte
|
|
|
|
|
|
|
|
// Cell value types enumeration.
|
|
|
|
const (
|
|
|
|
CellTypeUnset CellType = iota
|
|
|
|
CellTypeBool
|
|
|
|
CellTypeDate
|
|
|
|
CellTypeError
|
2022-10-25 10:24:45 +08:00
|
|
|
CellTypeFormula
|
|
|
|
CellTypeInlineString
|
2021-09-09 23:43:16 +08:00
|
|
|
CellTypeNumber
|
2022-10-25 10:24:45 +08:00
|
|
|
CellTypeSharedString
|
2021-09-09 23:43:16 +08:00
|
|
|
)
|
|
|
|
|
2018-05-26 16:23:15 +08:00
|
|
|
const (
|
|
|
|
// STCellFormulaTypeArray defined the formula is an array formula.
|
|
|
|
STCellFormulaTypeArray = "array"
|
|
|
|
// STCellFormulaTypeDataTable defined the formula is a data table formula.
|
|
|
|
STCellFormulaTypeDataTable = "dataTable"
|
|
|
|
// STCellFormulaTypeNormal defined the formula is a regular cell formula.
|
|
|
|
STCellFormulaTypeNormal = "normal"
|
|
|
|
// STCellFormulaTypeShared defined the formula is part of a shared formula.
|
|
|
|
STCellFormulaTypeShared = "shared"
|
|
|
|
)
|
|
|
|
|
2021-09-09 23:43:16 +08:00
|
|
|
// cellTypes mapping the cell's data type and enumeration.
|
|
|
|
var cellTypes = map[string]CellType{
|
|
|
|
"b": CellTypeBool,
|
|
|
|
"d": CellTypeDate,
|
|
|
|
"n": CellTypeNumber,
|
|
|
|
"e": CellTypeError,
|
2022-10-25 10:24:45 +08:00
|
|
|
"s": CellTypeSharedString,
|
|
|
|
"str": CellTypeFormula,
|
|
|
|
"inlineStr": CellTypeInlineString,
|
2021-09-09 23:43:16 +08:00
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// GetCellValue provides a function to get formatted value from cell by given
|
2022-09-18 00:07:15 +08:00
|
|
|
// worksheet name and cell reference in spreadsheet. The return value is
|
|
|
|
// converted to the 'string' data type. This function is concurrency safe. If
|
|
|
|
// the cell format can be applied to the value of a cell, the applied value
|
|
|
|
// will be returned, otherwise the original value will be returned. All cells'
|
|
|
|
// values will be the same in a merged range.
|
|
|
|
func (f *File) GetCellValue(sheet, cell string, opts ...Options) (string, error) {
|
|
|
|
return f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
|
2022-11-12 00:02:11 +08:00
|
|
|
sst, err := f.sharedStringsReader()
|
|
|
|
if err != nil {
|
|
|
|
return "", true, err
|
|
|
|
}
|
2023-01-20 11:10:04 +08:00
|
|
|
val, err := c.getValueFrom(f, sst, getOptions(opts...).RawCellValue)
|
2019-03-23 20:08:06 +08:00
|
|
|
return val, true, err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
})
|
2017-05-24 14:17:35 +08:00
|
|
|
}
|
|
|
|
|
2021-09-09 23:43:16 +08:00
|
|
|
// GetCellType provides a function to get the cell's data type by given
|
2022-09-18 00:07:15 +08:00
|
|
|
// worksheet name and cell reference in spreadsheet file.
|
|
|
|
func (f *File) GetCellType(sheet, cell string) (CellType, error) {
|
2021-09-09 23:43:16 +08:00
|
|
|
var (
|
|
|
|
err error
|
|
|
|
cellTypeStr string
|
2021-09-17 00:15:51 +08:00
|
|
|
cellType CellType
|
2021-09-09 23:43:16 +08:00
|
|
|
)
|
2022-09-18 00:07:15 +08:00
|
|
|
if cellTypeStr, err = f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
|
2021-09-09 23:43:16 +08:00
|
|
|
return c.T, true, nil
|
|
|
|
}); err != nil {
|
|
|
|
return CellTypeUnset, err
|
|
|
|
}
|
|
|
|
cellType = cellTypes[cellTypeStr]
|
|
|
|
return cellType, err
|
|
|
|
}
|
|
|
|
|
2022-09-11 00:04:04 +08:00
|
|
|
// SetCellValue provides a function to set the value of a cell. This function
|
|
|
|
// is concurrency safe. The specified coordinates should not be in the first
|
|
|
|
// row of the table, a complex number can be set with string text. The
|
|
|
|
// following shows the supported data types:
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// int
|
|
|
|
// int8
|
|
|
|
// int16
|
|
|
|
// int32
|
|
|
|
// int64
|
|
|
|
// uint
|
|
|
|
// uint8
|
|
|
|
// uint16
|
|
|
|
// uint32
|
|
|
|
// uint64
|
|
|
|
// float32
|
|
|
|
// float64
|
|
|
|
// string
|
|
|
|
// []byte
|
|
|
|
// time.Duration
|
|
|
|
// time.Time
|
|
|
|
// bool
|
|
|
|
// nil
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2022-04-30 09:54:11 +08:00
|
|
|
// Note that default date format is m/d/yy h:mm of time.Time type value. You
|
2022-09-18 00:07:15 +08:00
|
|
|
// can set numbers format by the SetCellStyle function. If you need to set the
|
2022-04-30 09:54:11 +08:00
|
|
|
// specialized date in Excel like January 0, 1900 or February 29, 1900, these
|
|
|
|
// times can not representation in Go language time.Time data type. Please set
|
|
|
|
// the cell value as number 0 or 60, then create and bind the date-time number
|
|
|
|
// format style for the cell.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetCellValue(sheet, cell string, value interface{}) error {
|
2019-04-16 10:57:21 +08:00
|
|
|
var err error
|
|
|
|
switch v := value.(type) {
|
|
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.setCellIntFunc(sheet, cell, v)
|
2019-04-16 10:57:21 +08:00
|
|
|
case float32:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellFloat(sheet, cell, float64(v), -1, 32)
|
2019-04-16 10:57:21 +08:00
|
|
|
case float64:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellFloat(sheet, cell, v, -1, 64)
|
2019-04-16 10:57:21 +08:00
|
|
|
case string:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellStr(sheet, cell, v)
|
2019-04-16 10:57:21 +08:00
|
|
|
case []byte:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellStr(sheet, cell, string(v))
|
2019-04-16 10:57:21 +08:00
|
|
|
case time.Duration:
|
2019-12-29 12:45:10 +08:00
|
|
|
_, d := setCellDuration(v)
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellDefault(sheet, cell, d)
|
2019-04-16 10:57:21 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.setDefaultTimeStyle(sheet, cell, 21)
|
2019-04-16 10:57:21 +08:00
|
|
|
case time.Time:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.setCellTimeFunc(sheet, cell, v)
|
2019-04-16 10:57:21 +08:00
|
|
|
case bool:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellBool(sheet, cell, v)
|
2019-04-16 10:57:21 +08:00
|
|
|
case nil:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellDefault(sheet, cell, "")
|
2019-04-16 10:57:21 +08:00
|
|
|
default:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellStr(sheet, cell, fmt.Sprint(value))
|
2019-04-16 10:57:21 +08:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-08-13 01:32:44 +08:00
|
|
|
// String extracts characters from a string item.
|
|
|
|
func (x xlsxSI) String() string {
|
2022-10-26 00:04:23 +08:00
|
|
|
var value strings.Builder
|
|
|
|
if x.T != nil {
|
|
|
|
value.WriteString(x.T.Val)
|
|
|
|
}
|
|
|
|
for _, s := range x.R {
|
|
|
|
if s.T != nil {
|
|
|
|
value.WriteString(s.T.Val)
|
2021-08-13 01:32:44 +08:00
|
|
|
}
|
|
|
|
}
|
2022-10-26 00:04:23 +08:00
|
|
|
if value.Len() == 0 {
|
|
|
|
return ""
|
2021-08-13 01:32:44 +08:00
|
|
|
}
|
2022-10-26 00:04:23 +08:00
|
|
|
return bstrUnmarshal(value.String())
|
2021-08-13 01:32:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// hasValue determine if cell non-blank value.
|
|
|
|
func (c *xlsxC) hasValue() bool {
|
|
|
|
return c.S != 0 || c.V != "" || c.F != nil || c.T != ""
|
|
|
|
}
|
|
|
|
|
2022-07-16 12:50:13 +08:00
|
|
|
// removeFormula delete formula for the cell.
|
2022-11-12 00:02:11 +08:00
|
|
|
func (f *File) removeFormula(c *xlsxC, ws *xlsxWorksheet, sheet string) error {
|
2022-07-18 00:21:34 +08:00
|
|
|
if c.F != nil && c.Vm == nil {
|
2022-11-08 00:35:19 +08:00
|
|
|
sheetID := f.getSheetID(sheet)
|
2022-11-12 00:02:11 +08:00
|
|
|
if err := f.deleteCalcChain(sheetID, c.R); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-07-18 00:21:34 +08:00
|
|
|
if c.F.T == STCellFormulaTypeShared && c.F.Ref != "" {
|
|
|
|
si := c.F.Si
|
|
|
|
for r, row := range ws.SheetData.Row {
|
|
|
|
for col, cell := range row.C {
|
|
|
|
if cell.F != nil && cell.F.Si != nil && *cell.F.Si == *si {
|
|
|
|
ws.SheetData.Row[r].C[col].F = nil
|
2022-11-12 00:02:11 +08:00
|
|
|
_ = f.deleteCalcChain(sheetID, cell.R)
|
2022-07-18 00:21:34 +08:00
|
|
|
}
|
2022-07-16 12:50:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-18 00:21:34 +08:00
|
|
|
c.F = nil
|
2022-07-16 12:50:13 +08:00
|
|
|
}
|
2022-11-12 00:02:11 +08:00
|
|
|
return nil
|
2022-07-16 12:50:13 +08:00
|
|
|
}
|
|
|
|
|
2019-04-16 10:57:21 +08:00
|
|
|
// setCellIntFunc is a wrapper of SetCellInt.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) setCellIntFunc(sheet, cell string, value interface{}) error {
|
2019-04-15 11:22:57 +08:00
|
|
|
var err error
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
switch v := value.(type) {
|
2017-07-24 10:26:02 +08:00
|
|
|
case int:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, v)
|
2017-07-24 10:26:02 +08:00
|
|
|
case int8:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-07-24 10:26:02 +08:00
|
|
|
case int16:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-07-24 10:26:02 +08:00
|
|
|
case int32:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-07-24 10:26:02 +08:00
|
|
|
case int64:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-10-18 18:42:20 +08:00
|
|
|
case uint:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-10-18 18:42:20 +08:00
|
|
|
case uint8:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-10-18 18:42:20 +08:00
|
|
|
case uint16:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-10-18 18:42:20 +08:00
|
|
|
case uint32:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2017-10-18 18:42:20 +08:00
|
|
|
case uint64:
|
2022-09-18 00:07:15 +08:00
|
|
|
err = f.SetCellInt(sheet, cell, int(v))
|
2019-04-16 10:57:21 +08:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// setCellTimeFunc provides a method to process time type of value for
|
|
|
|
// SetCellValue.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) setCellTimeFunc(sheet, cell string, value time.Time) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-16 10:57:21 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-12-29 12:45:10 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-07 00:57:43 +08:00
|
|
|
ws.Lock()
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
2021-07-07 00:57:43 +08:00
|
|
|
ws.Unlock()
|
2022-11-13 00:40:04 +08:00
|
|
|
var date1904, isNum bool
|
|
|
|
wb, err := f.workbookReader()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-05-02 12:30:18 +08:00
|
|
|
if wb != nil && wb.WorkbookPr != nil {
|
|
|
|
date1904 = wb.WorkbookPr.Date1904
|
|
|
|
}
|
2022-10-25 10:24:45 +08:00
|
|
|
if isNum, err = c.setCellTime(value, date1904); err != nil {
|
2019-12-29 12:45:10 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if isNum {
|
2022-09-18 00:07:15 +08:00
|
|
|
_ = f.setDefaultTimeStyle(sheet, cell, 22)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
2019-04-15 11:22:57 +08:00
|
|
|
return err
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2021-07-07 00:57:43 +08:00
|
|
|
// setCellTime prepares cell type and Excel time by given Go time.Time type
|
|
|
|
// timestamp.
|
2022-10-25 10:24:45 +08:00
|
|
|
func (c *xlsxC) setCellTime(value time.Time, date1904 bool) (isNum bool, err error) {
|
2019-12-29 12:45:10 +08:00
|
|
|
var excelTime float64
|
2021-11-24 00:09:35 +08:00
|
|
|
_, offset := value.In(value.Location()).Zone()
|
|
|
|
value = value.Add(time.Duration(offset) * time.Second)
|
2022-05-02 12:30:18 +08:00
|
|
|
if excelTime, err = timeToExcelTime(value, date1904); err != nil {
|
2019-12-29 12:45:10 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
isNum = excelTime > 0
|
|
|
|
if isNum {
|
2022-10-25 10:24:45 +08:00
|
|
|
c.setCellDefault(strconv.FormatFloat(excelTime, 'f', -1, 64))
|
2019-12-29 12:45:10 +08:00
|
|
|
} else {
|
2022-10-25 10:24:45 +08:00
|
|
|
c.setCellDefault(value.Format(time.RFC3339Nano))
|
2019-12-29 12:45:10 +08:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-07 00:57:43 +08:00
|
|
|
// setCellDuration prepares cell type and value by given Go time.Duration type
|
|
|
|
// time duration.
|
2019-12-29 12:45:10 +08:00
|
|
|
func setCellDuration(value time.Duration) (t string, v string) {
|
2022-04-30 09:54:11 +08:00
|
|
|
v = strconv.FormatFloat(value.Seconds()/86400, 'f', -1, 32)
|
2019-12-29 12:45:10 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// SetCellInt provides a function to set int type value of a cell by given
|
2022-09-18 00:07:15 +08:00
|
|
|
// worksheet name, cell reference and cell value.
|
|
|
|
func (f *File) SetCellInt(sheet, cell string, value int) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
|
|
|
c.T, c.V = setCellInt(value)
|
|
|
|
c.IS = nil
|
2022-11-12 00:02:11 +08:00
|
|
|
return f.removeFormula(c, ws, sheet)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2018-01-26 01:06:40 +08:00
|
|
|
|
2021-07-07 00:57:43 +08:00
|
|
|
// setCellInt prepares cell type and string type cell value by a given
|
|
|
|
// integer.
|
2019-12-29 12:45:10 +08:00
|
|
|
func setCellInt(value int) (t string, v string) {
|
|
|
|
v = strconv.Itoa(value)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// SetCellBool provides a function to set bool type value of a cell by given
|
2022-09-18 00:07:15 +08:00
|
|
|
// worksheet name, cell reference and cell value.
|
|
|
|
func (f *File) SetCellBool(sheet, cell string, value bool) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-07 00:57:43 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
|
|
|
c.T, c.V = setCellBool(value)
|
|
|
|
c.IS = nil
|
2022-11-12 00:02:11 +08:00
|
|
|
return f.removeFormula(c, ws, sheet)
|
2019-12-29 12:45:10 +08:00
|
|
|
}
|
|
|
|
|
2021-07-07 00:57:43 +08:00
|
|
|
// setCellBool prepares cell type and string type cell value by a given
|
|
|
|
// boolean value.
|
2019-12-29 12:45:10 +08:00
|
|
|
func setCellBool(value bool) (t string, v string) {
|
|
|
|
t = "b"
|
2018-01-26 01:06:40 +08:00
|
|
|
if value {
|
2019-12-29 12:45:10 +08:00
|
|
|
v = "1"
|
2018-01-26 01:06:40 +08:00
|
|
|
} else {
|
2019-12-29 12:45:10 +08:00
|
|
|
v = "0"
|
2018-01-26 01:06:40 +08:00
|
|
|
}
|
2019-12-29 12:45:10 +08:00
|
|
|
return
|
2018-01-26 01:06:40 +08:00
|
|
|
}
|
|
|
|
|
2022-09-18 00:07:15 +08:00
|
|
|
// SetCellFloat sets a floating point value into a cell. The precision
|
|
|
|
// parameter specifies how many places after the decimal will be shown
|
|
|
|
// while -1 is a special value that will use as many decimal places as
|
|
|
|
// necessary to represent the number. bitSize is 32 or 64 depending on if a
|
|
|
|
// float32 or float64 was originally used for the value. For Example:
|
2019-03-21 14:09:25 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// var x float32 = 1.325
|
|
|
|
// f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32)
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSize int) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-07 00:57:43 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
|
|
|
c.T, c.V = setCellFloat(value, precision, bitSize)
|
|
|
|
c.IS = nil
|
2022-11-12 00:02:11 +08:00
|
|
|
return f.removeFormula(c, ws, sheet)
|
2019-03-21 11:41:46 +08:00
|
|
|
}
|
|
|
|
|
2021-07-07 00:57:43 +08:00
|
|
|
// setCellFloat prepares cell type and string type cell value by a given
|
|
|
|
// float value.
|
2022-03-24 00:19:30 +08:00
|
|
|
func setCellFloat(value float64, precision, bitSize int) (t string, v string) {
|
|
|
|
v = strconv.FormatFloat(value, 'f', precision, bitSize)
|
2019-12-29 12:45:10 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// SetCellStr provides a function to set string type value of a cell. Total
|
|
|
|
// number of characters that a cell can contain 32767 characters.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetCellStr(sheet, cell, value string) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
2022-11-12 00:02:11 +08:00
|
|
|
if c.T, c.V, err = f.setCellString(value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c.IS = nil
|
2022-11-12 00:02:11 +08:00
|
|
|
return f.removeFormula(c, ws, sheet)
|
2019-12-29 12:45:10 +08:00
|
|
|
}
|
|
|
|
|
2020-05-26 02:09:39 +08:00
|
|
|
// setCellString provides a function to set string type to shared string
|
|
|
|
// table.
|
2021-12-27 23:34:14 +08:00
|
|
|
func (f *File) setCellString(value string) (t, v string, err error) {
|
2023-04-12 08:17:10 +08:00
|
|
|
if utf8.RuneCountInString(value) > TotalCellChars {
|
|
|
|
value = string([]rune(value)[:TotalCellChars])
|
2020-05-26 02:09:39 +08:00
|
|
|
}
|
|
|
|
t = "s"
|
2021-12-27 23:34:14 +08:00
|
|
|
var si int
|
|
|
|
if si, err = f.setSharedString(value); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v = strconv.Itoa(si)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// sharedStringsLoader load shared string table from system temporary file to
|
|
|
|
// memory, and reset shared string table for reader.
|
|
|
|
func (f *File) sharedStringsLoader() (err error) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2022-01-09 00:20:42 +08:00
|
|
|
if path, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok {
|
|
|
|
f.Pkg.Store(defaultXMLPathSharedStrings, f.readBytes(defaultXMLPathSharedStrings))
|
|
|
|
f.tempFiles.Delete(defaultXMLPathSharedStrings)
|
2022-01-11 00:24:24 +08:00
|
|
|
if err = os.Remove(path.(string)); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
f.SharedStrings = nil
|
|
|
|
}
|
|
|
|
if f.sharedStringTemp != nil {
|
|
|
|
if err := f.sharedStringTemp.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f.tempFiles.Delete(defaultTempFileSST)
|
|
|
|
f.sharedStringItem, err = nil, os.Remove(f.sharedStringTemp.Name())
|
|
|
|
f.sharedStringTemp = nil
|
2021-12-27 23:34:14 +08:00
|
|
|
}
|
2020-05-26 02:09:39 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// setSharedString provides a function to add string to the share string table.
|
2021-12-27 23:34:14 +08:00
|
|
|
func (f *File) setSharedString(val string) (int, error) {
|
|
|
|
if err := f.sharedStringsLoader(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-11-12 00:02:11 +08:00
|
|
|
sst, err := f.sharedStringsReader()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2020-08-15 00:09:50 +08:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2020-05-27 00:02:29 +08:00
|
|
|
if i, ok := f.sharedStringsMap[val]; ok {
|
2021-12-27 23:34:14 +08:00
|
|
|
return i, nil
|
2020-05-26 02:09:39 +08:00
|
|
|
}
|
|
|
|
sst.Count++
|
|
|
|
sst.UniqueCount++
|
2020-07-01 22:41:29 +08:00
|
|
|
t := xlsxT{Val: val}
|
2023-04-16 14:22:55 +08:00
|
|
|
val, t.Space = trimCellValue(val, false)
|
2020-07-01 22:41:29 +08:00
|
|
|
sst.SI = append(sst.SI, xlsxSI{T: &t})
|
2020-05-27 00:02:29 +08:00
|
|
|
f.sharedStringsMap[val] = sst.UniqueCount - 1
|
2021-12-27 23:34:14 +08:00
|
|
|
return sst.UniqueCount - 1, nil
|
2020-05-26 02:09:39 +08:00
|
|
|
}
|
|
|
|
|
2022-10-25 10:24:45 +08:00
|
|
|
// trimCellValue provides a function to set string type to cell.
|
2023-04-16 14:22:55 +08:00
|
|
|
func trimCellValue(value string, escape bool) (v string, ns xml.Attr) {
|
2023-04-12 08:17:10 +08:00
|
|
|
if utf8.RuneCountInString(value) > TotalCellChars {
|
|
|
|
value = string([]rune(value)[:TotalCellChars])
|
2019-04-14 12:55:44 +08:00
|
|
|
}
|
2023-04-16 14:22:55 +08:00
|
|
|
if escape {
|
2023-04-17 08:48:30 +08:00
|
|
|
var buf bytes.Buffer
|
|
|
|
_ = xml.EscapeText(&buf, []byte(value))
|
2023-04-16 14:22:55 +08:00
|
|
|
value = buf.String()
|
|
|
|
}
|
2021-07-31 14:20:29 +08:00
|
|
|
if len(value) > 0 {
|
|
|
|
prefix, suffix := value[0], value[len(value)-1]
|
2021-12-30 00:36:04 +08:00
|
|
|
for _, ascii := range []byte{9, 10, 13, 32} {
|
2021-07-31 14:20:29 +08:00
|
|
|
if prefix == ascii || suffix == ascii {
|
|
|
|
ns = xml.Attr{
|
|
|
|
Name: xml.Name{Space: NameSpaceXML, Local: "space"},
|
|
|
|
Value: "preserve",
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
2016-12-24 22:47:36 +08:00
|
|
|
}
|
|
|
|
}
|
2022-10-25 10:24:45 +08:00
|
|
|
v = bstrMarshal(value)
|
2019-12-29 12:45:10 +08:00
|
|
|
return
|
2017-05-05 14:40:28 +08:00
|
|
|
}
|
|
|
|
|
2022-10-25 10:24:45 +08:00
|
|
|
// setCellValue set cell data type and value for (inline) rich string cell or
|
|
|
|
// formula cell.
|
|
|
|
func (c *xlsxC) setCellValue(val string) {
|
|
|
|
if c.F != nil {
|
|
|
|
c.setStr(val)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.setInlineStr(val)
|
|
|
|
}
|
|
|
|
|
|
|
|
// setInlineStr set cell data type and value which containing an (inline) rich
|
|
|
|
// string.
|
|
|
|
func (c *xlsxC) setInlineStr(val string) {
|
|
|
|
c.T, c.V, c.IS = "inlineStr", "", &xlsxSI{T: &xlsxT{}}
|
2023-04-16 14:22:55 +08:00
|
|
|
c.IS.T.Val, c.IS.T.Space = trimCellValue(val, true)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// setStr set cell data type and value which containing a formula string.
|
|
|
|
func (c *xlsxC) setStr(val string) {
|
|
|
|
c.T, c.IS = "str", nil
|
2023-04-16 14:22:55 +08:00
|
|
|
c.V, c.XMLSpace = trimCellValue(val, false)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// getCellDate parse cell value which containing a boolean.
|
|
|
|
func (c *xlsxC) getCellBool(f *File, raw bool) (string, error) {
|
|
|
|
if !raw {
|
|
|
|
if c.V == "1" {
|
|
|
|
return "TRUE", nil
|
|
|
|
}
|
|
|
|
if c.V == "0" {
|
|
|
|
return "FALSE", nil
|
|
|
|
}
|
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(c, raw, CellTypeBool)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// setCellDefault prepares cell type and string type cell value by a given
|
|
|
|
// string.
|
|
|
|
func (c *xlsxC) setCellDefault(value string) {
|
|
|
|
if ok, _, _ := isNumeric(value); !ok {
|
2023-01-08 00:23:53 +08:00
|
|
|
if value != "" {
|
|
|
|
c.setInlineStr(value)
|
|
|
|
c.IS.T.Val = value
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.T, c.V, c.IS = value, value, nil
|
2022-10-25 10:24:45 +08:00
|
|
|
return
|
|
|
|
}
|
2023-02-07 00:08:11 +08:00
|
|
|
c.T, c.V = "", value
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// getCellDate parse cell value which contains a date in the ISO 8601 format.
|
|
|
|
func (c *xlsxC) getCellDate(f *File, raw bool) (string, error) {
|
|
|
|
if !raw {
|
|
|
|
layout := "20060102T150405.999"
|
|
|
|
if strings.HasSuffix(c.V, "Z") {
|
|
|
|
layout = "20060102T150405Z"
|
|
|
|
if strings.Contains(c.V, "-") {
|
|
|
|
layout = "2006-01-02T15:04:05Z"
|
|
|
|
}
|
|
|
|
} else if strings.Contains(c.V, "-") {
|
|
|
|
layout = "2006-01-02 15:04:05Z"
|
|
|
|
}
|
|
|
|
if timestamp, err := time.Parse(layout, strings.ReplaceAll(c.V, ",", ".")); err == nil {
|
|
|
|
excelTime, _ := timeToExcelTime(timestamp, false)
|
|
|
|
c.V = strconv.FormatFloat(excelTime, 'G', 15, 64)
|
|
|
|
}
|
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(c, raw, CellTypeBool)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// getValueFrom return a value from a column/row cell, this function is
|
|
|
|
// intended to be used with for range on rows an argument with the spreadsheet
|
|
|
|
// opened file.
|
|
|
|
func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
switch c.T {
|
|
|
|
case "b":
|
|
|
|
return c.getCellBool(f, raw)
|
|
|
|
case "d":
|
|
|
|
return c.getCellDate(f, raw)
|
|
|
|
case "s":
|
|
|
|
if c.V != "" {
|
2023-04-19 00:05:59 +08:00
|
|
|
xlsxSI, _ := strconv.Atoi(strings.TrimSpace(c.V))
|
2022-10-25 10:24:45 +08:00
|
|
|
if _, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok {
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(&xlsxC{S: c.S, V: f.getFromStringItem(xlsxSI)}, raw, CellTypeSharedString)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
if len(d.SI) > xlsxSI {
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(&xlsxC{S: c.S, V: d.SI[xlsxSI].String()}, raw, CellTypeSharedString)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(c, raw, CellTypeSharedString)
|
2022-10-25 10:24:45 +08:00
|
|
|
case "inlineStr":
|
|
|
|
if c.IS != nil {
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(&xlsxC{S: c.S, V: c.IS.String()}, raw, CellTypeInlineString)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(c, raw, CellTypeInlineString)
|
2022-10-25 10:24:45 +08:00
|
|
|
default:
|
|
|
|
if isNum, precision, decimal := isNumeric(c.V); isNum && !raw {
|
|
|
|
if precision > 15 {
|
|
|
|
c.V = strconv.FormatFloat(decimal, 'G', 15, 64)
|
|
|
|
} else {
|
|
|
|
c.V = strconv.FormatFloat(decimal, 'f', -1, 64)
|
|
|
|
}
|
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
return f.formattedValue(c, raw, CellTypeNumber)
|
2022-10-25 10:24:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// SetCellDefault provides a function to set string type value of a cell as
|
|
|
|
// default format without escaping the cell.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetCellDefault(sheet, cell, value string) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-07 00:57:43 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
2022-10-25 10:24:45 +08:00
|
|
|
c.setCellDefault(value)
|
2022-11-12 00:02:11 +08:00
|
|
|
return f.removeFormula(c, ws, sheet)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// GetCellFormula provides a function to get formula from cell by given
|
2022-09-18 00:07:15 +08:00
|
|
|
// worksheet name and cell reference in spreadsheet.
|
|
|
|
func (f *File) GetCellFormula(sheet, cell string) (string, error) {
|
|
|
|
return f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if c.F == nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return "", false, nil
|
2016-08-30 21:54:28 +08:00
|
|
|
}
|
2021-09-03 22:51:56 +08:00
|
|
|
if c.F.T == STCellFormulaTypeShared && c.F.Si != nil {
|
2022-01-09 00:20:42 +08:00
|
|
|
return getSharedFormula(x, *c.F.Si, c.R), true, nil
|
2018-05-26 16:23:15 +08:00
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return c.F.Content, true, nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
})
|
2018-05-26 16:23:15 +08:00
|
|
|
}
|
|
|
|
|
2019-09-22 20:52:01 +08:00
|
|
|
// FormulaOpts can be passed to SetCellFormula to use other formula types.
|
|
|
|
type FormulaOpts struct {
|
|
|
|
Type *string // Formula type
|
|
|
|
Ref *string // Shared formula ref
|
|
|
|
}
|
|
|
|
|
2021-09-03 22:51:56 +08:00
|
|
|
// SetCellFormula provides a function to set formula on the cell is taken
|
2022-07-18 00:21:34 +08:00
|
|
|
// according to the given worksheet name and cell formula settings. The result
|
|
|
|
// of the formula cell can be calculated when the worksheet is opened by the
|
|
|
|
// Office Excel application or can be using the "CalcCellValue" function also
|
|
|
|
// can get the calculated cell value. If the Excel application doesn't
|
|
|
|
// calculate the formula automatically when the workbook has been opened,
|
|
|
|
// please call "UpdateLinkedValue" after setting the cell formula functions.
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
|
|
|
// Example 1, set normal formula "=SUM(A1,B1)" for the cell "A3" on "Sheet1":
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetCellFormula("Sheet1", "A3", "=SUM(A1,B1)")
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
2023-01-20 11:10:04 +08:00
|
|
|
// Example 2, set one-dimensional vertical constant array (column array) formula
|
2021-09-03 22:51:56 +08:00
|
|
|
// "1,2,3" for the cell "A3" on "Sheet1":
|
|
|
|
//
|
2023-01-20 11:10:04 +08:00
|
|
|
// err := f.SetCellFormula("Sheet1", "A3", "={1;2;3}")
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
2023-01-20 11:10:04 +08:00
|
|
|
// Example 3, set one-dimensional horizontal constant array (row array)
|
2021-09-03 22:51:56 +08:00
|
|
|
// formula '"a","b","c"' for the cell "A3" on "Sheet1":
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetCellFormula("Sheet1", "A3", "={\"a\",\"b\",\"c\"}")
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
|
|
|
// Example 4, set two-dimensional constant array formula '{1,2,"a","b"}' for
|
|
|
|
// the cell "A3" on "Sheet1":
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// formulaType, ref := excelize.STCellFormulaTypeArray, "A3:A3"
|
2023-01-20 11:10:04 +08:00
|
|
|
// err := f.SetCellFormula("Sheet1", "A3", "={1,2;\"a\",\"b\"}",
|
2022-08-13 11:21:59 +08:00
|
|
|
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
|
|
|
// Example 5, set range array formula "A1:A2" for the cell "A3" on "Sheet1":
|
|
|
|
//
|
Breaking change: changed the function signature for 11 exported functions
* Change
`func (f *File) NewConditionalStyle(style string) (int, error)`
to
`func (f *File) NewConditionalStyle(style *Style) (int, error)`
* Change
`func (f *File) NewStyle(style interface{}) (int, error)`
to
`func (f *File) NewStyle(style *Style) (int, error)`
* Change
`func (f *File) AddChart(sheet, cell, opts string, combo ...string) error`
to
`func (f *File) AddChart(sheet, cell string, chart *ChartOptions, combo ...*ChartOptions) error`
* Change
`func (f *File) AddChartSheet(sheet, opts string, combo ...string) error`
to
`func (f *File) AddChartSheet(sheet string, chart *ChartOptions, combo ...*ChartOptions) error`
* Change
`func (f *File) AddShape(sheet, cell, opts string) error`
to
`func (f *File) AddShape(sheet, cell string, opts *Shape) error`
* Change
`func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, file []byte) error`
to
`func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *PictureOptions) error`
* Change
`func (f *File) AddTable(sheet, hCell, vCell, opts string) error`
to
`func (f *File) AddTable(sheet, reference string, opts *TableOptions) error`
* Change
`func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error`
to
`func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error`
* Change
`func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error`
to
`func (f *File) AutoFilter(sheet, reference string, opts *AutoFilterOptions) error`
* Change
`func (f *File) SetPanes(sheet, panes string) error`
to
`func (f *File) SetPanes(sheet string, panes *Panes) error`
* Change
`func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error`
to
`func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error`
* Change
`func (f *File) SetConditionalFormat(sheet, reference, opts string) error`
to
`func (f *File) SetConditionalFormat(sheet, reference string, opts []ConditionalFormatOptions) error`
* Add exported types:
* AutoFilterListOptions
* AutoFilterOptions
* Chart
* ChartAxis
* ChartDimension
* ChartLegend
* ChartLine
* ChartMarker
* ChartPlotArea
* ChartSeries
* ChartTitle
* ConditionalFormatOptions
* PaneOptions
* Panes
* PictureOptions
* Shape
* ShapeColor
* ShapeLine
* ShapeParagraph
* TableOptions
* This added support for set sheet visible as very hidden
* Return error when missing required parameters for set defined name
* Update unit test and comments
2022-12-30 00:50:08 +08:00
|
|
|
// formulaType, ref := excelize.STCellFormulaTypeArray, "A3:A3"
|
|
|
|
// err := f.SetCellFormula("Sheet1", "A3", "=A1:A2",
|
|
|
|
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
|
|
|
// Example 6, set shared formula "=A1+B1" for the cell "C1:C5"
|
|
|
|
// on "Sheet1", "C1" is the master cell:
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// formulaType, ref := excelize.STCellFormulaTypeShared, "C1:C5"
|
|
|
|
// err := f.SetCellFormula("Sheet1", "C1", "=A1+B1",
|
|
|
|
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
|
2021-09-03 22:51:56 +08:00
|
|
|
//
|
2021-09-05 11:59:50 +08:00
|
|
|
// Example 7, set table formula "=SUM(Table1[[A]:[B]])" for the cell "C2"
|
|
|
|
// on "Sheet1":
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// package main
|
2021-09-05 11:59:50 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// import (
|
|
|
|
// "fmt"
|
2021-09-05 11:59:50 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// "github.com/xuri/excelize/v2"
|
|
|
|
// )
|
2021-09-05 11:59:50 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// func main() {
|
|
|
|
// f := excelize.NewFile()
|
2023-01-02 11:47:31 +08:00
|
|
|
// defer func() {
|
|
|
|
// if err := f.Close(); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// }()
|
2022-08-13 11:21:59 +08:00
|
|
|
// for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
|
|
|
|
// if err := f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row); err != nil {
|
Breaking change: changed the function signature for 11 exported functions
* Change
`func (f *File) NewConditionalStyle(style string) (int, error)`
to
`func (f *File) NewConditionalStyle(style *Style) (int, error)`
* Change
`func (f *File) NewStyle(style interface{}) (int, error)`
to
`func (f *File) NewStyle(style *Style) (int, error)`
* Change
`func (f *File) AddChart(sheet, cell, opts string, combo ...string) error`
to
`func (f *File) AddChart(sheet, cell string, chart *ChartOptions, combo ...*ChartOptions) error`
* Change
`func (f *File) AddChartSheet(sheet, opts string, combo ...string) error`
to
`func (f *File) AddChartSheet(sheet string, chart *ChartOptions, combo ...*ChartOptions) error`
* Change
`func (f *File) AddShape(sheet, cell, opts string) error`
to
`func (f *File) AddShape(sheet, cell string, opts *Shape) error`
* Change
`func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, file []byte) error`
to
`func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *PictureOptions) error`
* Change
`func (f *File) AddTable(sheet, hCell, vCell, opts string) error`
to
`func (f *File) AddTable(sheet, reference string, opts *TableOptions) error`
* Change
`func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error`
to
`func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error`
* Change
`func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error`
to
`func (f *File) AutoFilter(sheet, reference string, opts *AutoFilterOptions) error`
* Change
`func (f *File) SetPanes(sheet, panes string) error`
to
`func (f *File) SetPanes(sheet string, panes *Panes) error`
* Change
`func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error`
to
`func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error`
* Change
`func (f *File) SetConditionalFormat(sheet, reference, opts string) error`
to
`func (f *File) SetConditionalFormat(sheet, reference string, opts []ConditionalFormatOptions) error`
* Add exported types:
* AutoFilterListOptions
* AutoFilterOptions
* Chart
* ChartAxis
* ChartDimension
* ChartLegend
* ChartLine
* ChartMarker
* ChartPlotArea
* ChartSeries
* ChartTitle
* ConditionalFormatOptions
* PaneOptions
* Panes
* PictureOptions
* Shape
* ShapeColor
* ShapeLine
* ShapeParagraph
* TableOptions
* This added support for set sheet visible as very hidden
* Return error when missing required parameters for set defined name
* Update unit test and comments
2022-12-30 00:50:08 +08:00
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
2022-08-13 11:21:59 +08:00
|
|
|
// }
|
|
|
|
// }
|
2023-03-25 13:30:13 +08:00
|
|
|
// if err := f.AddTable("Sheet1", &excelize.Table{
|
|
|
|
// Range: "A1:C2", Name: "Table1", StyleName: "TableStyleMedium2",
|
Breaking change: changed the function signature for 11 exported functions
* Change
`func (f *File) NewConditionalStyle(style string) (int, error)`
to
`func (f *File) NewConditionalStyle(style *Style) (int, error)`
* Change
`func (f *File) NewStyle(style interface{}) (int, error)`
to
`func (f *File) NewStyle(style *Style) (int, error)`
* Change
`func (f *File) AddChart(sheet, cell, opts string, combo ...string) error`
to
`func (f *File) AddChart(sheet, cell string, chart *ChartOptions, combo ...*ChartOptions) error`
* Change
`func (f *File) AddChartSheet(sheet, opts string, combo ...string) error`
to
`func (f *File) AddChartSheet(sheet string, chart *ChartOptions, combo ...*ChartOptions) error`
* Change
`func (f *File) AddShape(sheet, cell, opts string) error`
to
`func (f *File) AddShape(sheet, cell string, opts *Shape) error`
* Change
`func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, file []byte) error`
to
`func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *PictureOptions) error`
* Change
`func (f *File) AddTable(sheet, hCell, vCell, opts string) error`
to
`func (f *File) AddTable(sheet, reference string, opts *TableOptions) error`
* Change
`func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error`
to
`func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error`
* Change
`func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error`
to
`func (f *File) AutoFilter(sheet, reference string, opts *AutoFilterOptions) error`
* Change
`func (f *File) SetPanes(sheet, panes string) error`
to
`func (f *File) SetPanes(sheet string, panes *Panes) error`
* Change
`func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error`
to
`func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error`
* Change
`func (f *File) SetConditionalFormat(sheet, reference, opts string) error`
to
`func (f *File) SetConditionalFormat(sheet, reference string, opts []ConditionalFormatOptions) error`
* Add exported types:
* AutoFilterListOptions
* AutoFilterOptions
* Chart
* ChartAxis
* ChartDimension
* ChartLegend
* ChartLine
* ChartMarker
* ChartPlotArea
* ChartSeries
* ChartTitle
* ConditionalFormatOptions
* PaneOptions
* Panes
* PictureOptions
* Shape
* ShapeColor
* ShapeLine
* ShapeParagraph
* TableOptions
* This added support for set sheet visible as very hidden
* Return error when missing required parameters for set defined name
* Update unit test and comments
2022-12-30 00:50:08 +08:00
|
|
|
// }); err != nil {
|
2022-08-13 11:21:59 +08:00
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// formulaType := excelize.STCellFormulaTypeDataTable
|
|
|
|
// if err := f.SetCellFormula("Sheet1", "C2", "=SUM(Table1[[A]:[B]])",
|
|
|
|
// excelize.FormulaOpts{Type: &formulaType}); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// }
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetCellFormula(sheet, cell, formula string, opts ...FormulaOpts) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, _, _, err := f.prepareCell(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-02-22 22:17:38 +08:00
|
|
|
if formula == "" {
|
2022-09-18 00:07:15 +08:00
|
|
|
c.F = nil
|
2022-11-12 00:02:11 +08:00
|
|
|
return f.deleteCalcChain(f.getSheetID(sheet), cell)
|
2019-02-22 22:17:38 +08:00
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
|
2022-09-18 00:07:15 +08:00
|
|
|
if c.F != nil {
|
|
|
|
c.F.Content = formula
|
2017-01-19 14:05:32 +08:00
|
|
|
} else {
|
2022-09-18 00:07:15 +08:00
|
|
|
c.F = &xlsxF{Content: formula}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2019-09-22 20:52:01 +08:00
|
|
|
|
2022-09-18 00:07:15 +08:00
|
|
|
for _, opt := range opts {
|
|
|
|
if opt.Type != nil {
|
|
|
|
if *opt.Type == STCellFormulaTypeDataTable {
|
2021-09-05 11:59:50 +08:00
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c.F.T = *opt.Type
|
|
|
|
if c.F.T == STCellFormulaTypeShared {
|
|
|
|
if err = ws.setSharedFormula(*opt.Ref); err != nil {
|
2021-09-03 22:51:56 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-09-22 20:52:01 +08:00
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
if opt.Ref != nil {
|
|
|
|
c.F.Ref = *opt.Ref
|
2019-09-22 20:52:01 +08:00
|
|
|
}
|
|
|
|
}
|
2022-10-25 10:24:45 +08:00
|
|
|
c.T, c.IS = "str", nil
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2021-09-03 22:51:56 +08:00
|
|
|
// setSharedFormula set shared formula for the cells.
|
|
|
|
func (ws *xlsxWorksheet) setSharedFormula(ref string) error {
|
2022-09-28 00:04:17 +08:00
|
|
|
coordinates, err := rangeRefToCoordinates(ref)
|
2021-09-03 22:51:56 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_ = sortCoordinates(coordinates)
|
|
|
|
cnt := ws.countSharedFormula()
|
|
|
|
for c := coordinates[0]; c <= coordinates[2]; c++ {
|
|
|
|
for r := coordinates[1]; r <= coordinates[3]; r++ {
|
|
|
|
prepareSheetXML(ws, c, r)
|
|
|
|
cell := &ws.SheetData.Row[r-1].C[c-1]
|
|
|
|
if cell.F == nil {
|
|
|
|
cell.F = &xlsxF{}
|
|
|
|
}
|
|
|
|
cell.F.T = STCellFormulaTypeShared
|
|
|
|
cell.F.Si = &cnt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// countSharedFormula count shared formula in the given worksheet.
|
|
|
|
func (ws *xlsxWorksheet) countSharedFormula() (count int) {
|
|
|
|
for _, row := range ws.SheetData.Row {
|
|
|
|
for _, cell := range row.C {
|
|
|
|
if cell.F != nil && cell.F.Si != nil && *cell.F.Si+1 > count {
|
|
|
|
count = *cell.F.Si + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-18 00:21:34 +08:00
|
|
|
// GetCellHyperLink gets a cell hyperlink based on the given worksheet name and
|
2022-09-18 00:07:15 +08:00
|
|
|
// cell reference. If the cell has a hyperlink, it will return 'true' and
|
2022-07-18 00:21:34 +08:00
|
|
|
// the link address, otherwise it will return 'false' and an empty link
|
|
|
|
// address.
|
|
|
|
//
|
|
|
|
// For example, get a hyperlink to a 'H6' cell on a worksheet named 'Sheet1':
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// link, target, err := f.GetCellHyperLink("Sheet1", "H6")
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) GetCellHyperLink(sheet, cell string) (bool, string, error) {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// Check for correct cell name
|
2022-09-18 00:07:15 +08:00
|
|
|
if _, _, err := SplitCellName(cell); err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return false, "", err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, "", err
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.Hyperlinks != nil {
|
|
|
|
for _, link := range ws.Hyperlinks.Hyperlink {
|
2023-03-29 00:00:27 +08:00
|
|
|
ok, err := f.checkCellInRangeRef(cell, link.Ref)
|
|
|
|
if err != nil {
|
|
|
|
return false, "", err
|
|
|
|
}
|
|
|
|
if link.Ref == cell || ok {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if link.RID != "" {
|
2019-03-23 20:08:06 +08:00
|
|
|
return true, f.getSheetRelationshipsTargetByID(sheet, link.RID), err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return true, link.Location, err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2017-01-19 14:05:32 +08:00
|
|
|
}
|
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return false, "", err
|
2017-01-19 14:05:32 +08:00
|
|
|
}
|
2017-01-23 16:15:01 +08:00
|
|
|
|
2021-03-03 20:30:31 +08:00
|
|
|
// HyperlinkOpts can be passed to SetCellHyperlink to set optional hyperlink
|
|
|
|
// attributes (e.g. display value)
|
|
|
|
type HyperlinkOpts struct {
|
|
|
|
Display *string
|
|
|
|
Tooltip *string
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// SetCellHyperLink provides a function to set cell hyperlink by given
|
|
|
|
// worksheet name and link URL address. LinkType defines two types of
|
2022-03-30 00:01:38 +08:00
|
|
|
// hyperlink "External" for website or "Location" for moving to one of cell in
|
|
|
|
// this workbook. Maximum limit hyperlinks in a worksheet is 65530. This
|
|
|
|
// function is only used to set the hyperlink of the cell and doesn't affect
|
|
|
|
// the value of the cell. If you need to set the value of the cell, please use
|
|
|
|
// the other functions such as `SetCellStyle` or `SetSheetRow`. The below is
|
|
|
|
// example for external link.
|
2017-07-26 18:37:00 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// display, tooltip := "https://github.com/xuri/excelize", "Excelize on GitHub"
|
|
|
|
// if err := f.SetCellHyperLink("Sheet1", "A3",
|
2023-02-01 00:11:08 +08:00
|
|
|
// display, "External", excelize.HyperlinkOpts{
|
2022-08-13 11:21:59 +08:00
|
|
|
// Display: &display,
|
|
|
|
// Tooltip: &tooltip,
|
|
|
|
// }); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// // Set underline and font color style for the cell.
|
|
|
|
// style, err := f.NewStyle(&excelize.Style{
|
2023-02-27 00:05:36 +08:00
|
|
|
// Font: &excelize.Font{Color: "1265BE", Underline: "single"},
|
2022-08-13 11:21:59 +08:00
|
|
|
// })
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// err = f.SetCellStyle("Sheet1", "A3", "A3", style)
|
2017-07-26 18:37:00 +08:00
|
|
|
//
|
2022-03-24 00:19:30 +08:00
|
|
|
// This is another example for "Location":
|
2017-07-30 13:52:37 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetCellHyperLink(sheet, cell, link, linkType string, opts ...HyperlinkOpts) error {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// Check for correct cell name
|
2022-09-18 00:07:15 +08:00
|
|
|
if _, _, err := SplitCellName(cell); err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
2017-07-30 15:46:04 +08:00
|
|
|
}
|
2017-01-25 15:44:18 +08:00
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
if cell, err = f.mergeCellsParser(ws, cell); err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
|
|
|
|
var linkData xlsxHyperlink
|
2022-05-01 12:28:36 +08:00
|
|
|
idx := -1
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.Hyperlinks == nil {
|
|
|
|
ws.Hyperlinks = new(xlsxHyperlinks)
|
2019-04-21 00:04:42 +08:00
|
|
|
}
|
2022-05-01 12:28:36 +08:00
|
|
|
for i, hyperlink := range ws.Hyperlinks.Hyperlink {
|
2022-09-18 00:07:15 +08:00
|
|
|
if hyperlink.Ref == cell {
|
2022-05-01 12:28:36 +08:00
|
|
|
idx = i
|
|
|
|
linkData = hyperlink
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-04-21 00:04:42 +08:00
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
if len(ws.Hyperlinks.Hyperlink) > TotalSheetHyperlinks {
|
2021-05-10 00:09:24 +08:00
|
|
|
return ErrTotalSheetHyperlinks
|
2019-04-21 00:04:42 +08:00
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
switch linkType {
|
|
|
|
case "External":
|
2022-07-18 00:21:34 +08:00
|
|
|
sheetPath, _ := f.getSheetXMLPath(sheet)
|
2022-05-01 12:28:36 +08:00
|
|
|
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
|
|
|
|
rID := f.setRels(linkData.RID, sheetRels, SourceRelationshipHyperLink, link, linkType)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
linkData = xlsxHyperlink{
|
2022-09-18 00:07:15 +08:00
|
|
|
Ref: cell,
|
2017-08-08 20:08:54 +08:00
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
linkData.RID = "rId" + strconv.Itoa(rID)
|
2020-07-18 15:15:16 +08:00
|
|
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
case "Location":
|
|
|
|
linkData = xlsxHyperlink{
|
2022-09-18 00:07:15 +08:00
|
|
|
Ref: cell,
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
Location: link,
|
|
|
|
}
|
|
|
|
default:
|
2019-03-23 20:08:06 +08:00
|
|
|
return fmt.Errorf("invalid link type %q", linkType)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2021-03-03 20:30:31 +08:00
|
|
|
for _, o := range opts {
|
|
|
|
if o.Display != nil {
|
|
|
|
linkData.Display = *o.Display
|
|
|
|
}
|
|
|
|
if o.Tooltip != nil {
|
|
|
|
linkData.Tooltip = *o.Tooltip
|
|
|
|
}
|
|
|
|
}
|
2022-05-01 12:28:36 +08:00
|
|
|
if idx == -1 {
|
|
|
|
ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink, linkData)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ws.Hyperlinks.Hyperlink[idx] = linkData
|
|
|
|
return err
|
2017-08-08 20:08:54 +08:00
|
|
|
}
|
|
|
|
|
2022-04-30 09:54:11 +08:00
|
|
|
// getCellRichText returns rich text of cell by given string item.
|
|
|
|
func getCellRichText(si *xlsxSI) (runs []RichTextRun) {
|
2021-02-22 20:04:13 +08:00
|
|
|
for _, v := range si.R {
|
|
|
|
run := RichTextRun{
|
|
|
|
Text: v.T.Val,
|
|
|
|
}
|
2022-04-29 13:53:09 +08:00
|
|
|
if v.RPr != nil {
|
2022-11-02 08:42:00 +08:00
|
|
|
run.Font = newFont(v.RPr)
|
2021-02-22 20:04:13 +08:00
|
|
|
}
|
|
|
|
runs = append(runs, run)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-04-30 09:54:11 +08:00
|
|
|
// GetCellRichText provides a function to get rich text of cell by given
|
|
|
|
// worksheet.
|
|
|
|
func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err error) {
|
|
|
|
ws, err := f.workSheetReader(sheet)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, _, _, err := f.prepareCell(ws, cell)
|
2022-04-30 09:54:11 +08:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
siIdx, err := strconv.Atoi(c.V)
|
|
|
|
if err != nil || c.T != "s" {
|
2022-04-30 09:54:11 +08:00
|
|
|
return
|
|
|
|
}
|
2022-11-12 00:02:11 +08:00
|
|
|
sst, err := f.sharedStringsReader()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2022-04-30 09:54:11 +08:00
|
|
|
if len(sst.SI) <= siIdx || siIdx < 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
runs = getCellRichText(&sst.SI[siIdx])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-27 23:34:14 +08:00
|
|
|
// newRpr create run properties for the rich text by given font format.
|
|
|
|
func newRpr(fnt *Font) *xlsxRPr {
|
|
|
|
rpr := xlsxRPr{}
|
|
|
|
trueVal := ""
|
|
|
|
if fnt.Bold {
|
|
|
|
rpr.B = &trueVal
|
|
|
|
}
|
|
|
|
if fnt.Italic {
|
|
|
|
rpr.I = &trueVal
|
|
|
|
}
|
|
|
|
if fnt.Strike {
|
|
|
|
rpr.Strike = &trueVal
|
|
|
|
}
|
|
|
|
if fnt.Underline != "" {
|
|
|
|
rpr.U = &attrValString{Val: &fnt.Underline}
|
|
|
|
}
|
|
|
|
if fnt.Family != "" {
|
|
|
|
rpr.RFont = &attrValString{Val: &fnt.Family}
|
|
|
|
}
|
2022-06-13 23:38:59 +08:00
|
|
|
if inStrSlice([]string{"baseline", "superscript", "subscript"}, fnt.VertAlign, true) != -1 {
|
|
|
|
rpr.VertAlign = &attrValString{Val: &fnt.VertAlign}
|
|
|
|
}
|
|
|
|
if fnt.Size > 0 {
|
2021-12-27 23:34:14 +08:00
|
|
|
rpr.Sz = &attrValFloat{Val: &fnt.Size}
|
|
|
|
}
|
2022-10-15 00:03:49 +08:00
|
|
|
rpr.Color = newFontColor(fnt)
|
2021-12-27 23:34:14 +08:00
|
|
|
return &rpr
|
|
|
|
}
|
|
|
|
|
2022-11-02 08:42:00 +08:00
|
|
|
// newFont create font format by given run properties for the rich text.
|
|
|
|
func newFont(rPr *xlsxRPr) *Font {
|
|
|
|
font := Font{Underline: "none"}
|
|
|
|
font.Bold = rPr.B != nil
|
|
|
|
font.Italic = rPr.I != nil
|
|
|
|
if rPr.U != nil {
|
|
|
|
font.Underline = "single"
|
|
|
|
if rPr.U.Val != nil {
|
|
|
|
font.Underline = *rPr.U.Val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if rPr.RFont != nil && rPr.RFont.Val != nil {
|
|
|
|
font.Family = *rPr.RFont.Val
|
|
|
|
}
|
|
|
|
if rPr.Sz != nil && rPr.Sz.Val != nil {
|
|
|
|
font.Size = *rPr.Sz.Val
|
|
|
|
}
|
|
|
|
font.Strike = rPr.Strike != nil
|
|
|
|
if rPr.Color != nil {
|
|
|
|
font.Color = strings.TrimPrefix(rPr.Color.RGB, "FF")
|
|
|
|
if rPr.Color.Theme != nil {
|
|
|
|
font.ColorTheme = rPr.Color.Theme
|
|
|
|
}
|
|
|
|
font.ColorIndexed = rPr.Color.Indexed
|
|
|
|
font.ColorTint = rPr.Color.Tint
|
|
|
|
}
|
|
|
|
return &font
|
|
|
|
}
|
|
|
|
|
2022-10-10 00:11:18 +08:00
|
|
|
// setRichText provides a function to set rich text of a cell.
|
|
|
|
func setRichText(runs []RichTextRun) ([]xlsxR, error) {
|
|
|
|
var (
|
|
|
|
textRuns []xlsxR
|
|
|
|
totalCellChars int
|
|
|
|
)
|
|
|
|
for _, textRun := range runs {
|
|
|
|
totalCellChars += len(textRun.Text)
|
|
|
|
if totalCellChars > TotalCellChars {
|
|
|
|
return textRuns, ErrCellCharsLength
|
|
|
|
}
|
|
|
|
run := xlsxR{T: &xlsxT{}}
|
2023-04-16 14:22:55 +08:00
|
|
|
run.T.Val, run.T.Space = trimCellValue(textRun.Text, false)
|
2022-10-10 00:11:18 +08:00
|
|
|
fnt := textRun.Font
|
|
|
|
if fnt != nil {
|
|
|
|
run.RPr = newRpr(fnt)
|
|
|
|
}
|
|
|
|
textRuns = append(textRuns, run)
|
|
|
|
}
|
|
|
|
return textRuns, nil
|
|
|
|
}
|
|
|
|
|
2020-04-06 00:23:27 +08:00
|
|
|
// SetCellRichText provides a function to set cell with rich text by given
|
2020-04-09 01:00:14 +08:00
|
|
|
// worksheet. For example, set rich text on the A1 cell of the worksheet named
|
|
|
|
// Sheet1:
|
2017-01-25 15:44:18 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// package main
|
2020-04-06 00:23:27 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// import (
|
|
|
|
// "fmt"
|
2020-04-06 00:23:27 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// "github.com/xuri/excelize/v2"
|
|
|
|
// )
|
2020-04-06 00:23:27 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// func main() {
|
|
|
|
// f := excelize.NewFile()
|
2023-01-02 11:47:31 +08:00
|
|
|
// defer func() {
|
|
|
|
// if err := f.Close(); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// }()
|
2022-08-13 11:21:59 +08:00
|
|
|
// if err := f.SetRowHeight("Sheet1", 1, 35); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// if err := f.SetColWidth("Sheet1", "A", "A", 44); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// if err := f.SetCellRichText("Sheet1", "A1", []excelize.RichTextRun{
|
|
|
|
// {
|
|
|
|
// Text: "bold",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Bold: true,
|
|
|
|
// Color: "2354e8",
|
|
|
|
// Family: "Times New Roman",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: " and ",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Family: "Times New Roman",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: "italic ",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Bold: true,
|
|
|
|
// Color: "e83723",
|
|
|
|
// Italic: true,
|
|
|
|
// Family: "Times New Roman",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: "text with color and font-family,",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Bold: true,
|
|
|
|
// Color: "2354e8",
|
|
|
|
// Family: "Times New Roman",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: "\r\nlarge text with ",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Size: 14,
|
|
|
|
// Color: "ad23e8",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: "strike",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Color: "e89923",
|
|
|
|
// Strike: true,
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: " superscript",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Color: "dbc21f",
|
|
|
|
// VertAlign: "superscript",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: " and ",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Size: 14,
|
|
|
|
// Color: "ad23e8",
|
|
|
|
// VertAlign: "baseline",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: "underline",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Color: "23e833",
|
|
|
|
// Underline: "single",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// Text: " subscript.",
|
|
|
|
// Font: &excelize.Font{
|
|
|
|
// Color: "017505",
|
|
|
|
// VertAlign: "subscript",
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// }); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// style, err := f.NewStyle(&excelize.Style{
|
|
|
|
// Alignment: &excelize.Alignment{
|
|
|
|
// WrapText: true,
|
|
|
|
// },
|
|
|
|
// })
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// if err := f.SetCellStyle("Sheet1", "A1", "A1", style); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// }
|
2020-04-06 00:23:27 +08:00
|
|
|
func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
|
|
|
|
ws, err := f.workSheetReader(sheet)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c, col, row, err := f.prepareCell(ws, cell)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-12-27 23:34:14 +08:00
|
|
|
if err := f.sharedStringsLoader(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
2022-11-12 00:02:11 +08:00
|
|
|
si := xlsxSI{}
|
|
|
|
sst, err := f.sharedStringsReader()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-10-10 00:11:18 +08:00
|
|
|
if si.R, err = setRichText(runs); err != nil {
|
|
|
|
return err
|
2020-04-06 00:23:27 +08:00
|
|
|
}
|
This closes #787, avoid duplicate rich text string items, new formula fn: BIN2DEC, BIN2HEX, BIN2OCT, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN, OCT2DEC, OCT2HEX
2021-02-21 00:21:45 +08:00
|
|
|
for idx, strItem := range sst.SI {
|
|
|
|
if reflect.DeepEqual(strItem, si) {
|
2022-09-18 00:07:15 +08:00
|
|
|
c.T, c.V = "s", strconv.Itoa(idx)
|
This closes #787, avoid duplicate rich text string items, new formula fn: BIN2DEC, BIN2HEX, BIN2OCT, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN, OCT2DEC, OCT2HEX
2021-02-21 00:21:45 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-04-06 00:23:27 +08:00
|
|
|
sst.SI = append(sst.SI, si)
|
|
|
|
sst.Count++
|
|
|
|
sst.UniqueCount++
|
2022-09-18 00:07:15 +08:00
|
|
|
c.T, c.V = "s", strconv.Itoa(len(sst.SI)-1)
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
2017-01-25 15:44:18 +08:00
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// SetSheetRow writes an array to row by given worksheet name, starting
|
2022-09-18 00:07:15 +08:00
|
|
|
// cell reference and a pointer to array type 'slice'. This function is
|
2022-09-11 00:04:04 +08:00
|
|
|
// concurrency safe. For example, writes an array to row 6 start with the cell
|
|
|
|
// B6 on Sheet1:
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetSheetRow(sheet, cell string, slice interface{}) error {
|
|
|
|
return f.setSheetCells(sheet, cell, slice, rows)
|
2022-08-25 09:34:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetSheetCol writes an array to column by given worksheet name, starting
|
2022-09-18 00:07:15 +08:00
|
|
|
// cell reference and a pointer to array type 'slice'. For example, writes an
|
2022-08-25 09:34:29 +08:00
|
|
|
// array to column B start with the cell B6 on Sheet1:
|
|
|
|
//
|
|
|
|
// err := f.SetSheetCol("Sheet1", "B6", &[]interface{}{"1", nil, 2})
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) SetSheetCol(sheet, cell string, slice interface{}) error {
|
|
|
|
return f.setSheetCells(sheet, cell, slice, columns)
|
2022-08-25 09:34:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// setSheetCells provides a function to set worksheet cells value.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) setSheetCells(sheet, cell string, slice interface{}, dir adjustDirection) error {
|
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
2017-09-19 11:59:33 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
2017-09-19 11:59:33 +08:00
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// Make sure 'slice' is a Ptr to Slice
|
|
|
|
v := reflect.ValueOf(slice)
|
|
|
|
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
|
2021-07-05 00:03:56 +08:00
|
|
|
return ErrParameterInvalid
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
v = v.Elem()
|
|
|
|
for i := 0; i < v.Len(); i++ {
|
2022-08-25 09:34:29 +08:00
|
|
|
var cell string
|
|
|
|
var err error
|
|
|
|
if dir == rows {
|
|
|
|
cell, err = CoordinatesToCellName(col+i, row)
|
|
|
|
} else {
|
|
|
|
cell, err = CoordinatesToCellName(col, row+i)
|
|
|
|
}
|
2022-03-24 00:19:30 +08:00
|
|
|
// Error should never happen here. But keep checking to early detect regressions
|
|
|
|
// if it will be introduced in the future.
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
2019-08-19 15:53:56 +08:00
|
|
|
if err := f.SetCellValue(sheet, cell, v.Index(i).Interface()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2022-09-28 00:04:17 +08:00
|
|
|
// getCellInfo does common preparation for all set cell value functions.
|
2022-03-24 00:19:30 +08:00
|
|
|
func (f *File) prepareCell(ws *xlsxWorksheet, cell string) (*xlsxC, int, int, error) {
|
2019-03-23 20:08:06 +08:00
|
|
|
var err error
|
2020-11-10 23:48:09 +08:00
|
|
|
cell, err = f.mergeCellsParser(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, 0, err
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
2017-09-19 11:59:33 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil, 0, 0, err
|
2017-09-19 11:59:33 +08:00
|
|
|
}
|
2017-07-24 10:26:02 +08:00
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
prepareSheetXML(ws, col, row)
|
2021-07-05 00:03:56 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2020-11-10 23:48:09 +08:00
|
|
|
return &ws.SheetData.Row[row-1].C[col-1], col, row, err
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2022-09-28 00:04:17 +08:00
|
|
|
// getCellStringFunc does common value extraction workflow for all get cell
|
|
|
|
// value function. Passed function implements specific part of required
|
|
|
|
// logic.
|
2022-09-18 00:07:15 +08:00
|
|
|
func (f *File) getCellStringFunc(sheet, cell string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
cell, err = f.mergeCellsParser(ws, cell)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
_, row, err := CellNameToCoordinates(cell)
|
2017-09-19 11:59:33 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return "", err
|
2017-09-19 11:59:33 +08:00
|
|
|
}
|
2017-07-24 10:26:02 +08:00
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2020-08-15 00:09:50 +08:00
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
lastRowNum := 0
|
2020-11-10 23:48:09 +08:00
|
|
|
if l := len(ws.SheetData.Row); l > 0 {
|
|
|
|
lastRowNum = ws.SheetData.Row[l-1].R
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2017-07-24 10:26:02 +08:00
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// keep in mind: row starts from 1
|
|
|
|
if row > lastRowNum {
|
2019-03-23 20:08:06 +08:00
|
|
|
return "", nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2017-07-24 10:26:02 +08:00
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
for rowIdx := range ws.SheetData.Row {
|
|
|
|
rowData := &ws.SheetData.Row[rowIdx]
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if rowData.R != row {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for colIdx := range rowData.C {
|
|
|
|
colData := &rowData.C[colIdx]
|
2022-09-18 00:07:15 +08:00
|
|
|
if cell != colData.R {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
continue
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
val, ok, err := fn(ws, colData)
|
2019-03-23 20:08:06 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
return val, nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return "", nil
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// formattedValue provides a function to returns a value after formatted. If
|
|
|
|
// it is possible to apply a format to the cell value, it will do so, if not
|
|
|
|
// then an error will be returned, along with the raw value of the cell.
|
2023-04-19 00:05:59 +08:00
|
|
|
func (f *File) formattedValue(c *xlsxC, raw bool, cellType CellType) (string, error) {
|
|
|
|
if raw || c.S == 0 {
|
|
|
|
return c.V, nil
|
2022-11-12 00:02:11 +08:00
|
|
|
}
|
|
|
|
styleSheet, err := f.stylesReader()
|
|
|
|
if err != nil {
|
2023-04-19 00:05:59 +08:00
|
|
|
return c.V, err
|
2018-03-12 20:14:39 +08:00
|
|
|
}
|
2022-11-05 12:41:07 +08:00
|
|
|
if styleSheet.CellXfs == nil {
|
2023-04-19 00:05:59 +08:00
|
|
|
return c.V, err
|
2022-11-05 12:41:07 +08:00
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
if c.S >= len(styleSheet.CellXfs.Xf) || c.S < 0 {
|
|
|
|
return c.V, err
|
2020-10-04 21:07:39 +08:00
|
|
|
}
|
2020-12-12 16:17:00 +08:00
|
|
|
var numFmtID int
|
2023-04-19 00:05:59 +08:00
|
|
|
if styleSheet.CellXfs.Xf[c.S].NumFmtID != nil {
|
|
|
|
numFmtID = *styleSheet.CellXfs.Xf[c.S].NumFmtID
|
2020-12-12 16:17:00 +08:00
|
|
|
}
|
2022-11-13 00:40:04 +08:00
|
|
|
date1904 := false
|
|
|
|
wb, err := f.workbookReader()
|
|
|
|
if err != nil {
|
2023-04-19 00:05:59 +08:00
|
|
|
return c.V, err
|
2022-11-13 00:40:04 +08:00
|
|
|
}
|
2022-04-30 09:54:11 +08:00
|
|
|
if wb != nil && wb.WorkbookPr != nil {
|
|
|
|
date1904 = wb.WorkbookPr.Date1904
|
|
|
|
}
|
2022-05-02 12:30:18 +08:00
|
|
|
if ok := builtInNumFmtFunc[numFmtID]; ok != nil {
|
2023-04-19 00:05:59 +08:00
|
|
|
return ok(c.V, builtInNumFmt[numFmtID], date1904, cellType), err
|
2020-10-04 21:07:39 +08:00
|
|
|
}
|
2022-11-05 12:41:07 +08:00
|
|
|
if styleSheet.NumFmts == nil {
|
2023-04-19 00:05:59 +08:00
|
|
|
return c.V, err
|
2020-10-19 23:55:54 +08:00
|
|
|
}
|
2020-10-04 21:07:39 +08:00
|
|
|
for _, xlsxFmt := range styleSheet.NumFmts.NumFmt {
|
2020-11-23 00:01:06 +08:00
|
|
|
if xlsxFmt.NumFmtID == numFmtID {
|
2023-04-19 00:05:59 +08:00
|
|
|
return format(c.V, xlsxFmt.FormatCode, date1904, cellType), err
|
2020-10-04 21:07:39 +08:00
|
|
|
}
|
2018-03-12 20:14:39 +08:00
|
|
|
}
|
2023-04-19 00:05:59 +08:00
|
|
|
return c.V, err
|
2021-09-26 00:07:40 +08:00
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// prepareCellStyle provides a function to prepare style index of cell in
|
|
|
|
// worksheet by given column index and style index.
|
2022-01-27 22:37:32 +08:00
|
|
|
func (f *File) prepareCellStyle(ws *xlsxWorksheet, col, row, style int) int {
|
2022-05-02 12:30:18 +08:00
|
|
|
if style != 0 {
|
|
|
|
return style
|
|
|
|
}
|
2022-05-15 15:38:40 +08:00
|
|
|
if row <= len(ws.SheetData.Row) {
|
|
|
|
if styleID := ws.SheetData.Row[row-1].S; styleID != 0 {
|
|
|
|
return styleID
|
|
|
|
}
|
|
|
|
}
|
2022-05-02 12:30:18 +08:00
|
|
|
if ws.Cols != nil {
|
2020-11-10 23:48:09 +08:00
|
|
|
for _, c := range ws.Cols.Col {
|
2022-01-27 22:37:32 +08:00
|
|
|
if c.Min <= col && col <= c.Max && c.Style != 0 {
|
|
|
|
return c.Style
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return style
|
|
|
|
}
|
2018-03-12 20:14:39 +08:00
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// mergeCellsParser provides a function to check merged cells in worksheet by
|
2022-09-18 00:07:15 +08:00
|
|
|
// given cell reference.
|
|
|
|
func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error) {
|
|
|
|
cell = strings.ToUpper(cell)
|
2023-03-20 09:17:28 +08:00
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
|
|
|
if err != nil {
|
|
|
|
return cell, err
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.MergeCells != nil {
|
|
|
|
for i := 0; i < len(ws.MergeCells.Cells); i++ {
|
2022-01-27 22:37:32 +08:00
|
|
|
if ws.MergeCells.Cells[i] == nil {
|
|
|
|
ws.MergeCells.Cells = append(ws.MergeCells.Cells[:i], ws.MergeCells.Cells[i+1:]...)
|
|
|
|
i--
|
|
|
|
continue
|
|
|
|
}
|
2023-03-20 09:17:28 +08:00
|
|
|
if ref := ws.MergeCells.Cells[i].Ref; len(ws.MergeCells.Cells[i].rect) == 0 && ref != "" {
|
|
|
|
if strings.Count(ref, ":") != 1 {
|
|
|
|
ref += ":" + ref
|
|
|
|
}
|
|
|
|
rect, err := rangeRefToCoordinates(ref)
|
|
|
|
if err != nil {
|
|
|
|
return cell, err
|
|
|
|
}
|
|
|
|
_ = sortCoordinates(rect)
|
|
|
|
ws.MergeCells.Cells[i].rect = rect
|
2019-03-23 20:08:06 +08:00
|
|
|
}
|
2023-03-20 09:17:28 +08:00
|
|
|
if cellInRange([]int{col, row}, ws.MergeCells.Cells[i].rect) {
|
2022-09-18 00:07:15 +08:00
|
|
|
cell = strings.Split(ws.MergeCells.Cells[i].Ref, ":")[0]
|
2023-03-20 09:17:28 +08:00
|
|
|
break
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
2018-03-12 20:14:39 +08:00
|
|
|
}
|
2022-09-18 00:07:15 +08:00
|
|
|
return cell, nil
|
2018-03-12 20:14:39 +08:00
|
|
|
}
|
|
|
|
|
2022-09-28 00:04:17 +08:00
|
|
|
// checkCellInRangeRef provides a function to determine if a given cell reference
|
2022-09-18 00:07:15 +08:00
|
|
|
// in a range.
|
2023-01-02 11:47:31 +08:00
|
|
|
func (f *File) checkCellInRangeRef(cell, rangeRef string) (bool, error) {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return false, err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2017-01-25 15:44:18 +08:00
|
|
|
|
2023-01-02 11:47:31 +08:00
|
|
|
if rng := strings.Split(rangeRef, ":"); len(rng) != 2 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return false, err
|
2018-04-08 10:31:11 +08:00
|
|
|
}
|
2023-01-02 11:47:31 +08:00
|
|
|
coordinates, err := rangeRefToCoordinates(rangeRef)
|
2019-12-14 19:57:37 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2018-04-08 10:31:11 +08:00
|
|
|
|
2023-01-02 11:47:31 +08:00
|
|
|
return cellInRange([]int{col, row}, coordinates), err
|
2019-12-14 19:57:37 +08:00
|
|
|
}
|
2018-04-08 10:31:11 +08:00
|
|
|
|
2023-01-02 11:47:31 +08:00
|
|
|
// cellInRange provides a function to determine if a given range is within a
|
2019-12-14 19:57:37 +08:00
|
|
|
// range.
|
2023-01-02 11:47:31 +08:00
|
|
|
func cellInRange(cell, ref []int) bool {
|
2019-12-14 19:57:37 +08:00
|
|
|
return cell[0] >= ref[0] && cell[0] <= ref[2] && cell[1] >= ref[1] && cell[1] <= ref[3]
|
|
|
|
}
|
2017-01-25 15:44:18 +08:00
|
|
|
|
2019-12-14 19:57:37 +08:00
|
|
|
// isOverlap find if the given two rectangles overlap or not.
|
|
|
|
func isOverlap(rect1, rect2 []int) bool {
|
2023-01-02 11:47:31 +08:00
|
|
|
return cellInRange([]int{rect1[0], rect1[1]}, rect2) ||
|
|
|
|
cellInRange([]int{rect1[2], rect1[1]}, rect2) ||
|
|
|
|
cellInRange([]int{rect1[0], rect1[3]}, rect2) ||
|
|
|
|
cellInRange([]int{rect1[2], rect1[3]}, rect2) ||
|
|
|
|
cellInRange([]int{rect2[0], rect2[1]}, rect1) ||
|
|
|
|
cellInRange([]int{rect2[2], rect2[1]}, rect1) ||
|
|
|
|
cellInRange([]int{rect2[0], rect2[3]}, rect1) ||
|
|
|
|
cellInRange([]int{rect2[2], rect2[3]}, rect1)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2017-01-25 15:44:18 +08:00
|
|
|
|
2021-08-15 01:19:49 +08:00
|
|
|
// parseSharedFormula generate dynamic part of shared formula for target cell
|
|
|
|
// by given column and rows distance and origin shared formula.
|
|
|
|
func parseSharedFormula(dCol, dRow int, orig []byte) (res string, start int) {
|
|
|
|
var (
|
|
|
|
end int
|
|
|
|
stringLiteral bool
|
|
|
|
)
|
|
|
|
for end = 0; end < len(orig); end++ {
|
|
|
|
c := orig[end]
|
|
|
|
if c == '"' {
|
|
|
|
stringLiteral = !stringLiteral
|
|
|
|
}
|
|
|
|
if stringLiteral {
|
|
|
|
continue // Skip characters in quotes
|
|
|
|
}
|
|
|
|
if c >= 'A' && c <= 'Z' || c == '$' {
|
|
|
|
res += string(orig[start:end])
|
|
|
|
start = end
|
|
|
|
end++
|
|
|
|
foundNum := false
|
|
|
|
for ; end < len(orig); end++ {
|
|
|
|
idc := orig[end]
|
|
|
|
if idc >= '0' && idc <= '9' || idc == '$' {
|
|
|
|
foundNum = true
|
|
|
|
} else if idc >= 'A' && idc <= 'Z' {
|
|
|
|
if foundNum {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if foundNum {
|
|
|
|
cellID := string(orig[start:end])
|
|
|
|
res += shiftCell(cellID, dCol, dRow)
|
|
|
|
start = end
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-09 00:20:42 +08:00
|
|
|
// getSharedFormula find a cell contains the same formula as another cell,
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// the "shared" value can be used for the t attribute and the si attribute can
|
|
|
|
// be used to refer to the cell containing the formula. Two formulas are
|
|
|
|
// considered to be the same when their respective representations in
|
|
|
|
// R1C1-reference notation, are the same.
|
|
|
|
//
|
2022-03-24 00:19:30 +08:00
|
|
|
// Note that this function not validate ref tag to check the cell whether in
|
2022-09-28 00:04:17 +08:00
|
|
|
// allow range reference, and always return origin shared formula.
|
2022-09-18 00:07:15 +08:00
|
|
|
func getSharedFormula(ws *xlsxWorksheet, si int, cell string) string {
|
2020-11-10 23:48:09 +08:00
|
|
|
for _, r := range ws.SheetData.Row {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
for _, c := range r.C {
|
2021-09-03 22:51:56 +08:00
|
|
|
if c.F != nil && c.F.Ref != "" && c.F.T == STCellFormulaTypeShared && c.F.Si != nil && *c.F.Si == si {
|
2022-09-18 00:07:15 +08:00
|
|
|
col, row, _ := CellNameToCoordinates(cell)
|
2021-08-15 01:19:49 +08:00
|
|
|
sharedCol, sharedRow, _ := CellNameToCoordinates(c.R)
|
|
|
|
dCol := col - sharedCol
|
|
|
|
dRow := row - sharedRow
|
|
|
|
orig := []byte(c.F.Content)
|
|
|
|
res, start := parseSharedFormula(dCol, dRow, orig)
|
|
|
|
if start < len(orig) {
|
|
|
|
res += string(orig[start:])
|
|
|
|
}
|
|
|
|
return res
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
2017-01-25 15:44:18 +08:00
|
|
|
}
|
2021-08-15 01:19:49 +08:00
|
|
|
|
|
|
|
// shiftCell returns the cell shifted according to dCol and dRow taking into
|
2022-03-24 00:19:30 +08:00
|
|
|
// consideration absolute references with dollar sign ($)
|
2021-08-15 01:19:49 +08:00
|
|
|
func shiftCell(cellID string, dCol, dRow int) string {
|
|
|
|
fCol, fRow, _ := CellNameToCoordinates(cellID)
|
|
|
|
signCol, signRow := "", ""
|
|
|
|
if strings.Index(cellID, "$") == 0 {
|
|
|
|
signCol = "$"
|
|
|
|
} else {
|
|
|
|
// Shift column
|
|
|
|
fCol += dCol
|
|
|
|
}
|
|
|
|
if strings.LastIndex(cellID, "$") > 0 {
|
|
|
|
signRow = "$"
|
|
|
|
} else {
|
|
|
|
// Shift row
|
|
|
|
fRow += dRow
|
|
|
|
}
|
|
|
|
colName, _ := ColumnNumberToName(fCol)
|
|
|
|
return signCol + colName + signRow + strconv.Itoa(fRow)
|
|
|
|
}
|