Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xuri
excelize
提交
7291e787
excelize
项目概览
xuri
/
excelize
通知
13
Star
2
Fork
4
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
excelize
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
已验证
提交
7291e787
编写于
11月 02, 2023
作者:
xurime
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Support update volatile dependencies on inserting/deleting columns/rows
上级
b41a6cc3
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
192 addition
and
32 deletion
+192
-32
adjust.go
adjust.go
+71
-15
adjust_test.go
adjust_test.go
+20
-0
calcchain.go
calcchain.go
+36
-0
excelize.go
excelize.go
+10
-9
file.go
file.go
+1
-0
table.go
table.go
+1
-1
templates.go
templates.go
+3
-2
xmlCalcChain.go
xmlCalcChain.go
+47
-2
xmlTable.go
xmlTable.go
+3
-3
未找到文件。
adjust.go
浏览文件 @
7291e787
...
...
@@ -38,7 +38,7 @@ const (
// row: Index number of the row we're inserting/deleting before
// offset: Number of rows/column to insert/delete negative values indicate deletion
//
// TODO: adjust
PageBreaks, adjustComments, adjustDataValidation
s, adjustProtectedCells
// TODO: adjust
Comments, adjustDataValidations, adjustDrawings, adjustPageBreak
s, adjustProtectedCells
func
(
f
*
File
)
adjustHelper
(
sheet
string
,
dir
adjustDirection
,
num
,
offset
int
)
error
{
ws
,
err
:=
f
.
workSheetReader
(
sheet
)
if
err
!=
nil
{
...
...
@@ -66,6 +66,9 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
if
err
=
f
.
adjustCalcChain
(
dir
,
num
,
offset
,
sheetID
);
err
!=
nil
{
return
err
}
if
err
=
f
.
adjustVolatileDeps
(
dir
,
num
,
offset
,
sheetID
);
err
!=
nil
{
return
err
}
if
ws
.
MergeCells
!=
nil
&&
len
(
ws
.
MergeCells
.
Cells
)
==
0
{
ws
.
MergeCells
=
nil
}
...
...
@@ -498,7 +501,8 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
t
.
AutoFilter
.
Ref
=
t
.
Ref
}
_
=
f
.
setTableColumns
(
sheet
,
true
,
x1
,
y1
,
x2
,
&
t
)
t
.
TotalsRowCount
=
0
// Currently doesn't support query table
t
.
TableType
,
t
.
TotalsRowCount
,
t
.
ConnectionID
=
""
,
0
,
0
table
,
_
:=
xml
.
Marshal
(
t
)
f
.
saveFileList
(
tableXML
,
table
)
}
...
...
@@ -578,7 +582,6 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off
if
dir
==
rows
{
if
y1
==
num
&&
y2
==
num
&&
offset
<
0
{
f
.
deleteMergeCell
(
ws
,
i
)
i
--
continue
}
...
...
@@ -586,7 +589,6 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off
}
else
{
if
x1
==
num
&&
x2
==
num
&&
offset
<
0
{
f
.
deleteMergeCell
(
ws
,
i
)
i
--
continue
}
...
...
@@ -642,18 +644,15 @@ func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
}
}
// adjustC
alcChainRef update the cell reference in calculation chain when
// inserting or deleting rows or columns.
func
(
f
*
File
)
adjustCalcChainRef
(
i
,
c
,
r
,
offset
int
,
dir
adjustDirection
)
{
// adjustC
ellName returns updated cell name by giving column/row number and
//
offset on
inserting or deleting rows or columns.
func
adjustCellName
(
cell
string
,
dir
adjustDirection
,
c
,
r
,
offset
int
)
(
string
,
error
)
{
if
dir
==
rows
{
if
rn
:=
r
+
offset
;
rn
>
0
{
f
.
CalcChain
.
C
[
i
]
.
R
,
_
=
CoordinatesToCellName
(
c
,
rn
)
return
CoordinatesToCellName
(
c
,
rn
)
}
return
}
if
nc
:=
c
+
offset
;
nc
>
0
{
f
.
CalcChain
.
C
[
i
]
.
R
,
_
=
CoordinatesToCellName
(
nc
,
r
)
}
return
CoordinatesToCellName
(
c
+
offset
,
r
)
}
// adjustCalcChain provides a function to update the calculation chain when
...
...
@@ -665,7 +664,8 @@ func (f *File) adjustCalcChain(dir adjustDirection, num, offset, sheetID int) er
// If sheet ID is omitted, it is assumed to be the same as the i value of
// the previous cell.
var
prevSheetID
int
for
index
,
c
:=
range
f
.
CalcChain
.
C
{
for
i
:=
0
;
i
<
len
(
f
.
CalcChain
.
C
);
i
++
{
c
:=
f
.
CalcChain
.
C
[
i
]
if
c
.
I
==
0
{
c
.
I
=
prevSheetID
}
...
...
@@ -680,16 +680,72 @@ func (f *File) adjustCalcChain(dir adjustDirection, num, offset, sheetID int) er
if
dir
==
rows
&&
num
<=
rowNum
{
if
num
==
rowNum
&&
offset
==
-
1
{
_
=
f
.
deleteCalcChain
(
c
.
I
,
c
.
R
)
i
--
continue
}
f
.
adjustCalcChainRef
(
index
,
colNum
,
rowNum
,
offset
,
dir
)
f
.
CalcChain
.
C
[
i
]
.
R
,
_
=
adjustCellName
(
c
.
R
,
dir
,
colNum
,
rowNum
,
offset
)
}
if
dir
==
columns
&&
num
<=
colNum
{
if
num
==
colNum
&&
offset
==
-
1
{
_
=
f
.
deleteCalcChain
(
c
.
I
,
c
.
R
)
i
--
continue
}
f
.
adjustCalcChainRef
(
index
,
colNum
,
rowNum
,
offset
,
dir
)
f
.
CalcChain
.
C
[
i
]
.
R
,
_
=
adjustCellName
(
c
.
R
,
dir
,
colNum
,
rowNum
,
offset
)
}
}
return
nil
}
// adjustVolatileDepsTopic updates the volatile dependencies topic when
// inserting or deleting rows or columns.
func
(
vt
*
xlsxVolTypes
)
adjustVolatileDepsTopic
(
cell
string
,
dir
adjustDirection
,
indexes
[]
int
)
(
int
,
error
)
{
num
,
offset
,
i1
,
i2
,
i3
,
i4
:=
indexes
[
0
],
indexes
[
1
],
indexes
[
2
],
indexes
[
3
],
indexes
[
4
],
indexes
[
5
]
colNum
,
rowNum
,
err
:=
CellNameToCoordinates
(
cell
)
if
err
!=
nil
{
return
i4
,
err
}
if
dir
==
rows
&&
num
<=
rowNum
{
if
num
==
rowNum
&&
offset
==
-
1
{
vt
.
deleteVolTopicRef
(
i1
,
i2
,
i3
,
i4
)
i4
--
return
i4
,
err
}
vt
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
[
i4
]
.
R
,
_
=
adjustCellName
(
cell
,
dir
,
colNum
,
rowNum
,
offset
)
}
if
dir
==
columns
&&
num
<=
colNum
{
if
num
==
colNum
&&
offset
==
-
1
{
vt
.
deleteVolTopicRef
(
i1
,
i2
,
i3
,
i4
)
i4
--
return
i4
,
err
}
if
name
,
_
:=
adjustCellName
(
cell
,
dir
,
colNum
,
rowNum
,
offset
);
name
!=
""
{
vt
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
[
i4
]
.
R
,
_
=
adjustCellName
(
cell
,
dir
,
colNum
,
rowNum
,
offset
)
}
}
return
i4
,
err
}
// adjustVolatileDeps updates the volatile dependencies when inserting or
// deleting rows or columns.
func
(
f
*
File
)
adjustVolatileDeps
(
dir
adjustDirection
,
num
,
offset
,
sheetID
int
)
error
{
volTypes
,
err
:=
f
.
volatileDepsReader
()
if
err
!=
nil
||
volTypes
==
nil
{
return
err
}
for
i1
:=
0
;
i1
<
len
(
volTypes
.
VolType
);
i1
++
{
for
i2
:=
0
;
i2
<
len
(
volTypes
.
VolType
[
i1
]
.
Main
);
i2
++
{
for
i3
:=
0
;
i3
<
len
(
volTypes
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
);
i3
++
{
for
i4
:=
0
;
i4
<
len
(
volTypes
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
);
i4
++
{
ref
:=
volTypes
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
[
i4
]
if
ref
.
S
!=
sheetID
{
continue
}
if
i4
,
err
=
volTypes
.
adjustVolatileDepsTopic
(
ref
.
R
,
dir
,
[]
int
{
num
,
offset
,
i1
,
i2
,
i3
,
i4
});
err
!=
nil
{
return
err
}
}
}
}
}
return
nil
...
...
adjust_test.go
浏览文件 @
7291e787
...
...
@@ -927,3 +927,23 @@ func TestAdjustFormula(t *testing.T) {
assert
.
NoError
(
t
,
f
.
InsertCols
(
"Sheet1"
,
"A"
,
1
))
})
}
func
TestAdjustVolatileDeps
(
t
*
testing
.
T
)
{
f
:=
NewFile
()
f
.
Pkg
.
Store
(
defaultXMLPathVolatileDeps
,
[]
byte
(
fmt
.
Sprintf
(
`<volTypes xmlns="%s"><volType><main><tp><tr r="C2" s="2"/><tr r="C2" s="1"/><tr r="D3" s="1"/></tp></main></volType></volTypes>`
,
NameSpaceSpreadSheet
.
Value
)))
assert
.
NoError
(
t
,
f
.
InsertCols
(
"Sheet1"
,
"A"
,
1
))
assert
.
NoError
(
t
,
f
.
InsertRows
(
"Sheet1"
,
2
,
1
))
assert
.
Equal
(
t
,
"D3"
,
f
.
VolatileDeps
.
VolType
[
0
]
.
Main
[
0
]
.
Tp
[
0
]
.
Tr
[
1
]
.
R
)
assert
.
NoError
(
t
,
f
.
RemoveCol
(
"Sheet1"
,
"D"
))
assert
.
NoError
(
t
,
f
.
RemoveRow
(
"Sheet1"
,
4
))
assert
.
Len
(
t
,
f
.
VolatileDeps
.
VolType
[
0
]
.
Main
[
0
]
.
Tp
[
0
]
.
Tr
,
1
)
f
=
NewFile
()
f
.
Pkg
.
Store
(
defaultXMLPathVolatileDeps
,
MacintoshCyrillicCharset
)
assert
.
EqualError
(
t
,
f
.
InsertRows
(
"Sheet1"
,
2
,
1
),
"XML syntax error on line 1: invalid UTF-8"
)
f
=
NewFile
()
f
.
Pkg
.
Store
(
defaultXMLPathVolatileDeps
,
[]
byte
(
fmt
.
Sprintf
(
`<volTypes xmlns="%s"><volType><main><tp><tr r="A" s="1"/></tp></main></volType></volTypes>`
,
NameSpaceSpreadSheet
.
Value
)))
assert
.
Equal
(
t
,
newCellNameToCoordinatesError
(
"A"
,
newInvalidCellNameError
(
"A"
)),
f
.
InsertCols
(
"Sheet1"
,
"A"
,
1
))
f
.
volatileDepsWriter
()
}
calcchain.go
浏览文件 @
7291e787
...
...
@@ -81,3 +81,39 @@ func (c xlsxCalcChainCollection) Filter(fn func(v xlsxCalcChainC) bool) []xlsxCa
}
return
results
}
// volatileDepsReader provides a function to get the pointer to the structure
// after deserialization of xl/volatileDependencies.xml.
func
(
f
*
File
)
volatileDepsReader
()
(
*
xlsxVolTypes
,
error
)
{
if
f
.
VolatileDeps
==
nil
{
volatileDeps
,
ok
:=
f
.
Pkg
.
Load
(
defaultXMLPathVolatileDeps
)
if
!
ok
{
return
f
.
VolatileDeps
,
nil
}
f
.
VolatileDeps
=
new
(
xlsxVolTypes
)
if
err
:=
f
.
xmlNewDecoder
(
bytes
.
NewReader
(
namespaceStrictToTransitional
(
volatileDeps
.
([]
byte
))))
.
Decode
(
f
.
VolatileDeps
);
err
!=
nil
&&
err
!=
io
.
EOF
{
return
f
.
VolatileDeps
,
err
}
}
return
f
.
VolatileDeps
,
nil
}
// volatileDepsWriter provides a function to save xl/volatileDependencies.xml
// after serialize structure.
func
(
f
*
File
)
volatileDepsWriter
()
{
if
f
.
VolatileDeps
!=
nil
{
output
,
_
:=
xml
.
Marshal
(
f
.
VolatileDeps
)
f
.
saveFileList
(
defaultXMLPathVolatileDeps
,
output
)
}
}
// deleteVolTopicRef provides a function to remove cell reference on the
// volatile dependencies topic.
func
(
vt
*
xlsxVolTypes
)
deleteVolTopicRef
(
i1
,
i2
,
i3
,
i4
int
)
{
for
i
:=
range
vt
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
{
if
i
==
i4
{
vt
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
=
append
(
vt
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
[
:
i
],
vt
.
VolType
[
i1
]
.
Main
[
i2
]
.
Tp
[
i3
]
.
Tr
[
i
+
1
:
]
...
)
}
}
}
excelize.go
浏览文件 @
7291e787
...
...
@@ -29,31 +29,32 @@ import (
// File define a populated spreadsheet file struct.
type
File
struct
{
mu
sync
.
Mutex
options
*
Options
xmlAttr
sync
.
Map
checked
sync
.
Map
options
*
Options
sharedStringItem
[][]
uint
sharedStringsMap
map
[
string
]
int
sharedStringTemp
*
os
.
File
sheetMap
map
[
string
]
string
streams
map
[
string
]
*
StreamWriter
tempFiles
sync
.
Map
sharedStringsMap
map
[
string
]
int
sharedStringItem
[][]
uint
sharedStringTemp
*
os
.
File
xmlAttr
sync
.
Map
CalcChain
*
xlsxCalcChain
CharsetReader
charsetTranscoderFn
Comments
map
[
string
]
*
xlsxComments
ContentTypes
*
xlsxTypes
DecodeVMLDrawing
map
[
string
]
*
decodeVmlDrawing
Drawings
sync
.
Map
Path
string
Pkg
sync
.
Map
Relationships
sync
.
Map
SharedStrings
*
xlsxSST
Sheet
sync
.
Map
SheetCount
int
Styles
*
xlsxStyleSheet
Theme
*
decodeTheme
DecodeVMLDrawing
map
[
string
]
*
decodeVmlDrawing
VMLDrawing
map
[
string
]
*
vmlDrawing
VolatileDeps
*
xlsxVolTypes
WorkBook
*
xlsxWorkbook
Relationships
sync
.
Map
Pkg
sync
.
Map
CharsetReader
charsetTranscoderFn
}
// charsetTranscoderFn set user-defined codepage transcoder function for open
...
...
file.go
浏览文件 @
7291e787
...
...
@@ -176,6 +176,7 @@ func (f *File) writeToZip(zw *zip.Writer) error {
f
.
commentsWriter
()
f
.
contentTypesWriter
()
f
.
drawingsWriter
()
f
.
volatileDepsWriter
()
f
.
vmlDrawingWriter
()
f
.
workBookWriter
()
f
.
workSheetWriter
()
...
...
table.go
浏览文件 @
7291e787
...
...
@@ -292,7 +292,7 @@ func (f *File) setTableColumns(sheet string, showHeaderRow bool, x1, y1, x2 int,
}
header
=
append
(
header
,
name
)
if
column
:=
getTableColumn
(
name
);
column
!=
nil
{
column
.
ID
,
column
.
DataDxfID
=
idx
,
0
column
.
ID
,
column
.
DataDxfID
,
column
.
QueryTableFieldID
=
idx
,
0
,
0
tableColumns
=
append
(
tableColumns
,
column
)
continue
}
...
...
templates.go
浏览文件 @
7291e787
...
...
@@ -218,16 +218,17 @@ const (
)
const
(
defaultTempFileSST
=
"sharedStrings"
defaultXMLPathCalcChain
=
"xl/calcChain.xml"
defaultXMLPathContentTypes
=
"[Content_Types].xml"
defaultXMLPathDocPropsApp
=
"docProps/app.xml"
defaultXMLPathDocPropsCore
=
"docProps/core.xml"
defaultXMLPathCalcChain
=
"xl/calcChain.xml"
defaultXMLPathSharedStrings
=
"xl/sharedStrings.xml"
defaultXMLPathStyles
=
"xl/styles.xml"
defaultXMLPathTheme
=
"xl/theme/theme1.xml"
defaultXMLPathVolatileDeps
=
"xl/volatileDependencies.xml"
defaultXMLPathWorkbook
=
"xl/workbook.xml"
defaultXMLPathWorkbookRels
=
"xl/_rels/workbook.xml.rels"
defaultTempFileSST
=
"sharedStrings"
)
// IndexedColorMapping is the table of default mappings from indexed color value
...
...
xmlCalcChain.go
浏览文件 @
7291e787
...
...
@@ -11,10 +11,15 @@
package
excelize
import
"encoding/xml"
import
(
"encoding/xml"
"sync"
)
// xlsxCalcChain directly maps the calcChain element. This element represents the root of the calculation chain.
// xlsxCalcChain directly maps the calcChain element. This element represents
// the root of the calculation chain.
type
xlsxCalcChain
struct
{
mu
sync
.
Mutex
XMLName
xml
.
Name
`xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main calcChain"`
C
[]
xlsxCalcChainC
`xml:"c"`
}
...
...
@@ -82,3 +87,43 @@ type xlsxCalcChainC struct {
T
bool
`xml:"t,attr,omitempty"`
A
bool
`xml:"a,attr,omitempty"`
}
// xlsxVolTypes maps the volatileDependencies part provides a cache of data that
// supports Real Time Data (RTD) and CUBE functions in the workbook.
type
xlsxVolTypes
struct
{
mu
sync
.
Mutex
XMLName
xml
.
Name
`xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main volTypes"`
VolType
[]
xlsxVolType
`xml:"volType"`
ExtLst
*
xlsxExtLst
`xml:"extLst"`
}
// xlsxVolType represents dependency information for a specific type of external
// data server.
type
xlsxVolType
struct
{
Type
string
`xml:"type,attr"`
Main
[]
xlsxVolMain
`xml:"main"`
}
// xlsxVolMain represents dependency information for all topics within a
// volatile dependency type that share the same first string or function
// argument.
type
xlsxVolMain
struct
{
First
string
`xml:"first,attr"`
Tp
[]
xlsxVolTopic
`xml:"tp"`
}
// xlsxVolTopic represents dependency information for all topics within a
// volatile dependency type that share the same first string or argument.
type
xlsxVolTopic
struct
{
T
string
`xml:"t,attr,omitempty"`
V
string
`xml:"v"`
Stp
[]
string
`xml:"stp"`
Tr
[]
xlsxVolTopicRef
`xml:"tr"`
}
// xlsxVolTopicRef represents the reference to a cell that depends on this
// topic. Each topic can have one or more cells dependencies.
type
xlsxVolTopicRef
struct
{
R
string
`xml:"r,attr"`
S
int
`xml:"s,attr"`
}
xmlTable.go
浏览文件 @
7291e787
...
...
@@ -37,12 +37,12 @@ type xlsxTable struct {
DataDxfID
int
`xml:"dataDxfId,attr,omitempty"`
TotalsRowDxfID
int
`xml:"totalsRowDxfId,attr,omitempty"`
HeaderRowBorderDxfID
int
`xml:"headerRowBorderDxfId,attr,omitempty"`
TableBorderDxfI
d
int
`xml:"tableBorderDxfId,attr,omitempty"`
TotalsRowBorderDxfI
d
int
`xml:"totalsRowBorderDxfId,attr,omitempty"`
TableBorderDxfI
D
int
`xml:"tableBorderDxfId,attr,omitempty"`
TotalsRowBorderDxfI
D
int
`xml:"totalsRowBorderDxfId,attr,omitempty"`
HeaderRowCellStyle
string
`xml:"headerRowCellStyle,attr,omitempty"`
DataCellStyle
string
`xml:"dataCellStyle,attr,omitempty"`
TotalsRowCellStyle
string
`xml:"totalsRowCellStyle,attr,omitempty"`
ConnectionI
d
int
`xml:"connectionId,attr,omitempty"`
ConnectionI
D
int
`xml:"connectionId,attr,omitempty"`
AutoFilter
*
xlsxAutoFilter
`xml:"autoFilter"`
TableColumns
*
xlsxTableColumns
`xml:"tableColumns"`
TableStyleInfo
*
xlsxTableStyleInfo
`xml:"tableStyleInfo"`
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录