forked from p30928647/excelize
This closes #1096, memory usage optimization and another 4 changes
- Unzip shared string table to system temporary file when large inner XML, reduce memory usage about 70% - Remove unnecessary exported variable `XMLHeader`, we can using `encoding/xml` package's `xml.Header` instead of it - Using constant instead of inline text for default XML path - Rename exported option field `WorksheetUnzipMemLimit` to `UnzipXMLSizeLimit` - Unit test and documentation updated
This commit is contained in:
parent
6b1e592cbc
commit
89b85934f6
|
@ -25,7 +25,7 @@ func (f *File) calcChainReader() *xlsxCalcChain {
|
|||
|
||||
if f.CalcChain == nil {
|
||||
f.CalcChain = new(xlsxCalcChain)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathCalcChain)))).
|
||||
Decode(f.CalcChain); err != nil && err != io.EOF {
|
||||
log.Printf("xml decode error: %s", err)
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func (f *File) calcChainReader() *xlsxCalcChain {
|
|||
func (f *File) calcChainWriter() {
|
||||
if f.CalcChain != nil && f.CalcChain.C != nil {
|
||||
output, _ := xml.Marshal(f.CalcChain)
|
||||
f.saveFileList("xl/calcChain.xml", output)
|
||||
f.saveFileList(dafaultXMLPathCalcChain, output)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ func (f *File) deleteCalcChain(index int, axis string) {
|
|||
}
|
||||
if len(calc.C) == 0 {
|
||||
f.CalcChain = nil
|
||||
f.Pkg.Delete("xl/calcChain.xml")
|
||||
f.Pkg.Delete(dafaultXMLPathCalcChain)
|
||||
content := f.contentTypesReader()
|
||||
content.Lock()
|
||||
defer content.Unlock()
|
||||
|
|
|
@ -5,7 +5,7 @@ import "testing"
|
|||
func TestCalcChainReader(t *testing.T) {
|
||||
f := NewFile()
|
||||
f.CalcChain = nil
|
||||
f.Pkg.Store("xl/calcChain.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(dafaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
||||
f.calcChainReader()
|
||||
}
|
||||
|
||||
|
|
90
cell.go
90
cell.go
|
@ -14,6 +14,7 @@ package excelize
|
|||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -348,28 +349,49 @@ func (f *File) SetCellStr(sheet, axis, value string) error {
|
|||
ws.Lock()
|
||||
defer ws.Unlock()
|
||||
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
|
||||
cellData.T, cellData.V = f.setCellString(value)
|
||||
cellData.T, cellData.V, err = f.setCellString(value)
|
||||
return err
|
||||
}
|
||||
|
||||
// setCellString provides a function to set string type to shared string
|
||||
// table.
|
||||
func (f *File) setCellString(value string) (t string, v string) {
|
||||
func (f *File) setCellString(value string) (t, v string, err error) {
|
||||
if len(value) > TotalCellChars {
|
||||
value = value[:TotalCellChars]
|
||||
}
|
||||
t = "s"
|
||||
v = strconv.Itoa(f.setSharedString(value))
|
||||
var si int
|
||||
if si, err = f.setSharedString(value); err != nil {
|
||||
return
|
||||
}
|
||||
v = strconv.Itoa(si)
|
||||
return
|
||||
}
|
||||
|
||||
// sharedStringsLoader load shared string table from system temporary file to
|
||||
// memory, and reset shared string table for reader.
|
||||
func (f *File) sharedStringsLoader() (err error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
if path, ok := f.tempFiles.Load(dafaultXMLPathSharedStrings); ok {
|
||||
f.Pkg.Store(dafaultXMLPathSharedStrings, f.readBytes(dafaultXMLPathSharedStrings))
|
||||
f.tempFiles.Delete(dafaultXMLPathSharedStrings)
|
||||
err = os.Remove(path.(string))
|
||||
f.SharedStrings, f.sharedStringItemMap = nil, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// setSharedString provides a function to add string to the share string table.
|
||||
func (f *File) setSharedString(val string) int {
|
||||
func (f *File) setSharedString(val string) (int, error) {
|
||||
if err := f.sharedStringsLoader(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sst := f.sharedStringsReader()
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
if i, ok := f.sharedStringsMap[val]; ok {
|
||||
return i
|
||||
return i, nil
|
||||
}
|
||||
sst.Count++
|
||||
sst.UniqueCount++
|
||||
|
@ -377,7 +399,7 @@ func (f *File) setSharedString(val string) int {
|
|||
_, val, t.Space = setCellStr(val)
|
||||
sst.SI = append(sst.SI, xlsxSI{T: &t})
|
||||
f.sharedStringsMap[val] = sst.UniqueCount - 1
|
||||
return sst.UniqueCount - 1
|
||||
return sst.UniqueCount - 1, nil
|
||||
}
|
||||
|
||||
// setCellStr provides a function to set string type to cell.
|
||||
|
@ -762,6 +784,34 @@ func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err erro
|
|||
return
|
||||
}
|
||||
|
||||
// newRpr create run properties for the rich text by given font format.
|
||||
func newRpr(fnt *Font) *xlsxRPr {
|
||||
rpr := xlsxRPr{}
|
||||
trueVal := ""
|
||||
if fnt.Bold {
|
||||
rpr.B = &trueVal
|
||||
}
|
||||
if fnt.Italic {
|
||||
rpr.I = &trueVal
|
||||
}
|
||||
if fnt.Strike {
|
||||
rpr.Strike = &trueVal
|
||||
}
|
||||
if fnt.Underline != "" {
|
||||
rpr.U = &attrValString{Val: &fnt.Underline}
|
||||
}
|
||||
if fnt.Family != "" {
|
||||
rpr.RFont = &attrValString{Val: &fnt.Family}
|
||||
}
|
||||
if fnt.Size > 0.0 {
|
||||
rpr.Sz = &attrValFloat{Val: &fnt.Size}
|
||||
}
|
||||
if fnt.Color != "" {
|
||||
rpr.Color = &xlsxColor{RGB: getPaletteColor(fnt.Color)}
|
||||
}
|
||||
return &rpr
|
||||
}
|
||||
|
||||
// SetCellRichText provides a function to set cell with rich text by given
|
||||
// worksheet. For example, set rich text on the A1 cell of the worksheet named
|
||||
// Sheet1:
|
||||
|
@ -875,6 +925,9 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.sharedStringsLoader(); err != nil {
|
||||
return err
|
||||
}
|
||||
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
|
||||
si := xlsxSI{}
|
||||
sst := f.sharedStringsReader()
|
||||
|
@ -889,30 +942,7 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
|
|||
_, run.T.Val, run.T.Space = setCellStr(textRun.Text)
|
||||
fnt := textRun.Font
|
||||
if fnt != nil {
|
||||
rpr := xlsxRPr{}
|
||||
trueVal := ""
|
||||
if fnt.Bold {
|
||||
rpr.B = &trueVal
|
||||
}
|
||||
if fnt.Italic {
|
||||
rpr.I = &trueVal
|
||||
}
|
||||
if fnt.Strike {
|
||||
rpr.Strike = &trueVal
|
||||
}
|
||||
if fnt.Underline != "" {
|
||||
rpr.U = &attrValString{Val: &fnt.Underline}
|
||||
}
|
||||
if fnt.Family != "" {
|
||||
rpr.RFont = &attrValString{Val: &fnt.Family}
|
||||
}
|
||||
if fnt.Size > 0.0 {
|
||||
rpr.Sz = &attrValFloat{Val: &fnt.Size}
|
||||
}
|
||||
if fnt.Color != "" {
|
||||
rpr.Color = &xlsxColor{RGB: getPaletteColor(fnt.Color)}
|
||||
}
|
||||
run.RPr = &rpr
|
||||
run.RPr = newRpr(fnt)
|
||||
}
|
||||
textRuns = append(textRuns, run)
|
||||
}
|
||||
|
|
17
cell_test.go
17
cell_test.go
|
@ -649,3 +649,20 @@ func TestFormattedValue2(t *testing.T) {
|
|||
v = f.formattedValue(1, "43528", false)
|
||||
assert.Equal(t, "43528", v)
|
||||
}
|
||||
|
||||
func TestSharedStringsError(t *testing.T) {
|
||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipXMLSizeLimit: 128})
|
||||
assert.NoError(t, err)
|
||||
f.tempFiles.Store(dafaultXMLPathSharedStrings, "")
|
||||
assert.Equal(t, "1", f.getFromStringItemMap(1))
|
||||
|
||||
// Test reload the file error on set cell cell and rich text. The error message was different between macOS and Windows.
|
||||
err = f.SetCellValue("Sheet1", "A19", "A19")
|
||||
assert.Error(t, err)
|
||||
|
||||
f.tempFiles.Store(dafaultXMLPathSharedStrings, "")
|
||||
err = f.SetCellRichText("Sheet1", "A19", []RichTextRun{})
|
||||
assert.Error(t, err)
|
||||
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
|
20
docProps.go
20
docProps.go
|
@ -27,8 +27,8 @@ import (
|
|||
// Application | The name of the application that created this document.
|
||||
// |
|
||||
// ScaleCrop | Indicates the display mode of the document thumbnail. Set this element
|
||||
// | to TRUE to enable scaling of the document thumbnail to the display. Set
|
||||
// | this element to FALSE to enable cropping of the document thumbnail to
|
||||
// | to 'true' to enable scaling of the document thumbnail to the display. Set
|
||||
// | this element to 'false' to enable cropping of the document thumbnail to
|
||||
// | show only sections that will fit the display.
|
||||
// |
|
||||
// DocSecurity | Security level of a document as a numeric value. Document security is
|
||||
|
@ -41,8 +41,8 @@ import (
|
|||
// Company | The name of a company associated with the document.
|
||||
// |
|
||||
// LinksUpToDate | Indicates whether hyperlinks in a document are up-to-date. Set this
|
||||
// | element to TRUE to indicate that hyperlinks are updated. Set this
|
||||
// | element to FALSE to indicate that hyperlinks are outdated.
|
||||
// | element to 'true' to indicate that hyperlinks are updated. Set this
|
||||
// | element to 'false' to indicate that hyperlinks are outdated.
|
||||
// |
|
||||
// HyperlinksChanged | Specifies that one or more hyperlinks in this part were updated
|
||||
// | exclusively in this part by a producer. The next producer to open this
|
||||
|
@ -75,7 +75,7 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
|
|||
field string
|
||||
)
|
||||
app = new(xlsxProperties)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/app.xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsApp)))).
|
||||
Decode(app); err != nil && err != io.EOF {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
@ -95,14 +95,14 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
|
|||
}
|
||||
app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value
|
||||
output, err = xml.Marshal(app)
|
||||
f.saveFileList("docProps/app.xml", output)
|
||||
f.saveFileList(dafaultXMLPathDocPropsApp, output)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAppProps provides a function to get document application properties.
|
||||
func (f *File) GetAppProps() (ret *AppProperties, err error) {
|
||||
var app = new(xlsxProperties)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/app.xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsApp)))).
|
||||
Decode(app); err != nil && err != io.EOF {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
@ -181,7 +181,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
|
|||
)
|
||||
|
||||
core = new(decodeCoreProperties)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsCore)))).
|
||||
Decode(core); err != nil && err != io.EOF {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
@ -223,7 +223,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
|
|||
newProps.Modified.Text = docProperties.Modified
|
||||
}
|
||||
output, err = xml.Marshal(newProps)
|
||||
f.saveFileList("docProps/core.xml", output)
|
||||
f.saveFileList(dafaultXMLPathDocPropsCore, output)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
|
|||
func (f *File) GetDocProps() (ret *DocProperties, err error) {
|
||||
var core = new(decodeCoreProperties)
|
||||
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsCore)))).
|
||||
Decode(core); err != nil && err != io.EOF {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
|
|
@ -35,13 +35,13 @@ func TestSetAppProps(t *testing.T) {
|
|||
AppVersion: "16.0000",
|
||||
}))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetAppProps.xlsx")))
|
||||
f.Pkg.Store("docProps/app.xml", nil)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsApp, nil)
|
||||
assert.NoError(t, f.SetAppProps(&AppProperties{}))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test unsupported charset
|
||||
f = NewFile()
|
||||
f.Pkg.Store("docProps/app.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.SetAppProps(&AppProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
|
@ -53,14 +53,14 @@ func TestGetAppProps(t *testing.T) {
|
|||
props, err := f.GetAppProps()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, props.Application, "Microsoft Macintosh Excel")
|
||||
f.Pkg.Store("docProps/app.xml", nil)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsApp, nil)
|
||||
_, err = f.GetAppProps()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test unsupported charset
|
||||
f = NewFile()
|
||||
f.Pkg.Store("docProps/app.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
|
||||
_, err = f.GetAppProps()
|
||||
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
@ -87,13 +87,13 @@ func TestSetDocProps(t *testing.T) {
|
|||
Version: "1.0.0",
|
||||
}))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDocProps.xlsx")))
|
||||
f.Pkg.Store("docProps/core.xml", nil)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsCore, nil)
|
||||
assert.NoError(t, f.SetDocProps(&DocProperties{}))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test unsupported charset
|
||||
f = NewFile()
|
||||
f.Pkg.Store("docProps/core.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.SetDocProps(&DocProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
|
@ -105,14 +105,14 @@ func TestGetDocProps(t *testing.T) {
|
|||
props, err := f.GetDocProps()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, props.Creator, "Microsoft Office User")
|
||||
f.Pkg.Store("docProps/core.xml", nil)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsCore, nil)
|
||||
_, err = f.GetDocProps()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test unsupported charset
|
||||
f = NewFile()
|
||||
f.Pkg.Store("docProps/core.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
|
||||
_, err = f.GetDocProps()
|
||||
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
|
|
@ -143,8 +143,8 @@ var (
|
|||
// characters length that exceeds the limit.
|
||||
ErrCellCharsLength = fmt.Errorf("cell value must be 0-%d characters", TotalCellChars)
|
||||
// ErrOptionsUnzipSizeLimit defined the error message for receiving
|
||||
// invalid UnzipSizeLimit and WorksheetUnzipMemLimit.
|
||||
ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to WorksheetUnzipMemLimit")
|
||||
// invalid UnzipSizeLimit and UnzipXMLSizeLimit.
|
||||
ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to UnzipXMLSizeLimit")
|
||||
// ErrSave defined the error message for saving file.
|
||||
ErrSave = errors.New("no path defined for file, consider File.WriteTo or File.Write")
|
||||
// ErrAttrValBool defined the error message on marshal and unmarshal
|
||||
|
|
82
excelize.go
82
excelize.go
|
@ -32,29 +32,30 @@ import (
|
|||
// File define a populated spreadsheet file struct.
|
||||
type File struct {
|
||||
sync.Mutex
|
||||
options *Options
|
||||
xmlAttr map[string][]xml.Attr
|
||||
checked map[string]bool
|
||||
sheetMap map[string]string
|
||||
streams map[string]*StreamWriter
|
||||
tempFiles sync.Map
|
||||
CalcChain *xlsxCalcChain
|
||||
Comments map[string]*xlsxComments
|
||||
ContentTypes *xlsxTypes
|
||||
Drawings sync.Map
|
||||
Path string
|
||||
SharedStrings *xlsxSST
|
||||
sharedStringsMap map[string]int
|
||||
Sheet sync.Map
|
||||
SheetCount int
|
||||
Styles *xlsxStyleSheet
|
||||
Theme *xlsxTheme
|
||||
DecodeVMLDrawing map[string]*decodeVmlDrawing
|
||||
VMLDrawing map[string]*vmlDrawing
|
||||
WorkBook *xlsxWorkbook
|
||||
Relationships sync.Map
|
||||
Pkg sync.Map
|
||||
CharsetReader charsetTranscoderFn
|
||||
options *Options
|
||||
xmlAttr map[string][]xml.Attr
|
||||
checked map[string]bool
|
||||
sheetMap map[string]string
|
||||
streams map[string]*StreamWriter
|
||||
tempFiles sync.Map
|
||||
CalcChain *xlsxCalcChain
|
||||
Comments map[string]*xlsxComments
|
||||
ContentTypes *xlsxTypes
|
||||
Drawings sync.Map
|
||||
Path string
|
||||
SharedStrings *xlsxSST
|
||||
sharedStringsMap map[string]int
|
||||
sharedStringItemMap *sync.Map
|
||||
Sheet sync.Map
|
||||
SheetCount int
|
||||
Styles *xlsxStyleSheet
|
||||
Theme *xlsxTheme
|
||||
DecodeVMLDrawing map[string]*decodeVmlDrawing
|
||||
VMLDrawing map[string]*vmlDrawing
|
||||
WorkBook *xlsxWorkbook
|
||||
Relationships sync.Map
|
||||
Pkg sync.Map
|
||||
CharsetReader charsetTranscoderFn
|
||||
}
|
||||
|
||||
type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
|
||||
|
@ -68,17 +69,18 @@ type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, e
|
|||
//
|
||||
// UnzipSizeLimit specifies the unzip size limit in bytes on open the
|
||||
// spreadsheet, this value should be greater than or equal to
|
||||
// WorksheetUnzipMemLimit, the default size limit is 16GB.
|
||||
// UnzipXMLSizeLimit, the default size limit is 16GB.
|
||||
//
|
||||
// WorksheetUnzipMemLimit specifies the memory limit on unzipping worksheet in
|
||||
// bytes, worksheet XML will be extracted to system temporary directory when
|
||||
// the file size is over this value, this value should be less than or equal
|
||||
// to UnzipSizeLimit, the default value is 16MB.
|
||||
// UnzipXMLSizeLimit specifies the memory limit on unzipping worksheet and
|
||||
// shared string table in bytes, worksheet XML will be extracted to system
|
||||
// temporary directory when the file size is over this value, this value
|
||||
// should be less than or equal to UnzipSizeLimit, the default value is
|
||||
// 16MB.
|
||||
type Options struct {
|
||||
Password string
|
||||
RawCellValue bool
|
||||
UnzipSizeLimit int64
|
||||
WorksheetUnzipMemLimit int64
|
||||
Password string
|
||||
RawCellValue bool
|
||||
UnzipSizeLimit int64
|
||||
UnzipXMLSizeLimit int64
|
||||
}
|
||||
|
||||
// OpenFile take the name of an spreadsheet file and returns a populated
|
||||
|
@ -111,7 +113,7 @@ func OpenFile(filename string, opt ...Options) (*File, error) {
|
|||
// newFile is object builder
|
||||
func newFile() *File {
|
||||
return &File{
|
||||
options: &Options{UnzipSizeLimit: UnzipSizeLimit, WorksheetUnzipMemLimit: StreamChunkSize},
|
||||
options: &Options{UnzipSizeLimit: UnzipSizeLimit, UnzipXMLSizeLimit: StreamChunkSize},
|
||||
xmlAttr: make(map[string][]xml.Attr),
|
||||
checked: make(map[string]bool),
|
||||
sheetMap: make(map[string]string),
|
||||
|
@ -138,17 +140,17 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
|
|||
f.options = parseOptions(opt...)
|
||||
if f.options.UnzipSizeLimit == 0 {
|
||||
f.options.UnzipSizeLimit = UnzipSizeLimit
|
||||
if f.options.WorksheetUnzipMemLimit > f.options.UnzipSizeLimit {
|
||||
f.options.UnzipSizeLimit = f.options.WorksheetUnzipMemLimit
|
||||
if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {
|
||||
f.options.UnzipSizeLimit = f.options.UnzipXMLSizeLimit
|
||||
}
|
||||
}
|
||||
if f.options.WorksheetUnzipMemLimit == 0 {
|
||||
f.options.WorksheetUnzipMemLimit = StreamChunkSize
|
||||
if f.options.UnzipSizeLimit < f.options.WorksheetUnzipMemLimit {
|
||||
f.options.WorksheetUnzipMemLimit = f.options.UnzipSizeLimit
|
||||
if f.options.UnzipXMLSizeLimit == 0 {
|
||||
f.options.UnzipXMLSizeLimit = StreamChunkSize
|
||||
if f.options.UnzipSizeLimit < f.options.UnzipXMLSizeLimit {
|
||||
f.options.UnzipXMLSizeLimit = f.options.UnzipSizeLimit
|
||||
}
|
||||
}
|
||||
if f.options.WorksheetUnzipMemLimit > f.options.UnzipSizeLimit {
|
||||
if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {
|
||||
return nil, ErrOptionsUnzipSizeLimit
|
||||
}
|
||||
if bytes.Contains(b, oleIdentifier) {
|
||||
|
|
|
@ -201,7 +201,7 @@ func TestCharsetTranscoder(t *testing.T) {
|
|||
func TestOpenReader(t *testing.T) {
|
||||
_, err := OpenReader(strings.NewReader(""))
|
||||
assert.EqualError(t, err, "zip: not a valid zip file")
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password", WorksheetUnzipMemLimit: UnzipSizeLimit + 1})
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password", UnzipXMLSizeLimit: UnzipSizeLimit + 1})
|
||||
assert.EqualError(t, err, "decrypted file failed")
|
||||
|
||||
// Test open spreadsheet with unzip size limit.
|
||||
|
@ -225,7 +225,7 @@ func TestOpenReader(t *testing.T) {
|
|||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test open spreadsheet with invalid optioins.
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, WorksheetUnzipMemLimit: 2})
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, UnzipXMLSizeLimit: 2})
|
||||
assert.EqualError(t, err, ErrOptionsUnzipSizeLimit.Error())
|
||||
|
||||
// Test unexpected EOF.
|
||||
|
@ -1208,7 +1208,7 @@ func TestContentTypesReader(t *testing.T) {
|
|||
// Test unsupported charset.
|
||||
f := NewFile()
|
||||
f.ContentTypes = nil
|
||||
f.Pkg.Store("[Content_Types].xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||
f.contentTypesReader()
|
||||
}
|
||||
|
||||
|
@ -1216,7 +1216,7 @@ func TestWorkbookReader(t *testing.T) {
|
|||
// Test unsupported charset.
|
||||
f := NewFile()
|
||||
f.WorkBook = nil
|
||||
f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||
f.workbookReader()
|
||||
}
|
||||
|
||||
|
|
23
file.go
23
file.go
|
@ -14,6 +14,7 @@ package excelize
|
|||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -27,15 +28,15 @@ import (
|
|||
//
|
||||
func NewFile() *File {
|
||||
f := newFile()
|
||||
f.Pkg.Store("_rels/.rels", []byte(XMLHeader+templateRels))
|
||||
f.Pkg.Store("docProps/app.xml", []byte(XMLHeader+templateDocpropsApp))
|
||||
f.Pkg.Store("docProps/core.xml", []byte(XMLHeader+templateDocpropsCore))
|
||||
f.Pkg.Store("xl/_rels/workbook.xml.rels", []byte(XMLHeader+templateWorkbookRels))
|
||||
f.Pkg.Store("xl/theme/theme1.xml", []byte(XMLHeader+templateTheme))
|
||||
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(XMLHeader+templateSheet))
|
||||
f.Pkg.Store("xl/styles.xml", []byte(XMLHeader+templateStyles))
|
||||
f.Pkg.Store("xl/workbook.xml", []byte(XMLHeader+templateWorkbook))
|
||||
f.Pkg.Store("[Content_Types].xml", []byte(XMLHeader+templateContentTypes))
|
||||
f.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
|
||||
f.Pkg.Store(dafaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))
|
||||
f.Pkg.Store("xl/_rels/workbook.xml.rels", []byte(xml.Header+templateWorkbookRels))
|
||||
f.Pkg.Store("xl/theme/theme1.xml", []byte(xml.Header+templateTheme))
|
||||
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+templateSheet))
|
||||
f.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))
|
||||
f.Pkg.Store(defaultXMLPathWorkbook, []byte(xml.Header+templateWorkbook))
|
||||
f.Pkg.Store(defaultXMLPathContentTypes, []byte(xml.Header+templateContentTypes))
|
||||
f.SheetCount = 1
|
||||
f.CalcChain = f.calcChainReader()
|
||||
f.Comments = make(map[string]*xlsxComments)
|
||||
|
@ -159,6 +160,7 @@ func (f *File) writeToZip(zw *zip.Writer) error {
|
|||
f.workBookWriter()
|
||||
f.workSheetWriter()
|
||||
f.relsWriter()
|
||||
f.sharedStringsLoader()
|
||||
f.sharedStringsWriter()
|
||||
f.styleSheetWriter()
|
||||
|
||||
|
@ -196,6 +198,9 @@ func (f *File) writeToZip(zw *zip.Writer) error {
|
|||
return true
|
||||
})
|
||||
f.tempFiles.Range(func(path, content interface{}) bool {
|
||||
if _, ok := f.Pkg.Load(path); ok {
|
||||
return true
|
||||
}
|
||||
var fi io.Writer
|
||||
fi, err = zw.Create(path.(string))
|
||||
if err != nil {
|
||||
|
|
14
lib.go
14
lib.go
|
@ -30,8 +30,8 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
|||
var (
|
||||
err error
|
||||
docPart = map[string]string{
|
||||
"[content_types].xml": "[Content_Types].xml",
|
||||
"xl/sharedstrings.xml": "xl/sharedStrings.xml",
|
||||
"[content_types].xml": defaultXMLPathContentTypes,
|
||||
"xl/sharedstrings.xml": dafaultXMLPathSharedStrings,
|
||||
}
|
||||
fileList = make(map[string][]byte, len(r.File))
|
||||
worksheets int
|
||||
|
@ -47,9 +47,15 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
|||
if partName, ok := docPart[strings.ToLower(fileName)]; ok {
|
||||
fileName = partName
|
||||
}
|
||||
if strings.EqualFold(fileName, dafaultXMLPathSharedStrings) && fileSize > f.options.UnzipXMLSizeLimit {
|
||||
if tempFile, err := f.unzipToTemp(v); err == nil {
|
||||
f.tempFiles.Store(fileName, tempFile)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(fileName, "xl/worksheets/sheet") {
|
||||
worksheets++
|
||||
if fileSize > f.options.WorksheetUnzipMemLimit && !v.FileInfo().IsDir() {
|
||||
if fileSize > f.options.UnzipXMLSizeLimit && !v.FileInfo().IsDir() {
|
||||
if tempFile, err := f.unzipToTemp(v); err == nil {
|
||||
f.tempFiles.Store(fileName, tempFile)
|
||||
continue
|
||||
|
@ -120,7 +126,7 @@ func (f *File) readTemp(name string) (file *os.File, err error) {
|
|||
// saveFileList provides a function to update given file content in file list
|
||||
// of spreadsheet.
|
||||
func (f *File) saveFileList(name string, content []byte) {
|
||||
f.Pkg.Store(name, append([]byte(XMLHeader), content...))
|
||||
f.Pkg.Store(name, append([]byte(xml.Header), content...))
|
||||
}
|
||||
|
||||
// Read file content as string in a archive file.
|
||||
|
|
51
rows.go
51
rows.go
|
@ -21,6 +21,7 @@ import (
|
|||
"math/big"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/mohae/deepcopy"
|
||||
)
|
||||
|
@ -244,7 +245,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
|
|||
decoder *xml.Decoder
|
||||
tempFile *os.File
|
||||
)
|
||||
if needClose, decoder, tempFile, err = f.sheetDecoder(name); needClose && err == nil {
|
||||
if needClose, decoder, tempFile, err = f.xmlDecoder(name); needClose && err == nil {
|
||||
defer tempFile.Close()
|
||||
}
|
||||
for {
|
||||
|
@ -271,7 +272,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
|
|||
if xmlElement.Name.Local == "sheetData" {
|
||||
rows.f = f
|
||||
rows.sheet = name
|
||||
_, rows.decoder, rows.tempFile, err = f.sheetDecoder(name)
|
||||
_, rows.decoder, rows.tempFile, err = f.xmlDecoder(name)
|
||||
return &rows, err
|
||||
}
|
||||
}
|
||||
|
@ -279,9 +280,46 @@ func (f *File) Rows(sheet string) (*Rows, error) {
|
|||
return &rows, nil
|
||||
}
|
||||
|
||||
// sheetDecoder creates XML decoder by given path in the zip from memory data
|
||||
// getFromStringItemMap build shared string item map from system temporary
|
||||
// file at one time, and return value by given to string index.
|
||||
func (f *File) getFromStringItemMap(index int) string {
|
||||
if f.sharedStringItemMap != nil {
|
||||
if value, ok := f.sharedStringItemMap.Load(index); ok {
|
||||
return value.(string)
|
||||
}
|
||||
return strconv.Itoa(index)
|
||||
}
|
||||
f.sharedStringItemMap = &sync.Map{}
|
||||
needClose, decoder, tempFile, err := f.xmlDecoder(dafaultXMLPathSharedStrings)
|
||||
if needClose && err == nil {
|
||||
defer tempFile.Close()
|
||||
}
|
||||
var (
|
||||
inElement string
|
||||
i int
|
||||
)
|
||||
for {
|
||||
token, _ := decoder.Token()
|
||||
if token == nil {
|
||||
break
|
||||
}
|
||||
switch xmlElement := token.(type) {
|
||||
case xml.StartElement:
|
||||
inElement = xmlElement.Name.Local
|
||||
if inElement == "si" {
|
||||
si := xlsxSI{}
|
||||
_ = decoder.DecodeElement(&si, &xmlElement)
|
||||
f.sharedStringItemMap.Store(i, si.String())
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
return f.getFromStringItemMap(index)
|
||||
}
|
||||
|
||||
// xmlDecoder creates XML decoder by given path in the zip from memory data
|
||||
// or system temporary file.
|
||||
func (f *File) sheetDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
|
||||
func (f *File) xmlDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
|
||||
var (
|
||||
content []byte
|
||||
err error
|
||||
|
@ -373,7 +411,7 @@ func (f *File) sharedStringsReader() *xlsxSST {
|
|||
relPath := f.getWorkbookRelsPath()
|
||||
if f.SharedStrings == nil {
|
||||
var sharedStrings xlsxSST
|
||||
ss := f.readXML("xl/sharedStrings.xml")
|
||||
ss := f.readXML(dafaultXMLPathSharedStrings)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
|
||||
Decode(&sharedStrings); err != nil && err != io.EOF {
|
||||
log.Printf("xml decode error: %s", err)
|
||||
|
@ -415,6 +453,9 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
|
|||
if c.V != "" {
|
||||
xlsxSI := 0
|
||||
xlsxSI, _ = strconv.Atoi(c.V)
|
||||
if _, ok := f.tempFiles.Load(dafaultXMLPathSharedStrings); ok {
|
||||
return f.formattedValue(c.S, f.getFromStringItemMap(xlsxSI), raw), nil
|
||||
}
|
||||
if len(d.SI) > xlsxSI {
|
||||
return f.formattedValue(c.S, d.SI[xlsxSI].String(), raw), nil
|
||||
}
|
||||
|
|
11
rows_test.go
11
rows_test.go
|
@ -56,11 +56,18 @@ func TestRows(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Test reload the file to memory from system temporary directory.
|
||||
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{WorksheetUnzipMemLimit: 1024})
|
||||
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipXMLSizeLimit: 128})
|
||||
assert.NoError(t, err)
|
||||
value, err := f.GetCellValue("Sheet1", "A19")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Total:", value)
|
||||
// Test load shared string table to memory
|
||||
err = f.SetCellValue("Sheet1", "A19", "A19")
|
||||
assert.NoError(t, err)
|
||||
value, err = f.GetCellValue("Sheet1", "A19")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "A19", value)
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRow.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
|
@ -200,7 +207,7 @@ func TestColumns(t *testing.T) {
|
|||
|
||||
func TestSharedStringsReader(t *testing.T) {
|
||||
f := NewFile()
|
||||
f.Pkg.Store("xl/sharedStrings.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(dafaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
||||
f.sharedStringsReader()
|
||||
si := xlsxSI{}
|
||||
assert.EqualValues(t, "", si.String())
|
||||
|
|
6
sheet.go
6
sheet.go
|
@ -76,7 +76,7 @@ func (f *File) contentTypesReader() *xlsxTypes {
|
|||
f.ContentTypes = new(xlsxTypes)
|
||||
f.ContentTypes.Lock()
|
||||
defer f.ContentTypes.Unlock()
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).
|
||||
Decode(f.ContentTypes); err != nil && err != io.EOF {
|
||||
log.Printf("xml decode error: %s", err)
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ func (f *File) contentTypesReader() *xlsxTypes {
|
|||
func (f *File) contentTypesWriter() {
|
||||
if f.ContentTypes != nil {
|
||||
output, _ := xml.Marshal(f.ContentTypes)
|
||||
f.saveFileList("[Content_Types].xml", output)
|
||||
f.saveFileList(defaultXMLPathContentTypes, output)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ func (f *File) relsWriter() {
|
|||
|
||||
// setAppXML update docProps/app.xml file of XML.
|
||||
func (f *File) setAppXML() {
|
||||
f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp))
|
||||
f.saveFileList(dafaultXMLPathDocPropsApp, []byte(templateDocpropsApp))
|
||||
}
|
||||
|
||||
// replaceRelationshipsBytes; Some tools that read spreadsheet files have very
|
||||
|
|
|
@ -112,7 +112,7 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
|
|||
}
|
||||
f.streams[sheetPath] = sw
|
||||
|
||||
_, _ = sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap)
|
||||
_, _ = sw.rawData.WriteString(xml.Header + `<worksheet` + templateNamespaceIDMap)
|
||||
bulkAppendFields(&sw.rawData, sw.worksheet, 2, 5)
|
||||
return sw, err
|
||||
}
|
||||
|
|
|
@ -1078,7 +1078,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
|
|||
|
||||
if f.Styles == nil {
|
||||
f.Styles = new(xlsxStyleSheet)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathStyles)))).
|
||||
Decode(f.Styles); err != nil && err != io.EOF {
|
||||
log.Printf("xml decode error: %s", err)
|
||||
}
|
||||
|
@ -1092,7 +1092,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
|
|||
func (f *File) styleSheetWriter() {
|
||||
if f.Styles != nil {
|
||||
output, _ := xml.Marshal(f.Styles)
|
||||
f.saveFileList("xl/styles.xml", f.replaceNameSpaceBytes("xl/styles.xml", output))
|
||||
f.saveFileList(defaultXMLPathStyles, f.replaceNameSpaceBytes(defaultXMLPathStyles, output))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1101,7 +1101,7 @@ func (f *File) styleSheetWriter() {
|
|||
func (f *File) sharedStringsWriter() {
|
||||
if f.SharedStrings != nil {
|
||||
output, _ := xml.Marshal(f.SharedStrings)
|
||||
f.saveFileList("xl/sharedStrings.xml", f.replaceNameSpaceBytes("xl/sharedStrings.xml", output))
|
||||
f.saveFileList(dafaultXMLPathSharedStrings, f.replaceNameSpaceBytes(dafaultXMLPathSharedStrings, output))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -300,7 +300,7 @@ func TestStylesReader(t *testing.T) {
|
|||
f := NewFile()
|
||||
// Test read styles with unsupported charset.
|
||||
f.Styles = nil
|
||||
f.Pkg.Store("xl/styles.xml", MacintoshCyrillicCharset)
|
||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||
assert.EqualValues(t, new(xlsxStyleSheet), f.stylesReader())
|
||||
}
|
||||
|
||||
|
|
15
templates.go
15
templates.go
|
@ -14,13 +14,22 @@
|
|||
|
||||
package excelize
|
||||
|
||||
// XMLHeader define an XML declaration can also contain a standalone declaration.
|
||||
const XMLHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
|
||||
import "encoding/xml"
|
||||
|
||||
var (
|
||||
// XMLHeaderByte define an XML declaration can also contain a standalone
|
||||
// declaration.
|
||||
XMLHeaderByte = []byte(XMLHeader)
|
||||
XMLHeaderByte = []byte(xml.Header)
|
||||
)
|
||||
|
||||
const (
|
||||
defaultXMLPathContentTypes = "[Content_Types].xml"
|
||||
dafaultXMLPathDocPropsApp = "docProps/app.xml"
|
||||
dafaultXMLPathDocPropsCore = "docProps/core.xml"
|
||||
dafaultXMLPathCalcChain = "xl/calcChain.xml"
|
||||
dafaultXMLPathSharedStrings = "xl/sharedStrings.xml"
|
||||
defaultXMLPathStyles = "xl/styles.xml"
|
||||
defaultXMLPathWorkbook = "xl/workbook.xml"
|
||||
)
|
||||
|
||||
const templateDocpropsApp = `<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime><Application>Go Excelize</Application></Properties>`
|
||||
|
|
|
@ -37,21 +37,20 @@ var (
|
|||
|
||||
// Source relationship and namespace.
|
||||
const (
|
||||
SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
||||
SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
|
||||
SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
|
||||
SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
|
||||
SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
|
||||
SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
|
||||
SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
||||
SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
||||
SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
||||
SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"
|
||||
SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"
|
||||
SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
|
||||
SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
|
||||
SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
|
||||
|
||||
SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
||||
SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
|
||||
SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
|
||||
SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
|
||||
SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
|
||||
SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
|
||||
SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
||||
SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
||||
SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
||||
SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"
|
||||
SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"
|
||||
SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
|
||||
SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
|
||||
SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
|
||||
SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
|
||||
NameSpaceXML = "http://www.w3.org/XML/1998/namespace"
|
||||
NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance"
|
||||
|
|
Loading…
Reference in New Issue