This commit is contained in:
parent
f753e560fa
commit
fe639faa45
176
adjust.go
176
adjust.go
|
@ -29,6 +29,28 @@ const (
|
|||
rows adjustDirection = true
|
||||
)
|
||||
|
||||
// adjustHelperFunc defines functions to adjust helper.
|
||||
var adjustHelperFunc = [6]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
|
||||
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||
return f.adjustTable(ws, sheet, dir, num, offset, sheetID)
|
||||
},
|
||||
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 {
|
||||
return f.adjustVolatileDeps(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)
|
||||
},
|
||||
}
|
||||
|
||||
// adjustHelper provides a function to adjust rows and columns dimensions,
|
||||
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
|
||||
// columns.
|
||||
|
@ -56,23 +78,14 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
|
|||
f.adjustHyperlinks(ws, sheet, dir, num, offset)
|
||||
ws.checkSheet()
|
||||
_ = ws.checkRow()
|
||||
f.adjustTable(ws, sheet, dir, num, offset)
|
||||
if err = f.adjustMergeCells(ws, dir, num, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.adjustAutoFilter(ws, dir, num, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.adjustCalcChain(dir, num, offset, sheetID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.adjustVolatileDeps(dir, num, offset, sheetID); err != nil {
|
||||
return err
|
||||
for _, fn := range adjustHelperFunc {
|
||||
if err := fn(f, ws, sheet, dir, num, offset, sheetID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ws.MergeCells != nil && len(ws.MergeCells.Cells) == 0 {
|
||||
ws.MergeCells = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -460,9 +473,9 @@ func (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirec
|
|||
|
||||
// adjustTable provides a function to update the table when inserting or
|
||||
// deleting rows or columns.
|
||||
func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
|
||||
func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||
if ws.TableParts == nil || len(ws.TableParts.TableParts) == 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
for idx := 0; idx < len(ws.TableParts.TableParts); idx++ {
|
||||
tbl := ws.TableParts.TableParts[idx]
|
||||
|
@ -475,11 +488,11 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
|
|||
t := xlsxTable{}
|
||||
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
|
||||
Decode(&t); err != nil && err != io.EOF {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
coordinates, err := rangeRefToCoordinates(t.Ref)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
// Remove the table when deleting the header row of the table
|
||||
if dir == rows && num == coordinates[0] && offset == -1 {
|
||||
|
@ -506,11 +519,12 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
|
|||
table, _ := xml.Marshal(t)
|
||||
f.saveFileList(tableXML, table)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// adjustAutoFilter provides a function to update the auto filter when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, offset int) error {
|
||||
func (f *File) adjustAutoFilter(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||
if ws.AutoFilter == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -563,7 +577,7 @@ func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, nu
|
|||
|
||||
// adjustMergeCells provides a function to update merged cells when inserting
|
||||
// or deleting rows or columns.
|
||||
func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, offset int) error {
|
||||
func (f *File) adjustMergeCells(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||
if ws.MergeCells == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -657,7 +671,7 @@ func adjustCellName(cell string, dir adjustDirection, c, r, offset int) (string,
|
|||
|
||||
// adjustCalcChain provides a function to update the calculation chain when
|
||||
// inserting or deleting rows or columns.
|
||||
func (f *File) adjustCalcChain(dir adjustDirection, num, offset, sheetID int) error {
|
||||
func (f *File) adjustCalcChain(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||
if f.CalcChain == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -728,7 +742,7 @@ func (vt *xlsxVolTypes) adjustVolatileDepsTopic(cell string, dir adjustDirection
|
|||
|
||||
// adjustVolatileDeps updates the volatile dependencies when inserting or
|
||||
// deleting rows or columns.
|
||||
func (f *File) adjustVolatileDeps(dir adjustDirection, num, offset, sheetID int) error {
|
||||
func (f *File) adjustVolatileDeps(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||
volTypes, err := f.volatileDepsReader()
|
||||
if err != nil || volTypes == nil {
|
||||
return err
|
||||
|
@ -750,3 +764,123 @@ func (f *File) adjustVolatileDeps(dir adjustDirection, num, offset, sheetID int)
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
105
adjust_test.go
105
adjust_test.go
|
@ -1,10 +1,13 @@
|
|||
package excelize
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
_ "image/jpeg"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -19,7 +22,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
}, "Sheet1", rows, 0, 0, 1), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
assert.Equal(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
|
@ -28,7 +31,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
}, "Sheet1", rows, 0, 0, 1), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
assert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
|
@ -37,7 +40,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, rows, 1, -1))
|
||||
}, "Sheet1", rows, 1, -1, 1))
|
||||
assert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
|
@ -46,7 +49,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, columns, 1, -1))
|
||||
}, "Sheet1", columns, 1, -1, 1))
|
||||
assert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
|
@ -55,7 +58,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, columns, 1, -1))
|
||||
}, "Sheet1", columns, 1, -1, 1))
|
||||
|
||||
// Test adjust merge cells
|
||||
var cases []struct {
|
||||
|
@ -134,7 +137,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, c.dir, c.num, 1))
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, "Sheet1", c.dir, c.num, 1, 1))
|
||||
assert.Equal(t, c.expect, c.ws.MergeCells.Cells[0].Ref, c.label)
|
||||
assert.Equal(t, c.expectRect, c.ws.MergeCells.Cells[0].rect, c.label)
|
||||
}
|
||||
|
@ -223,7 +226,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, c.dir, c.num, -1))
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, "Sheet1", c.dir, c.num, -1, 1))
|
||||
assert.Equal(t, c.expect, c.ws.MergeCells.Cells[0].Ref, c.label)
|
||||
}
|
||||
|
||||
|
@ -271,7 +274,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, c.dir, c.num, -1))
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, "Sheet1", c.dir, c.num, -1, 1))
|
||||
assert.Len(t, c.ws.MergeCells.Cells, 0, c.label)
|
||||
}
|
||||
|
||||
|
@ -291,18 +294,18 @@ func TestAdjustAutoFilter(t *testing.T) {
|
|||
AutoFilter: &xlsxAutoFilter{
|
||||
Ref: "A1:A3",
|
||||
},
|
||||
}, rows, 1, -1))
|
||||
}, "Sheet1", rows, 1, -1, 1))
|
||||
// Test adjustAutoFilter with illegal cell reference
|
||||
assert.Equal(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||
AutoFilter: &xlsxAutoFilter{
|
||||
Ref: "A:B1",
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
}, "Sheet1", rows, 0, 0, 1), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
assert.Equal(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||
AutoFilter: &xlsxAutoFilter{
|
||||
Ref: "A1:B",
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
}, "Sheet1", rows, 0, 0, 1), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
}
|
||||
|
||||
func TestAdjustTable(t *testing.T) {
|
||||
|
@ -334,7 +337,7 @@ func TestAdjustTable(t *testing.T) {
|
|||
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
||||
// Test adjust table with invalid table range reference
|
||||
f.Pkg.Store("xl/tables/table1.xml", []byte(`<table ref="-" />`))
|
||||
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
||||
assert.Equal(t, ErrParameterInvalid, f.RemoveRow(sheetName, 1))
|
||||
}
|
||||
|
||||
func TestAdjustHelper(t *testing.T) {
|
||||
|
@ -947,3 +950,81 @@ func TestAdjustVolatileDeps(t *testing.T) {
|
|||
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.InsertCols("Sheet1", "A", 1))
|
||||
f.volatileDepsWriter()
|
||||
}
|
||||
|
||||
func TestAdjustDrawings(t *testing.T) {
|
||||
f := NewFile()
|
||||
// Test add pictures to sheet with positioning
|
||||
assert.NoError(t, f.AddPicture("Sheet1", "B2", filepath.Join("test", "images", "excel.jpg"), nil))
|
||||
assert.NoError(t, f.AddPicture("Sheet1", "B11", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{Positioning: "oneCell"}))
|
||||
assert.NoError(t, f.AddPicture("Sheet1", "B21", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{Positioning: "absolute"}))
|
||||
|
||||
// Test adjust pictures on inserting columns and rows
|
||||
assert.NoError(t, f.InsertCols("Sheet1", "A", 1))
|
||||
assert.NoError(t, f.InsertRows("Sheet1", 1, 1))
|
||||
assert.NoError(t, f.InsertCols("Sheet1", "C", 1))
|
||||
assert.NoError(t, f.InsertRows("Sheet1", 5, 1))
|
||||
assert.NoError(t, f.InsertRows("Sheet1", 15, 1))
|
||||
cells, err := f.GetPictureCells("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"D3", "D13", "B21"}, cells)
|
||||
wb := filepath.Join("test", "TestAdjustDrawings.xlsx")
|
||||
assert.NoError(t, f.SaveAs(wb))
|
||||
|
||||
// Test adjust pictures on deleting columns and rows
|
||||
assert.NoError(t, f.RemoveCol("Sheet1", "A"))
|
||||
assert.NoError(t, f.RemoveRow("Sheet1", 1))
|
||||
cells, err = f.GetPictureCells("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"C2", "C12", "B21"}, cells)
|
||||
|
||||
// Test adjust existing pictures on inserting columns and rows
|
||||
f, err = OpenFile(wb)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.InsertCols("Sheet1", "A", 1))
|
||||
assert.NoError(t, f.InsertRows("Sheet1", 1, 1))
|
||||
assert.NoError(t, f.InsertCols("Sheet1", "D", 1))
|
||||
assert.NoError(t, f.InsertRows("Sheet1", 5, 1))
|
||||
assert.NoError(t, f.InsertRows("Sheet1", 16, 1))
|
||||
cells, err = f.GetPictureCells("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"F4", "F15", "B21"}, cells)
|
||||
|
||||
// Test adjust drawings with unsupported charset
|
||||
f, err = OpenFile(wb)
|
||||
assert.NoError(t, err)
|
||||
f.Pkg.Store("xl/drawings/drawing1.xml", MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.InsertCols("Sheet1", "A", 1), "XML syntax error on line 1: invalid UTF-8")
|
||||
|
||||
errors := []error{ErrColumnNumber, ErrColumnNumber, ErrMaxRows, ErrMaxRows}
|
||||
cells = []string{"XFD1", "XFB1"}
|
||||
for i, cell := range cells {
|
||||
f = NewFile()
|
||||
assert.NoError(t, f.AddPicture("Sheet1", cell, filepath.Join("test", "images", "excel.jpg"), nil))
|
||||
assert.Equal(t, errors[i], f.InsertCols("Sheet1", "A", 1))
|
||||
assert.NoError(t, f.SaveAs(wb))
|
||||
f, err = OpenFile(wb)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, errors[i], f.InsertCols("Sheet1", "A", 1))
|
||||
}
|
||||
errors = []error{ErrMaxRows, ErrMaxRows}
|
||||
cells = []string{"A1048576", "A1048570"}
|
||||
for i, cell := range cells {
|
||||
f = NewFile()
|
||||
assert.NoError(t, f.AddPicture("Sheet1", cell, filepath.Join("test", "images", "excel.jpg"), nil))
|
||||
assert.Equal(t, errors[i], f.InsertRows("Sheet1", 1, 1))
|
||||
assert.NoError(t, f.SaveAs(wb))
|
||||
f, err = OpenFile(wb)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, errors[i], f.InsertRows("Sheet1", 1, 1))
|
||||
}
|
||||
|
||||
a := xdrCellAnchor{}
|
||||
assert.NoError(t, a.adjustDrawings(columns, 0, 0))
|
||||
p := xlsxCellAnchorPos{}
|
||||
assert.NoError(t, p.adjustDrawings(columns, 0, 0, ""))
|
||||
|
||||
f, err = OpenFile(wb)
|
||||
assert.NoError(t, err)
|
||||
f.Pkg.Store("xl/drawings/drawing1.xml", []byte(xml.Header+`<wsDr xmlns="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><twoCellAnchor><from><col>0</col><colOff>0</colOff><row>0</row><rowOff>0</rowOff></from><to><col>1</col><colOff>0</colOff><row>1</row><rowOff>0</rowOff></to><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"></mc:AlternateContent><clientData/></twoCellAnchor></wsDr>`))
|
||||
assert.NoError(t, f.InsertCols("Sheet1", "A", 1))
|
||||
}
|
||||
|
|
95
picture.go
95
picture.go
|
@ -15,7 +15,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/xml"
|
||||
"image"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
@ -471,7 +470,7 @@ func (f *File) GetPictures(sheet, cell string) ([]Picture, error) {
|
|||
return nil, err
|
||||
}
|
||||
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
|
||||
drawingXML := strings.ReplaceAll(target, "..", "xl")
|
||||
drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
|
||||
drawingRelationships := strings.ReplaceAll(
|
||||
strings.ReplaceAll(target, "../drawings", "xl/drawings/_rels"), ".xml", ".xml.rels")
|
||||
|
||||
|
@ -492,7 +491,7 @@ func (f *File) GetPictureCells(sheet string) ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
|
||||
drawingXML := strings.ReplaceAll(target, "..", "xl")
|
||||
drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
|
||||
drawingRelationships := strings.ReplaceAll(
|
||||
strings.ReplaceAll(target, "../drawings", "xl/drawings/_rels"), ".xml", ".xml.rels")
|
||||
|
||||
|
@ -553,15 +552,15 @@ func (f *File) DeletePicture(sheet, cell string) error {
|
|||
// getPicture provides a function to get picture base name and raw content
|
||||
// embed in spreadsheet by given coordinates and drawing relationships.
|
||||
func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (pics []Picture, err error) {
|
||||
var (
|
||||
deWsDr = new(decodeWsDr)
|
||||
wsDr *xlsxWsDr
|
||||
)
|
||||
var wsDr *xlsxWsDr
|
||||
if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
|
||||
return
|
||||
}
|
||||
anchorCond := func(a *xdrCellAnchor) bool { return a.From.Col == col && a.From.Row == row }
|
||||
anchorCb := func(a *xdrCellAnchor, r *xlsxRelationship) {
|
||||
wsDr.mu.Lock()
|
||||
defer wsDr.mu.Unlock()
|
||||
cond := func(from *xlsxFrom) bool { return from.Col == col && from.Row == row }
|
||||
cond2 := func(from *decodeFrom) bool { return from.Col == col && from.Row == row }
|
||||
cb := func(a *xdrCellAnchor, r *xlsxRelationship) {
|
||||
pic := Picture{Extension: filepath.Ext(r.Target), Format: &GraphicOptions{}}
|
||||
if buffer, _ := f.Pkg.Load(strings.ReplaceAll(r.Target, "..", "xl")); buffer != nil {
|
||||
pic.File = buffer.([]byte)
|
||||
|
@ -569,14 +568,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
|
|||
pics = append(pics, pic)
|
||||
}
|
||||
}
|
||||
f.extractCellAnchor(drawingRelationships, wsDr, anchorCond, anchorCb)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
|
||||
Decode(deWsDr); err != nil && err != io.EOF {
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
decodeAnchorCond := func(a *decodeCellAnchor) bool { return a.From.Col == col && a.From.Row == row }
|
||||
decodeAnchorCb := func(a *decodeCellAnchor, r *xlsxRelationship) {
|
||||
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship) {
|
||||
pic := Picture{Extension: filepath.Ext(r.Target), Format: &GraphicOptions{}}
|
||||
if buffer, _ := f.Pkg.Load(strings.ReplaceAll(r.Target, "..", "xl")); buffer != nil {
|
||||
pic.File = buffer.([]byte)
|
||||
|
@ -584,11 +576,11 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
|
|||
pics = append(pics, pic)
|
||||
}
|
||||
}
|
||||
for _, anchor := range deWsDr.TwoCellAnchor {
|
||||
f.extractDecodeCellAnchor(anchor, drawingRelationships, decodeAnchorCond, decodeAnchorCb)
|
||||
for _, anchor := range wsDr.TwoCellAnchor {
|
||||
f.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)
|
||||
}
|
||||
for _, anchor := range deWsDr.OneCellAnchor {
|
||||
f.extractDecodeCellAnchor(anchor, drawingRelationships, decodeAnchorCond, decodeAnchorCb)
|
||||
for _, anchor := range wsDr.OneCellAnchor {
|
||||
f.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -596,18 +588,14 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
|
|||
// extractCellAnchor extract drawing object from cell anchor by giving drawing
|
||||
// cell anchor, drawing relationships part path, conditional and callback
|
||||
// function.
|
||||
func (f *File) extractCellAnchor(drawingRelationships string, wsDr *xlsxWsDr,
|
||||
cond func(anchor *xdrCellAnchor) bool, cb func(anchor *xdrCellAnchor, rels *xlsxRelationship),
|
||||
func (f *File) extractCellAnchor(anchor *xdrCellAnchor, drawingRelationships string,
|
||||
cond func(from *xlsxFrom) bool, cb func(anchor *xdrCellAnchor, rels *xlsxRelationship),
|
||||
cond2 func(from *decodeFrom) bool, cb2 func(anchor *decodeCellAnchor, rels *xlsxRelationship),
|
||||
) {
|
||||
var (
|
||||
anchor *xdrCellAnchor
|
||||
drawRel *xlsxRelationship
|
||||
)
|
||||
wsDr.mu.Lock()
|
||||
defer wsDr.mu.Unlock()
|
||||
for _, anchor = range wsDr.TwoCellAnchor {
|
||||
var drawRel *xlsxRelationship
|
||||
if anchor.GraphicFrame == "" {
|
||||
if anchor.From != nil && anchor.Pic != nil {
|
||||
if cond(anchor) {
|
||||
if cond(anchor.From) {
|
||||
if drawRel = f.getDrawingRelationships(drawingRelationships,
|
||||
anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
|
||||
if _, ok := supportedImageTypes[strings.ToLower(filepath.Ext(drawRel.Target))]; ok {
|
||||
|
@ -616,25 +604,24 @@ func (f *File) extractCellAnchor(drawingRelationships string, wsDr *xlsxWsDr,
|
|||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
f.extractDecodeCellAnchor(anchor, drawingRelationships, cond2, cb2)
|
||||
}
|
||||
|
||||
// extractDecodeCellAnchor extract drawing object from cell anchor by giving
|
||||
// decoded drawing cell anchor, drawing relationships part path, conditional and
|
||||
// callback function.
|
||||
func (f *File) extractDecodeCellAnchor(anchor *decodeCellAnchor, drawingRelationships string,
|
||||
cond func(anchor *decodeCellAnchor) bool, cb func(anchor *decodeCellAnchor, rels *xlsxRelationship),
|
||||
func (f *File) extractDecodeCellAnchor(anchor *xdrCellAnchor, drawingRelationships string,
|
||||
cond func(from *decodeFrom) bool, cb func(anchor *decodeCellAnchor, rels *xlsxRelationship),
|
||||
) {
|
||||
var (
|
||||
drawRel *xlsxRelationship
|
||||
deCellAnchor = new(decodeCellAnchor)
|
||||
)
|
||||
if err := f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + anchor.Content + "</decodeCellAnchor>")).
|
||||
Decode(deCellAnchor); err != nil && err != io.EOF {
|
||||
return
|
||||
}
|
||||
_ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + anchor.GraphicFrame + "</decodeCellAnchor>")).Decode(&deCellAnchor)
|
||||
if deCellAnchor.From != nil && deCellAnchor.Pic != nil {
|
||||
if cond(deCellAnchor) {
|
||||
if cond(deCellAnchor.From) {
|
||||
drawRel = f.getDrawingRelationships(drawingRelationships, deCellAnchor.Pic.BlipFill.Blip.Embed)
|
||||
if _, ok := supportedImageTypes[strings.ToLower(filepath.Ext(drawRel.Target))]; ok {
|
||||
cb(deCellAnchor, drawRel)
|
||||
|
@ -720,42 +707,36 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, opts *Gr
|
|||
// worksheet by given drawing part path and drawing relationships path.
|
||||
func (f *File) getPictureCells(drawingXML, drawingRelationships string) ([]string, error) {
|
||||
var (
|
||||
cells []string
|
||||
err error
|
||||
deWsDr *decodeWsDr
|
||||
wsDr *xlsxWsDr
|
||||
cells []string
|
||||
err error
|
||||
wsDr *xlsxWsDr
|
||||
)
|
||||
if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
|
||||
return cells, err
|
||||
}
|
||||
anchorCond := func(a *xdrCellAnchor) bool { return true }
|
||||
anchorCb := func(a *xdrCellAnchor, r *xlsxRelationship) {
|
||||
wsDr.mu.Lock()
|
||||
defer wsDr.mu.Unlock()
|
||||
cond := func(from *xlsxFrom) bool { return true }
|
||||
cond2 := func(from *decodeFrom) bool { return true }
|
||||
cb := func(a *xdrCellAnchor, r *xlsxRelationship) {
|
||||
if _, ok := f.Pkg.Load(strings.ReplaceAll(r.Target, "..", "xl")); ok {
|
||||
if cell, err := CoordinatesToCellName(a.From.Col+1, a.From.Row+1); err == nil && inStrSlice(cells, cell, true) == -1 {
|
||||
cells = append(cells, cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
f.extractCellAnchor(drawingRelationships, wsDr, anchorCond, anchorCb)
|
||||
deWsDr = new(decodeWsDr)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
|
||||
Decode(deWsDr); err != nil && err != io.EOF {
|
||||
return cells, err
|
||||
}
|
||||
err = nil
|
||||
decodeAnchorCond := func(a *decodeCellAnchor) bool { return true }
|
||||
decodeAnchorCb := func(a *decodeCellAnchor, r *xlsxRelationship) {
|
||||
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship) {
|
||||
if _, ok := f.Pkg.Load(strings.ReplaceAll(r.Target, "..", "xl")); ok {
|
||||
if cell, err := CoordinatesToCellName(a.From.Col+1, a.From.Row+1); err == nil && inStrSlice(cells, cell, true) == -1 {
|
||||
cells = append(cells, cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, anchor := range deWsDr.TwoCellAnchor {
|
||||
f.extractDecodeCellAnchor(anchor, drawingRelationships, decodeAnchorCond, decodeAnchorCb)
|
||||
for _, anchor := range wsDr.TwoCellAnchor {
|
||||
f.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)
|
||||
}
|
||||
for _, anchor := range deWsDr.OneCellAnchor {
|
||||
f.extractDecodeCellAnchor(anchor, drawingRelationships, decodeAnchorCond, decodeAnchorCb)
|
||||
for _, anchor := range wsDr.OneCellAnchor {
|
||||
f.extractCellAnchor(anchor, drawingRelationships, cond, cb, cond2, cb2)
|
||||
}
|
||||
return cells, err
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ func TestAddPicture(t *testing.T) {
|
|||
// Test get picture cells
|
||||
cells, err := f.GetPictureCells("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"A30", "F21", "B30", "Q1", "Q8", "Q15", "Q22", "Q28"}, cells)
|
||||
assert.Equal(t, []string{"F21", "A30", "B30", "Q1", "Q8", "Q15", "Q22", "Q28"}, cells)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f, err = OpenFile(filepath.Join("test", "TestAddPicture1.xlsx"))
|
||||
|
@ -92,6 +92,7 @@ func TestAddPicture(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"F21", "A30", "B30", "Q1", "Q8", "Q15", "Q22", "Q28"}, cells)
|
||||
// Test get picture cells with unsupported charset
|
||||
f.Drawings.Delete(path)
|
||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||
_, err = f.GetPictureCells("Sheet1")
|
||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||
|
@ -226,6 +227,7 @@ func TestGetPicture(t *testing.T) {
|
|||
|
||||
// Test get pictures with unsupported charset
|
||||
path := "xl/drawings/drawing1.xml"
|
||||
f.Drawings.Delete(path)
|
||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||
_, err = f.getPicture(20, 5, path, "xl/drawings/_rels/drawing2.xml.rels")
|
||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||
|
@ -388,7 +390,7 @@ func TestGetPictureCells(t *testing.T) {
|
|||
|
||||
func TestExtractDecodeCellAnchor(t *testing.T) {
|
||||
f := NewFile()
|
||||
cond := func(a *decodeCellAnchor) bool { return true }
|
||||
cond := func(a *decodeFrom) bool { return true }
|
||||
cb := func(a *decodeCellAnchor, r *xlsxRelationship) {}
|
||||
f.extractDecodeCellAnchor(&decodeCellAnchor{Content: string(MacintoshCyrillicCharset)}, "", cond, cb)
|
||||
f.extractDecodeCellAnchor(&xdrCellAnchor{GraphicFrame: string(MacintoshCyrillicCharset)}, "", cond, cb)
|
||||
}
|
||||
|
|
|
@ -18,13 +18,32 @@ import "encoding/xml"
|
|||
// specifies a two cell anchor placeholder for a group, a shape, or a drawing
|
||||
// element. It moves with cells and its extents are in EMU units.
|
||||
type decodeCellAnchor struct {
|
||||
EditAs string `xml:"editAs,attr,omitempty"`
|
||||
From *decodeFrom `xml:"from"`
|
||||
To *decodeTo `xml:"to"`
|
||||
Sp *decodeSp `xml:"sp"`
|
||||
Pic *decodePic `xml:"pic"`
|
||||
ClientData *decodeClientData `xml:"clientData"`
|
||||
Content string `xml:",innerxml"`
|
||||
EditAs string `xml:"editAs,attr,omitempty"`
|
||||
From *decodeFrom `xml:"from"`
|
||||
To *decodeTo `xml:"to"`
|
||||
Sp *decodeSp `xml:"sp"`
|
||||
Pic *decodePic `xml:"pic"`
|
||||
ClientData *decodeClientData `xml:"clientData"`
|
||||
AlternateContent []*xlsxAlternateContent `xml:"mc:AlternateContent"`
|
||||
Content string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
// decodeCellAnchorPos defines the structure used to deserialize the cell anchor
|
||||
// for adjust drawing object on inserting/deleting column/rows.
|
||||
type decodeCellAnchorPos struct {
|
||||
EditAs string `xml:"editAs,attr,omitempty"`
|
||||
From *xlsxFrom `xml:"from"`
|
||||
To *xlsxTo `xml:"to"`
|
||||
Pos *xlsxInnerXML `xml:"pos"`
|
||||
Ext *xlsxInnerXML `xml:"ext"`
|
||||
Sp *xlsxInnerXML `xml:"sp"`
|
||||
GrpSp *xlsxInnerXML `xml:"grpSp"`
|
||||
GraphicFrame *xlsxInnerXML `xml:"graphicFrame"`
|
||||
CxnSp *xlsxInnerXML `xml:"cxnSp"`
|
||||
Pic *xlsxInnerXML `xml:"pic"`
|
||||
ContentPart *xlsxInnerXML `xml:"contentPart"`
|
||||
AlternateContent []*xlsxAlternateContent `xml:"AlternateContent"`
|
||||
ClientData *xlsxInnerXML `xml:"clientData"`
|
||||
}
|
||||
|
||||
// xdrSp (Shape) directly maps the sp element. This element specifies the
|
||||
|
|
|
@ -230,6 +230,24 @@ type xdrCellAnchor struct {
|
|||
ClientData *xdrClientData `xml:"xdr:clientData"`
|
||||
}
|
||||
|
||||
// xlsxCellAnchorPos defines the structure used to serialize the cell anchor for
|
||||
// adjust drawing object on inserting/deleting column/rows.
|
||||
type xlsxCellAnchorPos struct {
|
||||
EditAs string `xml:"editAs,attr,omitempty"`
|
||||
From *xlsxFrom `xml:"xdr:from"`
|
||||
To *xlsxTo `xml:"xdr:to"`
|
||||
Pos *xlsxInnerXML `xml:"xdr:pos"`
|
||||
Ext *xlsxInnerXML `xml:"xdr:ext"`
|
||||
Sp *xlsxInnerXML `xml:"xdr:sp"`
|
||||
GrpSp *xlsxInnerXML `xml:"xdr:grpSp"`
|
||||
GraphicFrame *xlsxInnerXML `xml:"xdr:graphicFrame"`
|
||||
CxnSp *xlsxInnerXML `xml:"xdr:cxnSp"`
|
||||
Pic *xlsxInnerXML `xml:"xdr:pic"`
|
||||
ContentPart *xlsxInnerXML `xml:"xdr:contentPart"`
|
||||
AlternateContent []*xlsxAlternateContent `xml:"mc:AlternateContent"`
|
||||
ClientData *xlsxInnerXML `xml:"xdr:clientData"`
|
||||
}
|
||||
|
||||
// xlsxPoint2D describes the position of a drawing element within a spreadsheet.
|
||||
type xlsxPoint2D struct {
|
||||
XMLName xml.Name `xml:"xdr:pos"`
|
||||
|
|
Loading…
Reference in New Issue