1 // Copyright 2016 - 2024 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)
625 y1, y2 = f.adjustMergeCellsHelper(y1, y2, num, offset)
627 if x1 == num && x2 == num && offset < 0 {
628 f.deleteMergeCell(ws, i)
633 x1, x2 = f.adjustMergeCellsHelper(x1, x2, num, offset)
635 if x1 == x2 && y1 == y2 {
636 f.deleteMergeCell(ws, i)
640 mergedCells.rect = []int{x1, y1, x2, y2}
641 if mergedCells.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {
648 // adjustMergeCellsHelper provides a function for adjusting merge cells to
649 // compare and calculate cell reference by the given pivot, operation reference
651 func (f *File) adjustMergeCellsHelper(p1, p2, num, offset int) (int, int) {
660 } else if num <= p2 {
665 if num < p1 || (num == p1 && num == p2) {
668 } else if num <= p2 {
674 // deleteMergeCell provides a function to delete merged cell by given index.
675 func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
679 if len(ws.MergeCells.Cells) > idx {
680 ws.MergeCells.Cells = append(ws.MergeCells.Cells[:idx], ws.MergeCells.Cells[idx+1:]...)
681 ws.MergeCells.Count = len(ws.MergeCells.Cells)
685 // adjustCellName returns updated cell name by giving column/row number and
686 // offset on inserting or deleting rows or columns.
687 func adjustCellName(cell string, dir adjustDirection, c, r, offset int) (string, error) {
689 if rn := r + offset; rn > 0 {
690 return CoordinatesToCellName(c, rn)
693 return CoordinatesToCellName(c+offset, r)
696 // adjustCalcChain provides a function to update the calculation chain when
697 // inserting or deleting rows or columns.
698 func (f *File) adjustCalcChain(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
699 if f.CalcChain == nil {
702 // If sheet ID is omitted, it is assumed to be the same as the i value of
703 // the previous cell.
705 for i := 0; i < len(f.CalcChain.C); i++ {
706 c := f.CalcChain.C[i]
714 colNum, rowNum, err := CellNameToCoordinates(c.R)
718 if dir == rows && num <= rowNum {
719 if num == rowNum && offset == -1 {
720 _ = f.deleteCalcChain(c.I, c.R)
724 f.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)
726 if dir == columns && num <= colNum {
727 if num == colNum && offset == -1 {
728 _ = f.deleteCalcChain(c.I, c.R)
732 f.CalcChain.C[i].R, _ = adjustCellName(c.R, dir, colNum, rowNum, offset)
738 // adjustVolatileDepsTopic updates the volatile dependencies topic when
739 // inserting or deleting rows or columns.
740 func (vt *xlsxVolTypes) adjustVolatileDepsTopic(cell string, dir adjustDirection, indexes []int) (int, error) {
741 num, offset, i1, i2, i3, i4 := indexes[0], indexes[1], indexes[2], indexes[3], indexes[4], indexes[5]
742 colNum, rowNum, err := CellNameToCoordinates(cell)
746 if dir == rows && num <= rowNum {
747 if num == rowNum && offset == -1 {
748 vt.deleteVolTopicRef(i1, i2, i3, i4)
752 vt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)
754 if dir == columns && num <= colNum {
755 if num == colNum && offset == -1 {
756 vt.deleteVolTopicRef(i1, i2, i3, i4)
760 if name, _ := adjustCellName(cell, dir, colNum, rowNum, offset); name != "" {
761 vt.VolType[i1].Main[i2].Tp[i3].Tr[i4].R, _ = adjustCellName(cell, dir, colNum, rowNum, offset)
767 // adjustVolatileDeps updates the volatile dependencies when inserting or
768 // deleting rows or columns.
769 func (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
770 volTypes, err := f.volatileDepsReader()
771 if err != nil || volTypes == nil {
774 for i1 := 0; i1 < len(volTypes.VolType); i1++ {
775 for i2 := 0; i2 < len(volTypes.VolType[i1].Main); i2++ {
776 for i3 := 0; i3 < len(volTypes.VolType[i1].Main[i2].Tp); i3++ {
777 for i4 := 0; i4 < len(volTypes.VolType[i1].Main[i2].Tp[i3].Tr); i4++ {
778 ref := volTypes.VolType[i1].Main[i2].Tp[i3].Tr[i4]
779 if ref.S != sheetID {
782 if i4, err = volTypes.adjustVolatileDepsTopic(ref.R, dir, []int{num, offset, i1, i2, i3, i4}); err != nil {
792 // adjustConditionalFormats updates the cell reference of the worksheet
793 // conditional formatting when inserting or deleting rows or columns.
794 func (f *File) adjustConditionalFormats(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
795 for i := 0; i < len(ws.ConditionalFormatting); i++ {
796 cf := ws.ConditionalFormatting[i]
800 ref, del, err := f.adjustCellRef(cf.SQRef, dir, num, offset)
805 ws.ConditionalFormatting = append(ws.ConditionalFormatting[:i],
806 ws.ConditionalFormatting[i+1:]...)
810 ws.ConditionalFormatting[i].SQRef = ref
815 // adjustDataValidations updates the range of data validations for the worksheet
816 // when inserting or deleting rows or columns.
817 func (f *File) adjustDataValidations(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
818 for _, sheetN := range f.GetSheetList() {
819 worksheet, err := f.workSheetReader(sheetN)
821 if err.Error() == newNotWorksheetError(sheetN).Error() {
826 if worksheet.DataValidations == nil {
829 for i := 0; i < len(worksheet.DataValidations.DataValidation); i++ {
830 dv := worksheet.DataValidations.DataValidation[i]
835 ref, del, err := f.adjustCellRef(dv.Sqref, dir, num, offset)
840 worksheet.DataValidations.DataValidation = append(worksheet.DataValidations.DataValidation[:i],
841 worksheet.DataValidations.DataValidation[i+1:]...)
845 worksheet.DataValidations.DataValidation[i].Sqref = ref
847 if worksheet.DataValidations.DataValidation[i].Formula1 != nil {
848 formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula1.Content)
849 if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
852 worksheet.DataValidations.DataValidation[i].Formula1 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
854 if worksheet.DataValidations.DataValidation[i].Formula2 != nil {
855 formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula2.Content)
856 if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
859 worksheet.DataValidations.DataValidation[i].Formula2 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
862 if worksheet.DataValidations.Count = len(worksheet.DataValidations.DataValidation); worksheet.DataValidations.Count == 0 {
863 worksheet.DataValidations = nil
869 // adjustDrawings updates the starting anchor of the two cell anchor pictures
870 // and charts object when inserting or deleting rows or columns.
871 func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
873 if dir == columns && from.Col+1 >= num && from.Col+offset >= 0 {
874 if from.Col+offset >= MaxColumns {
875 return false, ErrColumnNumber
878 ok = editAs == "oneCell"
880 if dir == rows && from.Row+1 >= num && from.Row+offset >= 0 {
881 if from.Row+offset >= TotalRows {
882 return false, ErrMaxRows
885 ok = editAs == "oneCell"
890 // adjustDrawings updates the ending anchor of the two cell anchor pictures
891 // and charts object when inserting or deleting rows or columns.
892 func (to *xlsxTo) adjustDrawings(dir adjustDirection, num, offset int, editAs string, ok bool) error {
893 if dir == columns && to.Col+1 >= num && to.Col+offset >= 0 && ok {
894 if to.Col+offset >= MaxColumns {
895 return ErrColumnNumber
899 if dir == rows && to.Row+1 >= num && to.Row+offset >= 0 && ok {
900 if to.Row+offset >= TotalRows {
908 // adjustDrawings updates the two cell anchor pictures and charts object when
909 // inserting or deleting rows or columns.
910 func (a *xdrCellAnchor) adjustDrawings(dir adjustDirection, num, offset int) error {
912 if a.From == nil || a.To == nil || editAs == "absolute" {
915 ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
919 return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
922 // adjustDrawings updates the existing two cell anchor pictures and charts
923 // object when inserting or deleting rows or columns.
924 func (a *xlsxCellAnchorPos) adjustDrawings(dir adjustDirection, num, offset int, editAs string) error {
925 if a.From == nil || a.To == nil || editAs == "absolute" {
928 ok, err := a.From.adjustDrawings(dir, num, offset, editAs)
932 return a.To.adjustDrawings(dir, num, offset, editAs, ok || editAs == "")
935 // adjustDrawings updates the pictures and charts object when inserting or
936 // deleting rows or columns.
937 func (f *File) adjustDrawings(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
938 if ws.Drawing == nil {
941 target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
942 drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
947 if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
950 anchorCb := func(a *xdrCellAnchor) error {
951 if a.GraphicFrame == "" {
952 return a.adjustDrawings(dir, num, offset)
954 deCellAnchor := decodeCellAnchor{}
955 deCellAnchorPos := decodeCellAnchorPos{}
956 _ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + a.GraphicFrame + "</decodeCellAnchor>")).Decode(&deCellAnchor)
957 _ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchorPos>" + a.GraphicFrame + "</decodeCellAnchorPos>")).Decode(&deCellAnchorPos)
958 xlsxCellAnchorPos := xlsxCellAnchorPos(deCellAnchorPos)
959 for i := 0; i < len(xlsxCellAnchorPos.AlternateContent); i++ {
960 xlsxCellAnchorPos.AlternateContent[i].XMLNSMC = SourceRelationshipCompatibility.Value
962 if deCellAnchor.From != nil {
963 xlsxCellAnchorPos.From = &xlsxFrom{
964 Col: deCellAnchor.From.Col, ColOff: deCellAnchor.From.ColOff,
965 Row: deCellAnchor.From.Row, RowOff: deCellAnchor.From.RowOff,
968 if deCellAnchor.To != nil {
969 xlsxCellAnchorPos.To = &xlsxTo{
970 Col: deCellAnchor.To.Col, ColOff: deCellAnchor.To.ColOff,
971 Row: deCellAnchor.To.Row, RowOff: deCellAnchor.To.RowOff,
974 if err = xlsxCellAnchorPos.adjustDrawings(dir, num, offset, a.EditAs); err != nil {
977 cellAnchor, _ := xml.Marshal(xlsxCellAnchorPos)
978 a.GraphicFrame = strings.TrimSuffix(strings.TrimPrefix(string(cellAnchor), "<xlsxCellAnchorPos>"), "</xlsxCellAnchorPos>")
981 for _, anchor := range wsDr.TwoCellAnchor {
982 if err = anchorCb(anchor); err != nil {
989 // adjustDefinedNames updates the cell reference of the defined names when
990 // inserting or deleting rows or columns.
991 func (f *File) adjustDefinedNames(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
992 wb, err := f.workbookReader()
996 if wb.DefinedNames != nil {
997 for i := 0; i < len(wb.DefinedNames.DefinedName); i++ {
998 data := wb.DefinedNames.DefinedName[i].Data
999 if data, err = f.adjustFormulaRef(sheet, "", data, true, dir, num, offset); err == nil {
1000 wb.DefinedNames.DefinedName[i].Data = data