c5_labsci/zciyon/xlsx/xmlSharedStrings.go

165 lines
4.7 KiB
Go

package xlsx
import (
"encoding/xml"
"errors"
"strings"
)
// xlsxSST directly maps the sst element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main currently
// I have not checked this for completeness - it does as much as I need.
type xlsxSST struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main sst"`
Count int `xml:"count,attr"`
UniqueCount int `xml:"uniqueCount,attr"`
SI []xlsxSI `xml:"si"`
}
// xlsxSI directly maps the si element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
// currently I have not checked this for completeness - it does as
// much as I need.
type xlsxSI struct {
T *xlsxT `xml:"t"`
R []xlsxR `xml:"r"`
}
// xlsxR directly maps the r element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
// currently I have not checked this for completeness - it does as
// much as I need.
type xlsxR struct {
RPr *xlsxRunProperties `xml:"rPr"`
T xlsxT `xml:"t"`
}
// xlsxRunProperties directly maps the rPr element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main
type xlsxRunProperties struct {
RFont *xlsxVal `xml:"rFont"`
Charset *xlsxIntVal `xml:"charset"`
Family *xlsxIntVal `xml:"family"`
B xlsxBoolProp `xml:"b"`
I xlsxBoolProp `xml:"i"`
Strike xlsxBoolProp `xml:"strike"`
Outline xlsxBoolProp `xml:"outline"`
Shadow xlsxBoolProp `xml:"shadow"`
Condense xlsxBoolProp `xml:"condense"`
Extend xlsxBoolProp `xml:"extend"`
Color *xlsxColor `xml:"color"`
Sz *xlsxFloatVal `xml:"sz"`
U *xlsxVal `xml:"u"`
VertAlign *xlsxVal `xml:"vertAlign"`
Scheme *xlsxVal `xml:"scheme"`
}
// xlsxBoolProp handles "CT_BooleanProperty" type which is declared in the XML Schema of Office Open XML.
// XML attribute "val" is optional. If "val" was omitted, the property value becomes "true".
// On the serialization, the struct which has "true" will be serialized an empty XML tag without "val" attributes,
// and the struct which has "false" will not be serialized.
type xlsxBoolProp struct {
Val bool `xml:"val,attr"`
}
// MarshalXML implements xml.Marshaler interface for xlsxBoolProp
func (b *xlsxBoolProp) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if b.Val {
if err := e.EncodeToken(start); err != nil {
return err
}
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
return err
}
}
return nil
}
// UnmarshalXML implements xml.Unmarshaler interface for xlsxBoolProp
func (b *xlsxBoolProp) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
boolVal := true
for _, attr := range start.Attr {
if attr.Name.Space == "" && attr.Name.Local == "val" {
// supports xsd:boolean
switch attr.Value {
case "true", "1":
boolVal = true
case "false", "0":
boolVal = false
default:
return errors.New(
"Cannot unmarshal into xlsxBoolProp: \"" +
attr.Value + "\" is not a valid boolean value")
}
}
}
b.Val = boolVal
return d.Skip()
}
// xlsxIntVal is like xlsxVal, except it has an int value
type xlsxIntVal struct {
Val int `xml:"val,attr"`
}
// xlsxFloatVal is like xlsxVal, except it has a float value
type xlsxFloatVal struct {
Val float64 `xml:"val,attr"`
}
// xlsxT represents a text. It will be serialized as a XML tag which has character data.
// Attribute xml:space="preserve" will be added to the XML tag if needed.
type xlsxT struct {
Text string `xml:",chardata"`
}
// MarshalXML implements xml.Marshaler interface for xlsxT
func (t *xlsxT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if needPreserve(t.Text) {
attr := xml.Attr{
Name: xml.Name{Local: "xml:space"},
Value: "preserve",
}
start.Attr = append(start.Attr, attr)
}
if err := e.EncodeToken(start); err != nil {
return err
}
if err := e.EncodeToken(xml.CharData(t.Text)); err != nil {
return err
}
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
return err
}
return nil
}
// getText is a nil-safe utility function that gets a string from xlsxT.
// If the pointer of xlsxT was nil, returns an empty string.
func (t *xlsxT) getText() string {
if t == nil {
return ""
}
return t.Text
}
// needPreserve determines whether xml:space="preserve" is needed.
func needPreserve(s string) bool {
if len(s) == 0 {
return false
}
// Note:
// xml:space="preserve" is not needed for CR and TAB
// because they are serialized as "
" and "	".
c := s[0]
if c <= 32 && c != 9 && c != 13 {
return true
}
c = s[len(s)-1]
if c <= 32 && c != 9 && c != 13 {
return true
}
return strings.ContainsRune(s, '\u000a')
}