2024-01-09 20:56:20 +08:00
|
|
|
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of
|
2019-03-20 15:13:41 +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
|
2024-01-18 15:31:43 +08:00
|
|
|
// data. This library needs Go version 1.18 or later.
|
2019-03-20 15:13:41 +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
|
|
|
package excelize
|
|
|
|
|
2022-07-16 12:50:13 +08:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/xml"
|
|
|
|
"io"
|
2023-10-29 13:40:21 +08:00
|
|
|
"strconv"
|
2022-07-16 12:50:13 +08:00
|
|
|
"strings"
|
2023-11-01 00:52:18 +08:00
|
|
|
"unicode"
|
2023-10-24 00:05:52 +08:00
|
|
|
|
|
|
|
"github.com/xuri/efp"
|
2022-07-16 12:50:13 +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
|
|
|
type adjustDirection bool
|
|
|
|
|
|
|
|
const (
|
|
|
|
columns adjustDirection = false
|
|
|
|
rows adjustDirection = true
|
|
|
|
)
|
|
|
|
|
2023-11-06 09:51:19 +08:00
|
|
|
// adjustHelperFunc defines functions to adjust helper.
|
2023-11-11 00:04:05 +08:00
|
|
|
var adjustHelperFunc = [9]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
|
2023-11-10 09:25:59 +08:00
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
return f.adjustConditionalFormats(ws, sheet, dir, num, offset, sheetID)
|
|
|
|
},
|
2023-11-11 00:04:05 +08:00
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
return f.adjustDataValidations(ws, sheet, dir, num, offset, sheetID)
|
|
|
|
},
|
2023-11-06 09:51:19 +08:00
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2023-11-08 00:01:35 +08:00
|
|
|
return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
|
|
|
|
},
|
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
return f.adjustDrawings(ws, sheet, dir, num, offset, sheetID)
|
2023-11-06 09:51:19 +08:00
|
|
|
},
|
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
return f.adjustMergeCells(ws, sheet, dir, num, offset, sheetID)
|
|
|
|
},
|
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
return f.adjustAutoFilter(ws, sheet, dir, num, offset, sheetID)
|
|
|
|
},
|
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
return f.adjustCalcChain(ws, sheet, dir, num, offset, sheetID)
|
|
|
|
},
|
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2023-11-08 00:01:35 +08:00
|
|
|
return f.adjustTable(ws, sheet, dir, num, offset, sheetID)
|
2023-11-06 09:51:19 +08:00
|
|
|
},
|
|
|
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2023-11-08 00:01:35 +08:00
|
|
|
return f.adjustVolatileDeps(ws, sheet, dir, num, offset, sheetID)
|
2023-11-06 09:51:19 +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
|
|
|
// adjustHelper provides a function to adjust rows and columns dimensions,
|
|
|
|
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
|
|
|
|
// columns.
|
|
|
|
//
|
|
|
|
// sheet: Worksheet name that we're editing
|
|
|
|
// column: Index number of the column we're inserting/deleting before
|
|
|
|
// row: Index number of the row we're inserting/deleting before
|
|
|
|
// offset: Number of rows/column to insert/delete negative values indicate deletion
|
|
|
|
//
|
2023-11-11 00:04:05 +08:00
|
|
|
// TODO: adjustComments, adjustPageBreaks, adjustProtectedCells
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-02 22:23:16 +08:00
|
|
|
sheetID := f.getSheetID(sheet)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if dir == rows {
|
2023-10-24 00:05:52 +08:00
|
|
|
err = f.adjustRowDimensions(sheet, ws, num, offset)
|
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
|
|
|
} else {
|
2023-10-24 00:05:52 +08:00
|
|
|
err = f.adjustColDimensions(sheet, ws, num, offset)
|
2022-08-31 00:02:48 +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
|
|
|
f.adjustHyperlinks(ws, sheet, dir, num, offset)
|
2023-10-31 00:01:57 +08:00
|
|
|
ws.checkSheet()
|
|
|
|
_ = ws.checkRow()
|
2023-11-06 09:51:19 +08:00
|
|
|
for _, fn := range adjustHelperFunc {
|
|
|
|
if err := fn(f, ws, sheet, dir, num, offset, sheetID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-02 00:15:41 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.MergeCells != nil && len(ws.MergeCells.Cells) == 0 {
|
|
|
|
ws.MergeCells = nil
|
2019-08-03 23:10:01 +08:00
|
|
|
}
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2022-11-11 01:50:07 +08:00
|
|
|
// adjustCols provides a function to update column style when inserting or
|
|
|
|
// deleting columns.
|
|
|
|
func (f *File) adjustCols(ws *xlsxWorksheet, col, offset int) error {
|
|
|
|
if ws.Cols == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for i := 0; i < len(ws.Cols.Col); i++ {
|
|
|
|
if offset > 0 {
|
|
|
|
if ws.Cols.Col[i].Min >= col {
|
2023-11-08 00:01:35 +08:00
|
|
|
if ws.Cols.Col[i].Min += offset; ws.Cols.Col[i].Min > MaxColumns {
|
2022-11-11 01:50:07 +08:00
|
|
|
ws.Cols.Col = append(ws.Cols.Col[:i], ws.Cols.Col[i+1:]...)
|
2023-11-08 00:01:35 +08:00
|
|
|
i--
|
|
|
|
continue
|
2022-11-11 01:50:07 +08:00
|
|
|
}
|
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
if ws.Cols.Col[i].Max >= col || ws.Cols.Col[i].Max+1 == col {
|
|
|
|
if ws.Cols.Col[i].Max += offset; ws.Cols.Col[i].Max > MaxColumns {
|
|
|
|
ws.Cols.Col[i].Max = MaxColumns
|
|
|
|
}
|
2022-11-11 01:50:07 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
continue
|
2022-11-11 01:50:07 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
if ws.Cols.Col[i].Min == col && ws.Cols.Col[i].Max == col {
|
|
|
|
ws.Cols.Col = append(ws.Cols.Col[:i], ws.Cols.Col[i+1:]...)
|
|
|
|
i--
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ws.Cols.Col[i].Min > col {
|
|
|
|
ws.Cols.Col[i].Min += offset
|
|
|
|
}
|
|
|
|
if ws.Cols.Col[i].Max >= col {
|
|
|
|
ws.Cols.Col[i].Max += offset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(ws.Cols.Col) == 0 {
|
|
|
|
ws.Cols = nil
|
2022-11-11 01:50:07 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// adjustColDimensions provides a function to update column dimensions when
|
|
|
|
// inserting or deleting rows or columns.
|
2023-10-24 00:05:52 +08:00
|
|
|
func (f *File) adjustColDimensions(sheet string, ws *xlsxWorksheet, col, offset int) error {
|
2022-09-07 00:18:16 +08:00
|
|
|
for rowIdx := range ws.SheetData.Row {
|
|
|
|
for _, v := range ws.SheetData.Row[rowIdx].C {
|
|
|
|
if cellCol, _, _ := CellNameToCoordinates(v.R); col <= cellCol {
|
|
|
|
if newCol := cellCol + offset; newCol > 0 && newCol > MaxColumns {
|
|
|
|
return ErrColumnNumber
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-01 00:52:18 +08:00
|
|
|
for _, sheetN := range f.GetSheetList() {
|
|
|
|
worksheet, err := f.workSheetReader(sheetN)
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == newNotWorksheetError(sheetN).Error() {
|
|
|
|
continue
|
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
|
|
|
}
|
2023-11-01 00:52:18 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
for rowIdx := range worksheet.SheetData.Row {
|
|
|
|
for colIdx, v := range worksheet.SheetData.Row[rowIdx].C {
|
|
|
|
if cellCol, cellRow, _ := CellNameToCoordinates(v.R); sheetN == sheet && col <= cellCol {
|
|
|
|
if newCol := cellCol + offset; newCol > 0 {
|
|
|
|
worksheet.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
|
|
|
|
}
|
|
|
|
}
|
2024-01-18 15:31:43 +08:00
|
|
|
if err := f.adjustFormula(sheet, sheetN, &worksheet.SheetData.Row[rowIdx].C[colIdx], columns, col, offset, false); err != nil {
|
2023-11-01 00:52:18 +08:00
|
|
|
return err
|
|
|
|
}
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-11 01:50:07 +08:00
|
|
|
return f.adjustCols(ws, col, offset)
|
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
|
|
|
}
|
|
|
|
|
|
|
|
// adjustRowDimensions provides a function to update row dimensions when
|
|
|
|
// inserting or deleting rows or columns.
|
2023-10-24 00:05:52 +08:00
|
|
|
func (f *File) adjustRowDimensions(sheet string, ws *xlsxWorksheet, row, offset int) error {
|
2023-11-01 00:52:18 +08:00
|
|
|
for _, sheetN := range f.GetSheetList() {
|
|
|
|
if sheetN == sheet {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
worksheet, err := f.workSheetReader(sheetN)
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == newNotWorksheetError(sheetN).Error() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
numOfRows := len(worksheet.SheetData.Row)
|
|
|
|
for i := 0; i < numOfRows; i++ {
|
|
|
|
r := &worksheet.SheetData.Row[i]
|
|
|
|
if err = f.adjustSingleRowFormulas(sheet, sheetN, r, row, offset, false); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-07 00:18:16 +08:00
|
|
|
totalRows := len(ws.SheetData.Row)
|
|
|
|
if totalRows == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
lastRow := &ws.SheetData.Row[totalRows-1]
|
2023-11-18 16:44:45 +08:00
|
|
|
if newRow := *lastRow.R + offset; *lastRow.R >= row && newRow > 0 && newRow > TotalRows {
|
2022-09-07 00:18:16 +08:00
|
|
|
return ErrMaxRows
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
numOfRows := len(ws.SheetData.Row)
|
|
|
|
for i := 0; i < numOfRows; i++ {
|
2020-11-10 23:48:09 +08:00
|
|
|
r := &ws.SheetData.Row[i]
|
2023-11-18 16:44:45 +08:00
|
|
|
if newRow := *r.R + offset; *r.R >= row && newRow > 0 {
|
2023-10-29 13:40:21 +08:00
|
|
|
r.adjustSingleRowDimensions(offset)
|
|
|
|
}
|
2023-11-01 00:52:18 +08:00
|
|
|
if err := f.adjustSingleRowFormulas(sheet, sheet, r, row, offset, false); err != nil {
|
2023-10-29 13:40:21 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
2022-08-31 00:02:48 +08:00
|
|
|
return nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2022-01-09 00:20:42 +08:00
|
|
|
// adjustSingleRowDimensions provides a function to adjust single row dimensions.
|
2023-10-29 13:40:21 +08:00
|
|
|
func (r *xlsxRow) adjustSingleRowDimensions(offset int) {
|
2023-11-18 16:44:45 +08:00
|
|
|
r.R = intPtr(*r.R + offset)
|
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 i, col := range r.C {
|
|
|
|
colName, _, _ := SplitCellName(col.R)
|
2023-11-18 16:44:45 +08:00
|
|
|
r.C[i].R, _ = JoinCellName(colName, *r.R)
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustSingleRowFormulas provides a function to adjust single row formulas.
|
2023-11-01 00:52:18 +08:00
|
|
|
func (f *File) adjustSingleRowFormulas(sheet, sheetN string, r *xlsxRow, num, offset int, si bool) error {
|
2024-01-18 15:31:43 +08:00
|
|
|
for i := 0; i < len(r.C); i++ {
|
|
|
|
if err := f.adjustFormula(sheet, sheetN, &r.C[i], rows, num, offset, si); err != nil {
|
2023-10-24 00:05:52 +08:00
|
|
|
return err
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2023-10-24 00:05:52 +08:00
|
|
|
return nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2023-10-29 13:40:21 +08:00
|
|
|
// adjustCellRef provides a function to adjust cell reference.
|
2023-11-10 09:25:59 +08:00
|
|
|
func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (string, bool, error) {
|
2023-10-29 13:40:21 +08:00
|
|
|
if !strings.Contains(ref, ":") {
|
|
|
|
ref += ":" + ref
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
2023-11-10 09:25:59 +08:00
|
|
|
var delete bool
|
2023-10-29 13:40:21 +08:00
|
|
|
coordinates, err := rangeRefToCoordinates(ref)
|
|
|
|
if err != nil {
|
2023-11-10 09:25:59 +08:00
|
|
|
return ref, delete, err
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
if dir == columns {
|
2023-11-10 09:25:59 +08:00
|
|
|
if offset < 0 && coordinates[0] == coordinates[2] {
|
|
|
|
delete = true
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
if coordinates[0] >= num {
|
2023-07-04 00:06:37 +08:00
|
|
|
coordinates[0] += offset
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
if coordinates[2] >= num {
|
2023-07-04 00:06:37 +08:00
|
|
|
coordinates[2] += offset
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
} else {
|
2023-11-10 09:25:59 +08:00
|
|
|
if offset < 0 && coordinates[1] == coordinates[3] {
|
|
|
|
delete = true
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
if coordinates[1] >= num {
|
2023-07-04 00:06:37 +08:00
|
|
|
coordinates[1] += offset
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
if coordinates[3] >= num {
|
2023-07-04 00:06:37 +08:00
|
|
|
coordinates[3] += offset
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
2024-03-01 10:12:17 +08:00
|
|
|
ref, err = coordinatesToRangeRef(coordinates)
|
2023-11-10 09:25:59 +08:00
|
|
|
return ref, delete, err
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// adjustFormula provides a function to adjust formula reference and shared
|
|
|
|
// formula reference.
|
2024-01-18 15:31:43 +08:00
|
|
|
func (f *File) adjustFormula(sheet, sheetN string, cell *xlsxC, dir adjustDirection, num, offset int, si bool) error {
|
|
|
|
var err error
|
|
|
|
if cell.f != "" {
|
|
|
|
if cell.f, err = f.adjustFormulaRef(sheet, sheetN, cell.f, false, dir, num, offset); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if cell.F == nil {
|
2023-10-29 13:40:21 +08:00
|
|
|
return nil
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
2024-01-18 15:31:43 +08:00
|
|
|
if cell.F.Ref != "" && sheet == sheetN {
|
|
|
|
if cell.F.Ref, _, err = f.adjustCellRef(cell.F.Ref, dir, num, offset); err != nil {
|
2023-07-04 00:06:37 +08:00
|
|
|
return err
|
|
|
|
}
|
2024-01-18 15:31:43 +08:00
|
|
|
if si && cell.F.Si != nil {
|
|
|
|
cell.F.Si = intPtr(*cell.F.Si + 1)
|
2023-07-04 00:06:37 +08:00
|
|
|
}
|
|
|
|
}
|
2024-01-18 15:31:43 +08:00
|
|
|
if cell.F.Content != "" {
|
|
|
|
if cell.F.Content, err = f.adjustFormulaRef(sheet, sheetN, cell.F.Content, false, dir, num, offset); err != nil {
|
2023-10-24 00:05:52 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-07-04 00:06:37 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-01 00:52:18 +08:00
|
|
|
// escapeSheetName enclose sheet name in single quotation marks if the giving
|
|
|
|
// worksheet name includes spaces or non-alphabetical characters.
|
|
|
|
func escapeSheetName(name string) string {
|
|
|
|
if strings.IndexFunc(name, func(r rune) bool {
|
|
|
|
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
|
|
|
|
}) != -1 {
|
2023-11-08 00:01:35 +08:00
|
|
|
return "'" + strings.ReplaceAll(name, "'", "''") + "'"
|
2023-11-01 00:52:18 +08:00
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2023-10-29 13:40:21 +08:00
|
|
|
// adjustFormulaColumnName adjust column name in the formula reference.
|
2023-11-08 00:01:35 +08:00
|
|
|
func adjustFormulaColumnName(name, operand string, abs, keepRelative bool, dir adjustDirection, num, offset int) (string, string, bool, error) {
|
|
|
|
if name == "" || (!abs && keepRelative) {
|
|
|
|
return "", operand + name, abs, nil
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
col, err := ColumnNameToNumber(name)
|
|
|
|
if err != nil {
|
2023-11-08 00:01:35 +08:00
|
|
|
return "", operand, false, err
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
if dir == columns && col >= num {
|
2024-02-29 09:16:39 +08:00
|
|
|
if col += offset; col < 1 {
|
|
|
|
col = 1
|
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
colName, err := ColumnNumberToName(col)
|
|
|
|
return "", operand + colName, false, err
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
return "", operand + name, false, nil
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// adjustFormulaRowNumber adjust row number in the formula reference.
|
2023-11-08 00:01:35 +08:00
|
|
|
func adjustFormulaRowNumber(name, operand string, abs, keepRelative bool, dir adjustDirection, num, offset int) (string, string, bool, error) {
|
|
|
|
if name == "" || (!abs && keepRelative) {
|
|
|
|
return "", operand + name, abs, nil
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
row, _ := strconv.Atoi(name)
|
|
|
|
if dir == rows && row >= num {
|
2024-02-29 09:16:39 +08:00
|
|
|
if row += offset; row < 1 {
|
|
|
|
row = 1
|
|
|
|
}
|
|
|
|
if row > TotalRows {
|
2023-11-08 00:01:35 +08:00
|
|
|
return "", operand + name, false, ErrMaxRows
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
return "", operand + strconv.Itoa(row), false, nil
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
return "", operand + name, false, nil
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
|
2023-10-31 00:01:57 +08:00
|
|
|
// adjustFormulaOperandRef adjust cell reference in the operand tokens for the formula.
|
2023-11-08 00:01:35 +08:00
|
|
|
func adjustFormulaOperandRef(row, col, operand string, abs, keepRelative bool, dir adjustDirection, num int, offset int) (string, string, string, bool, error) {
|
|
|
|
var err error
|
|
|
|
col, operand, abs, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset)
|
|
|
|
if err != nil {
|
|
|
|
return row, col, operand, abs, err
|
2023-10-31 00:01:57 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
row, operand, abs, err = adjustFormulaRowNumber(row, operand, abs, keepRelative, dir, num, offset)
|
|
|
|
return row, col, operand, abs, err
|
2023-10-31 00:01:57 +08:00
|
|
|
}
|
|
|
|
|
2023-10-29 13:40:21 +08:00
|
|
|
// adjustFormulaOperand adjust range operand tokens for the formula.
|
2023-11-08 00:01:35 +08:00
|
|
|
func (f *File) adjustFormulaOperand(sheet, sheetN string, keepRelative bool, token efp.Token, dir adjustDirection, num int, offset int) (string, error) {
|
2023-10-31 00:01:57 +08:00
|
|
|
var (
|
2023-11-01 00:52:18 +08:00
|
|
|
err error
|
2023-11-08 00:01:35 +08:00
|
|
|
abs bool
|
2023-11-01 00:52:18 +08:00
|
|
|
sheetName, col, row, operand string
|
|
|
|
cell = token.TValue
|
|
|
|
tokens = strings.Split(token.TValue, "!")
|
2023-10-31 00:01:57 +08:00
|
|
|
)
|
|
|
|
if len(tokens) == 2 { // have a worksheet
|
2023-11-01 00:52:18 +08:00
|
|
|
sheetName, cell = tokens[0], tokens[1]
|
|
|
|
operand = escapeSheetName(sheetName) + "!"
|
|
|
|
}
|
2023-11-11 00:04:05 +08:00
|
|
|
if sheetName == "" {
|
|
|
|
sheetName = sheetN
|
|
|
|
}
|
|
|
|
if sheet != sheetName {
|
2023-11-01 00:52:18 +08:00
|
|
|
return operand + cell, err
|
2023-10-31 00:01:57 +08:00
|
|
|
}
|
|
|
|
for _, r := range cell {
|
2023-11-08 00:01:35 +08:00
|
|
|
if r == '$' {
|
|
|
|
if col, operand, _, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset); err != nil {
|
|
|
|
return operand, err
|
|
|
|
}
|
|
|
|
abs = true
|
|
|
|
operand += string(r)
|
|
|
|
continue
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') {
|
|
|
|
col += string(r)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if '0' <= r && r <= '9' {
|
|
|
|
row += string(r)
|
2023-11-08 00:01:35 +08:00
|
|
|
col, operand, abs, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset)
|
|
|
|
if err != nil {
|
|
|
|
return operand, err
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
if row, col, operand, abs, err = adjustFormulaOperandRef(row, col, operand, abs, keepRelative, dir, num, offset); err != nil {
|
2023-10-31 00:01:57 +08:00
|
|
|
return operand, err
|
2023-10-29 13:40:21 +08:00
|
|
|
}
|
|
|
|
operand += string(r)
|
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
_, _, operand, _, err = adjustFormulaOperandRef(row, col, operand, abs, keepRelative, dir, num, offset)
|
2023-10-29 13:40:21 +08:00
|
|
|
return operand, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustFormulaRef returns adjusted formula by giving adjusting direction and
|
|
|
|
// the base number of column or row, and offset.
|
2023-11-08 00:01:35 +08:00
|
|
|
func (f *File) adjustFormulaRef(sheet, sheetN, formula string, keepRelative bool, dir adjustDirection, num, offset int) (string, error) {
|
2023-10-24 00:05:52 +08:00
|
|
|
var (
|
2023-10-29 13:40:21 +08:00
|
|
|
val string
|
2023-10-24 00:05:52 +08:00
|
|
|
definedNames []string
|
|
|
|
ps = efp.ExcelParser()
|
|
|
|
)
|
|
|
|
for _, definedName := range f.GetDefinedName() {
|
|
|
|
if definedName.Scope == "Workbook" || definedName.Scope == sheet {
|
|
|
|
definedNames = append(definedNames, definedName.Name)
|
|
|
|
}
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
for _, token := range ps.Parse(formula) {
|
2023-11-08 00:01:35 +08:00
|
|
|
if token.TType == efp.TokenTypeUnknown {
|
|
|
|
val = formula
|
|
|
|
break
|
|
|
|
}
|
2023-10-24 00:05:52 +08:00
|
|
|
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeRange {
|
|
|
|
if inStrSlice(definedNames, token.TValue, true) != -1 {
|
2023-10-29 13:40:21 +08:00
|
|
|
val += token.TValue
|
2023-10-24 00:05:52 +08:00
|
|
|
continue
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
if strings.ContainsAny(token.TValue, "[]") {
|
|
|
|
val += token.TValue
|
|
|
|
continue
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
operand, err := f.adjustFormulaOperand(sheet, sheetN, keepRelative, token, dir, num, offset)
|
2023-10-24 00:05:52 +08:00
|
|
|
if err != nil {
|
2023-10-29 13:40:21 +08:00
|
|
|
return val, err
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
val += operand
|
|
|
|
continue
|
|
|
|
}
|
2024-01-18 15:31:43 +08:00
|
|
|
if isFunctionStartToken(token) {
|
2023-10-29 13:40:21 +08:00
|
|
|
val += token.TValue + string(efp.ParenOpen)
|
|
|
|
continue
|
|
|
|
}
|
2024-01-18 15:31:43 +08:00
|
|
|
if isFunctionStopToken(token) {
|
2023-10-29 13:40:21 +08:00
|
|
|
val += token.TValue + string(efp.ParenClose)
|
2023-10-24 00:05:52 +08:00
|
|
|
continue
|
|
|
|
}
|
2023-10-31 00:01:57 +08:00
|
|
|
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {
|
2023-11-01 00:52:18 +08:00
|
|
|
val += string(efp.QuoteDouble) + strings.ReplaceAll(token.TValue, "\"", "\"\"") + string(efp.QuoteDouble)
|
2023-10-31 00:01:57 +08:00
|
|
|
continue
|
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
val += token.TValue
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
2023-10-29 13:40:21 +08:00
|
|
|
return val, nil
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
|
|
|
|
2024-01-29 10:18:21 +08:00
|
|
|
// adjustRangeSheetName returns replaced range reference by given source and
|
|
|
|
// target sheet name.
|
|
|
|
func adjustRangeSheetName(rng, source, target string) string {
|
|
|
|
cellRefs := strings.Split(rng, ",")
|
|
|
|
for i, cellRef := range cellRefs {
|
|
|
|
rangeRefs := strings.Split(cellRef, ":")
|
|
|
|
for j, rangeRef := range rangeRefs {
|
|
|
|
parts := strings.Split(rangeRef, "!")
|
|
|
|
for k, part := range parts {
|
|
|
|
singleQuote := strings.HasPrefix(part, "'") && strings.HasSuffix(part, "'")
|
|
|
|
if singleQuote {
|
|
|
|
part = strings.TrimPrefix(strings.TrimSuffix(part, "'"), "'")
|
|
|
|
}
|
|
|
|
if part == source {
|
|
|
|
if part = target; singleQuote {
|
|
|
|
part = "'" + part + "'"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parts[k] = part
|
|
|
|
}
|
|
|
|
rangeRefs[j] = strings.Join(parts, "!")
|
|
|
|
}
|
|
|
|
cellRefs[i] = strings.Join(rangeRefs, ":")
|
|
|
|
}
|
|
|
|
return strings.Join(cellRefs, ",")
|
|
|
|
}
|
|
|
|
|
2024-01-18 15:31:43 +08:00
|
|
|
// arrayFormulaOperandToken defines meta fields for transforming the array
|
|
|
|
// formula to the normal formula.
|
|
|
|
type arrayFormulaOperandToken struct {
|
|
|
|
operandTokenIndex, topLeftCol, topLeftRow, bottomRightCol, bottomRightRow int
|
|
|
|
sheetName, sourceCellRef, targetCellRef string
|
|
|
|
}
|
|
|
|
|
|
|
|
// setCoordinates convert each corner cell reference in the array formula cell
|
|
|
|
// range to the coordinate number.
|
|
|
|
func (af *arrayFormulaOperandToken) setCoordinates() error {
|
|
|
|
for i, ref := range strings.Split(af.sourceCellRef, ":") {
|
|
|
|
cellRef, col, row, err := parseRef(ref)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var c, r int
|
|
|
|
if col {
|
2024-01-21 00:05:28 +08:00
|
|
|
if cellRef.Row = TotalRows; i == 0 {
|
2024-01-18 15:31:43 +08:00
|
|
|
cellRef.Row = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if row {
|
2024-01-21 00:05:28 +08:00
|
|
|
if cellRef.Col = MaxColumns; i == 0 {
|
2024-01-18 15:31:43 +08:00
|
|
|
cellRef.Col = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if c, r = cellRef.Col, cellRef.Row; cellRef.Sheet != "" {
|
|
|
|
af.sheetName = cellRef.Sheet + "!"
|
|
|
|
}
|
|
|
|
if af.topLeftCol == 0 || c < af.topLeftCol {
|
|
|
|
af.topLeftCol = c
|
|
|
|
}
|
|
|
|
if af.topLeftRow == 0 || r < af.topLeftRow {
|
|
|
|
af.topLeftRow = r
|
|
|
|
}
|
|
|
|
if c > af.bottomRightCol {
|
|
|
|
af.bottomRightCol = c
|
|
|
|
}
|
|
|
|
if r > af.bottomRightRow {
|
|
|
|
af.bottomRightRow = r
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// transformArrayFormula transforms an array formula to the normal formula by
|
|
|
|
// giving a formula tokens list and formula operand tokens list.
|
|
|
|
func transformArrayFormula(tokens []efp.Token, afs []arrayFormulaOperandToken) string {
|
|
|
|
var val string
|
|
|
|
for i, token := range tokens {
|
|
|
|
var skip bool
|
|
|
|
for _, af := range afs {
|
|
|
|
if af.operandTokenIndex == i {
|
|
|
|
val += af.sheetName + af.targetCellRef
|
|
|
|
skip = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if skip {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if isFunctionStartToken(token) {
|
|
|
|
val += token.TValue + string(efp.ParenOpen)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if isFunctionStopToken(token) {
|
|
|
|
val += token.TValue + string(efp.ParenClose)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {
|
|
|
|
val += string(efp.QuoteDouble) + strings.ReplaceAll(token.TValue, "\"", "\"\"") + string(efp.QuoteDouble)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val += token.TValue
|
|
|
|
}
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
|
|
|
// getArrayFormulaTokens returns parsed formula token and operand related token
|
|
|
|
// list for in array formula.
|
|
|
|
func getArrayFormulaTokens(sheet, formula string, definedNames []DefinedName) ([]efp.Token, []arrayFormulaOperandToken, error) {
|
|
|
|
var (
|
|
|
|
ps = efp.ExcelParser()
|
|
|
|
tokens = ps.Parse(formula)
|
|
|
|
arrayFormulaOperandTokens []arrayFormulaOperandToken
|
|
|
|
)
|
|
|
|
for i, token := range tokens {
|
|
|
|
if token.TSubType == efp.TokenSubTypeRange && token.TType == efp.TokenTypeOperand {
|
|
|
|
tokenVal := token.TValue
|
|
|
|
for _, definedName := range definedNames {
|
|
|
|
if (definedName.Scope == "Workbook" || definedName.Scope == sheet) && definedName.Name == tokenVal {
|
|
|
|
tokenVal = definedName.RefersTo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(strings.Split(tokenVal, ":")) > 1 {
|
|
|
|
arrayFormulaOperandToken := arrayFormulaOperandToken{
|
|
|
|
operandTokenIndex: i,
|
|
|
|
sourceCellRef: tokenVal,
|
|
|
|
}
|
|
|
|
if err := arrayFormulaOperandToken.setCoordinates(); err != nil {
|
|
|
|
return tokens, arrayFormulaOperandTokens, err
|
|
|
|
}
|
|
|
|
arrayFormulaOperandTokens = append(arrayFormulaOperandTokens, arrayFormulaOperandToken)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tokens, arrayFormulaOperandTokens, nil
|
|
|
|
}
|
|
|
|
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
// adjustHyperlinks provides a function to update hyperlinks when inserting or
|
|
|
|
// deleting rows or columns.
|
2020-11-10 23:48:09 +08:00
|
|
|
func (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
|
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
|
|
|
// short path
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.Hyperlinks == nil || len(ws.Hyperlinks.Hyperlink) == 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
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// order is important
|
|
|
|
if offset < 0 {
|
2020-11-10 23:48:09 +08:00
|
|
|
for i := len(ws.Hyperlinks.Hyperlink) - 1; i >= 0; i-- {
|
|
|
|
linkData := ws.Hyperlinks.Hyperlink[i]
|
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
|
|
|
colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)
|
|
|
|
|
|
|
|
if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
|
|
|
|
f.deleteSheetRelationships(sheet, linkData.RID)
|
2020-11-10 23:48:09 +08:00
|
|
|
if len(ws.Hyperlinks.Hyperlink) > 1 {
|
|
|
|
ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink[:i],
|
|
|
|
ws.Hyperlinks.Hyperlink[i+1:]...)
|
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
|
|
|
} else {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.Hyperlinks = nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.Hyperlinks == nil {
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
return
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
for i := range ws.Hyperlinks.Hyperlink {
|
|
|
|
link := &ws.Hyperlinks.Hyperlink[i] // get reference
|
2023-11-08 00:01:35 +08:00
|
|
|
link.Ref, _ = f.adjustFormulaRef(sheet, sheet, link.Ref, false, dir, num, offset)
|
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-07-16 12:50:13 +08:00
|
|
|
// adjustTable provides a function to update the table when inserting or
|
|
|
|
// deleting rows or columns.
|
2023-11-06 09:51:19 +08:00
|
|
|
func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2022-07-16 12:50:13 +08:00
|
|
|
if ws.TableParts == nil || len(ws.TableParts.TableParts) == 0 {
|
2023-11-06 09:51:19 +08:00
|
|
|
return nil
|
2022-07-16 12:50:13 +08:00
|
|
|
}
|
|
|
|
for idx := 0; idx < len(ws.TableParts.TableParts); idx++ {
|
|
|
|
tbl := ws.TableParts.TableParts[idx]
|
|
|
|
target := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)
|
|
|
|
tableXML := strings.ReplaceAll(target, "..", "xl")
|
|
|
|
content, ok := f.Pkg.Load(tableXML)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
t := xlsxTable{}
|
|
|
|
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
|
|
|
|
Decode(&t); err != nil && err != io.EOF {
|
2023-11-08 00:01:35 +08:00
|
|
|
return err
|
2022-07-16 12:50:13 +08:00
|
|
|
}
|
2022-09-28 00:04:17 +08:00
|
|
|
coordinates, err := rangeRefToCoordinates(t.Ref)
|
2022-07-16 12:50:13 +08:00
|
|
|
if err != nil {
|
2023-11-06 09:51:19 +08:00
|
|
|
return err
|
2022-07-16 12:50:13 +08:00
|
|
|
}
|
|
|
|
// Remove the table when deleting the header row of the table
|
2023-10-24 00:05:52 +08:00
|
|
|
if dir == rows && num == coordinates[0] && offset == -1 {
|
2022-07-16 12:50:13 +08:00
|
|
|
ws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)
|
|
|
|
ws.TableParts.Count = len(ws.TableParts.TableParts)
|
|
|
|
idx--
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
|
|
|
|
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
|
2023-05-23 00:18:55 +08:00
|
|
|
if y2-y1 < 1 || x2-x1 < 0 {
|
2022-07-16 12:50:13 +08:00
|
|
|
ws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)
|
|
|
|
ws.TableParts.Count = len(ws.TableParts.TableParts)
|
|
|
|
idx--
|
|
|
|
continue
|
|
|
|
}
|
2024-03-01 10:12:17 +08:00
|
|
|
t.Ref, _ = coordinatesToRangeRef([]int{x1, y1, x2, y2})
|
2022-07-16 12:50:13 +08:00
|
|
|
if t.AutoFilter != nil {
|
|
|
|
t.AutoFilter.Ref = t.Ref
|
|
|
|
}
|
2023-10-31 00:01:57 +08:00
|
|
|
_ = f.setTableColumns(sheet, true, x1, y1, x2, &t)
|
2023-11-02 00:15:41 +08:00
|
|
|
// Currently doesn't support query table
|
|
|
|
t.TableType, t.TotalsRowCount, t.ConnectionID = "", 0, 0
|
2022-07-16 12:50:13 +08:00
|
|
|
table, _ := xml.Marshal(t)
|
|
|
|
f.saveFileList(tableXML, table)
|
|
|
|
}
|
2023-11-06 09:51:19 +08:00
|
|
|
return nil
|
2022-07-16 12:50:13 +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
|
|
|
// adjustAutoFilter provides a function to update the auto filter when
|
|
|
|
// inserting or deleting rows or columns.
|
2023-11-06 09:51:19 +08:00
|
|
|
func (f *File) adjustAutoFilter(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.AutoFilter == nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2022-09-28 00:04:17 +08:00
|
|
|
coordinates, err := rangeRefToCoordinates(ws.AutoFilter.Ref)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2019-06-12 08:10:33 +08:00
|
|
|
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
|
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-06-12 08:10:33 +08:00
|
|
|
if (dir == rows && y1 == num && offset < 0) || (dir == columns && x1 == num && x2 == num) {
|
2020-11-10 23:48:09 +08:00
|
|
|
ws.AutoFilter = nil
|
|
|
|
for rowIdx := range ws.SheetData.Row {
|
|
|
|
rowData := &ws.SheetData.Row[rowIdx]
|
2023-11-18 16:44:45 +08:00
|
|
|
if rowData.R != nil && *rowData.R > y1 && *rowData.R <= y2 {
|
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.Hidden = false
|
|
|
|
}
|
|
|
|
}
|
2022-11-15 22:08:37 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2019-06-12 08:10:33 +08:00
|
|
|
coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
|
|
|
|
x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]
|
|
|
|
|
2024-03-01 10:12:17 +08:00
|
|
|
ws.AutoFilter.Ref, err = coordinatesToRangeRef([]int{x1, y1, x2, y2})
|
2022-11-15 22:08:37 +08:00
|
|
|
return err
|
2019-06-12 08:10:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// adjustAutoFilterHelper provides a function for adjusting auto filter to
|
2023-10-24 00:05:52 +08:00
|
|
|
// compare and calculate cell reference by the giving adjusting direction,
|
|
|
|
// operation reference and offset.
|
2019-06-12 08:10:33 +08:00
|
|
|
func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, num, offset int) []int {
|
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 dir == rows {
|
2019-06-12 08:10:33 +08:00
|
|
|
if coordinates[1] >= num {
|
|
|
|
coordinates[1] += offset
|
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-06-12 08:10:33 +08:00
|
|
|
if coordinates[3] >= num {
|
|
|
|
coordinates[3] += offset
|
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-07-16 12:50:13 +08:00
|
|
|
return coordinates
|
|
|
|
}
|
|
|
|
if coordinates[0] >= num {
|
|
|
|
coordinates[0] += offset
|
|
|
|
}
|
|
|
|
if coordinates[2] >= num {
|
|
|
|
coordinates[2] += offset
|
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-06-12 08:10:33 +08:00
|
|
|
return coordinates
|
|
|
|
}
|
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
|
|
|
|
|
|
|
// adjustMergeCells provides a function to update merged cells when inserting
|
|
|
|
// or deleting rows or columns.
|
2023-11-06 09:51:19 +08:00
|
|
|
func (f *File) adjustMergeCells(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.MergeCells == nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return nil
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
|
|
|
|
2020-11-10 23:48:09 +08:00
|
|
|
for i := 0; i < len(ws.MergeCells.Cells); i++ {
|
2022-09-28 00:04:17 +08:00
|
|
|
mergedCells := ws.MergeCells.Cells[i]
|
2022-10-26 00:04:23 +08:00
|
|
|
mergedCellsRef := mergedCells.Ref
|
|
|
|
if !strings.Contains(mergedCellsRef, ":") {
|
|
|
|
mergedCellsRef += ":" + mergedCellsRef
|
|
|
|
}
|
|
|
|
coordinates, err := rangeRefToCoordinates(mergedCellsRef)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
if err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2019-06-12 08:10:33 +08:00
|
|
|
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
|
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 dir == rows {
|
2019-06-12 08:10:33 +08:00
|
|
|
if y1 == num && y2 == num && offset < 0 {
|
2020-11-10 23:48:09 +08:00
|
|
|
f.deleteMergeCell(ws, i)
|
2023-12-13 09:22:41 +08:00
|
|
|
i--
|
2021-12-06 22:37:25 +08:00
|
|
|
continue
|
2019-06-12 08:10:33 +08:00
|
|
|
}
|
2021-12-06 22:37:25 +08:00
|
|
|
|
|
|
|
y1, y2 = f.adjustMergeCellsHelper(y1, y2, num, offset)
|
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
|
|
|
} else {
|
2019-06-12 08:10:33 +08:00
|
|
|
if x1 == num && x2 == num && offset < 0 {
|
2020-11-10 23:48:09 +08:00
|
|
|
f.deleteMergeCell(ws, i)
|
2023-12-13 09:22:41 +08:00
|
|
|
i--
|
2021-12-06 22:37:25 +08:00
|
|
|
continue
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2021-12-06 22:37:25 +08:00
|
|
|
|
|
|
|
x1, x2 = f.adjustMergeCellsHelper(x1, x2, num, offset)
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2021-12-06 22:37:25 +08:00
|
|
|
if x1 == x2 && y1 == y2 {
|
2020-11-10 23:48:09 +08:00
|
|
|
f.deleteMergeCell(ws, i)
|
2019-07-20 19:24:57 +08:00
|
|
|
i--
|
2021-12-06 22:37:25 +08:00
|
|
|
continue
|
2019-06-12 08:10:33 +08:00
|
|
|
}
|
2022-09-28 00:04:17 +08:00
|
|
|
mergedCells.rect = []int{x1, y1, x2, y2}
|
2024-03-01 10:12:17 +08:00
|
|
|
if mergedCells.Ref, err = coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2019-06-12 08:10:33 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
|
2019-06-12 08:10:33 +08:00
|
|
|
// adjustMergeCellsHelper provides a function for adjusting merge cells to
|
2023-12-13 09:22:41 +08:00
|
|
|
// compare and calculate cell reference by the given pivot, operation reference
|
|
|
|
// and offset.
|
2021-12-06 22:37:25 +08:00
|
|
|
func (f *File) adjustMergeCellsHelper(p1, p2, num, offset int) (int, int) {
|
|
|
|
if p2 < p1 {
|
|
|
|
p1, p2 = p2, p1
|
|
|
|
}
|
|
|
|
|
|
|
|
if offset >= 0 {
|
|
|
|
if num <= p1 {
|
|
|
|
p1 += offset
|
|
|
|
p2 += offset
|
|
|
|
} else if num <= p2 {
|
|
|
|
p2 += offset
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
}
|
2021-12-06 22:37:25 +08:00
|
|
|
return p1, p2
|
|
|
|
}
|
|
|
|
if num < p1 || (num == p1 && num == p2) {
|
|
|
|
p1 += offset
|
|
|
|
p2 += offset
|
|
|
|
} else if num <= p2 {
|
|
|
|
p2 += offset
|
2019-06-12 08:10:33 +08:00
|
|
|
}
|
2021-12-06 22:37:25 +08:00
|
|
|
return p1, p2
|
2019-06-12 08:10:33 +08:00
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
|
2019-06-12 08:10:33 +08:00
|
|
|
// deleteMergeCell provides a function to delete merged cell by given index.
|
2020-11-10 23:48:09 +08:00
|
|
|
func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
|
2021-12-06 22:37:25 +08:00
|
|
|
if idx < 0 {
|
|
|
|
return
|
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if len(ws.MergeCells.Cells) > idx {
|
|
|
|
ws.MergeCells.Cells = append(ws.MergeCells.Cells[:idx], ws.MergeCells.Cells[idx+1:]...)
|
|
|
|
ws.MergeCells.Count = len(ws.MergeCells.Cells)
|
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-06-08 00:00:55 +08:00
|
|
|
|
2023-11-02 00:15:41 +08:00
|
|
|
// adjustCellName returns updated cell name by giving column/row number and
|
|
|
|
// offset on inserting or deleting rows or columns.
|
|
|
|
func adjustCellName(cell string, dir adjustDirection, c, r, offset int) (string, error) {
|
2023-10-24 00:05:52 +08:00
|
|
|
if dir == rows {
|
|
|
|
if rn := r + offset; rn > 0 {
|
2023-11-02 00:15:41 +08:00
|
|
|
return CoordinatesToCellName(c, rn)
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
|
|
|
}
|
2023-11-02 00:15:41 +08:00
|
|
|
return CoordinatesToCellName(c+offset, r)
|
2023-10-24 00:05:52 +08:00
|
|
|
}
|
|
|
|
|
2019-06-08 00:00:55 +08:00
|
|
|
// adjustCalcChain provides a function to update the calculation chain when
|
|
|
|
// inserting or deleting rows or columns.
|
2023-11-06 09:51:19 +08:00
|
|
|
func (f *File) adjustCalcChain(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2019-06-08 00:00:55 +08:00
|
|
|
if f.CalcChain == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-10-24 00:05:52 +08:00
|
|
|
// If sheet ID is omitted, it is assumed to be the same as the i value of
|
|
|
|
// the previous cell.
|
|
|
|
var prevSheetID int
|
2023-11-02 00:15:41 +08:00
|
|
|
for i := 0; i < len(f.CalcChain.C); i++ {
|
|
|
|
c := f.CalcChain.C[i]
|
2023-10-24 00:05:52 +08:00
|
|
|
if c.I == 0 {
|
|
|
|
c.I = prevSheetID
|
|
|
|
}
|
|
|
|
prevSheetID = c.I
|
2021-02-02 22:23:16 +08:00
|
|
|
if c.I != sheetID {
|
|
|
|
continue
|
|
|
|
}
|
2019-06-08 00:00:55 +08:00
|
|
|
colNum, rowNum, err := CellNameToCoordinates(c.R)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if dir == rows && num <= rowNum {
|
2023-10-24 00:05:52 +08:00
|
|
|
if num == rowNum && offset == -1 {
|
|
|
|
_ = f.deleteCalcChain(c.I, c.R)
|
2023-11-02 00:15:41 +08:00
|
|
|
i--
|
2023-10-24 00:05:52 +08:00
|
|
|
continue
|
2019-06-08 00:00:55 +08:00
|
|
|
}
|
2023-11-02 00:15:41 +08:00
|
|
|
f.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)
|
2019-06-08 00:00:55 +08:00
|
|
|
}
|
|
|
|
if dir == columns && num <= colNum {
|
2023-10-24 00:05:52 +08:00
|
|
|
if num == colNum && offset == -1 {
|
|
|
|
_ = f.deleteCalcChain(c.I, c.R)
|
2023-11-02 00:15:41 +08:00
|
|
|
i--
|
2023-10-24 00:05:52 +08:00
|
|
|
continue
|
2019-06-08 00:00:55 +08:00
|
|
|
}
|
2023-11-02 00:15:41 +08:00
|
|
|
f.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustVolatileDepsTopic updates the volatile dependencies topic when
|
|
|
|
// inserting or deleting rows or columns.
|
|
|
|
func (vt *xlsxVolTypes) adjustVolatileDepsTopic(cell string, dir adjustDirection, indexes []int) (int, error) {
|
|
|
|
num, offset, i1, i2, i3, i4 := indexes[0], indexes[1], indexes[2], indexes[3], indexes[4], indexes[5]
|
|
|
|
colNum, rowNum, err := CellNameToCoordinates(cell)
|
|
|
|
if err != nil {
|
|
|
|
return i4, err
|
|
|
|
}
|
|
|
|
if dir == rows && num <= rowNum {
|
|
|
|
if num == rowNum && offset == -1 {
|
|
|
|
vt.deleteVolTopicRef(i1, i2, i3, i4)
|
|
|
|
i4--
|
|
|
|
return i4, err
|
|
|
|
}
|
|
|
|
vt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)
|
|
|
|
}
|
|
|
|
if dir == columns && num <= colNum {
|
|
|
|
if num == colNum && offset == -1 {
|
|
|
|
vt.deleteVolTopicRef(i1, i2, i3, i4)
|
|
|
|
i4--
|
|
|
|
return i4, err
|
|
|
|
}
|
|
|
|
if name, _ := adjustCellName(cell, dir, colNum, rowNum, offset); name != "" {
|
|
|
|
vt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return i4, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustVolatileDeps updates the volatile dependencies when inserting or
|
|
|
|
// deleting rows or columns.
|
2023-11-06 09:51:19 +08:00
|
|
|
func (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
2023-11-02 00:15:41 +08:00
|
|
|
volTypes, err := f.volatileDepsReader()
|
|
|
|
if err != nil || volTypes == nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for i1 := 0; i1 < len(volTypes.VolType); i1++ {
|
|
|
|
for i2 := 0; i2 < len(volTypes.VolType[i1].Main); i2++ {
|
|
|
|
for i3 := 0; i3 < len(volTypes.VolType[i1].Main[i2].Tp); i3++ {
|
|
|
|
for i4 := 0; i4 < len(volTypes.VolType[i1].Main[i2].Tp[i3].Tr); i4++ {
|
|
|
|
ref := volTypes.VolType[i1].Main[i2].Tp[i3].Tr[i4]
|
|
|
|
if ref.S != sheetID {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if i4, err = volTypes.adjustVolatileDepsTopic(ref.R, dir, []int{num, offset, i1, i2, i3, i4}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-08 00:00:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-11-06 09:51:19 +08:00
|
|
|
|
2023-11-10 09:25:59 +08:00
|
|
|
// adjustConditionalFormats updates the cell reference of the worksheet
|
|
|
|
// conditional formatting when inserting or deleting rows or columns.
|
|
|
|
func (f *File) adjustConditionalFormats(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
for i := 0; i < len(ws.ConditionalFormatting); i++ {
|
|
|
|
cf := ws.ConditionalFormatting[i]
|
|
|
|
if cf == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ref, del, err := f.adjustCellRef(cf.SQRef, dir, num, offset)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if del {
|
|
|
|
ws.ConditionalFormatting = append(ws.ConditionalFormatting[:i],
|
|
|
|
ws.ConditionalFormatting[i+1:]...)
|
|
|
|
i--
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ws.ConditionalFormatting[i].SQRef = ref
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-11 00:04:05 +08:00
|
|
|
// adjustDataValidations updates the range of data validations for the worksheet
|
|
|
|
// when inserting or deleting rows or columns.
|
|
|
|
func (f *File) adjustDataValidations(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
for _, sheetN := range f.GetSheetList() {
|
|
|
|
worksheet, err := f.workSheetReader(sheetN)
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == newNotWorksheetError(sheetN).Error() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if worksheet.DataValidations == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for i := 0; i < len(worksheet.DataValidations.DataValidation); i++ {
|
|
|
|
dv := worksheet.DataValidations.DataValidation[i]
|
|
|
|
if dv == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if sheet == sheetN {
|
|
|
|
ref, del, err := f.adjustCellRef(dv.Sqref, dir, num, offset)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if del {
|
|
|
|
worksheet.DataValidations.DataValidation = append(worksheet.DataValidations.DataValidation[:i],
|
|
|
|
worksheet.DataValidations.DataValidation[i+1:]...)
|
|
|
|
i--
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
worksheet.DataValidations.DataValidation[i].Sqref = ref
|
|
|
|
}
|
|
|
|
if worksheet.DataValidations.DataValidation[i].Formula1 != nil {
|
|
|
|
formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula1.Content)
|
|
|
|
if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
worksheet.DataValidations.DataValidation[i].Formula1 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
|
|
|
|
}
|
|
|
|
if worksheet.DataValidations.DataValidation[i].Formula2 != nil {
|
|
|
|
formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula2.Content)
|
|
|
|
if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
worksheet.DataValidations.DataValidation[i].Formula2 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if worksheet.DataValidations.Count = len(worksheet.DataValidations.DataValidation); worksheet.DataValidations.Count == 0 {
|
|
|
|
worksheet.DataValidations = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-06 09:51:19 +08:00
|
|
|
// adjustDrawings updates the starting anchor of the two cell anchor pictures
|
|
|
|
// and charts object when inserting or deleting rows or columns.
|
|
|
|
func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
|
|
|
|
var ok bool
|
|
|
|
if dir == columns && from.Col+1 >= num && from.Col+offset >= 0 {
|
|
|
|
if from.Col+offset >= MaxColumns {
|
|
|
|
return false, ErrColumnNumber
|
|
|
|
}
|
|
|
|
from.Col += offset
|
|
|
|
ok = editAs == "oneCell"
|
|
|
|
}
|
|
|
|
if dir == rows && from.Row+1 >= num && from.Row+offset >= 0 {
|
|
|
|
if from.Row+offset >= TotalRows {
|
|
|
|
return false, ErrMaxRows
|
|
|
|
}
|
|
|
|
from.Row += offset
|
|
|
|
ok = editAs == "oneCell"
|
|
|
|
}
|
|
|
|
return ok, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustDrawings updates the ending anchor of the two cell anchor pictures
|
|
|
|
// and charts object when inserting or deleting rows or columns.
|
|
|
|
func (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, editAs string, ok bool) error {
|
|
|
|
if dir == columns && to.Col+1 >= num && to.Col+offset >= 0 && ok {
|
|
|
|
if to.Col+offset >= MaxColumns {
|
|
|
|
return ErrColumnNumber
|
|
|
|
}
|
|
|
|
to.Col += offset
|
|
|
|
}
|
|
|
|
if dir == rows && to.Row+1 >= num && to.Row+offset >= 0 && ok {
|
|
|
|
if to.Row+offset >= TotalRows {
|
|
|
|
return ErrMaxRows
|
|
|
|
}
|
|
|
|
to.Row += offset
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustDrawings updates the two cell anchor pictures and charts object when
|
|
|
|
// inserting or deleting rows or columns.
|
|
|
|
func (a *xdrCellAnchor) adjustDrawings(dir adjustDirection, num, offset int) error {
|
|
|
|
editAs := a.EditAs
|
|
|
|
if a.From == nil || a.To == nil || editAs == "absolute" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustDrawings updates the existing two cell anchor pictures and charts
|
|
|
|
// object when inserting or deleting rows or columns.
|
|
|
|
func (a *xlsxCellAnchorPos) adjustDrawings(dir adjustDirection, num, offset int, editAs string) error {
|
|
|
|
if a.From == nil || a.To == nil || editAs == "absolute" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjustDrawings updates the pictures and charts object when inserting or
|
|
|
|
// deleting rows or columns.
|
|
|
|
func (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
if ws.Drawing == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
|
|
|
|
drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
wsDr *xlsxWsDr
|
|
|
|
)
|
|
|
|
if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
anchorCb := func(a *xdrCellAnchor) error {
|
|
|
|
if a.GraphicFrame == "" {
|
|
|
|
return a.adjustDrawings(dir, num, offset)
|
|
|
|
}
|
|
|
|
deCellAnchor := decodeCellAnchor{}
|
|
|
|
deCellAnchorPos := decodeCellAnchorPos{}
|
|
|
|
_ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + a.GraphicFrame + "</decodeCellAnchor>")).Decode(&deCellAnchor)
|
|
|
|
_ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchorPos>" + a.GraphicFrame + "</decodeCellAnchorPos>")).Decode(&deCellAnchorPos)
|
|
|
|
xlsxCellAnchorPos := xlsxCellAnchorPos(deCellAnchorPos)
|
|
|
|
for i := 0; i < len(xlsxCellAnchorPos.AlternateContent); i++ {
|
|
|
|
xlsxCellAnchorPos.AlternateContent[i].XMLNSMC = SourceRelationshipCompatibility.Value
|
|
|
|
}
|
|
|
|
if deCellAnchor.From != nil {
|
|
|
|
xlsxCellAnchorPos.From = &xlsxFrom{
|
|
|
|
Col: deCellAnchor.From.Col, ColOff: deCellAnchor.From.ColOff,
|
|
|
|
Row: deCellAnchor.From.Row, RowOff: deCellAnchor.From.RowOff,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if deCellAnchor.To != nil {
|
|
|
|
xlsxCellAnchorPos.To = &xlsxTo{
|
|
|
|
Col: deCellAnchor.To.Col, ColOff: deCellAnchor.To.ColOff,
|
|
|
|
Row: deCellAnchor.To.Row, RowOff: deCellAnchor.To.RowOff,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err = xlsxCellAnchorPos.adjustDrawings(dir, num, offset, a.EditAs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cellAnchor, _ := xml.Marshal(xlsxCellAnchorPos)
|
|
|
|
a.GraphicFrame = strings.TrimSuffix(strings.TrimPrefix(string(cellAnchor), "<xlsxCellAnchorPos>"), "</xlsxCellAnchorPos>")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, anchor := range wsDr.TwoCellAnchor {
|
|
|
|
if err = anchorCb(anchor); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-11-08 00:01:35 +08:00
|
|
|
|
|
|
|
// adjustDefinedNames updates the cell reference of the defined names when
|
|
|
|
// inserting or deleting rows or columns.
|
|
|
|
func (f *File) adjustDefinedNames(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
|
|
|
wb, err := f.workbookReader()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if wb.DefinedNames != nil {
|
|
|
|
for i := 0; i < len(wb.DefinedNames.DefinedName); i++ {
|
|
|
|
data := wb.DefinedNames.DefinedName[i].Data
|
|
|
|
if data, err = f.adjustFormulaRef(sheet, "", data, true, dir, num, offset); err == nil {
|
|
|
|
wb.DefinedNames.DefinedName[i].Data = data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|