2022-01-09 00:20:42 +08:00
|
|
|
// Copyright 2016 - 2022 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
|
|
|
|
// data. This 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"
|
2018-05-05 13:33:19 +08:00
|
|
|
"fmt"
|
2019-12-20 00:30:48 +08:00
|
|
|
"io"
|
2022-01-11 00:24:24 +08:00
|
|
|
"io/ioutil"
|
2019-12-20 00:30:48 +08:00
|
|
|
"log"
|
2017-07-24 10:26:02 +08:00
|
|
|
"math"
|
2021-10-15 21:45:46 +08:00
|
|
|
"math/big"
|
2021-09-18 23:20:24 +08:00
|
|
|
"os"
|
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
|
|
|
)
|
|
|
|
|
2022-07-18 00:21:34 +08:00
|
|
|
// GetRows return all the rows in a sheet by given worksheet name, returned as
|
|
|
|
// a two-dimensional array, where the value of the cell is converted to the
|
|
|
|
// string type. If the cell format can be applied to the value of the cell,
|
|
|
|
// the applied value will be used, otherwise the original value will be used.
|
|
|
|
// GetRows fetched the rows with value or formula cells, the continually blank
|
|
|
|
// cells in the tail of each row will be skipped, so the length of each row
|
|
|
|
// may be inconsistent.
|
|
|
|
//
|
|
|
|
// For example, get and traverse the value of all cells by rows on a worksheet
|
|
|
|
// named 'Sheet1':
|
2016-11-02 12:58:51 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// rows, err := f.GetRows("Sheet1")
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// for _, row := range rows {
|
|
|
|
// for _, colCell := range row {
|
|
|
|
// fmt.Print(colCell, "\t")
|
|
|
|
// }
|
|
|
|
// fmt.Println()
|
|
|
|
// }
|
2021-09-05 11:59:50 +08:00
|
|
|
func (f *File) GetRows(sheet string, opts ...Options) ([][]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
|
|
|
|
}
|
2021-05-16 00:03:09 +08:00
|
|
|
results, cur, max := make([][]string, 0, 64), 0, 0
|
2019-08-05 06:23:42 +08:00
|
|
|
for rows.Next() {
|
2021-05-16 00:03:09 +08:00
|
|
|
cur++
|
2021-09-05 11:59:50 +08:00
|
|
|
row, err := rows.Columns(opts...)
|
2019-08-05 06:23:42 +08:00
|
|
|
if err != nil {
|
|
|
|
break
|
2016-11-02 12:58:51 +08:00
|
|
|
}
|
2019-08-05 06:23:42 +08:00
|
|
|
results = append(results, row)
|
2021-05-16 00:03:09 +08:00
|
|
|
if len(row) > 0 {
|
|
|
|
max = cur
|
|
|
|
}
|
2016-11-02 12:58:51 +08:00
|
|
|
}
|
2021-09-18 23:20:24 +08:00
|
|
|
return results[:max], rows.Close()
|
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 {
|
2022-01-17 08:05:52 +08:00
|
|
|
err error
|
|
|
|
curRow, seekRow int
|
|
|
|
needClose, rawCellValue bool
|
|
|
|
sheet string
|
|
|
|
f *File
|
|
|
|
tempFile *os.File
|
|
|
|
sst *xlsxSST
|
|
|
|
decoder *xml.Decoder
|
|
|
|
token xml.Token
|
2022-08-11 00:20:48 +08:00
|
|
|
curRowOpts, seekRowOpts RowOpts
|
2021-11-05 00:01:34 +08:00
|
|
|
}
|
|
|
|
|
2018-05-05 13:33:19 +08:00
|
|
|
// Next will return true if find the next row element.
|
|
|
|
func (rows *Rows) Next() bool {
|
2022-01-17 08:05:52 +08:00
|
|
|
rows.seekRow++
|
|
|
|
if rows.curRow >= rows.seekRow {
|
2022-08-11 00:20:48 +08:00
|
|
|
rows.curRowOpts = rows.seekRowOpts
|
2022-01-17 08:05:52 +08:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
token, _ := rows.decoder.Token()
|
|
|
|
if token == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
switch xmlElement := token.(type) {
|
|
|
|
case xml.StartElement:
|
|
|
|
if xmlElement.Name.Local == "row" {
|
|
|
|
rows.curRow++
|
|
|
|
if rowNum, _ := attrValToInt("r", xmlElement.Attr); rowNum != 0 {
|
|
|
|
rows.curRow = rowNum
|
|
|
|
}
|
|
|
|
rows.token = token
|
2022-08-11 00:20:48 +08:00
|
|
|
rows.curRowOpts = extractRowOpts(xmlElement.Attr)
|
2022-01-17 08:05:52 +08:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
case xml.EndElement:
|
|
|
|
if xmlElement.Name.Local == "sheetData" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
2022-08-11 00:20:48 +08:00
|
|
|
// GetRowOpts will return the RowOpts of the current row.
|
|
|
|
func (rows *Rows) GetRowOpts() RowOpts {
|
|
|
|
return rows.curRowOpts
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-09-18 23:20:24 +08:00
|
|
|
// Close closes the open worksheet XML file in the system temporary
|
|
|
|
// directory.
|
|
|
|
func (rows *Rows) Close() error {
|
|
|
|
if rows.tempFile != nil {
|
|
|
|
return rows.tempFile.Close()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-06-16 00:01:32 +08:00
|
|
|
// Columns return the current row's column values. This fetches the worksheet
|
|
|
|
// data as a stream, returns each cell in a row as is, and will not skip empty
|
|
|
|
// rows in the tail of the worksheet.
|
2021-09-05 11:59:50 +08:00
|
|
|
func (rows *Rows) Columns(opts ...Options) ([]string, error) {
|
2022-01-17 08:05:52 +08:00
|
|
|
if rows.curRow > rows.seekRow {
|
|
|
|
return nil, nil
|
2019-12-31 01:01:16 +08:00
|
|
|
}
|
2022-01-17 08:05:52 +08:00
|
|
|
var rowIterator rowXMLIterator
|
|
|
|
var token xml.Token
|
|
|
|
rows.rawCellValue, rows.sst = parseOptions(opts...).RawCellValue, rows.f.sharedStringsReader()
|
2019-11-23 04:13:59 +08:00
|
|
|
for {
|
2022-01-17 08:05:52 +08:00
|
|
|
if rows.token != nil {
|
|
|
|
token = rows.token
|
|
|
|
} else if token, _ = rows.decoder.Token(); token == nil {
|
2019-11-23 04:13:59 +08:00
|
|
|
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" {
|
2022-01-17 08:05:52 +08:00
|
|
|
rowNum := 0
|
|
|
|
if rowNum, rowIterator.err = attrValToInt("r", xmlElement.Attr); rowNum != 0 {
|
|
|
|
rows.curRow = rowNum
|
|
|
|
} else if rows.token == nil {
|
|
|
|
rows.curRow++
|
2020-11-18 22:08:40 +08:00
|
|
|
}
|
2022-08-11 00:20:48 +08:00
|
|
|
rows.token = token
|
|
|
|
rows.seekRowOpts = extractRowOpts(xmlElement.Attr)
|
2022-01-17 08:05:52 +08:00
|
|
|
if rows.curRow > rows.seekRow {
|
|
|
|
rows.token = nil
|
2021-02-05 22:52:31 +08:00
|
|
|
return rowIterator.columns, rowIterator.err
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
|
|
|
}
|
2022-01-17 08:05:52 +08:00
|
|
|
if rows.rowXMLHandler(&rowIterator, &xmlElement, rows.rawCellValue); rowIterator.err != nil {
|
|
|
|
rows.token = nil
|
2021-02-05 22:52:31 +08:00
|
|
|
return rowIterator.columns, rowIterator.err
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
2022-01-17 08:05:52 +08:00
|
|
|
rows.token = nil
|
2019-11-23 04:13:59 +08:00
|
|
|
case xml.EndElement:
|
2022-01-17 08:05:52 +08:00
|
|
|
if xmlElement.Name.Local == "sheetData" {
|
2021-02-05 22:52:31 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-08-17 10:59:52 +08:00
|
|
|
// extractRowOpts extract row element attributes.
|
2022-08-11 00:20:48 +08:00
|
|
|
func extractRowOpts(attrs []xml.Attr) RowOpts {
|
|
|
|
rowOpts := RowOpts{Height: defaultRowHeight}
|
|
|
|
if styleID, err := attrValToInt("s", attrs); err == nil && styleID > 0 && styleID < MaxCellStyles {
|
|
|
|
rowOpts.StyleID = styleID
|
|
|
|
}
|
|
|
|
if hidden, err := attrValToBool("hidden", attrs); err == nil {
|
|
|
|
rowOpts.Hidden = hidden
|
|
|
|
}
|
|
|
|
if height, err := attrValToFloat("ht", attrs); err == nil {
|
|
|
|
rowOpts.Height = height
|
|
|
|
}
|
|
|
|
return rowOpts
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-08-28 00:16:41 +08:00
|
|
|
// ErrSheetNotExist defines an error of sheet that does not exist
|
2018-05-05 13:33:19 +08:00
|
|
|
type ErrSheetNotExist struct {
|
|
|
|
SheetName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (err ErrSheetNotExist) Error() string {
|
2022-08-28 00:16:41 +08:00
|
|
|
return fmt.Sprintf("sheet %s does not exist", 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 {
|
2022-01-17 08:05:52 +08:00
|
|
|
err error
|
|
|
|
inElement string
|
|
|
|
cellCol int
|
|
|
|
columns []string
|
2021-02-05 22:52:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// rowXMLHandler parse the row XML element of the worksheet.
|
2022-01-17 08:05:52 +08:00
|
|
|
func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement, raw bool) {
|
2021-02-05 22:52:31 +08:00
|
|
|
if rowIterator.inElement == "c" {
|
|
|
|
rowIterator.cellCol++
|
|
|
|
colCell := xlsxC{}
|
2022-01-17 08:05:52 +08:00
|
|
|
_ = rows.decoder.DecodeElement(&colCell, xmlElement)
|
2021-02-05 22:52:31 +08:00
|
|
|
if colCell.R != "" {
|
|
|
|
if rowIterator.cellCol, _, rowIterator.err = CellNameToCoordinates(colCell.R); rowIterator.err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
blank := rowIterator.cellCol - len(rowIterator.columns)
|
2022-01-17 08:05:52 +08:00
|
|
|
if val, _ := colCell.getValueFrom(rows.f, rows.sst, raw); val != "" || colCell.F != nil {
|
2021-05-16 00:03:09 +08:00
|
|
|
rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val)
|
|
|
|
}
|
2021-02-05 22:52:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// rows, err := f.Rows("Sheet1")
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// for rows.Next() {
|
|
|
|
// row, err := rows.Columns()
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// for _, colCell := range row {
|
|
|
|
// fmt.Print(colCell, "\t")
|
|
|
|
// }
|
|
|
|
// fmt.Println()
|
|
|
|
// }
|
|
|
|
// if err = rows.Close(); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
2018-05-05 13:33:19 +08:00
|
|
|
func (f *File) Rows(sheet string) (*Rows, error) {
|
2022-07-18 00:21:34 +08:00
|
|
|
name, ok := f.getSheetXMLPath(sheet)
|
2018-05-05 13:33:19 +08:00
|
|
|
if !ok {
|
|
|
|
return nil, ErrSheetNotExist{sheet}
|
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
if ws, ok := f.Sheet.Load(name); ok && ws != nil {
|
|
|
|
worksheet := ws.(*xlsxWorksheet)
|
|
|
|
worksheet.Lock()
|
|
|
|
defer worksheet.Unlock()
|
2019-11-23 04:13:59 +08:00
|
|
|
// flush data
|
2021-07-05 00:03:56 +08:00
|
|
|
output, _ := xml.Marshal(worksheet)
|
2020-07-18 15:15:16 +08:00
|
|
|
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
|
2019-11-23 04:13:59 +08:00
|
|
|
}
|
2022-01-17 08:05:52 +08:00
|
|
|
var err error
|
|
|
|
rows := Rows{f: f, sheet: name}
|
|
|
|
rows.needClose, rows.decoder, rows.tempFile, err = f.xmlDecoder(name)
|
|
|
|
return &rows, err
|
2018-05-05 13:33:19 +08:00
|
|
|
}
|
|
|
|
|
2022-01-11 00:24:24 +08:00
|
|
|
// getFromStringItem build shared string item offset list from system temporary
|
2021-12-27 23:34:14 +08:00
|
|
|
// file at one time, and return value by given to string index.
|
2022-01-11 00:24:24 +08:00
|
|
|
func (f *File) getFromStringItem(index int) string {
|
|
|
|
if f.sharedStringTemp != nil {
|
|
|
|
if len(f.sharedStringItem) <= index {
|
|
|
|
return strconv.Itoa(index)
|
2021-12-27 23:34:14 +08:00
|
|
|
}
|
2022-01-11 00:24:24 +08:00
|
|
|
offsetRange := f.sharedStringItem[index]
|
|
|
|
buf := make([]byte, offsetRange[1]-offsetRange[0])
|
|
|
|
if _, err := f.sharedStringTemp.ReadAt(buf, int64(offsetRange[0])); err != nil {
|
|
|
|
return strconv.Itoa(index)
|
|
|
|
}
|
|
|
|
return string(buf)
|
2021-12-27 23:34:14 +08:00
|
|
|
}
|
2022-01-09 00:20:42 +08:00
|
|
|
needClose, decoder, tempFile, err := f.xmlDecoder(defaultXMLPathSharedStrings)
|
2021-12-27 23:34:14 +08:00
|
|
|
if needClose && err == nil {
|
|
|
|
defer tempFile.Close()
|
|
|
|
}
|
2022-01-11 00:24:24 +08:00
|
|
|
f.sharedStringItem = [][]uint{}
|
|
|
|
f.sharedStringTemp, _ = ioutil.TempFile(os.TempDir(), "excelize-")
|
|
|
|
f.tempFiles.Store(defaultTempFileSST, f.sharedStringTemp.Name())
|
2021-12-27 23:34:14 +08:00
|
|
|
var (
|
|
|
|
inElement string
|
2022-01-11 00:24:24 +08:00
|
|
|
i, offset uint
|
2021-12-27 23:34:14 +08:00
|
|
|
)
|
|
|
|
for {
|
|
|
|
token, _ := decoder.Token()
|
|
|
|
if token == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
switch xmlElement := token.(type) {
|
|
|
|
case xml.StartElement:
|
|
|
|
inElement = xmlElement.Name.Local
|
|
|
|
if inElement == "si" {
|
|
|
|
si := xlsxSI{}
|
|
|
|
_ = decoder.DecodeElement(&si, &xmlElement)
|
2022-01-11 00:24:24 +08:00
|
|
|
|
|
|
|
startIdx := offset
|
|
|
|
n, _ := f.sharedStringTemp.WriteString(si.String())
|
|
|
|
offset += uint(n)
|
|
|
|
f.sharedStringItem = append(f.sharedStringItem, []uint{startIdx, offset})
|
2021-12-27 23:34:14 +08:00
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-11 00:24:24 +08:00
|
|
|
return f.getFromStringItem(index)
|
2021-12-27 23:34:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// xmlDecoder creates XML decoder by given path in the zip from memory data
|
2021-09-18 23:20:24 +08:00
|
|
|
// or system temporary file.
|
2021-12-27 23:34:14 +08:00
|
|
|
func (f *File) xmlDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
|
2021-09-18 23:20:24 +08:00
|
|
|
var (
|
|
|
|
content []byte
|
|
|
|
err error
|
|
|
|
tempFile *os.File
|
|
|
|
)
|
|
|
|
if content = f.readXML(name); len(content) > 0 {
|
|
|
|
return false, f.xmlNewDecoder(bytes.NewReader(content)), tempFile, err
|
|
|
|
}
|
|
|
|
tempFile, err = f.readTemp(name)
|
|
|
|
return true, f.xmlNewDecoder(tempFile), tempFile, err
|
|
|
|
}
|
|
|
|
|
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
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetRowHeight("Sheet1", 1, 50)
|
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 {
|
2021-05-10 00:09:24 +08:00
|
|
|
return ErrMaxRowHeight
|
2020-09-18 22:20:58 +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
|
|
|
|
}
|
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
|
2021-06-13 14:38:01 +08:00
|
|
|
// name and row number.
|
2017-06-29 11:14:33 +08:00
|
|
|
func (f *File) getRowHeight(sheet string, row int) int {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, _ := f.workSheetReader(sheet)
|
2021-07-06 00:31:04 +08:00
|
|
|
ws.Lock()
|
|
|
|
defer ws.Unlock()
|
2020-11-10 23:48:09 +08:00
|
|
|
for i := range ws.SheetData.Row {
|
|
|
|
v := &ws.SheetData.Row[i]
|
2021-06-13 14:42:09 +08:00
|
|
|
if v.R == row && v.Ht != 0 {
|
2017-06-29 11:14:33 +08:00
|
|
|
return int(convertRowHeightToPixels(v.Ht))
|
|
|
|
}
|
|
|
|
}
|
2022-01-09 00:20:42 +08:00
|
|
|
// Optimization for when the row heights haven't changed.
|
2017-06-29 11:14:33 +08:00
|
|
|
return int(defaultRowHeightPixels)
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// GetRowHeight provides a function to get row height by given worksheet name
|
2021-06-13 14:38:01 +08:00
|
|
|
// and row number. For example, get the height of the first row in Sheet1:
|
2018-04-02 10:59:15 +08:00
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// height, err := f.GetRowHeight("Sheet1", 1)
|
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
|
|
|
}
|
2022-01-23 00:32:34 +08:00
|
|
|
ht := defaultRowHeight
|
2020-08-22 18:58:43 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2022-01-09 00:20:42 +08:00
|
|
|
// Optimization 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
|
2022-01-09 00:20:42 +08:00
|
|
|
ss := f.readXML(defaultXMLPathSharedStrings)
|
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)
|
|
|
|
}
|
2021-05-24 15:27:36 +08:00
|
|
|
if sharedStrings.Count == 0 {
|
|
|
|
sharedStrings.Count = len(sharedStrings.SI)
|
|
|
|
}
|
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
|
2022-01-09 00:20:42 +08:00
|
|
|
// intended to be used with for range on rows an argument with the spreadsheet
|
2020-11-10 23:48:09 +08:00
|
|
|
// opened file.
|
2021-09-05 11:59:50 +08:00
|
|
|
func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (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 {
|
2022-03-08 00:03:02 +08:00
|
|
|
case "b":
|
|
|
|
if !raw {
|
|
|
|
if c.V == "1" {
|
|
|
|
return "TRUE", nil
|
|
|
|
}
|
|
|
|
if c.V == "0" {
|
|
|
|
return "FALSE", nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return f.formattedValue(c.S, c.V, raw), nil
|
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)
|
2022-01-09 00:20:42 +08:00
|
|
|
if _, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok {
|
2022-01-11 00:24:24 +08:00
|
|
|
return f.formattedValue(c.S, f.getFromStringItem(xlsxSI), raw), nil
|
2021-12-27 23:34:14 +08:00
|
|
|
}
|
2020-04-01 15:38:37 +08:00
|
|
|
if len(d.SI) > xlsxSI {
|
2021-09-05 11:59:50 +08:00
|
|
|
return f.formattedValue(c.S, d.SI[xlsxSI].String(), raw), nil
|
2020-04-01 15:38:37 +08:00
|
|
|
}
|
2019-12-20 22:22:56 +08:00
|
|
|
}
|
2021-09-05 11:59:50 +08:00
|
|
|
return f.formattedValue(c.S, c.V, raw), nil
|
2016-11-02 12:58:51 +08:00
|
|
|
case "str":
|
2021-09-05 11:59:50 +08:00
|
|
|
return f.formattedValue(c.S, c.V, raw), nil
|
2018-04-09 19:44:08 +08:00
|
|
|
case "inlineStr":
|
2020-11-10 23:48:09 +08:00
|
|
|
if c.IS != nil {
|
2021-09-05 11:59:50 +08:00
|
|
|
return f.formattedValue(c.S, c.IS.String(), raw), nil
|
2019-12-11 00:02:33 +08:00
|
|
|
}
|
2021-09-05 11:59:50 +08:00
|
|
|
return f.formattedValue(c.S, c.V, raw), nil
|
2016-11-02 12:58:51 +08:00
|
|
|
default:
|
2022-03-19 00:05:47 +08:00
|
|
|
if isNum, precision := isNumeric(c.V); isNum && !raw {
|
|
|
|
if precision == 0 {
|
|
|
|
c.V = roundPrecision(c.V, 15)
|
|
|
|
} else {
|
|
|
|
c.V = roundPrecision(c.V, -1)
|
|
|
|
}
|
|
|
|
}
|
2021-09-05 11:59:50 +08:00
|
|
|
return f.formattedValue(c.S, c.V, raw), 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
|
|
|
|
2021-10-15 21:45:46 +08:00
|
|
|
// roundPrecision provides a function to format floating-point number text
|
|
|
|
// with precision, if the given text couldn't be parsed to float, this will
|
|
|
|
// return the original string.
|
|
|
|
func roundPrecision(text string, prec int) string {
|
|
|
|
decimal := big.Float{}
|
|
|
|
if _, ok := decimal.SetString(text); ok {
|
|
|
|
flt, _ := decimal.Float64()
|
|
|
|
if prec == -1 {
|
2022-03-18 00:52:10 +08:00
|
|
|
return strconv.FormatFloat(flt, 'G', 15, 64)
|
2021-10-15 21:45:46 +08:00
|
|
|
}
|
|
|
|
return strconv.FormatFloat(flt, 'f', -1, 64)
|
2020-11-19 21:38:35 +08:00
|
|
|
}
|
2021-10-15 21:45:46 +08:00
|
|
|
return text
|
2020-11-19 21:38:35 +08:00
|
|
|
}
|
|
|
|
|
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
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetRowVisible("Sheet1", 2, false)
|
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
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// visible, err := f.GetRowVisible("Sheet1", 2)
|
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
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err := f.SetRowOutlineLevel("Sheet1", 2, 1)
|
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 {
|
2021-05-10 00:09:24 +08:00
|
|
|
return ErrOutlineLevel
|
2019-09-24 21:53:19 +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].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
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// level, err := f.GetRowOutlineLevel("Sheet1", 2)
|
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
|
|
|
//
|
2022-08-13 11:21:59 +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
|
|
|
}
|
|
|
|
|
2022-08-31 00:02:48 +08:00
|
|
|
// InsertRows provides a function to insert new rows after the given Excel row
|
|
|
|
// number starting from 1 and number of rows. For example, create two rows
|
|
|
|
// before row 3 in Sheet1:
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
2022-08-31 00:02:48 +08:00
|
|
|
// err := f.InsertRows("Sheet1", 3, 2)
|
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.
|
2022-08-31 00:02:48 +08:00
|
|
|
func (f *File) InsertRows(sheet string, row, n 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
|
|
|
}
|
2022-08-31 00:02:48 +08:00
|
|
|
if row >= TotalRows || n >= TotalRows {
|
|
|
|
return ErrMaxRows
|
|
|
|
}
|
|
|
|
if n < 1 {
|
|
|
|
return ErrParameterInvalid
|
|
|
|
}
|
|
|
|
return f.adjustHelper(sheet, rows, row, n)
|
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
|
|
|
//
|
2022-08-13 11:21:59 +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
|
|
|
//
|
2022-08-13 11:21:59 +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
|
|
|
|
}
|
2022-01-14 00:28:31 +08:00
|
|
|
|
|
|
|
if 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-01-14 00:28:31 +08:00
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
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...)
|
2022-01-09 00:20:42 +08:00
|
|
|
f.adjustSingleRowDimensions(&rowCopy, row2)
|
2018-12-27 22:28:28 +08:00
|
|
|
|
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 {
|
2021-08-13 01:32:44 +08:00
|
|
|
coordinates, err := areaRefToCoordinates(rng.Ref)
|
2020-02-25 00:19:22 +08:00
|
|
|
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]
|
2021-08-13 01:32:44 +08:00
|
|
|
coordinates, _ := areaRefToCoordinates(areaData.Ref)
|
2020-02-25 00:19:22 +08:00
|
|
|
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
|
|
|
//
|
2022-08-13 11:21:59 +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>
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
|
|
|
// in this case, we should to change it to
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// <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>
|
2017-07-24 10:26:02 +08:00
|
|
|
//
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2021-08-22 13:36:56 +08:00
|
|
|
// SetRowStyle provides a function to set the style of rows by given worksheet
|
|
|
|
// name, row range, and style ID. Note that this will overwrite the existing
|
|
|
|
// styles for the rows, it won't append or merge style with existing styles.
|
2021-08-17 00:01:44 +08:00
|
|
|
//
|
|
|
|
// For example set style of row 1 on Sheet1:
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err = f.SetRowStyle("Sheet1", 1, 1, styleID)
|
2021-08-17 00:01:44 +08:00
|
|
|
//
|
|
|
|
// Set style of rows 1 to 10 on Sheet1:
|
|
|
|
//
|
2022-08-13 11:21:59 +08:00
|
|
|
// err = f.SetRowStyle("Sheet1", 1, 10, styleID)
|
2021-08-17 00:01:44 +08:00
|
|
|
func (f *File) SetRowStyle(sheet string, start, end, styleID int) error {
|
|
|
|
if end < start {
|
|
|
|
start, end = end, start
|
|
|
|
}
|
|
|
|
if start < 1 {
|
|
|
|
return newInvalidRowNumberError(start)
|
|
|
|
}
|
|
|
|
if end > TotalRows {
|
|
|
|
return ErrMaxRows
|
|
|
|
}
|
|
|
|
if styleID < 0 {
|
|
|
|
return newInvalidStyleID(styleID)
|
|
|
|
}
|
|
|
|
ws, err := f.workSheetReader(sheet)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
prepareSheetXML(ws, 0, end)
|
|
|
|
for row := start - 1; row < end; row++ {
|
|
|
|
ws.SheetData.Row[row].S = styleID
|
|
|
|
ws.SheetData.Row[row].CustomFormat = true
|
2022-05-01 12:28:36 +08:00
|
|
|
for i := range ws.SheetData.Row[row].C {
|
|
|
|
if _, rowNum, err := CellNameToCoordinates(ws.SheetData.Row[row].C[i].R); err == nil && rowNum-1 == row {
|
|
|
|
ws.SheetData.Row[row].C[i].S = styleID
|
|
|
|
}
|
|
|
|
}
|
2021-08-17 00:01:44 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|