forked from p30928647/excelize
Fix #523, add stream writer for generate new worksheet with huge amounts of data
This commit is contained in:
parent
842b942c71
commit
08d1a86c3a
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.10 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// StreamWriter defined the type of stream writer.
|
||||
type StreamWriter struct {
|
||||
tmpFile *os.File
|
||||
File *File
|
||||
Sheet string
|
||||
SheetID int
|
||||
SheetData bytes.Buffer
|
||||
}
|
||||
|
||||
// NewStreamWriter return stream writer struct by given worksheet name for
|
||||
// generate new worksheet with large amounts of data. Note that after set
|
||||
// rows, you must call the 'Flush' method to end the streaming writing
|
||||
// process and ensure that the order of line numbers is ascending. For
|
||||
// example, set data for worksheet of size 102400 rows x 50 columns with
|
||||
// numbers:
|
||||
//
|
||||
// file := excelize.NewFile()
|
||||
// streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// for rowID := 1; rowID <= 102400; rowID++ {
|
||||
// row := make([]interface{}, 50)
|
||||
// for colID := 0; colID < 50; colID++ {
|
||||
// row[colID] = rand.Intn(640000)
|
||||
// }
|
||||
// cell, _ := excelize.CoordinatesToCellName(1, rowID)
|
||||
// if err := streamWriter.SetRow(cell, &row); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
// if err := streamWriter.Flush(); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// if err := file.SaveAs("Book1.xlsx"); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
|
||||
sheetID := f.GetSheetIndex(sheet)
|
||||
if sheetID == 0 {
|
||||
return nil, fmt.Errorf("sheet %s is not exist", sheet)
|
||||
}
|
||||
rsw := &StreamWriter{
|
||||
File: f,
|
||||
Sheet: sheet,
|
||||
SheetID: sheetID,
|
||||
}
|
||||
rsw.SheetData.WriteString("<sheetData>")
|
||||
return rsw, nil
|
||||
}
|
||||
|
||||
// SetRow writes an array to streaming row by given worksheet name, starting
|
||||
// coordinate and a pointer to array type 'slice'. Note that, cell settings
|
||||
// with styles are not supported currently and after set rows, you must call the
|
||||
// 'Flush' method to end the streaming writing process. The following
|
||||
// shows the supported data types:
|
||||
//
|
||||
// int
|
||||
// string
|
||||
//
|
||||
func (sw *StreamWriter) SetRow(axis string, slice interface{}) error {
|
||||
col, row, err := CellNameToCoordinates(axis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Make sure 'slice' is a Ptr to Slice
|
||||
v := reflect.ValueOf(slice)
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
|
||||
return errors.New("pointer to slice expected")
|
||||
}
|
||||
v = v.Elem()
|
||||
sw.SheetData.WriteString(fmt.Sprintf(`<row r="%d">`, row))
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
axis, err := CoordinatesToCellName(col+i, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch val := v.Index(i).Interface().(type) {
|
||||
case int:
|
||||
sw.SheetData.WriteString(fmt.Sprintf(`<c r="%s"><v>%d</v></c>`, axis, val))
|
||||
case string:
|
||||
sw.SheetData.WriteString(sw.setCellStr(axis, val))
|
||||
default:
|
||||
sw.SheetData.WriteString(sw.setCellStr(axis, fmt.Sprint(val)))
|
||||
}
|
||||
}
|
||||
sw.SheetData.WriteString(`</row>`)
|
||||
// Try to use local storage
|
||||
chunk := 1 << 24
|
||||
if sw.SheetData.Len() >= chunk {
|
||||
if sw.tmpFile == nil {
|
||||
err := sw.createTmp()
|
||||
if err != nil {
|
||||
// can not use local storage
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// use local storage
|
||||
_, err := sw.tmpFile.Write(sw.SheetData.Bytes())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sw.SheetData.Reset()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Flush ending the streaming writing process.
|
||||
func (sw *StreamWriter) Flush() error {
|
||||
sw.SheetData.WriteString(`</sheetData>`)
|
||||
|
||||
ws, err := sw.File.workSheetReader(sw.Sheet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
|
||||
delete(sw.File.Sheet, sheetXML)
|
||||
delete(sw.File.checked, sheetXML)
|
||||
var sheetDataByte []byte
|
||||
if sw.tmpFile != nil {
|
||||
// close the local storage file
|
||||
if err = sw.tmpFile.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Open(sw.tmpFile.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sheetDataByte, err = ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := file.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Remove(sw.tmpFile.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sheetDataByte = append(sheetDataByte, sw.SheetData.Bytes()...)
|
||||
replaceMap := map[string][]byte{
|
||||
"XMLName": []byte{},
|
||||
"SheetData": sheetDataByte,
|
||||
}
|
||||
sw.SheetData.Reset()
|
||||
sw.File.XLSX[fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)] =
|
||||
StreamMarshalSheet(ws, replaceMap)
|
||||
return err
|
||||
}
|
||||
|
||||
// createTmp creates a temporary file in the operating system default
|
||||
// temporary directory.
|
||||
func (sw *StreamWriter) createTmp() (err error) {
|
||||
sw.tmpFile, err = ioutil.TempFile(os.TempDir(), "excelize-")
|
||||
return err
|
||||
}
|
||||
|
||||
// StreamMarshalSheet provides method to serialization worksheets by field as
|
||||
// streaming.
|
||||
func StreamMarshalSheet(ws *xlsxWorksheet, replaceMap map[string][]byte) []byte {
|
||||
s := reflect.ValueOf(ws).Elem()
|
||||
typeOfT := s.Type()
|
||||
var marshalResult []byte
|
||||
marshalResult = append(marshalResult, []byte(XMLHeader+`<worksheet`+templateNamespaceIDMap)...)
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
content, ok := replaceMap[typeOfT.Field(i).Name]
|
||||
if ok {
|
||||
marshalResult = append(marshalResult, content...)
|
||||
continue
|
||||
}
|
||||
out, _ := xml.Marshal(f.Interface())
|
||||
marshalResult = append(marshalResult, out...)
|
||||
}
|
||||
marshalResult = append(marshalResult, []byte(`</worksheet>`)...)
|
||||
return marshalResult
|
||||
}
|
||||
|
||||
// setCellStr provides a function to set string type value of a cell as
|
||||
// streaming. Total number of characters that a cell can contain 32767
|
||||
// characters.
|
||||
func (sw *StreamWriter) setCellStr(axis, value string) string {
|
||||
if len(value) > 32767 {
|
||||
value = value[0:32767]
|
||||
}
|
||||
// Leading and ending space(s) character detection.
|
||||
if len(value) > 0 && (value[0] == 32 || value[len(value)-1] == 32) {
|
||||
return fmt.Sprintf(`<c xml:space="preserve" r="%s" t="str"><v>%s</v></c>`, axis, value)
|
||||
}
|
||||
return fmt.Sprintf(`<c r="%s" t="str"><v>%s</v></c>`, axis, value)
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package excelize
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStreamWriter(t *testing.T) {
|
||||
file := NewFile()
|
||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test max characters in a cell.
|
||||
row := make([]interface{}, 1)
|
||||
row[0] = strings.Repeat("c", 32769)
|
||||
assert.NoError(t, streamWriter.SetRow("A1", &row))
|
||||
|
||||
// Test leading and ending space(s) character characters in a cell.
|
||||
row = make([]interface{}, 1)
|
||||
row[0] = " characters"
|
||||
assert.NoError(t, streamWriter.SetRow("A2", &row))
|
||||
|
||||
row = make([]interface{}, 1)
|
||||
row[0] = []byte("Word")
|
||||
assert.NoError(t, streamWriter.SetRow("A3", &row))
|
||||
|
||||
for rowID := 10; rowID <= 51200; rowID++ {
|
||||
row := make([]interface{}, 50)
|
||||
for colID := 0; colID < 50; colID++ {
|
||||
row[colID] = rand.Intn(640000)
|
||||
}
|
||||
cell, _ := CoordinatesToCellName(1, rowID)
|
||||
assert.NoError(t, streamWriter.SetRow(cell, &row))
|
||||
}
|
||||
|
||||
err = streamWriter.Flush()
|
||||
assert.NoError(t, err)
|
||||
// Save xlsx file by the given path.
|
||||
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriter.xlsx")))
|
||||
|
||||
// Test error exceptions
|
||||
streamWriter, err = file.NewStreamWriter("SheetN")
|
||||
assert.EqualError(t, err, "sheet SheetN is not exist")
|
||||
}
|
||||
|
||||
func TestFlush(t *testing.T) {
|
||||
// Test error exceptions
|
||||
file := NewFile()
|
||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
streamWriter.Sheet = "SheetN"
|
||||
assert.EqualError(t, streamWriter.Flush(), "sheet SheetN is not exist")
|
||||
}
|
||||
|
||||
func TestSetRow(t *testing.T) {
|
||||
// Test error exceptions
|
||||
file := NewFile()
|
||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualError(t, streamWriter.SetRow("A", &[]interface{}{}), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
|
||||
assert.EqualError(t, streamWriter.SetRow("A1", []interface{}{}), `pointer to slice expected`)
|
||||
}
|
|
@ -44,6 +44,7 @@ type xlsxTable struct {
|
|||
// applied column by column to a table of data in the worksheet. This collection
|
||||
// expresses AutoFilter settings.
|
||||
type xlsxAutoFilter struct {
|
||||
XMLName xml.Name `xml:"autoFilter"`
|
||||
Ref string `xml:"ref,attr"`
|
||||
FilterColumn *xlsxFilterColumn `xml:"filterColumn"`
|
||||
}
|
||||
|
|
180
xmlWorksheet.go
180
xmlWorksheet.go
|
@ -59,7 +59,8 @@ type xlsxWorksheet struct {
|
|||
|
||||
// xlsxDrawing change r:id to rid in the namespace.
|
||||
type xlsxDrawing struct {
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"drawing"`
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxHeaderFooter directly maps the headerFooter element in the namespace
|
||||
|
@ -70,6 +71,7 @@ type xlsxDrawing struct {
|
|||
// footers on the first page can differ from those on odd- and even-numbered
|
||||
// pages. In the latter case, the first page is not considered an odd page.
|
||||
type xlsxHeaderFooter struct {
|
||||
XMLName xml.Name `xml:"headerFooter"`
|
||||
AlignWithMargins bool `xml:"alignWithMargins,attr,omitempty"`
|
||||
DifferentFirst bool `xml:"differentFirst,attr,omitempty"`
|
||||
DifferentOddEven bool `xml:"differentOddEven,attr,omitempty"`
|
||||
|
@ -91,32 +93,33 @@ type xlsxHeaderFooter struct {
|
|||
// each of the left section, center section and right section of a header and
|
||||
// a footer.
|
||||
type xlsxDrawingHF struct {
|
||||
Content string `xml:",chardata"`
|
||||
Content string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
// xlsxPageSetUp directly maps the pageSetup element in the namespace
|
||||
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page setup
|
||||
// settings for the worksheet.
|
||||
type xlsxPageSetUp struct {
|
||||
BlackAndWhite bool `xml:"blackAndWhite,attr,omitempty"`
|
||||
CellComments string `xml:"cellComments,attr,omitempty"`
|
||||
Copies int `xml:"copies,attr,omitempty"`
|
||||
Draft bool `xml:"draft,attr,omitempty"`
|
||||
Errors string `xml:"errors,attr,omitempty"`
|
||||
FirstPageNumber int `xml:"firstPageNumber,attr,omitempty"`
|
||||
FitToHeight int `xml:"fitToHeight,attr,omitempty"`
|
||||
FitToWidth int `xml:"fitToWidth,attr,omitempty"`
|
||||
HorizontalDPI float32 `xml:"horizontalDpi,attr,omitempty"`
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
Orientation string `xml:"orientation,attr,omitempty"`
|
||||
PageOrder string `xml:"pageOrder,attr,omitempty"`
|
||||
PaperHeight string `xml:"paperHeight,attr,omitempty"`
|
||||
PaperSize int `xml:"paperSize,attr,omitempty"`
|
||||
PaperWidth string `xml:"paperWidth,attr,omitempty"`
|
||||
Scale int `xml:"scale,attr,omitempty"`
|
||||
UseFirstPageNumber bool `xml:"useFirstPageNumber,attr,omitempty"`
|
||||
UsePrinterDefaults bool `xml:"usePrinterDefaults,attr,omitempty"`
|
||||
VerticalDPI float32 `xml:"verticalDpi,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"pageSetup"`
|
||||
BlackAndWhite bool `xml:"blackAndWhite,attr,omitempty"`
|
||||
CellComments string `xml:"cellComments,attr,omitempty"`
|
||||
Copies int `xml:"copies,attr,omitempty"`
|
||||
Draft bool `xml:"draft,attr,omitempty"`
|
||||
Errors string `xml:"errors,attr,omitempty"`
|
||||
FirstPageNumber int `xml:"firstPageNumber,attr,omitempty"`
|
||||
FitToHeight int `xml:"fitToHeight,attr,omitempty"`
|
||||
FitToWidth int `xml:"fitToWidth,attr,omitempty"`
|
||||
HorizontalDPI float32 `xml:"horizontalDpi,attr,omitempty"`
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
Orientation string `xml:"orientation,attr,omitempty"`
|
||||
PageOrder string `xml:"pageOrder,attr,omitempty"`
|
||||
PaperHeight string `xml:"paperHeight,attr,omitempty"`
|
||||
PaperSize int `xml:"paperSize,attr,omitempty"`
|
||||
PaperWidth string `xml:"paperWidth,attr,omitempty"`
|
||||
Scale int `xml:"scale,attr,omitempty"`
|
||||
UseFirstPageNumber bool `xml:"useFirstPageNumber,attr,omitempty"`
|
||||
UsePrinterDefaults bool `xml:"usePrinterDefaults,attr,omitempty"`
|
||||
VerticalDPI float32 `xml:"verticalDpi,attr,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxPrintOptions directly maps the printOptions element in the namespace
|
||||
|
@ -124,44 +127,48 @@ type xlsxPageSetUp struct {
|
|||
// the sheet. Printer-specific settings are stored separately in the Printer
|
||||
// Settings part.
|
||||
type xlsxPrintOptions struct {
|
||||
GridLines bool `xml:"gridLines,attr,omitempty"`
|
||||
GridLinesSet bool `xml:"gridLinesSet,attr,omitempty"`
|
||||
Headings bool `xml:"headings,attr,omitempty"`
|
||||
HorizontalCentered bool `xml:"horizontalCentered,attr,omitempty"`
|
||||
VerticalCentered bool `xml:"verticalCentered,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"printOptions"`
|
||||
GridLines bool `xml:"gridLines,attr,omitempty"`
|
||||
GridLinesSet bool `xml:"gridLinesSet,attr,omitempty"`
|
||||
Headings bool `xml:"headings,attr,omitempty"`
|
||||
HorizontalCentered bool `xml:"horizontalCentered,attr,omitempty"`
|
||||
VerticalCentered bool `xml:"verticalCentered,attr,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxPageMargins directly maps the pageMargins element in the namespace
|
||||
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Page margins for
|
||||
// a sheet or a custom sheet view.
|
||||
type xlsxPageMargins struct {
|
||||
Bottom float64 `xml:"bottom,attr"`
|
||||
Footer float64 `xml:"footer,attr"`
|
||||
Header float64 `xml:"header,attr"`
|
||||
Left float64 `xml:"left,attr"`
|
||||
Right float64 `xml:"right,attr"`
|
||||
Top float64 `xml:"top,attr"`
|
||||
XMLName xml.Name `xml:"pageMargins"`
|
||||
Bottom float64 `xml:"bottom,attr"`
|
||||
Footer float64 `xml:"footer,attr"`
|
||||
Header float64 `xml:"header,attr"`
|
||||
Left float64 `xml:"left,attr"`
|
||||
Right float64 `xml:"right,attr"`
|
||||
Top float64 `xml:"top,attr"`
|
||||
}
|
||||
|
||||
// xlsxSheetFormatPr directly maps the sheetFormatPr element in the namespace
|
||||
// http://schemas.openxmlformats.org/spreadsheetml/2006/main. This element
|
||||
// specifies the sheet formatting properties.
|
||||
type xlsxSheetFormatPr struct {
|
||||
BaseColWidth uint8 `xml:"baseColWidth,attr,omitempty"`
|
||||
DefaultColWidth float64 `xml:"defaultColWidth,attr,omitempty"`
|
||||
DefaultRowHeight float64 `xml:"defaultRowHeight,attr"`
|
||||
CustomHeight bool `xml:"customHeight,attr,omitempty"`
|
||||
ZeroHeight bool `xml:"zeroHeight,attr,omitempty"`
|
||||
ThickTop bool `xml:"thickTop,attr,omitempty"`
|
||||
ThickBottom bool `xml:"thickBottom,attr,omitempty"`
|
||||
OutlineLevelRow uint8 `xml:"outlineLevelRow,attr,omitempty"`
|
||||
OutlineLevelCol uint8 `xml:"outlineLevelCol,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"sheetFormatPr"`
|
||||
BaseColWidth uint8 `xml:"baseColWidth,attr,omitempty"`
|
||||
DefaultColWidth float64 `xml:"defaultColWidth,attr,omitempty"`
|
||||
DefaultRowHeight float64 `xml:"defaultRowHeight,attr"`
|
||||
CustomHeight bool `xml:"customHeight,attr,omitempty"`
|
||||
ZeroHeight bool `xml:"zeroHeight,attr,omitempty"`
|
||||
ThickTop bool `xml:"thickTop,attr,omitempty"`
|
||||
ThickBottom bool `xml:"thickBottom,attr,omitempty"`
|
||||
OutlineLevelRow uint8 `xml:"outlineLevelRow,attr,omitempty"`
|
||||
OutlineLevelCol uint8 `xml:"outlineLevelCol,attr,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxSheetViews directly maps the sheetViews element in the namespace
|
||||
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - Worksheet views
|
||||
// collection.
|
||||
type xlsxSheetViews struct {
|
||||
XMLName xml.Name `xml:"sheetViews"`
|
||||
SheetView []xlsxSheetView `xml:"sheetView"`
|
||||
}
|
||||
|
||||
|
@ -263,7 +270,8 @@ type xlsxTabColor struct {
|
|||
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
|
||||
// not checked it for completeness - it does as much as I need.
|
||||
type xlsxCols struct {
|
||||
Col []xlsxCol `xml:"col"`
|
||||
XMLName xml.Name `xml:"cols"`
|
||||
Col []xlsxCol `xml:"col"`
|
||||
}
|
||||
|
||||
// xlsxCol directly maps the col (Column Width & Formatting). Defines column
|
||||
|
@ -289,7 +297,8 @@ type xlsxCol struct {
|
|||
// When an entire column is formatted, only the first cell in that column is
|
||||
// considered used.
|
||||
type xlsxDimension struct {
|
||||
Ref string `xml:"ref,attr"`
|
||||
XMLName xml.Name `xml:"dimension"`
|
||||
Ref string `xml:"ref,attr"`
|
||||
}
|
||||
|
||||
// xlsxSheetData directly maps the sheetData element in the namespace
|
||||
|
@ -322,6 +331,7 @@ type xlsxRow struct {
|
|||
// xlsxCustomSheetViews directly maps the customSheetViews element. This is a
|
||||
// collection of custom sheet views.
|
||||
type xlsxCustomSheetViews struct {
|
||||
XMLName xml.Name `xml:"customSheetViews"`
|
||||
CustomSheetView []*xlsxCustomSheetView `xml:"customSheetView"`
|
||||
}
|
||||
|
||||
|
@ -384,13 +394,15 @@ type xlsxMergeCell struct {
|
|||
// xlsxMergeCells directly maps the mergeCells element. This collection
|
||||
// expresses all the merged cells in the sheet.
|
||||
type xlsxMergeCells struct {
|
||||
Count int `xml:"count,attr,omitempty"`
|
||||
Cells []*xlsxMergeCell `xml:"mergeCell,omitempty"`
|
||||
XMLName xml.Name `xml:"mergeCells"`
|
||||
Count int `xml:"count,attr,omitempty"`
|
||||
Cells []*xlsxMergeCell `xml:"mergeCell,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxDataValidations expresses all data validation information for cells in a
|
||||
// sheet which have data validation features applied.
|
||||
type xlsxDataValidations struct {
|
||||
XMLName xml.Name `xml:"dataValidations"`
|
||||
Count int `xml:"count,attr,omitempty"`
|
||||
DisablePrompts bool `xml:"disablePrompts,attr,omitempty"`
|
||||
XWindow int `xml:"xWindow,attr,omitempty"`
|
||||
|
@ -434,14 +446,15 @@ type DataValidation struct {
|
|||
// str (String) | Cell containing a formula string.
|
||||
//
|
||||
type xlsxC struct {
|
||||
R string `xml:"r,attr"` // Cell ID, e.g. A1
|
||||
S int `xml:"s,attr,omitempty"` // Style reference.
|
||||
// Str string `xml:"str,attr,omitempty"` // Style reference.
|
||||
T string `xml:"t,attr,omitempty"` // Type.
|
||||
F *xlsxF `xml:"f,omitempty"` // Formula
|
||||
V string `xml:"v,omitempty"` // Value
|
||||
IS *xlsxSI `xml:"is"`
|
||||
XMLName xml.Name `xml:"c"`
|
||||
XMLSpace xml.Attr `xml:"space,attr,omitempty"`
|
||||
R string `xml:"r,attr"` // Cell ID, e.g. A1
|
||||
S int `xml:"s,attr,omitempty"` // Style reference.
|
||||
// Str string `xml:"str,attr,omitempty"` // Style reference.
|
||||
T string `xml:"t,attr,omitempty"` // Type.
|
||||
F *xlsxF `xml:"f,omitempty"` // Formula
|
||||
V string `xml:"v,omitempty"` // Value
|
||||
IS *xlsxSI `xml:"is"`
|
||||
}
|
||||
|
||||
func (c *xlsxC) hasValue() bool {
|
||||
|
@ -461,27 +474,28 @@ type xlsxF struct {
|
|||
// xlsxSheetProtection collection expresses the sheet protection options to
|
||||
// enforce when the sheet is protected.
|
||||
type xlsxSheetProtection struct {
|
||||
AlgorithmName string `xml:"algorithmName,attr,omitempty"`
|
||||
Password string `xml:"password,attr,omitempty"`
|
||||
HashValue string `xml:"hashValue,attr,omitempty"`
|
||||
SaltValue string `xml:"saltValue,attr,omitempty"`
|
||||
SpinCount int `xml:"spinCount,attr,omitempty"`
|
||||
Sheet bool `xml:"sheet,attr"`
|
||||
Objects bool `xml:"objects,attr"`
|
||||
Scenarios bool `xml:"scenarios,attr"`
|
||||
FormatCells bool `xml:"formatCells,attr"`
|
||||
FormatColumns bool `xml:"formatColumns,attr"`
|
||||
FormatRows bool `xml:"formatRows,attr"`
|
||||
InsertColumns bool `xml:"insertColumns,attr"`
|
||||
InsertRows bool `xml:"insertRows,attr"`
|
||||
InsertHyperlinks bool `xml:"insertHyperlinks,attr"`
|
||||
DeleteColumns bool `xml:"deleteColumns,attr"`
|
||||
DeleteRows bool `xml:"deleteRows,attr"`
|
||||
SelectLockedCells bool `xml:"selectLockedCells,attr"`
|
||||
Sort bool `xml:"sort,attr"`
|
||||
AutoFilter bool `xml:"autoFilter,attr"`
|
||||
PivotTables bool `xml:"pivotTables,attr"`
|
||||
SelectUnlockedCells bool `xml:"selectUnlockedCells,attr"`
|
||||
XMLName xml.Name `xml:"sheetProtection"`
|
||||
AlgorithmName string `xml:"algorithmName,attr,omitempty"`
|
||||
Password string `xml:"password,attr,omitempty"`
|
||||
HashValue string `xml:"hashValue,attr,omitempty"`
|
||||
SaltValue string `xml:"saltValue,attr,omitempty"`
|
||||
SpinCount int `xml:"spinCount,attr,omitempty"`
|
||||
Sheet bool `xml:"sheet,attr"`
|
||||
Objects bool `xml:"objects,attr"`
|
||||
Scenarios bool `xml:"scenarios,attr"`
|
||||
FormatCells bool `xml:"formatCells,attr"`
|
||||
FormatColumns bool `xml:"formatColumns,attr"`
|
||||
FormatRows bool `xml:"formatRows,attr"`
|
||||
InsertColumns bool `xml:"insertColumns,attr"`
|
||||
InsertRows bool `xml:"insertRows,attr"`
|
||||
InsertHyperlinks bool `xml:"insertHyperlinks,attr"`
|
||||
DeleteColumns bool `xml:"deleteColumns,attr"`
|
||||
DeleteRows bool `xml:"deleteRows,attr"`
|
||||
SelectLockedCells bool `xml:"selectLockedCells,attr"`
|
||||
Sort bool `xml:"sort,attr"`
|
||||
AutoFilter bool `xml:"autoFilter,attr"`
|
||||
PivotTables bool `xml:"pivotTables,attr"`
|
||||
SelectUnlockedCells bool `xml:"selectUnlockedCells,attr"`
|
||||
}
|
||||
|
||||
// xlsxPhoneticPr (Phonetic Properties) represents a collection of phonetic
|
||||
|
@ -492,9 +506,10 @@ type xlsxSheetProtection struct {
|
|||
// every phonetic hint is expressed as a phonetic run (rPh), and these
|
||||
// properties specify how to display that phonetic run.
|
||||
type xlsxPhoneticPr struct {
|
||||
Alignment string `xml:"alignment,attr,omitempty"`
|
||||
FontID *int `xml:"fontId,attr"`
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"phoneticPr"`
|
||||
Alignment string `xml:"alignment,attr,omitempty"`
|
||||
FontID *int `xml:"fontId,attr"`
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
}
|
||||
|
||||
// A Conditional Format is a format, such as cell shading or font color, that a
|
||||
|
@ -502,8 +517,9 @@ type xlsxPhoneticPr struct {
|
|||
// condition is true. This collection expresses conditional formatting rules
|
||||
// applied to a particular cell or range.
|
||||
type xlsxConditionalFormatting struct {
|
||||
SQRef string `xml:"sqref,attr,omitempty"`
|
||||
CfRule []*xlsxCfRule `xml:"cfRule"`
|
||||
XMLName xml.Name `xml:"conditionalFormatting"`
|
||||
SQRef string `xml:"sqref,attr,omitempty"`
|
||||
CfRule []*xlsxCfRule `xml:"cfRule"`
|
||||
}
|
||||
|
||||
// xlsxCfRule (Conditional Formatting Rule) represents a description of a
|
||||
|
@ -568,6 +584,7 @@ type xlsxCfvo struct {
|
|||
// be stored in a package as a relationship. Hyperlinks shall be identified by
|
||||
// containing a target which specifies the destination of the given hyperlink.
|
||||
type xlsxHyperlinks struct {
|
||||
XMLName xml.Name `xml:"hyperlinks"`
|
||||
Hyperlink []xlsxHyperlink `xml:"hyperlink"`
|
||||
}
|
||||
|
||||
|
@ -612,6 +629,7 @@ type xlsxHyperlink struct {
|
|||
// </worksheet>
|
||||
//
|
||||
type xlsxTableParts struct {
|
||||
XMLName xml.Name `xml:"tableParts"`
|
||||
Count int `xml:"count,attr,omitempty"`
|
||||
TableParts []*xlsxTablePart `xml:"tablePart"`
|
||||
}
|
||||
|
@ -629,7 +647,8 @@ type xlsxTablePart struct {
|
|||
// <picture r:id="rId1"/>
|
||||
//
|
||||
type xlsxPicture struct {
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"picture"`
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
}
|
||||
|
||||
// xlsxLegacyDrawing directly maps the legacyDrawing element in the namespace
|
||||
|
@ -642,7 +661,8 @@ type xlsxPicture struct {
|
|||
// can also be used to explain assumptions made in a formula or to call out
|
||||
// something special about the cell.
|
||||
type xlsxLegacyDrawing struct {
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"legacyDrawing"`
|
||||
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||||
}
|
||||
|
||||
type xlsxInnerXML struct {
|
||||
|
|
Loading…
Reference in New Issue