1 // Copyright 2016 - 2023 The excelize Authors. All rights reserved. Use of
2 // this source code is governed by a BSD-style license that can be found in
5 // Package excelize providing a set of functions that allow you to write to and
6 // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
7 // writing spreadsheet documents generated by Microsoft Excelâ„¢ 2007 and later.
8 // Supports complex components by high compatibility, and provided streaming
9 // API for generating or reading data from a worksheet with huge amounts of
10 // data. This library needs Go version 1.16 or later.
25 type adjustDirection bool
28 columns adjustDirection = false
29 rows adjustDirection = true
32 // adjustHelperFunc defines functions to adjust helper.
33 var adjustHelperFunc = [9]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
34 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
35 return f.adjustConditionalFormats(ws, sheet, dir, num, offset, sheetID)
37 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
38 return f.adjustDataValidations(ws, sheet, dir, num, offset, sheetID)
40 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
41 return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
43 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
44 return f.adjustDrawings(ws, sheet, dir, num, offset, sheetID)
46 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
47 return f.adjustMergeCells(ws, sheet, dir, num, offset, sheetID)
49 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
50 return f.adjustAutoFilter(ws, sheet, dir, num, offset, sheetID)
52 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
53 return f.adjustCalcChain(ws, sheet, dir, num, offset, sheetID)
55 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
56 return f.adjustTable(ws, sheet, dir, num, offset, sheetID)
58 func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
59 return f.adjustVolatileDeps(ws, sheet, dir, num, offset, sheetID)
63 // adjustHelper provides a function to adjust rows and columns dimensions,
64 // hyperlinks, merged cells and auto filter when inserting or deleting rows or
67 // sheet: Worksheet name that we're editing
68 // column: Index number of the column we're inserting/deleting before
69 // row: Index number of the row we're inserting/deleting before
70 // offset: Number of rows/column to insert/delete negative values indicate deletion
72 // TODO: adjustComments, adjustPageBreaks, adjustProtectedCells
73 func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
74 ws, err := f.workSheetReader(sheet)
78 sheetID := f.getSheetID(sheet)
80 err = f.adjustRowDimensions(sheet, ws, num, offset)
82 err = f.adjustColDimensions(sheet, ws, num, offset)
87 f.adjustHyperlinks(ws, sheet, dir, num, offset)
90 for _, fn := range adjustHelperFunc {
91 if err := fn(f, ws, sheet, dir, num, offset, sheetID); err != nil {
95 if ws.MergeCells != nil && len(ws.MergeCells.Cells) == 0 {
101 // adjustCols provides a function to update column style when inserting or
103 func (f *File) adjustCols(ws *xlsxWorksheet, col, offset int) error {
107 for i := 0; i < len(ws.Cols.Col); i++ {
109 if ws.Cols.Col[i].Min >= col {
110 if ws.Cols.Col[i].Min += offset; ws.Cols.Col[i].Min > MaxColumns {
111 ws.Cols.Col = append(ws.Cols.Col[:i], ws.Cols.Col[i+1:]...)
116 if ws.Cols.Col[i].Max >= col || ws.Cols.Col[i].Max+1 == col {
117 if ws.Cols.Col[i].Max += offset; ws.Cols.Col[i].Max > MaxColumns {
118 ws.Cols.Col[i].Max = MaxColumns
123 if ws.Cols.Col[i].Min == col && ws.Cols.Col[i].Max == col {
124 ws.Cols.Col = append(ws.Cols.Col[:i], ws.Cols.Col[i+1:]...)
128 if ws.Cols.Col[i].Min > col {
129 ws.Cols.Col[i].Min += offset
131 if ws.Cols.Col[i].Max >= col {
132 ws.Cols.Col[i].Max += offset
135 if len(ws.Cols.Col) == 0 {
141 // adjustColDimensions provides a function to update column dimensions when
142 // inserting or deleting rows or columns.
143 func (f *File) adjustColDimensions(sheet string, ws *xlsxWorksheet, col, offset int) error {
144 for rowIdx := range ws.SheetData.Row {
145 for _, v := range ws.SheetData.Row[rowIdx].C {
146 if cellCol, _, _ := CellNameToCoordinates(v.R); col <= cellCol {
147 if newCol := cellCol + offset; newCol > 0 && newCol > MaxColumns {
148 return ErrColumnNumber
153 for _, sheetN := range f.GetSheetList() {
154 worksheet, err := f.workSheetReader(sheetN)
156 if err.Error() == newNotWorksheetError(sheetN).Error() {
161 for rowIdx := range worksheet.SheetData.Row {
162 for colIdx, v := range worksheet.SheetData.Row[rowIdx].C {
163 if cellCol, cellRow, _ := CellNameToCoordinates(v.R); sheetN == sheet && col <= cellCol {
164 if newCol := cellCol + offset; newCol > 0 {
165 worksheet.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
168 if err := f.adjustFormula(sheet, sheetN, worksheet.SheetData.Row[rowIdx].C[colIdx].F, columns, col, offset, false); err != nil {
174 return f.adjustCols(ws, col, offset)
177 // adjustRowDimensions provides a function to update row dimensions when
178 // inserting or deleting rows or columns.
179 func (f *File) adjustRowDimensions(sheet string, ws *xlsxWorksheet, row, offset int) error {
180 for _, sheetN := range f.GetSheetList() {
184 worksheet, err := f.workSheetReader(sheetN)
186 if err.Error() == newNotWorksheetError(sheetN).Error() {
191 numOfRows := len(worksheet.SheetData.Row)
192 for i := 0; i < numOfRows; i++ {
193 r := &worksheet.SheetData.Row[i]
194 if err = f.adjustSingleRowFormulas(sheet, sheetN, r, row, offset, false); err != nil {
199 totalRows := len(ws.SheetData.Row)
203 lastRow := &ws.SheetData.Row[totalRows-1]
204 if newRow := *lastRow.R + offset; *lastRow.R >= row && newRow > 0 && newRow > TotalRows {
207 numOfRows := len(ws.SheetData.Row)
208 for i := 0; i < numOfRows; i++ {
209 r := &ws.SheetData.Row[i]
210 if newRow := *r.R + offset; *r.R >= row && newRow > 0 {
211 r.adjustSingleRowDimensions(offset)
213 if err := f.adjustSingleRowFormulas(sheet, sheet, r, row, offset, false); err != nil {
220 // adjustSingleRowDimensions provides a function to adjust single row dimensions.
221 func (r *xlsxRow) adjustSingleRowDimensions(offset int) {
222 r.R = intPtr(*r.R + offset)
223 for i, col := range r.C {
224 colName, _, _ := SplitCellName(col.R)
225 r.C[i].R, _ = JoinCellName(colName, *r.R)
229 // adjustSingleRowFormulas provides a function to adjust single row formulas.
230 func (f *File) adjustSingleRowFormulas(sheet, sheetN string, r *xlsxRow, num, offset int, si bool) error {
231 for _, col := range r.C {
232 if err := f.adjustFormula(sheet, sheetN, col.F, rows, num, offset, si); err != nil {
239 // adjustCellRef provides a function to adjust cell reference.
240 func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (string, bool, error) {
241 if !strings.Contains(ref, ":") {
245 coordinates, err := rangeRefToCoordinates(ref)
247 return ref, delete, err
250 if offset < 0 && coordinates[0] == coordinates[2] {
253 if coordinates[0] >= num {
254 coordinates[0] += offset
256 if coordinates[2] >= num {
257 coordinates[2] += offset
260 if offset < 0 && coordinates[1] == coordinates[3] {
263 if coordinates[1] >= num {
264 coordinates[1] += offset
266 if coordinates[3] >= num {
267 coordinates[3] += offset
270 ref, err = f.coordinatesToRangeRef(coordinates)
271 return ref, delete, err
274 // adjustFormula provides a function to adjust formula reference and shared
275 // formula reference.
276 func (f *File) adjustFormula(sheet, sheetN string, formula *xlsxF, dir adjustDirection, num, offset int, si bool) error {
281 if formula.Ref != "" && sheet == sheetN {
282 if formula.Ref, _, err = f.adjustCellRef(formula.Ref, dir, num, offset); err != nil {
285 if si && formula.Si != nil {
286 formula.Si = intPtr(*formula.Si + 1)
289 if formula.Content != "" {
290 if formula.Content, err = f.adjustFormulaRef(sheet, sheetN, formula.Content, false, dir, num, offset); err != nil {
297 // isFunctionStop provides a function to check if token is a function stop.
298 func isFunctionStop(token efp.Token) bool {
299 return token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop
302 // isFunctionStart provides a function to check if token is a function start.
303 func isFunctionStart(token efp.Token) bool {
304 return token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart
307 // escapeSheetName enclose sheet name in single quotation marks if the giving
308 // worksheet name includes spaces or non-alphabetical characters.
309 func escapeSheetName(name string) string {
310 if strings.IndexFunc(name, func(r rune) bool {
311 return !unicode.IsLetter(r) && !unicode.IsNumber(r)
313 return "'" + strings.ReplaceAll(name, "'", "''") + "'"
318 // adjustFormulaColumnName adjust column name in the formula reference.
319 func adjustFormulaColumnName(name, operand string, abs, keepRelative bool, dir adjustDirection, num, offset int) (string, string, bool, error) {
320 if name == "" || (!abs && keepRelative) {
321 return "", operand + name, abs, nil
323 col, err := ColumnNameToNumber(name)
325 return "", operand, false, err
327 if dir == columns && col >= num {
329 colName, err := ColumnNumberToName(col)
330 return "", operand + colName, false, err
332 return "", operand + name, false, nil
335 // adjustFormulaRowNumber adjust row number in the formula reference.
336 func adjustFormulaRowNumber(name, operand string, abs, keepRelative bool, dir adjustDirection, num, offset int) (string, string, bool, error) {
337 if name == "" || (!abs && keepRelative) {
338 return "", operand + name, abs, nil
340 row, _ := strconv.Atoi(name)
341 if dir == rows && row >= num {
343 if row <= 0 || row > TotalRows {
344 return "", operand + name, false, ErrMaxRows
346 return "", operand + strconv.Itoa(row), false, nil
348 return "", operand + name, false, nil
351 // adjustFormulaOperandRef adjust cell reference in the operand tokens for the formula.
352 func adjustFormulaOperandRef(row, col, operand string, abs, keepRelative bool, dir adjustDirection, num int, offset int) (string, string, string, bool, error) {
354 col, operand, abs, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset)
356 return row, col, operand, abs, err
358 row, operand, abs, err = adjustFormulaRowNumber(row, operand, abs, keepRelative, dir, num, offset)
359 return row, col, operand, abs, err
362 // adjustFormulaOperand adjust range operand tokens for the formula.
363 func (f *File) adjustFormulaOperand(sheet, sheetN string, keepRelative bool, token efp.Token, dir adjustDirection, num int, offset int) (string, error) {
367 sheetName, col, row, operand string
369 tokens = strings.Split(token.TValue, "!")
371 if len(tokens) == 2 { // have a worksheet
372 sheetName, cell = tokens[0], tokens[1]
373 operand = escapeSheetName(sheetName) + "!"
378 if sheet != sheetName {
379 return operand + cell, err
381 for _, r := range cell {
383 if col, operand, _, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset); err != nil {
390 if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') {
394 if '0' <= r && r <= '9' {
396 col, operand, abs, err = adjustFormulaColumnName(col, operand, abs, keepRelative, dir, num, offset)
402 if row, col, operand, abs, err = adjustFormulaOperandRef(row, col, operand, abs, keepRelative, dir, num, offset); err != nil {
407 _, _, operand, _, err = adjustFormulaOperandRef(row, col, operand, abs, keepRelative, dir, num, offset)
411 // adjustFormulaRef returns adjusted formula by giving adjusting direction and
412 // the base number of column or row, and offset.
413 func (f *File) adjustFormulaRef(sheet, sheetN, formula string, keepRelative bool, dir adjustDirection, num, offset int) (string, error) {
416 definedNames []string
417 ps = efp.ExcelParser()
419 for _, definedName := range f.GetDefinedName() {
420 if definedName.Scope == "Workbook" || definedName.Scope == sheet {
421 definedNames = append(definedNames, definedName.Name)
424 for _, token := range ps.Parse(formula) {
425 if token.TType == efp.TokenTypeUnknown {
429 if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeRange {
430 if inStrSlice(definedNames, token.TValue, true) != -1 {
434 if strings.ContainsAny(token.TValue, "[]") {
438 operand, err := f.adjustFormulaOperand(sheet, sheetN, keepRelative, token, dir, num, offset)
445 if isFunctionStart(token) {
446 val += token.TValue + string(efp.ParenOpen)
449 if isFunctionStop(token) {
450 val += token.TValue + string(efp.ParenClose)
453 if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {
454 val += string(efp.QuoteDouble) + strings.ReplaceAll(token.TValue, "\"", "\"\"") + string(efp.QuoteDouble)
462 // adjustHyperlinks provides a function to update hyperlinks when inserting or
463 // deleting rows or columns.
464 func (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
466 if ws.Hyperlinks == nil || len(ws.Hyperlinks.Hyperlink) == 0 {
470 // order is important
472 for i := len(ws.Hyperlinks.Hyperlink) - 1; i >= 0; i-- {
473 linkData := ws.Hyperlinks.Hyperlink[i]
474 colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)
476 if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
477 f.deleteSheetRelationships(sheet, linkData.RID)
478 if len(ws.Hyperlinks.Hyperlink) > 1 {
479 ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink[:i],
480 ws.Hyperlinks.Hyperlink[i+1:]...)
487 if ws.Hyperlinks == nil {
490 for i := range ws.Hyperlinks.Hyperlink {
491 link := &ws.Hyperlinks.Hyperlink[i] // get reference
492 link.Ref, _ = f.adjustFormulaRef(sheet, sheet, link.Ref, false, dir, num, offset)
496 // adjustTable provides a function to update the table when inserting or
497 // deleting rows or columns.
498 func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
499 if ws.TableParts == nil || len(ws.TableParts.TableParts) == 0 {
502 for idx := 0; idx < len(ws.TableParts.TableParts); idx++ {
503 tbl := ws.TableParts.TableParts[idx]
504 target := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)
505 tableXML := strings.ReplaceAll(target, "..", "xl")
506 content, ok := f.Pkg.Load(tableXML)
511 if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
512 Decode(&t); err != nil && err != io.EOF {
515 coordinates, err := rangeRefToCoordinates(t.Ref)
519 // Remove the table when deleting the header row of the table
520 if dir == rows && num == coordinates[0] && offset == -1 {
521 ws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)
522 ws.TableParts.Count = len(ws.TableParts.TableParts)
526 coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
527 x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
528 if y2-y1 < 1 || x2-x1 < 0 {
529 ws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)
530 ws.TableParts.Count = len(ws.TableParts.TableParts)
534 t.Ref, _ = f.coordinatesToRangeRef([]int{x1, y1, x2, y2})
535 if t.AutoFilter != nil {
536 t.AutoFilter.Ref = t.Ref
538 _ = f.setTableColumns(sheet, true, x1, y1, x2, &t)
539 // Currently doesn't support query table
540 t.TableType, t.TotalsRowCount, t.ConnectionID = "", 0, 0
541 table, _ := xml.Marshal(t)
542 f.saveFileList(tableXML, table)
547 // adjustAutoFilter provides a function to update the auto filter when
548 // inserting or deleting rows or columns.
549 func (f *File) adjustAutoFilter(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
550 if ws.AutoFilter == nil {
554 coordinates, err := rangeRefToCoordinates(ws.AutoFilter.Ref)
558 x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
560 if (dir == rows && y1 == num && offset < 0) || (dir == columns && x1 == num && x2 == num) {
562 for rowIdx := range ws.SheetData.Row {
563 rowData := &ws.SheetData.Row[rowIdx]
564 if rowData.R != nil && *rowData.R > y1 && *rowData.R <= y2 {
565 rowData.Hidden = false
571 coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
572 x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]
574 ws.AutoFilter.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2})
578 // adjustAutoFilterHelper provides a function for adjusting auto filter to
579 // compare and calculate cell reference by the giving adjusting direction,
580 // operation reference and offset.
581 func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, num, offset int) []int {
583 if coordinates[1] >= num {
584 coordinates[1] += offset
586 if coordinates[3] >= num {
587 coordinates[3] += offset
591 if coordinates[0] >= num {
592 coordinates[0] += offset
594 if coordinates[2] >= num {
595 coordinates[2] += offset
600 // adjustMergeCells provides a function to update merged cells when inserting
601 // or deleting rows or columns.
602 func (f *File) adjustMergeCells(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
603 if ws.MergeCells == nil {
607 for i := 0; i < len(ws.MergeCells.Cells); i++ {
608 mergedCells := ws.MergeCells.Cells[i]
609 mergedCellsRef := mergedCells.Ref
610 if !strings.Contains(mergedCellsRef, ":") {
611 mergedCellsRef += ":" + mergedCellsRef
613 coordinates, err := rangeRefToCoordinates(mergedCellsRef)
617 x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
619 if y1 == num && y2 == num && offset < 0 {
620 f.deleteMergeCell(ws, i)
624 y1, y2 = f.adjustMergeCellsHelper(y1, y2, num, offset)
626 if x1 == num && x2 == num && offset < 0 {
627 f.deleteMergeCell(ws, i)
631 x1, x2 = f.adjustMergeCellsHelper(x1, x2, num, offset)
633 if x1 == x2 && y1 == y2 {
634 f.deleteMergeCell(ws, i)
638 mergedCells.rect = []int{x1, y1, x2, y2}
639 if mergedCells.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {
646 // adjustMergeCellsHelper provides a function for adjusting merge cells to
647 // compare and calculate cell reference by the given pivot, operation reference and
649 func (f *File) adjustMergeCellsHelper(p1, p2, num, offset int) (int, int) {
658 } else if num <= p2 {
663 if num < p1 || (num == p1 && num == p2) {
666 } else if num <= p2 {
672 // deleteMergeCell provides a function to delete merged cell by given index.
673 func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
677 if len(ws.MergeCells.Cells) > idx {
678 ws.MergeCells.Cells = append(ws.MergeCells.Cells[:idx], ws.MergeCells.Cells[idx+1:]...)
679 ws.MergeCells.Count = len(ws.MergeCells.Cells)
683 // adjustCellName returns updated cell name by giving column/row number and
684 // offset on inserting or deleting rows or columns.
685 func adjustCellName(cell string, dir adjustDirection, c, r, offset int) (string, error) {
687 if rn := r + offset; rn > 0 {
688 return CoordinatesToCellName(c, rn)
691 return CoordinatesToCellName(c+offset, r)
694 // adjustCalcChain provides a function to update the calculation chain when
695 // inserting or deleting rows or columns.
696 func (f *File) adjustCalcChain(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
697 if f.CalcChain == nil {
700 // If sheet ID is omitted, it is assumed to be the same as the i value of
701 // the previous cell.
703 for i := 0; i < len(f.CalcChain.C); i++ {
704 c := f.CalcChain.C[i]
712 colNum, rowNum, err := CellNameToCoordinates(c.R)
716 if dir == rows && num <= rowNum {
717 if num == rowNum && offset == -1 {
718 _ = f.deleteCalcChain(c.I, c.R)
722 f.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)
724 if dir == columns && num <= colNum {
725 if num == colNum && offset == -1 {
726 _ = f.deleteCalcChain(c.I, c.R)
730 f.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)
736 // adjustVolatileDepsTopic updates the volatile dependencies topic when
737 // inserting or deleting rows or columns.
738 func (vt *xlsxVolTypes) adjustVolatileDepsTopic(cell string, dir adjustDirection, indexes []int) (int, error) {
739 num, offset, i1, i2, i3, i4 := indexes[0], indexes[1], indexes[2], indexes[3], indexes[4], indexes[5]
740 colNum, rowNum, err := CellNameToCoordinates(cell)
744 if dir == rows && num <= rowNum {
745 if num == rowNum && offset == -1 {
746 vt.deleteVolTopicRef(i1, i2, i3, i4)
750 vt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)
752 if dir == columns && num <= colNum {
753 if num == colNum && offset == -1 {
754 vt.deleteVolTopicRef(i1, i2, i3, i4)
758 if name, _ := adjustCellName(cell, dir, colNum, rowNum, offset); name != "" {
759 vt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)
765 // adjustVolatileDeps updates the volatile dependencies when inserting or
766 // deleting rows or columns.
767 func (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
768 volTypes, err := f.volatileDepsReader()
769 if err != nil || volTypes == nil {
772 for i1 := 0; i1 < len(volTypes.VolType); i1++ {
773 for i2 := 0; i2 < len(volTypes.VolType[i1].Main); i2++ {
774 for i3 := 0; i3 < len(volTypes.VolType[i1].Main[i2].Tp); i3++ {
775 for i4 := 0; i4 < len(volTypes.VolType[i1].Main[i2].Tp[i3].Tr); i4++ {
776 ref := volTypes.VolType[i1].Main[i2].Tp[i3].Tr[i4]
777 if ref.S != sheetID {
780 if i4, err = volTypes.adjustVolatileDepsTopic(ref.R, dir, []int{num, offset, i1, i2, i3, i4}); err != nil {
790 // adjustConditionalFormats updates the cell reference of the worksheet
791 // conditional formatting when inserting or deleting rows or columns.
792 func (f *File) adjustConditionalFormats(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
793 for i := 0; i < len(ws.ConditionalFormatting); i++ {
794 cf := ws.ConditionalFormatting[i]
798 ref, del, err := f.adjustCellRef(cf.SQRef, dir, num, offset)
803 ws.ConditionalFormatting = append(ws.ConditionalFormatting[:i],
804 ws.ConditionalFormatting[i+1:]...)
808 ws.ConditionalFormatting[i].SQRef = ref
813 // adjustDataValidations updates the range of data validations for the worksheet
814 // when inserting or deleting rows or columns.
815 func (f *File) adjustDataValidations(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
816 for _, sheetN := range f.GetSheetList() {
817 worksheet, err := f.workSheetReader(sheetN)
819 if err.Error() == newNotWorksheetError(sheetN).Error() {
824 if worksheet.DataValidations == nil {
827 for i := 0; i < len(worksheet.DataValidations.DataValidation); i++ {
828 dv := worksheet.DataValidations.DataValidation[i]
833 ref, del, err := f.adjustCellRef(dv.Sqref, dir, num, offset)
838 worksheet.DataValidations.DataValidation = append(worksheet.DataValidations.DataValidation[:i],
839 worksheet.DataValidations.DataValidation[i+1:]...)
843 worksheet.DataValidations.DataValidation[i].Sqref = ref
845 if worksheet.DataValidations.DataValidation[i].Formula1 != nil {
846 formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula1.Content)
847 if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
850 worksheet.DataValidations.DataValidation[i].Formula1 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
852 if worksheet.DataValidations.DataValidation[i].Formula2 != nil {
853 formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula2.Content)
854 if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
857 worksheet.DataValidations.DataValidation[i].Formula2 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
860 if worksheet.DataValidations.Count = len(worksheet.DataValidations.DataValidation); worksheet.DataValidations.Count == 0 {
861 worksheet.DataValidations = nil
867 // adjustDrawings updates the starting anchor of the two cell anchor pictures
868 // and charts object when inserting or deleting rows or columns.
869 func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
871 if dir == columns && from.Col+1 >= num && from.Col+offset >= 0 {
872 if from.Col+offset >= MaxColumns {
873 return false, ErrColumnNumber
876 ok = editAs == "oneCell"
878 if dir == rows && from.Row+1 >= num && from.Row+offset >= 0 {
879 if from.Row+offset >= TotalRows {
880 return false, ErrMaxRows
883 ok = editAs == "oneCell"
888 // adjustDrawings updates the ending anchor of the two cell anchor pictures
889 // and charts object when inserting or deleting rows or columns.
890 func (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, editAs string, ok bool) error {
891 if dir == columns && to.Col+1 >= num && to.Col+offset >= 0 && ok {
892 if to.Col+offset >= MaxColumns {
893 return ErrColumnNumber
897 if dir == rows && to.Row+1 >= num && to.Row+offset >= 0 && ok {
898 if to.Row+offset >= TotalRows {
906 // adjustDrawings updates the two cell anchor pictures and charts object when
907 // inserting or deleting rows or columns.
908 func (a *xdrCellAnchor) adjustDrawings(dir adjustDirection, num, offset int) error {
910 if a.From == nil || a.To == nil || editAs == "absolute" {
913 ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
917 return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
920 // adjustDrawings updates the existing two cell anchor pictures and charts
921 // object when inserting or deleting rows or columns.
922 func (a *xlsxCellAnchorPos) adjustDrawings(dir adjustDirection, num, offset int, editAs string) error {
923 if a.From == nil || a.To == nil || editAs == "absolute" {
926 ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
930 return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
933 // adjustDrawings updates the pictures and charts object when inserting or
934 // deleting rows or columns.
935 func (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
936 if ws.Drawing == nil {
939 target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
940 drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
945 if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
948 anchorCb := func(a *xdrCellAnchor) error {
949 if a.GraphicFrame == "" {
950 return a.adjustDrawings(dir, num, offset)
952 deCellAnchor := decodeCellAnchor{}
953 deCellAnchorPos := decodeCellAnchorPos{}
954 _ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + a.GraphicFrame + "</decodeCellAnchor>")).Decode(&deCellAnchor)
955 _ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchorPos>" + a.GraphicFrame + "</decodeCellAnchorPos>")).Decode(&deCellAnchorPos)
956 xlsxCellAnchorPos := xlsxCellAnchorPos(deCellAnchorPos)
957 for i := 0; i < len(xlsxCellAnchorPos.AlternateContent); i++ {
958 xlsxCellAnchorPos.AlternateContent[i].XMLNSMC = SourceRelationshipCompatibility.Value
960 if deCellAnchor.From != nil {
961 xlsxCellAnchorPos.From = &xlsxFrom{
962 Col: deCellAnchor.From.Col, ColOff: deCellAnchor.From.ColOff,
963 Row: deCellAnchor.From.Row, RowOff: deCellAnchor.From.RowOff,
966 if deCellAnchor.To != nil {
967 xlsxCellAnchorPos.To = &xlsxTo{
968 Col: deCellAnchor.To.Col, ColOff: deCellAnchor.To.ColOff,
969 Row: deCellAnchor.To.Row, RowOff: deCellAnchor.To.RowOff,
972 if err = xlsxCellAnchorPos.adjustDrawings(dir, num, offset, a.EditAs); err != nil {
975 cellAnchor, _ := xml.Marshal(xlsxCellAnchorPos)
976 a.GraphicFrame = strings.TrimSuffix(strings.TrimPrefix(string(cellAnchor), "<xlsxCellAnchorPos>"), "</xlsxCellAnchorPos>")
979 for _, anchor := range wsDr.TwoCellAnchor {
980 if err = anchorCb(anchor); err != nil {
987 // adjustDefinedNames updates the cell reference of the defined names when
988 // inserting or deleting rows or columns.
989 func (f *File) adjustDefinedNames(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
990 wb, err := f.workbookReader()
994 if wb.DefinedNames != nil {
995 for i := 0; i < len(wb.DefinedNames.DefinedName); i++ {
996 data := wb.DefinedNames.DefinedName[i].Data
997 if data, err = f.adjustFormulaRef(sheet, "", data, true, dir, num, offset); err == nil {
998 wb.DefinedNames.DefinedName[i].Data = data