diff --git a/comment_test.go b/comment_test.go
index 952763dfd7..01f1e42e3e 100644
--- a/comment_test.go
+++ b/comment_test.go
@@ -12,6 +12,7 @@
package excelize
import (
+ "encoding/xml"
"path/filepath"
"strings"
"testing"
@@ -38,7 +39,7 @@ func TestAddComments(t *testing.T) {
}
f.Comments["xl/comments2.xml"] = nil
- f.Pkg.Store("xl/comments2.xml", []byte(`Excelize: Excelize: `))
+ f.Pkg.Store("xl/comments2.xml", []byte(xml.Header+`Excelize: Excelize: `))
comments := f.GetComments()
assert.EqualValues(t, 2, len(comments["Sheet1"]))
assert.EqualValues(t, 1, len(comments["Sheet2"]))
diff --git a/drawing.go b/drawing.go
index 0bc79005f7..5582bb4ff1 100644
--- a/drawing.go
+++ b/drawing.go
@@ -1160,6 +1160,12 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
log.Printf("xml decode error: %s", err)
}
content.R = decodeWsDr.R
+ for _, v := range decodeWsDr.AlternateContent {
+ content.AlternateContent = append(content.AlternateContent, &xlsxAlternateContent{
+ Content: v.Content,
+ XMLNSMC: SourceRelationshipCompatibility.Value,
+ })
+ }
for _, v := range decodeWsDr.OneCellAnchor {
content.OneCellAnchor = append(content.OneCellAnchor, &xdrCellAnchor{
EditAs: v.EditAs,
diff --git a/drawing_test.go b/drawing_test.go
index d33977fb89..e37b771fdd 100644
--- a/drawing_test.go
+++ b/drawing_test.go
@@ -12,6 +12,7 @@
package excelize
import (
+ "encoding/xml"
"sync"
"testing"
)
@@ -22,9 +23,13 @@ func TestDrawingParser(t *testing.T) {
Pkg: sync.Map{},
}
f.Pkg.Store("charset", MacintoshCyrillicCharset)
- f.Pkg.Store("wsDr", []byte(``))
+ f.Pkg.Store("wsDr", []byte(xml.Header+``))
// Test with one cell anchor
f.drawingParser("wsDr")
// Test with unsupported charset
f.drawingParser("charset")
+ // Test with alternate content
+ f.Drawings = sync.Map{}
+ f.Pkg.Store("wsDr", []byte(xml.Header+``))
+ f.drawingParser("wsDr")
}
diff --git a/rows_test.go b/rows_test.go
index 1286a3773d..208b2de841 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -2,6 +2,7 @@ package excelize
import (
"bytes"
+ "encoding/xml"
"fmt"
"path/filepath"
"testing"
@@ -901,12 +902,12 @@ func TestErrSheetNotExistError(t *testing.T) {
func TestCheckRow(t *testing.T) {
f := NewFile()
- f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`12345
`))
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+`12345
`))
_, err := f.GetRows("Sheet1")
assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", false))
f = NewFile()
- f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`12345
`))
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+`12345
`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
delete(f.checked, "xl/worksheets/sheet1.xml")
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", false), newCellNameToCoordinatesError("-", newInvalidCellNameError("-")).Error())
diff --git a/sheet.go b/sheet.go
index b440fb945f..78fcaf2130 100644
--- a/sheet.go
+++ b/sheet.go
@@ -158,6 +158,13 @@ func (f *File) workSheetWriter() {
if sheet.SheetPr != nil || sheet.Drawing != nil || sheet.Hyperlinks != nil || sheet.Picture != nil || sheet.TableParts != nil {
f.addNameSpaces(p.(string), SourceRelationship)
}
+ if sheet.DecodeAlternateContent != nil {
+ sheet.AlternateContent = &xlsxAlternateContent{
+ Content: sheet.DecodeAlternateContent.Content,
+ XMLNSMC: SourceRelationshipCompatibility.Value,
+ }
+ }
+ sheet.DecodeAlternateContent = nil
// reusing buffer
_ = encoder.Encode(sheet)
f.saveFileList(p.(string), replaceRelationshipsBytes(f.replaceNameSpaceBytes(p.(string), buffer.Bytes())))
diff --git a/sheet_test.go b/sheet_test.go
index 7df9018b1c..429f617247 100644
--- a/sheet_test.go
+++ b/sheet_test.go
@@ -1,6 +1,7 @@
package excelize
import (
+ "encoding/xml"
"fmt"
"path/filepath"
"strconv"
@@ -404,6 +405,20 @@ func TestSetSheetName(t *testing.T) {
assert.Equal(t, "Sheet1", f.GetSheetName(0))
}
+func TestWorksheetWriter(t *testing.T) {
+ f := NewFile()
+ // Test set cell value with alternate content
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ worksheet := xml.Header + `%d
`
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(worksheet, 1)))
+ f.checked = nil
+ assert.NoError(t, f.SetCellValue("Sheet1", "A1", 2))
+ f.workSheetWriter()
+ value, ok := f.Pkg.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ assert.Equal(t, fmt.Sprintf(worksheet, 2), string(value.([]byte)))
+}
+
func TestGetWorkbookPath(t *testing.T) {
f := NewFile()
f.Pkg.Delete("_rels/.rels")
@@ -413,7 +428,7 @@ func TestGetWorkbookPath(t *testing.T) {
func TestGetWorkbookRelsPath(t *testing.T) {
f := NewFile()
f.Pkg.Delete("xl/_rels/.rels")
- f.Pkg.Store("_rels/.rels", []byte(``))
+ f.Pkg.Store("_rels/.rels", []byte(xml.Header+``))
assert.Equal(t, "_rels/workbook.xml.rels", f.getWorkbookRelsPath())
}
diff --git a/workbook.go b/workbook.go
index 3d9fa480b2..c65397b6fc 100644
--- a/workbook.go
+++ b/workbook.go
@@ -101,6 +101,13 @@ func (f *File) workbookReader() *xlsxWorkbook {
// structure.
func (f *File) workBookWriter() {
if f.WorkBook != nil {
+ if f.WorkBook.DecodeAlternateContent != nil {
+ f.WorkBook.AlternateContent = &xlsxAlternateContent{
+ Content: f.WorkBook.DecodeAlternateContent.Content,
+ XMLNSMC: SourceRelationshipCompatibility.Value,
+ }
+ }
+ f.WorkBook.DecodeAlternateContent = nil
output, _ := xml.Marshal(f.WorkBook)
f.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output)))
}
diff --git a/xmlDecodeDrawing.go b/xmlDecodeDrawing.go
index 9091440023..fb920be1d8 100644
--- a/xmlDecodeDrawing.go
+++ b/xmlDecodeDrawing.go
@@ -63,12 +63,13 @@ type decodeCNvSpPr struct {
// changed after serialization and deserialization, two different structures
// are defined. decodeWsDr just for deserialization.
type decodeWsDr struct {
- A string `xml:"xmlns a,attr"`
- Xdr string `xml:"xmlns xdr,attr"`
- R string `xml:"xmlns r,attr"`
- OneCellAnchor []*decodeCellAnchor `xml:"oneCellAnchor,omitempty"`
- TwoCellAnchor []*decodeCellAnchor `xml:"twoCellAnchor,omitempty"`
- XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing wsDr,omitempty"`
+ XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing wsDr,omitempty"`
+ A string `xml:"xmlns a,attr"`
+ Xdr string `xml:"xmlns xdr,attr"`
+ R string `xml:"xmlns r,attr"`
+ AlternateContent []*xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"`
+ OneCellAnchor []*decodeCellAnchor `xml:"oneCellAnchor,omitempty"`
+ TwoCellAnchor []*decodeCellAnchor `xml:"twoCellAnchor,omitempty"`
}
// decodeTwoCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape
diff --git a/xmlDrawing.go b/xmlDrawing.go
index c96034ce5c..4bf43ec89b 100644
--- a/xmlDrawing.go
+++ b/xmlDrawing.go
@@ -320,13 +320,14 @@ type xlsxPoint2D struct {
// wsDr.
type xlsxWsDr struct {
sync.Mutex
- XMLName xml.Name `xml:"xdr:wsDr"`
- AbsoluteAnchor []*xdrCellAnchor `xml:"xdr:absoluteAnchor"`
- OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
- TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
- A string `xml:"xmlns:a,attr,omitempty"`
- Xdr string `xml:"xmlns:xdr,attr,omitempty"`
- R string `xml:"xmlns:r,attr,omitempty"`
+ XMLName xml.Name `xml:"xdr:wsDr"`
+ A string `xml:"xmlns:a,attr,omitempty"`
+ Xdr string `xml:"xmlns:xdr,attr,omitempty"`
+ R string `xml:"xmlns:r,attr,omitempty"`
+ AlternateContent []*xlsxAlternateContent `xml:"mc:AlternateContent"`
+ AbsoluteAnchor []*xdrCellAnchor `xml:"xdr:absoluteAnchor"`
+ OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
+ TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
}
// xlsxGraphicFrame (Graphic Frame) directly maps the xdr:graphicFrame element.
diff --git a/xmlWorkbook.go b/xmlWorkbook.go
index 2bb417c36d..e344dbff99 100644
--- a/xmlWorkbook.go
+++ b/xmlWorkbook.go
@@ -35,27 +35,29 @@ type xlsxRelationship struct {
// content of the workbook. The workbook's child elements each have their own
// subclause references.
type xlsxWorkbook struct {
- XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook"`
- Conformance string `xml:"conformance,attr,omitempty"`
- FileVersion *xlsxFileVersion `xml:"fileVersion"`
- FileSharing *xlsxExtLst `xml:"fileSharing"`
- WorkbookPr *xlsxWorkbookPr `xml:"workbookPr"`
- WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
- BookViews *xlsxBookViews `xml:"bookViews"`
- Sheets xlsxSheets `xml:"sheets"`
- FunctionGroups *xlsxExtLst `xml:"functionGroups"`
- ExternalReferences *xlsxExternalReferences `xml:"externalReferences"`
- DefinedNames *xlsxDefinedNames `xml:"definedNames"`
- CalcPr *xlsxCalcPr `xml:"calcPr"`
- OleSize *xlsxExtLst `xml:"oleSize"`
- CustomWorkbookViews *xlsxCustomWorkbookViews `xml:"customWorkbookViews"`
- PivotCaches *xlsxPivotCaches `xml:"pivotCaches"`
- SmartTagPr *xlsxExtLst `xml:"smartTagPr"`
- SmartTagTypes *xlsxExtLst `xml:"smartTagTypes"`
- WebPublishing *xlsxExtLst `xml:"webPublishing"`
- FileRecoveryPr *xlsxFileRecoveryPr `xml:"fileRecoveryPr"`
- WebPublishObjects *xlsxExtLst `xml:"webPublishObjects"`
- ExtLst *xlsxExtLst `xml:"extLst"`
+ XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook"`
+ Conformance string `xml:"conformance,attr,omitempty"`
+ FileVersion *xlsxFileVersion `xml:"fileVersion"`
+ FileSharing *xlsxExtLst `xml:"fileSharing"`
+ AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"`
+ DecodeAlternateContent *xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"`
+ WorkbookPr *xlsxWorkbookPr `xml:"workbookPr"`
+ WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
+ BookViews *xlsxBookViews `xml:"bookViews"`
+ Sheets xlsxSheets `xml:"sheets"`
+ FunctionGroups *xlsxExtLst `xml:"functionGroups"`
+ ExternalReferences *xlsxExternalReferences `xml:"externalReferences"`
+ DefinedNames *xlsxDefinedNames `xml:"definedNames"`
+ CalcPr *xlsxCalcPr `xml:"calcPr"`
+ OleSize *xlsxExtLst `xml:"oleSize"`
+ CustomWorkbookViews *xlsxCustomWorkbookViews `xml:"customWorkbookViews"`
+ PivotCaches *xlsxPivotCaches `xml:"pivotCaches"`
+ SmartTagPr *xlsxExtLst `xml:"smartTagPr"`
+ SmartTagTypes *xlsxExtLst `xml:"smartTagTypes"`
+ WebPublishing *xlsxExtLst `xml:"webPublishing"`
+ FileRecoveryPr *xlsxFileRecoveryPr `xml:"fileRecoveryPr"`
+ WebPublishObjects *xlsxExtLst `xml:"webPublishObjects"`
+ ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxFileRecoveryPr maps sheet recovery information. This element defines
diff --git a/xmlWorksheet.go b/xmlWorksheet.go
index 4a9c88a9dc..c327d3c4ac 100644
--- a/xmlWorksheet.go
+++ b/xmlWorksheet.go
@@ -20,46 +20,48 @@ import (
// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
type xlsxWorksheet struct {
sync.Mutex
- XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
- SheetPr *xlsxSheetPr `xml:"sheetPr"`
- Dimension *xlsxDimension `xml:"dimension"`
- SheetViews *xlsxSheetViews `xml:"sheetViews"`
- SheetFormatPr *xlsxSheetFormatPr `xml:"sheetFormatPr"`
- Cols *xlsxCols `xml:"cols"`
- SheetData xlsxSheetData `xml:"sheetData"`
- SheetCalcPr *xlsxInnerXML `xml:"sheetCalcPr"`
- SheetProtection *xlsxSheetProtection `xml:"sheetProtection"`
- ProtectedRanges *xlsxInnerXML `xml:"protectedRanges"`
- Scenarios *xlsxInnerXML `xml:"scenarios"`
- AutoFilter *xlsxAutoFilter `xml:"autoFilter"`
- SortState *xlsxSortState `xml:"sortState"`
- DataConsolidate *xlsxInnerXML `xml:"dataConsolidate"`
- CustomSheetViews *xlsxCustomSheetViews `xml:"customSheetViews"`
- MergeCells *xlsxMergeCells `xml:"mergeCells"`
- PhoneticPr *xlsxPhoneticPr `xml:"phoneticPr"`
- ConditionalFormatting []*xlsxConditionalFormatting `xml:"conditionalFormatting"`
- DataValidations *xlsxDataValidations `xml:"dataValidations"`
- Hyperlinks *xlsxHyperlinks `xml:"hyperlinks"`
- PrintOptions *xlsxPrintOptions `xml:"printOptions"`
- PageMargins *xlsxPageMargins `xml:"pageMargins"`
- PageSetUp *xlsxPageSetUp `xml:"pageSetup"`
- HeaderFooter *xlsxHeaderFooter `xml:"headerFooter"`
- RowBreaks *xlsxBreaks `xml:"rowBreaks"`
- ColBreaks *xlsxBreaks `xml:"colBreaks"`
- CustomProperties *xlsxInnerXML `xml:"customProperties"`
- CellWatches *xlsxInnerXML `xml:"cellWatches"`
- IgnoredErrors *xlsxInnerXML `xml:"ignoredErrors"`
- SmartTags *xlsxInnerXML `xml:"smartTags"`
- Drawing *xlsxDrawing `xml:"drawing"`
- LegacyDrawing *xlsxLegacyDrawing `xml:"legacyDrawing"`
- LegacyDrawingHF *xlsxLegacyDrawingHF `xml:"legacyDrawingHF"`
- DrawingHF *xlsxDrawingHF `xml:"drawingHF"`
- Picture *xlsxPicture `xml:"picture"`
- OleObjects *xlsxInnerXML `xml:"oleObjects"`
- Controls *xlsxInnerXML `xml:"controls"`
- WebPublishItems *xlsxInnerXML `xml:"webPublishItems"`
- TableParts *xlsxTableParts `xml:"tableParts"`
- ExtLst *xlsxExtLst `xml:"extLst"`
+ XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
+ SheetPr *xlsxSheetPr `xml:"sheetPr"`
+ Dimension *xlsxDimension `xml:"dimension"`
+ SheetViews *xlsxSheetViews `xml:"sheetViews"`
+ SheetFormatPr *xlsxSheetFormatPr `xml:"sheetFormatPr"`
+ Cols *xlsxCols `xml:"cols"`
+ SheetData xlsxSheetData `xml:"sheetData"`
+ SheetCalcPr *xlsxInnerXML `xml:"sheetCalcPr"`
+ SheetProtection *xlsxSheetProtection `xml:"sheetProtection"`
+ ProtectedRanges *xlsxInnerXML `xml:"protectedRanges"`
+ Scenarios *xlsxInnerXML `xml:"scenarios"`
+ AutoFilter *xlsxAutoFilter `xml:"autoFilter"`
+ SortState *xlsxSortState `xml:"sortState"`
+ DataConsolidate *xlsxInnerXML `xml:"dataConsolidate"`
+ CustomSheetViews *xlsxCustomSheetViews `xml:"customSheetViews"`
+ MergeCells *xlsxMergeCells `xml:"mergeCells"`
+ PhoneticPr *xlsxPhoneticPr `xml:"phoneticPr"`
+ ConditionalFormatting []*xlsxConditionalFormatting `xml:"conditionalFormatting"`
+ DataValidations *xlsxDataValidations `xml:"dataValidations"`
+ Hyperlinks *xlsxHyperlinks `xml:"hyperlinks"`
+ PrintOptions *xlsxPrintOptions `xml:"printOptions"`
+ PageMargins *xlsxPageMargins `xml:"pageMargins"`
+ PageSetUp *xlsxPageSetUp `xml:"pageSetup"`
+ HeaderFooter *xlsxHeaderFooter `xml:"headerFooter"`
+ RowBreaks *xlsxBreaks `xml:"rowBreaks"`
+ ColBreaks *xlsxBreaks `xml:"colBreaks"`
+ CustomProperties *xlsxInnerXML `xml:"customProperties"`
+ CellWatches *xlsxInnerXML `xml:"cellWatches"`
+ IgnoredErrors *xlsxInnerXML `xml:"ignoredErrors"`
+ SmartTags *xlsxInnerXML `xml:"smartTags"`
+ Drawing *xlsxDrawing `xml:"drawing"`
+ LegacyDrawing *xlsxLegacyDrawing `xml:"legacyDrawing"`
+ LegacyDrawingHF *xlsxLegacyDrawingHF `xml:"legacyDrawingHF"`
+ DrawingHF *xlsxDrawingHF `xml:"drawingHF"`
+ Picture *xlsxPicture `xml:"picture"`
+ OleObjects *xlsxInnerXML `xml:"oleObjects"`
+ Controls *xlsxInnerXML `xml:"controls"`
+ WebPublishItems *xlsxInnerXML `xml:"webPublishItems"`
+ TableParts *xlsxTableParts `xml:"tableParts"`
+ ExtLst *xlsxExtLst `xml:"extLst"`
+ AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"`
+ DecodeAlternateContent *xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"`
}
// xlsxDrawing change r:id to rid in the namespace.
@@ -692,6 +694,16 @@ type xlsxLegacyDrawingHF struct {
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}
+// xlsxAlternateContent is a container for a sequence of multiple
+// representations of a given piece of content. The program reading the file
+// should only process one of these, and the one chosen should be based on
+// which conditions match.
+type xlsxAlternateContent struct {
+ XMLNSMC string `xml:"xmlns:mc,attr,omitempty"`
+ Content string `xml:",innerxml"`
+}
+
+// xlsxInnerXML holds parts of XML content currently not unmarshal.
type xlsxInnerXML struct {
Content string `xml:",innerxml"`
}