Performance improvements
This commit is contained in:
parent
59f6af21a3
commit
0f2a905324
|
@ -80,9 +80,10 @@ func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
|
||||||
// adjustRowDimensions provides a function to update row dimensions when
|
// adjustRowDimensions provides a function to update row dimensions when
|
||||||
// inserting or deleting rows or columns.
|
// inserting or deleting rows or columns.
|
||||||
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
|
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
|
||||||
for i, r := range xlsx.SheetData.Row {
|
for i := range xlsx.SheetData.Row {
|
||||||
|
r := &xlsx.SheetData.Row[i]
|
||||||
if newRow := r.R + offset; r.R >= row && newRow > 0 {
|
if newRow := r.R + offset; r.R >= row && newRow > 0 {
|
||||||
f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow)
|
f.ajustSingleRowDimensions(r, newRow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1288,7 +1288,7 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
|
||||||
}
|
}
|
||||||
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
|
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
|
||||||
deTwoCellAnchor = new(decodeTwoCellAnchor)
|
deTwoCellAnchor = new(decodeTwoCellAnchor)
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
|
if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
|
||||||
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
|
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
|
||||||
err = fmt.Errorf("xml decode error: %s", err)
|
err = fmt.Errorf("xml decode error: %s", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -234,10 +234,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
|
||||||
// replaceRelationshipsNameSpaceBytes provides a function to replace
|
// replaceRelationshipsNameSpaceBytes provides a function to replace
|
||||||
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
|
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
|
||||||
func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
|
func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
|
||||||
var oldXmlns = []byte(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
var oldXmlns = stringToBytes(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||||
var newXmlns = []byte(templateNamespaceIDMap)
|
var newXmlns = []byte(templateNamespaceIDMap)
|
||||||
contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1)
|
return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
|
||||||
return contentMarshal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
||||||
|
|
42
lib.go
42
lib.go
|
@ -17,6 +17,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadZipReader can be used to read an XLSX in memory without touching the
|
// ReadZipReader can be used to read an XLSX in memory without touching the
|
||||||
|
@ -103,7 +104,7 @@ func JoinCellName(col string, row int) (string, error) {
|
||||||
if row < 1 {
|
if row < 1 {
|
||||||
return "", newInvalidRowNumberError(row)
|
return "", newInvalidRowNumberError(row)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s%d", normCol, row), nil
|
return normCol + strconv.Itoa(row), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ColumnNameToNumber provides a function to convert Excel sheet column name
|
// ColumnNameToNumber provides a function to convert Excel sheet column name
|
||||||
|
@ -190,6 +191,7 @@ func CoordinatesToCellName(col, row int) (string, error) {
|
||||||
}
|
}
|
||||||
colname, err := ColumnNumberToName(col)
|
colname, err := ColumnNumberToName(col)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Error should never happens here.
|
||||||
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
|
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s%d", colname, row), nil
|
return fmt.Sprintf("%s%d", colname, row), nil
|
||||||
|
@ -235,11 +237,47 @@ func namespaceStrictToTransitional(content []byte) []byte {
|
||||||
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
|
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
|
||||||
}
|
}
|
||||||
for s, n := range namespaceTranslationDic {
|
for s, n := range namespaceTranslationDic {
|
||||||
content = bytes.Replace(content, []byte(s), []byte(n), -1)
|
content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
|
||||||
}
|
}
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stringToBytes cast a string to bytes pointer and assign the value of this
|
||||||
|
// pointer.
|
||||||
|
func stringToBytes(s string) []byte {
|
||||||
|
return *(*[]byte)(unsafe.Pointer(&s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytesReplace replace old bytes with given new.
|
||||||
|
func bytesReplace(s, old, new []byte, n int) []byte {
|
||||||
|
if n == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(old) < len(new) {
|
||||||
|
return bytes.Replace(s, old, new, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 0 {
|
||||||
|
n = len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wid, i, j, w int
|
||||||
|
for i, j = 0, 0; i < len(s) && j < n; j++ {
|
||||||
|
wid = bytes.Index(s[i:], old)
|
||||||
|
if wid < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
w += copy(s[w:], s[i:i+wid])
|
||||||
|
w += copy(s[w:], new)
|
||||||
|
i += wid + len(old)
|
||||||
|
}
|
||||||
|
|
||||||
|
w += copy(s[w:], s[i:])
|
||||||
|
return s[0:w]
|
||||||
|
}
|
||||||
|
|
||||||
// genSheetPasswd provides a method to generate password for worksheet
|
// genSheetPasswd provides a method to generate password for worksheet
|
||||||
// protection by given plaintext. When an Excel sheet is being protected with
|
// protection by given plaintext. When an Excel sheet is being protected with
|
||||||
// a password, a 16-bit (two byte) long hash is generated. To verify a
|
// a password, a 16-bit (two byte) long hash is generated. To verify a
|
||||||
|
|
|
@ -203,3 +203,8 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBytesReplace(t *testing.T) {
|
||||||
|
s := []byte{0x01}
|
||||||
|
assert.EqualValues(t, s, bytesReplace(s, []byte{}, []byte{}, 0))
|
||||||
|
}
|
||||||
|
|
|
@ -510,7 +510,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
|
||||||
err = nil
|
err = nil
|
||||||
for _, anchor := range deWsDr.TwoCellAnchor {
|
for _, anchor := range deWsDr.TwoCellAnchor {
|
||||||
deTwoCellAnchor = new(decodeTwoCellAnchor)
|
deTwoCellAnchor = new(decodeTwoCellAnchor)
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
|
if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
|
||||||
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
|
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
|
||||||
err = fmt.Errorf("xml decode error: %s", err)
|
err = fmt.Errorf("xml decode error: %s", err)
|
||||||
return
|
return
|
||||||
|
|
14
rows.go
14
rows.go
|
@ -424,14 +424,16 @@ func (f *File) RemoveRow(sheet string, row int) error {
|
||||||
if row > len(xlsx.SheetData.Row) {
|
if row > len(xlsx.SheetData.Row) {
|
||||||
return f.adjustHelper(sheet, rows, row, -1)
|
return f.adjustHelper(sheet, rows, row, -1)
|
||||||
}
|
}
|
||||||
for rowIdx := range xlsx.SheetData.Row {
|
keep := 0
|
||||||
if xlsx.SheetData.Row[rowIdx].R == row {
|
for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
|
||||||
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx],
|
v := &xlsx.SheetData.Row[rowIdx]
|
||||||
xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1]
|
if v.R != row {
|
||||||
return f.adjustHelper(sheet, rows, row, -1)
|
xlsx.SheetData.Row[keep] = *v
|
||||||
|
keep++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
|
||||||
|
return f.adjustHelper(sheet, rows, row, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertRow provides a function to insert a new row after given Excel row
|
// InsertRow provides a function to insert a new row after given Excel row
|
||||||
|
|
6
sheet.go
6
sheet.go
|
@ -206,9 +206,9 @@ func (f *File) setAppXML() {
|
||||||
// requirements about the structure of the input XML. This function is a
|
// requirements about the structure of the input XML. This function is a
|
||||||
// horrible hack to fix that after the XML marshalling is completed.
|
// horrible hack to fix that after the XML marshalling is completed.
|
||||||
func replaceRelationshipsBytes(content []byte) []byte {
|
func replaceRelationshipsBytes(content []byte) []byte {
|
||||||
oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
|
oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
|
||||||
newXmlns := []byte("r")
|
newXmlns := stringToBytes("r")
|
||||||
return bytes.Replace(content, oldXmlns, newXmlns, -1)
|
return bytesReplace(content, oldXmlns, newXmlns, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetActiveSheet provides function to set default active worksheet of XLSX by
|
// SetActiveSheet provides function to set default active worksheet of XLSX by
|
||||||
|
|
|
@ -516,7 +516,7 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
|
||||||
for idx, ext = range decodeExtLst.Ext {
|
for idx, ext = range decodeExtLst.Ext {
|
||||||
if ext.URI == ExtURISparklineGroups {
|
if ext.URI == ExtURISparklineGroups {
|
||||||
decodeSparklineGroups = new(decodeX14SparklineGroups)
|
decodeSparklineGroups = new(decodeX14SparklineGroups)
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader([]byte(ext.Content))).
|
if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes(ext.Content))).
|
||||||
Decode(decodeSparklineGroups); err != nil && err != io.EOF {
|
Decode(decodeSparklineGroups); err != nil && err != io.EOF {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,7 +365,7 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
|
||||||
buf.WriteString(`>`)
|
buf.WriteString(`>`)
|
||||||
if c.V != "" {
|
if c.V != "" {
|
||||||
buf.WriteString(`<v>`)
|
buf.WriteString(`<v>`)
|
||||||
xml.EscapeText(buf, []byte(c.V))
|
xml.EscapeText(buf, stringToBytes(c.V))
|
||||||
buf.WriteString(`</v>`)
|
buf.WriteString(`</v>`)
|
||||||
}
|
}
|
||||||
buf.WriteString(`</c>`)
|
buf.WriteString(`</c>`)
|
||||||
|
|
Loading…
Reference in New Issue