diff --git a/cell.go b/cell.go index 1f01ce36..edfcb3c8 100644 --- a/cell.go +++ b/cell.go @@ -451,17 +451,22 @@ func (f *File) setSharedString(val string) (int, error) { sst.Count++ sst.UniqueCount++ t := xlsxT{Val: val} - val, t.Space = trimCellValue(val) + val, t.Space = trimCellValue(val, false) sst.SI = append(sst.SI, xlsxSI{T: &t}) f.sharedStringsMap[val] = sst.UniqueCount - 1 return sst.UniqueCount - 1, nil } // trimCellValue provides a function to set string type to cell. -func trimCellValue(value string) (v string, ns xml.Attr) { +func trimCellValue(value string, escape bool) (v string, ns xml.Attr) { if utf8.RuneCountInString(value) > TotalCellChars { value = string([]rune(value)[:TotalCellChars]) } + buf := &bytes.Buffer{} + if escape { + _ = xml.EscapeText(buf, []byte(value)) + value = buf.String() + } if len(value) > 0 { prefix, suffix := value[0], value[len(value)-1] for _, ascii := range []byte{9, 10, 13, 32} { @@ -492,15 +497,13 @@ func (c *xlsxC) setCellValue(val string) { // string. func (c *xlsxC) setInlineStr(val string) { c.T, c.V, c.IS = "inlineStr", "", &xlsxSI{T: &xlsxT{}} - buf := &bytes.Buffer{} - _ = xml.EscapeText(buf, []byte(val)) - c.IS.T.Val, c.IS.T.Space = trimCellValue(buf.String()) + c.IS.T.Val, c.IS.T.Space = trimCellValue(val, true) } // setStr set cell data type and value which containing a formula string. func (c *xlsxC) setStr(val string) { c.T, c.IS = "str", nil - c.V, c.XMLSpace = trimCellValue(val) + c.V, c.XMLSpace = trimCellValue(val, false) } // getCellDate parse cell value which containing a boolean. @@ -1031,7 +1034,7 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) { return textRuns, ErrCellCharsLength } run := xlsxR{T: &xlsxT{}} - run.T.Val, run.T.Space = trimCellValue(textRun.Text) + run.T.Val, run.T.Space = trimCellValue(textRun.Text, false) fnt := textRun.Font if fnt != nil { run.RPr = newRpr(fnt) diff --git a/cell_test.go b/cell_test.go index 8f731b12..fdfd5132 100644 --- a/cell_test.go +++ b/cell_test.go @@ -177,16 +177,36 @@ func TestSetCellFloat(t *testing.T) { } func TestSetCellValuesMultiByte(t *testing.T) { - value := strings.Repeat("\u042B", TotalCellChars+1) - f := NewFile() - err := f.SetCellValue("Sheet1", "A1", value) + row := []interface{}{ + // Test set cell value with multi byte characters value + strings.Repeat("\u4E00", TotalCellChars+1), + // Test set cell value with XML escape characters + strings.Repeat("<>", TotalCellChars/2), + strings.Repeat(">", TotalCellChars-1), + strings.Repeat(">", TotalCellChars+1), + } + assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &row)) + // Test set cell value with XML escape characters in stream writer + _, err := f.NewSheet("Sheet2") assert.NoError(t, err) - - v, err := f.GetCellValue("Sheet1", "A1") + streamWriter, err := f.NewStreamWriter("Sheet2") assert.NoError(t, err) - assert.NotEqual(t, value, v) - assert.Equal(t, TotalCellChars, len([]rune(v))) + assert.NoError(t, streamWriter.SetRow("A1", row)) + assert.NoError(t, streamWriter.Flush()) + for _, sheetName := range []string{"Sheet1", "Sheet2"} { + for cell, expected := range map[string]int{ + "A1": TotalCellChars, + "B1": TotalCellChars - 1, + "C1": TotalCellChars - 1, + "D1": TotalCellChars, + } { + result, err := f.GetCellValue(sheetName, cell) + assert.NoError(t, err) + assert.Len(t, []rune(result), expected) + } + } + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellValuesMultiByte.xlsx"))) } func TestSetCellValue(t *testing.T) {