2021-02-15 00:09:35 +08:00
|
|
|
// Copyright 2016 - 2021 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.
|
|
|
|
//
|
|
|
|
// Package excelize providing a set of functions that allow you to write to
|
2020-06-22 00:14:56 +08:00
|
|
|
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
2021-03-30 23:02:22 +08:00
|
|
|
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
2020-06-22 00:14:56 +08:00
|
|
|
// complex components by high compatibility, and provided streaming API for
|
|
|
|
// generating or reading data from a worksheet with huge amounts of data. This
|
2021-04-04 15:29:43 +08:00
|
|
|
// library needs Go version 1.15 or later.
|
2018-09-14 00:58:48 +08:00
|
|
|
|
2016-10-31 19:13:22 +08:00
|
|
|
package excelize
|
|
|
|
|
|
|
|
import (
|
2019-11-23 04:13:59 +08:00
|
|
|
"bytes"
|
2016-10-31 19:13:22 +08:00
|
|
|
"encoding/xml"
|
2019-09-24 21:53:19 +08:00
|
|
|
"errors"
|
2018-05-05 13:33:19 +08:00
|
|
|
"fmt"
|
2019-12-20 00:30:48 +08:00
|
|
|
"io"
|
|
|
|
"log"
|
2017-07-24 10:26:02 +08:00
|
|
|
"math"
|
2016-11-02 12:58:51 +08:00
|
|
|
"strconv"
|
2020-11-15 10:58:45 +08:00
|
|
|
|
|
|
|
"github.com/mohae/deepcopy"
|
2016-10-31 19:13:22 +08:00
|
|
|
)
|
|
|
|
|
2017-09-13 22:00:33 +08:00
|
|
|
// GetRows return all the rows in a sheet by given worksheet name (case
|
|
|
|
// sensitive). For example:
|
2016-11-02 12:58:51 +08:00
|
|
|
//
|
2020-06-27 00:02:47 +08:00
|
|
|
// rows, err := f.GetRows("Sheet1")
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2019-12-31 01:01:16 +08:00
|
|
|
// return
|
|
|
|
// }
|
2020-06-27 00:02:47 +08:00
|
|
|
// for _, row := range rows {
|
2016-11-02 12:58:51 +08:00
|
|
|
// for _, colCell := range row {
|
2020-06-28 00:02:32 +08:00
|
|
|
// fmt.Print(colCell, "\t")
|
2016-11-02 12:58:51 +08:00
|
|
|
// }
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println()
|
2016-11-02 12:58:51 +08:00
|
|
|
// }
|
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) GetRows(sheet string) ([][]string, error) {
|
2019-08-05 06:23:42 +08:00
|
|
|
rows, err := f.Rows(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-08-05 06:23:42 +08:00
|
|
|
results := make([][]string, 0, 64)
|
|
|
|
for rows.Next() {
|
|
|
|
row, err := rows.Columns()
|
|
|
|
if err != nil {
|
|
|
|
break
|
2016-11-02 12:58:51 +08:00
|
|
|
}
|
2019-08-05 06:23:42 +08:00
|
|
|
results = append(results, row)
|
2016-11-02 12:58:51 +08:00
|
|
|
}
|
2019-08-05 06:23:42 +08:00
|
|
|
return results, nil
|
2016-10-31 19:13:22 +08:00
|
|
|
}
|
|
|
|
|
2020-06-27 00:02:47 +08:00
|
|
|
// Rows defines an iterator to a sheet.
|
2018-05-05 13:33:19 +08:00
|
|
|
type Rows struct {
|
2019-12-31 01:01:16 +08:00
|
|
|
err error
|
|
|
|
curRow, totalRow, stashRow int
|
|
|
|
sheet string
|
|
|
|
f *File
|
|
|
|
decoder *xml.Decoder
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Next will return true if find the next row element.
|
|
|
|
func (rows *Rows) Next() bool {
|
2019-10-18 14:57:35 +08:00
|
|
|
rows.curRow++
|
2019-11-23 04:13:59 +08:00
|
|
|
return rows.curRow <= rows.totalRow
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
2020-06-27 00:02:47 +08:00
|
|
|
// Error will return the error when the error occurs.
|
2018-05-05 13:33:19 +08:00
|
|
|
func (rows *Rows) Error() error {
|
|
|
|
return rows.err
|
|
|
|
}
|
|
|
|
|
2020-06-27 00:02:47 +08:00
|
|
|
// Columns return the current row's column values.
|
2019-03-23 20:08:06 +08:00
|
|
|
func (rows *Rows) Columns() ([]string, error) {
|
2021-02-05 22:52:31 +08:00
|
|
|
var rowIterator rowXMLIterator
|
2019-12-31 01:01:16 +08:00
|
|
|
if rows.stashRow >= rows.curRow {
|
2021-02-05 22:52:31 +08:00
|
|
|
return rowIterator.columns, rowIterator.err
|
2019-12-31 01:01:16 +08:00
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
rowIterator.rows = rows
|
|
|
|
rowIterator.d = rows.f.sharedStringsReader()
|
2019-11-23 04:13:59 +08:00
|
|
|
for {
|
|
|
|
token, _ := rows.decoder.Token()
|
|
|
|
if token == nil {
|
|
|
|
break
|
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
switch xmlElement := token.(type) {
|
2019-11-23 04:13:59 +08:00
|
|
|
case xml.StartElement:
|
2021-02-05 22:52:31 +08:00
|
|
|
rowIterator.inElement = xmlElement.Name.Local
|
|
|
|
if rowIterator.inElement == "row" {
|
|
|
|
rowIterator.row++
|
|
|
|
if rowIterator.attrR, rowIterator.err = attrValToInt("r", xmlElement.Attr); rowIterator.attrR != 0 {
|
|
|
|
rowIterator.row = rowIterator.attrR
|
2020-11-18 22:08:40 +08:00
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
if rowIterator.row > rowIterator.rows.curRow {
|
|
|
|
rowIterator.rows.stashRow = rowIterator.row - 1
|
|
|
|
return rowIterator.columns, rowIterator.err
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
rowXMLHandler(&rowIterator, &xmlElement)
|
|
|
|
if rowIterator.err != nil {
|
|
|
|
return rowIterator.columns, rowIterator.err
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
|
|
|
case xml.EndElement:
|
2021-02-05 22:52:31 +08:00
|
|
|
rowIterator.inElement = xmlElement.Name.Local
|
|
|
|
if rowIterator.row == 0 {
|
|
|
|
rowIterator.row = rowIterator.rows.curRow
|
|
|
|
}
|
|
|
|
if rowIterator.inElement == "row" && rowIterator.row+1 < rowIterator.rows.curRow {
|
|
|
|
return rowIterator.columns, rowIterator.err
|
2020-11-18 22:08:40 +08:00
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
if rowIterator.inElement == "sheetData" {
|
|
|
|
return rowIterator.columns, rowIterator.err
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
}
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
return rowIterator.columns, rowIterator.err
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
2020-07-18 15:15:16 +08:00
|
|
|
// appendSpace append blank characters to slice by given length and source slice.
|
|
|
|
func appendSpace(l int, s []string) []string {
|
|
|
|
for i := 1; i < l; i++ {
|
|
|
|
s = append(s, "")
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2018-05-05 13:33:19 +08:00
|
|
|
// ErrSheetNotExist defines an error of sheet is not exist
|
|
|
|
type ErrSheetNotExist struct {
|
|
|
|
SheetName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (err ErrSheetNotExist) Error() string {
|
2019-11-23 04:13:59 +08:00
|
|
|
return fmt.Sprintf("sheet %s is not exist", string(err.SheetName))
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
2021-02-05 22:52:31 +08:00
|
|
|
// rowXMLIterator defined runtime use field for the worksheet row SAX parser.
|
|
|
|
type rowXMLIterator struct {
|
|
|
|
err error
|
|
|
|
inElement string
|
|
|
|
attrR, cellCol, row int
|
|
|
|
columns []string
|
|
|
|
rows *Rows
|
|
|
|
d *xlsxSST
|
|
|
|
}
|
|
|
|
|
|
|
|
// rowXMLHandler parse the row XML element of the worksheet.
|
|
|
|
func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement) {
|
|
|
|
rowIterator.err = nil
|
|
|
|
if rowIterator.inElement == "c" {
|
|
|
|
rowIterator.cellCol++
|
|
|
|
colCell := xlsxC{}
|
|
|
|
_ = rowIterator.rows.decoder.DecodeElement(&colCell, xmlElement)
|
|
|
|
if colCell.R != "" {
|
|
|
|
if rowIterator.cellCol, _, rowIterator.err = CellNameToCoordinates(colCell.R); rowIterator.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
blank := rowIterator.cellCol - len(rowIterator.columns)
|
|
|
|
val, _ := colCell.getValueFrom(rowIterator.rows.f, rowIterator.d)
|
|
|
|
rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 17:36:53 +08:00
|
|
|
// Rows returns a rows iterator, used for streaming reading data for a
|
|
|
|
// worksheet with a large data. For example:
|
2018-05-05 13:33:19 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// rows, err := f.Rows("Sheet1")
|
2020-01-03 23:57:25 +08:00
|
|
|
// if err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2020-01-03 23:57:25 +08:00
|
|
|
// return
|
|
|
|
// }
|
2018-05-05 13:33:19 +08:00
|
|
|
// for rows.Next() {
|
2019-06-20 00:00:40 +08:00
|
|
|
// row, err := rows.Columns()
|
2020-01-03 23:57:25 +08:00
|
|
|
// if err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2020-01-03 23:57:25 +08:00
|
|
|
// }
|
2019-03-23 20:08:06 +08:00
|
|
|
// for _, colCell := range row {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Print(colCell, "\t")
|
2018-05-05 13:33:19 +08:00
|
|
|
// }
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println()
|
2018-05-05 13:33:19 +08:00
|
|
|
// }
|
|
|
|
//
|
|
|
|
func (f *File) Rows(sheet string) (*Rows, error) {
|
|
|
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrSheetNotExist{sheet}
|
|
|
|
}
|
2019-11-23 04:13:59 +08:00
|
|
|
if f.Sheet[name] != nil {
|
|
|
|
// flush data
|
|
|
|
output, _ := xml.Marshal(f.Sheet[name])
|
2020-07-18 15:15:16 +08:00
|
|
|
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
|
|
|
var (
|
2020-06-28 00:02:32 +08:00
|
|
|
err error
|
|
|
|
inElement string
|
|
|
|
row int
|
|
|
|
rows Rows
|
2019-11-23 04:13:59 +08:00
|
|
|
)
|
2019-12-20 01:00:15 +08:00
|
|
|
decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
2019-11-23 04:13:59 +08:00
|
|
|
for {
|
|
|
|
token, _ := decoder.Token()
|
|
|
|
if token == nil {
|
|
|
|
break
|
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
switch xmlElement := token.(type) {
|
2019-11-23 04:13:59 +08:00
|
|
|
case xml.StartElement:
|
2021-02-05 22:52:31 +08:00
|
|
|
inElement = xmlElement.Name.Local
|
2019-11-23 04:13:59 +08:00
|
|
|
if inElement == "row" {
|
2020-06-28 00:02:32 +08:00
|
|
|
row++
|
2021-02-05 22:52:31 +08:00
|
|
|
for _, attr := range xmlElement.Attr {
|
2019-11-23 04:13:59 +08:00
|
|
|
if attr.Name.Local == "r" {
|
2020-06-28 00:02:32 +08:00
|
|
|
row, err = strconv.Atoi(attr.Value)
|
2019-11-23 04:13:59 +08:00
|
|
|
if err != nil {
|
|
|
|
return &rows, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rows.totalRow = row
|
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
case xml.EndElement:
|
|
|
|
if xmlElement.Name.Local == "sheetData" {
|
|
|
|
rows.f = f
|
|
|
|
rows.sheet = name
|
|
|
|
rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
|
|
|
return &rows, nil
|
|
|
|
}
|
2019-11-23 04:13:59 +08:00
|
|
|
default:
|
|
|
|
}
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
2019-11-23 04:13:59 +08:00
|
|
|
return &rows, nil
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
2018-04-02 10:59:15 +08:00
|
|
|
// SetRowHeight provides a function to set the height of a single row. For
|
|
|
|
// example, set the height of the first row in Sheet1:
|
2017-02-02 10:59:31 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.SetRowHeight("Sheet1", 1, 50)
|
2017-02-02 10:59:31 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) SetRowHeight(sheet string, row int, height float64) error {
|
2019-03-06 21:40:45 +08:00
|
|
|
if row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return newInvalidRowNumberError(row)
|
2019-03-06 21:40:45 +08:00
|
|
|
}
|
2020-09-18 22:20:58 +08:00
|
|
|
if height > MaxRowHeight {
|
|
|
|
return errors.New("the height of the row must be smaller than or equal to 409 points")
|
|
|
|
}
|
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
|
|
|
|
}
|
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
|
|
|
prepareSheetXML(ws, 0, 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
|
|
|
|
2018-04-02 10:59:15 +08:00
|
|
|
rowIdx := row - 1
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.SheetData.Row[rowIdx].Ht = height
|
|
|
|
ws.SheetData.Row[rowIdx].CustomHeight = true
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2017-02-02 10:59:31 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// getRowHeight provides a function to get row height in pixels by given sheet
|
2017-06-29 11:14:33 +08:00
|
|
|
// name and row index.
|
|
|
|
func (f *File) getRowHeight(sheet string, row int) int {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, _ := f.workSheetReader(sheet)
|
|
|
|
for i := range ws.SheetData.Row {
|
|
|
|
v := &ws.SheetData.Row[i]
|
2017-06-29 11:14:33 +08:00
|
|
|
if v.R == row+1 && v.Ht != 0 {
|
|
|
|
return int(convertRowHeightToPixels(v.Ht))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Optimisation for when the row heights haven't changed.
|
|
|
|
return int(defaultRowHeightPixels)
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// GetRowHeight provides a function to get row height by given worksheet name
|
2018-04-02 10:59:15 +08:00
|
|
|
// and row index. For example, get the height of the first row in Sheet1:
|
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// height, err := f.GetRowHeight("Sheet1", 1)
|
2018-04-02 10:59:15 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) GetRowHeight(sheet string, row int) (float64, 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 row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return defaultRowHeightPixels, newInvalidRowNumberError(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
|
|
|
}
|
2020-08-22 18:58:43 +08:00
|
|
|
var ht = defaultRowHeight
|
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
2020-08-22 18:58:43 +08:00
|
|
|
return ht, err
|
|
|
|
}
|
2021-02-08 18:05:15 +08:00
|
|
|
if ws.SheetFormatPr != nil && ws.SheetFormatPr.CustomHeight {
|
2020-08-22 18:58:43 +08:00
|
|
|
ht = ws.SheetFormatPr.DefaultRowHeight
|
2019-04-15 11:22:57 +08:00
|
|
|
}
|
2020-08-22 18:58:43 +08:00
|
|
|
if row > len(ws.SheetData.Row) {
|
|
|
|
return ht, nil // it will be better to use 0, but we take care with BC
|
2019-03-06 21:40:45 +08:00
|
|
|
}
|
2020-08-22 18:58:43 +08:00
|
|
|
for _, v := range ws.SheetData.Row {
|
2018-04-02 10:59:15 +08:00
|
|
|
if v.R == row && v.Ht != 0 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return v.Ht, nil
|
2017-06-29 11:14:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Optimisation for when the row heights haven't changed.
|
2020-08-22 18:58:43 +08:00
|
|
|
return ht, nil
|
2017-06-29 11:14:33 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// sharedStringsReader provides a function to get the pointer to the structure
|
2017-06-29 13:28:44 +08:00
|
|
|
// after deserialization of xl/sharedStrings.xml.
|
|
|
|
func (f *File) sharedStringsReader() *xlsxSST {
|
2019-12-20 00:30:48 +08:00
|
|
|
var err error
|
2020-08-15 00:09:50 +08:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2020-11-06 20:03:13 +08:00
|
|
|
relPath := f.getWorkbookRelsPath()
|
2017-06-29 13:28:44 +08:00
|
|
|
if f.SharedStrings == nil {
|
|
|
|
var sharedStrings xlsxSST
|
2018-03-02 10:19:40 +08:00
|
|
|
ss := f.readXML("xl/sharedStrings.xml")
|
2019-12-20 00:30:48 +08:00
|
|
|
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
|
|
|
|
Decode(&sharedStrings); err != nil && err != io.EOF {
|
|
|
|
log.Printf("xml decode error: %s", err)
|
|
|
|
}
|
2020-07-15 23:32:00 +08:00
|
|
|
if sharedStrings.UniqueCount == 0 {
|
|
|
|
sharedStrings.UniqueCount = sharedStrings.Count
|
|
|
|
}
|
2017-06-29 13:28:44 +08:00
|
|
|
f.SharedStrings = &sharedStrings
|
2020-05-27 00:02:29 +08:00
|
|
|
for i := range sharedStrings.SI {
|
2020-07-01 22:41:29 +08:00
|
|
|
if sharedStrings.SI[i].T != nil {
|
|
|
|
f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
|
2020-05-27 00:02:29 +08:00
|
|
|
}
|
|
|
|
}
|
2020-05-26 02:09:39 +08:00
|
|
|
f.addContentTypePart(0, "sharedStrings")
|
2020-11-04 00:28:20 +08:00
|
|
|
rels := f.relsReader(relPath)
|
2020-05-26 02:09:39 +08:00
|
|
|
for _, rel := range rels.Relationships {
|
2020-11-04 00:28:20 +08:00
|
|
|
if rel.Target == "/xl/sharedStrings.xml" {
|
2020-05-26 02:09:39 +08:00
|
|
|
return f.SharedStrings
|
|
|
|
}
|
|
|
|
}
|
2020-11-04 00:28:20 +08:00
|
|
|
// Update workbook.xml.rels
|
|
|
|
f.addRels(relPath, SourceRelationshipSharedStrings, "/xl/sharedStrings.xml", "")
|
2017-06-29 13:28:44 +08:00
|
|
|
}
|
2019-12-20 00:30:48 +08:00
|
|
|
|
2017-06-29 13:28:44 +08:00
|
|
|
return f.SharedStrings
|
2016-10-31 19:13:22 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// getValueFrom return a value from a column/row cell, this function is
|
2020-11-10 23:48:09 +08:00
|
|
|
// inteded to be used with for range on rows an argument with the spreadsheet
|
|
|
|
// opened file.
|
|
|
|
func (c *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
|
2020-08-15 00:09:50 +08:00
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
2020-11-10 23:48:09 +08:00
|
|
|
switch c.T {
|
2016-11-02 12:58:51 +08:00
|
|
|
case "s":
|
2020-11-10 23:48:09 +08:00
|
|
|
if c.V != "" {
|
2020-04-01 15:38:37 +08:00
|
|
|
xlsxSI := 0
|
2020-11-10 23:48:09 +08:00
|
|
|
xlsxSI, _ = strconv.Atoi(c.V)
|
2020-04-01 15:38:37 +08:00
|
|
|
if len(d.SI) > xlsxSI {
|
2020-11-10 23:48:09 +08:00
|
|
|
return f.formattedValue(c.S, d.SI[xlsxSI].String()), nil
|
2020-04-01 15:38:37 +08:00
|
|
|
}
|
2019-12-20 22:22:56 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
return f.formattedValue(c.S, c.V), nil
|
2016-11-02 12:58:51 +08:00
|
|
|
case "str":
|
2020-11-10 23:48:09 +08:00
|
|
|
return f.formattedValue(c.S, c.V), nil
|
2018-04-09 19:44:08 +08:00
|
|
|
case "inlineStr":
|
2020-11-10 23:48:09 +08:00
|
|
|
if c.IS != nil {
|
|
|
|
return f.formattedValue(c.S, c.IS.String()), nil
|
2019-12-11 00:02:33 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
return f.formattedValue(c.S, c.V), nil
|
2016-11-02 12:58:51 +08:00
|
|
|
default:
|
2021-01-27 13:51:47 +08:00
|
|
|
isNum, precision := isNumeric(c.V)
|
|
|
|
if isNum && precision > 15 {
|
|
|
|
val, _ := roundPrecision(c.V)
|
2020-11-10 23:48:09 +08:00
|
|
|
if val != c.V {
|
|
|
|
return f.formattedValue(c.S, val), nil
|
2020-10-04 21:07:39 +08:00
|
|
|
}
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
return f.formattedValue(c.S, c.V), nil
|
2016-11-02 12:58:51 +08:00
|
|
|
}
|
2016-10-31 19:13:22 +08:00
|
|
|
}
|
2017-06-08 11:11:11 +08:00
|
|
|
|
2020-11-19 21:38:35 +08:00
|
|
|
// roundPrecision round precision for numeric.
|
|
|
|
func roundPrecision(value string) (result string, err error) {
|
|
|
|
var num float64
|
|
|
|
if num, err = strconv.ParseFloat(value, 64); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
result = fmt.Sprintf("%g", math.Round(num*numericPrecision)/numericPrecision)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-03-07 15:13:32 +08:00
|
|
|
// SetRowVisible provides a function to set visible of a single row by given
|
2019-03-06 21:40:45 +08:00
|
|
|
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
|
2017-06-08 11:11:11 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.SetRowVisible("Sheet1", 2, false)
|
2017-06-08 11:11:11 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
|
2019-03-06 21:40:45 +08:00
|
|
|
if row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return newInvalidRowNumberError(row)
|
2017-06-14 15:01:49 +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
|
|
|
|
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
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
prepareSheetXML(ws, 0, row)
|
|
|
|
ws.SheetData.Row[row-1].Hidden = !visible
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2017-06-08 11:11:11 +08:00
|
|
|
}
|
|
|
|
|
2019-03-07 15:13:32 +08:00
|
|
|
// GetRowVisible provides a function to get visible of a single row by given
|
|
|
|
// worksheet name and Excel row number. For example, get visible state of row
|
|
|
|
// 2 in Sheet1:
|
2017-06-08 11:11:11 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// visible, err := f.GetRowVisible("Sheet1", 2)
|
2017-06-08 11:11:11 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) GetRowVisible(sheet string, row int) (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 row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return false, newInvalidRowNumberError(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
|
|
|
}
|
|
|
|
|
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 row > len(ws.SheetData.Row) {
|
2019-03-23 20:08:06 +08:00
|
|
|
return false, nil
|
2019-03-06 21:40:45 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
return !ws.SheetData.Row[row-1].Hidden, nil
|
2017-06-08 11:11:11 +08:00
|
|
|
}
|
2017-07-24 10:26:02 +08:00
|
|
|
|
2018-05-11 10:14:18 +08:00
|
|
|
// SetRowOutlineLevel provides a function to set outline level number of a
|
2019-09-24 21:53:19 +08:00
|
|
|
// single row by given worksheet name and Excel row number. The value of
|
|
|
|
// parameter 'level' is 1-7. For example, outline row 2 in Sheet1 to level 1:
|
2018-05-08 02:32:20 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.SetRowOutlineLevel("Sheet1", 2, 1)
|
2018-05-08 02:32:20 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
|
2019-03-06 21:40:45 +08:00
|
|
|
if row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return newInvalidRowNumberError(row)
|
2019-03-06 21:40:45 +08:00
|
|
|
}
|
2019-09-24 21:53:19 +08:00
|
|
|
if level > 7 || level < 1 {
|
|
|
|
return errors.New("invalid outline level")
|
|
|
|
}
|
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
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
prepareSheetXML(ws, 0, row)
|
|
|
|
ws.SheetData.Row[row-1].OutlineLevel = level
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2018-05-08 02:32:20 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// GetRowOutlineLevel provides a function to get outline level number of a
|
2019-03-20 15:13:41 +08:00
|
|
|
// single row by given worksheet name and Excel row number. For example, get
|
|
|
|
// outline number of row 2 in Sheet1:
|
2018-05-08 02:32:20 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// level, err := f.GetRowOutlineLevel("Sheet1", 2)
|
2018-05-08 02:32:20 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, 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 row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return 0, newInvalidRowNumberError(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
|
|
|
}
|
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 0, err
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if row > len(ws.SheetData.Row) {
|
2019-03-23 20:08:06 +08:00
|
|
|
return 0, nil
|
2019-03-06 21:40:45 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
return ws.SheetData.Row[row-1].OutlineLevel, nil
|
2018-05-08 02:32:20 +08:00
|
|
|
}
|
|
|
|
|
2019-03-07 16:03:31 +08:00
|
|
|
// RemoveRow provides a function to remove single row by given worksheet name
|
2019-03-06 21:40:45 +08:00
|
|
|
// and Excel row number. For example, remove row 3 in Sheet1:
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.RemoveRow("Sheet1", 3)
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2019-02-22 22:17:38 +08:00
|
|
|
// Use this method with caution, which will affect changes in references such
|
|
|
|
// as formulas, charts, and so on. If there is any referenced value of the
|
|
|
|
// worksheet, it will cause a file error when you open it. The excelize only
|
|
|
|
// partially updates these references currently.
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) RemoveRow(sheet string, row int) 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 row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return newInvalidRowNumberError(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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if row > len(ws.SheetData.Row) {
|
2019-06-12 08:10:33 +08:00
|
|
|
return f.adjustHelper(sheet, rows, row, -1)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
2020-04-02 00:41:14 +08:00
|
|
|
keep := 0
|
2020-11-10 23:48:09 +08:00
|
|
|
for rowIdx := 0; rowIdx < len(ws.SheetData.Row); rowIdx++ {
|
|
|
|
v := &ws.SheetData.Row[rowIdx]
|
2020-04-02 00:41:14 +08:00
|
|
|
if v.R != row {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.SheetData.Row[keep] = *v
|
2020-04-02 00:41:14 +08:00
|
|
|
keep++
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.SheetData.Row = ws.SheetData.Row[:keep]
|
2020-04-02 00:41:14 +08:00
|
|
|
return f.adjustHelper(sheet, rows, row, -1)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2019-03-07 15:13:32 +08:00
|
|
|
// InsertRow provides a function to insert a new row after given Excel row
|
|
|
|
// number starting from 1. For example, create a new row before row 3 in
|
|
|
|
// Sheet1:
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.InsertRow("Sheet1", 3)
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2019-06-08 00:00:55 +08:00
|
|
|
// Use this method with caution, which will affect changes in references such
|
|
|
|
// as formulas, charts, and so on. If there is any referenced value of the
|
|
|
|
// worksheet, it will cause a file error when you open it. The excelize only
|
|
|
|
// partially updates these references currently.
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) InsertRow(sheet string, row int) error {
|
2019-03-06 21:40:45 +08:00
|
|
|
if row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return newInvalidRowNumberError(row)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return f.adjustHelper(sheet, rows, row, 1)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2019-04-16 14:50:16 +08:00
|
|
|
// DuplicateRow inserts a copy of specified row (by its Excel row number) below
|
2018-12-26 13:33:40 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.DuplicateRow("Sheet1", 2)
|
2018-12-26 13:33:40 +08:00
|
|
|
//
|
2019-02-22 22:17:38 +08:00
|
|
|
// Use this method with caution, which will affect changes in references such
|
|
|
|
// as formulas, charts, and so on. If there is any referenced value of the
|
|
|
|
// worksheet, it will cause a file error when you open it. The excelize only
|
|
|
|
// partially updates these references currently.
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) DuplicateRow(sheet string, row int) error {
|
|
|
|
return f.DuplicateRowTo(sheet, row, row+1)
|
2018-12-27 22:28:28 +08:00
|
|
|
}
|
|
|
|
|
2019-03-06 21:40:45 +08:00
|
|
|
// DuplicateRowTo inserts a copy of specified row by it Excel number
|
|
|
|
// to specified row position moving down exists rows after target position
|
2018-12-27 22:28:28 +08:00
|
|
|
//
|
2019-04-20 14:57:50 +08:00
|
|
|
// err := f.DuplicateRowTo("Sheet1", 2, 7)
|
2018-12-27 22:28:28 +08:00
|
|
|
//
|
2019-02-22 22:17:38 +08:00
|
|
|
// Use this method with caution, which will affect changes in references such
|
|
|
|
// as formulas, charts, and so on. If there is any referenced value of the
|
|
|
|
// worksheet, it will cause a file error when you open it. The excelize only
|
|
|
|
// partially updates these references currently.
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) DuplicateRowTo(sheet string, row, row2 int) 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 row < 1 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return newInvalidRowNumberError(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
|
|
|
}
|
2019-03-06 21:40:45 +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
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if row > len(ws.SheetData.Row) || row2 < 1 || row == row2 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2018-12-26 13:33:40 +08:00
|
|
|
}
|
|
|
|
|
2018-12-27 22:28:28 +08:00
|
|
|
var ok bool
|
|
|
|
var rowCopy xlsxRow
|
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
for i, r := range ws.SheetData.Row {
|
2018-12-26 13:33:40 +08:00
|
|
|
if r.R == row {
|
2020-11-15 10:58:45 +08:00
|
|
|
rowCopy = deepcopy.Copy(ws.SheetData.Row[i]).(xlsxRow)
|
2018-12-27 22:28:28 +08:00
|
|
|
ok = true
|
2018-12-26 13:33:40 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2018-12-27 22:28:28 +08:00
|
|
|
if !ok {
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2018-12-27 22:28:28 +08:00
|
|
|
}
|
2018-12-26 13:33:40 +08:00
|
|
|
|
2019-03-23 20:08:06 +08:00
|
|
|
if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-27 22:28:28 +08:00
|
|
|
|
|
|
|
idx2 := -1
|
2020-11-10 23:48:09 +08:00
|
|
|
for i, r := range ws.SheetData.Row {
|
2018-12-27 22:28:28 +08:00
|
|
|
if r.R == row2 {
|
|
|
|
idx2 = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if idx2 == -1 && len(ws.SheetData.Row) >= row2 {
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2018-12-26 13:33:40 +08:00
|
|
|
}
|
2018-12-27 22:28:28 +08:00
|
|
|
|
|
|
|
rowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)
|
|
|
|
f.ajustSingleRowDimensions(&rowCopy, row2)
|
|
|
|
|
2018-12-26 13:33:40 +08:00
|
|
|
if idx2 != -1 {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.SheetData.Row[idx2] = rowCopy
|
2018-12-26 13:33:40 +08:00
|
|
|
} else {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.SheetData.Row = append(ws.SheetData.Row, rowCopy)
|
2018-12-26 13:33:40 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
return f.duplicateMergeCells(sheet, ws, row, row2)
|
2020-02-25 00:19:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// duplicateMergeCells merge cells in the destination row if there are single
|
|
|
|
// row merged cells in the copied row.
|
2020-11-10 23:48:09 +08:00
|
|
|
func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 int) error {
|
|
|
|
if ws.MergeCells == nil {
|
2020-02-25 00:19:22 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if row > row2 {
|
|
|
|
row++
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
for _, rng := range ws.MergeCells.Cells {
|
2020-02-25 00:19:22 +08:00
|
|
|
coordinates, err := f.areaRefToCoordinates(rng.Ref)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if coordinates[1] < row2 && row2 < coordinates[3] {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
for i := 0; i < len(ws.MergeCells.Cells); i++ {
|
|
|
|
areaData := ws.MergeCells.Cells[i]
|
2020-02-25 00:19:22 +08:00
|
|
|
coordinates, _ := f.areaRefToCoordinates(areaData.Ref)
|
|
|
|
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
|
|
|
|
if y1 == y2 && y1 == row {
|
|
|
|
from, _ := CoordinatesToCellName(x1, row2)
|
|
|
|
to, _ := CoordinatesToCellName(x2, row2)
|
|
|
|
if err := f.MergeCell(sheet, from, to); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2018-12-26 13:33:40 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// checkRow provides a function to check and fill each column element for all
|
|
|
|
// rows and make that is continuous in a worksheet of XML. For example:
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
|
|
|
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
|
|
|
// <c r="A15" s="2" />
|
|
|
|
// <c r="B15" s="2" />
|
|
|
|
// <c r="F15" s="1" />
|
|
|
|
// <c r="G15" s="1" />
|
|
|
|
// </row>
|
|
|
|
//
|
|
|
|
// in this case, we should to change it to
|
|
|
|
//
|
|
|
|
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
|
|
|
// <c r="A15" s="2" />
|
|
|
|
// <c r="B15" s="2" />
|
|
|
|
// <c r="C15" s="2" />
|
|
|
|
// <c r="D15" s="2" />
|
|
|
|
// <c r="E15" s="2" />
|
|
|
|
// <c r="F15" s="1" />
|
|
|
|
// <c r="G15" s="1" />
|
|
|
|
// </row>
|
|
|
|
//
|
|
|
|
// Noteice: this method could be very slow for large spreadsheets (more than
|
|
|
|
// 3000 rows one sheet).
|
2020-11-10 23:48:09 +08:00
|
|
|
func checkRow(ws *xlsxWorksheet) error {
|
|
|
|
for rowIdx := range ws.SheetData.Row {
|
|
|
|
rowData := &ws.SheetData.Row[rowIdx]
|
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
|
|
|
colCount := len(rowData.C)
|
|
|
|
if colCount == 0 {
|
|
|
|
continue
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
2020-04-24 08:26:16 +08:00
|
|
|
// check and fill the cell without r attribute in a row element
|
|
|
|
rCount := 0
|
|
|
|
for idx, cell := range rowData.C {
|
|
|
|
rCount++
|
|
|
|
if cell.R != "" {
|
|
|
|
lastR, _, err := CellNameToCoordinates(cell.R)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if lastR > rCount {
|
|
|
|
rCount = lastR
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
rowData.C[idx].R, _ = CoordinatesToCellName(rCount, rowIdx+1)
|
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
|
|
|
|
if err != nil {
|
|
|
|
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
|
|
|
|
|
|
|
if colCount < lastCol {
|
|
|
|
oldList := rowData.C
|
|
|
|
newlist := make([]xlsxC, 0, lastCol)
|
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
rowData.C = ws.SheetData.Row[rowIdx].C[:0]
|
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 colIdx := 0; colIdx < lastCol; colIdx++ {
|
2019-03-23 20:08:06 +08:00
|
|
|
cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newlist = append(newlist, xlsxC{R: cellName})
|
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
|
|
|
}
|
|
|
|
|
|
|
|
rowData.C = newlist
|
|
|
|
|
|
|
|
for colIdx := range oldList {
|
|
|
|
colData := &oldList[colIdx]
|
2019-03-23 20:08:06 +08:00
|
|
|
colNum, _, err := CellNameToCoordinates(colData.R)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.SheetData.Row[rowIdx].C[colNum-1] = *colData
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// convertRowHeightToPixels provides a function to convert the height of a
|
|
|
|
// cell from user's units to pixels. If the height hasn't been set by the user
|
|
|
|
// we use the default value. If the row is hidden it has a value of zero.
|
2017-07-24 10:26:02 +08:00
|
|
|
func convertRowHeightToPixels(height float64) float64 {
|
|
|
|
var pixels float64
|
|
|
|
if height == 0 {
|
|
|
|
return pixels
|
|
|
|
}
|
|
|
|
pixels = math.Ceil(4.0 / 3.0 * height)
|
|
|
|
return pixels
|
|
|
|
}
|