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
|
||||
// inserting or deleting rows or columns.
|
||||
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 {
|
||||
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++ {
|
||||
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 {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
|
|
@ -234,10 +234,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
|
|||
// replaceRelationshipsNameSpaceBytes provides a function to replace
|
||||
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
|
||||
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)
|
||||
contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1)
|
||||
return contentMarshal
|
||||
return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
|
||||
}
|
||||
|
||||
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
||||
|
|
42
lib.go
42
lib.go
|
@ -17,6 +17,7 @@ import (
|
|||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
|
@ -190,6 +191,7 @@ func CoordinatesToCellName(col, row int) (string, error) {
|
|||
}
|
||||
colname, err := ColumnNumberToName(col)
|
||||
if err != nil {
|
||||
// Error should never happens here.
|
||||
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
|
||||
}
|
||||
return fmt.Sprintf("%s%d", colname, row), nil
|
||||
|
@ -235,11 +237,47 @@ func namespaceStrictToTransitional(content []byte) []byte {
|
|||
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
|
||||
}
|
||||
for s, n := range namespaceTranslationDic {
|
||||
content = bytes.Replace(content, []byte(s), []byte(n), -1)
|
||||
content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
|
||||
}
|
||||
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
|
||||
// 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
|
||||
|
|
|
@ -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
|
||||
for _, anchor := range deWsDr.TwoCellAnchor {
|
||||
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 {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
|
16
rows.go
16
rows.go
|
@ -424,15 +424,17 @@ func (f *File) RemoveRow(sheet string, row int) error {
|
|||
if row > len(xlsx.SheetData.Row) {
|
||||
return f.adjustHelper(sheet, rows, row, -1)
|
||||
}
|
||||
for rowIdx := range xlsx.SheetData.Row {
|
||||
if xlsx.SheetData.Row[rowIdx].R == row {
|
||||
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx],
|
||||
xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1]
|
||||
keep := 0
|
||||
for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
|
||||
v := &xlsx.SheetData.Row[rowIdx]
|
||||
if v.R != row {
|
||||
xlsx.SheetData.Row[keep] = *v
|
||||
keep++
|
||||
}
|
||||
}
|
||||
xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
|
||||
return f.adjustHelper(sheet, rows, row, -1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertRow provides a function to insert a new row after given Excel row
|
||||
// number starting from 1. For example, create a new row before row 3 in
|
||||
|
|
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
|
||||
// horrible hack to fix that after the XML marshalling is completed.
|
||||
func replaceRelationshipsBytes(content []byte) []byte {
|
||||
oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
|
||||
newXmlns := []byte("r")
|
||||
return bytes.Replace(content, oldXmlns, newXmlns, -1)
|
||||
oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
|
||||
newXmlns := stringToBytes("r")
|
||||
return bytesReplace(content, oldXmlns, newXmlns, -1)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if ext.URI == ExtURISparklineGroups {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue