Skip to content

Commit 2f5704b

Browse files
Stream writer support to set inline rich text cell (qax-os#1121)
Co-authored-by: zhengchao.deng <zhengchao.deng@meican.com>
1 parent b1e776e commit 2f5704b

6 files changed

+67
-39
lines changed

cell.go

+25-17
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,28 @@ func newRpr(fnt *Font) *xlsxRPr {
885885
return &rpr
886886
}
887887

888+
// setRichText provides a function to set rich text of a cell.
889+
func setRichText(runs []RichTextRun) ([]xlsxR, error) {
890+
var (
891+
textRuns []xlsxR
892+
totalCellChars int
893+
)
894+
for _, textRun := range runs {
895+
totalCellChars += len(textRun.Text)
896+
if totalCellChars > TotalCellChars {
897+
return textRuns, ErrCellCharsLength
898+
}
899+
run := xlsxR{T: &xlsxT{}}
900+
_, run.T.Val, run.T.Space = setCellStr(textRun.Text)
901+
fnt := textRun.Font
902+
if fnt != nil {
903+
run.RPr = newRpr(fnt)
904+
}
905+
textRuns = append(textRuns, run)
906+
}
907+
return textRuns, nil
908+
}
909+
888910
// SetCellRichText provides a function to set cell with rich text by given
889911
// worksheet. For example, set rich text on the A1 cell of the worksheet named
890912
// Sheet1:
@@ -1016,24 +1038,10 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
10161038
return err
10171039
}
10181040
c.S = f.prepareCellStyle(ws, col, row, c.S)
1019-
si := xlsxSI{}
1020-
sst := f.sharedStringsReader()
1021-
var textRuns []xlsxR
1022-
totalCellChars := 0
1023-
for _, textRun := range runs {
1024-
totalCellChars += len(textRun.Text)
1025-
if totalCellChars > TotalCellChars {
1026-
return ErrCellCharsLength
1027-
}
1028-
run := xlsxR{T: &xlsxT{}}
1029-
_, run.T.Val, run.T.Space = setCellStr(textRun.Text)
1030-
fnt := textRun.Font
1031-
if fnt != nil {
1032-
run.RPr = newRpr(fnt)
1033-
}
1034-
textRuns = append(textRuns, run)
1041+
si, sst := xlsxSI{}, f.sharedStringsReader()
1042+
if si.R, err = setRichText(runs); err != nil {
1043+
return err
10351044
}
1036-
si.R = textRuns
10371045
for idx, strItem := range sst.SI {
10381046
if reflect.DeepEqual(strItem, si) {
10391047
c.T, c.V = "s", strconv.Itoa(idx)

excelize.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -444,12 +444,12 @@ func (f *File) UpdateLinkedValue() error {
444444
// AddVBAProject provides the method to add vbaProject.bin file which contains
445445
// functions and/or macros. The file extension should be .xlsm. For example:
446446
//
447-
// codeName := "Sheet1"
448-
// if err := f.SetSheetProps("Sheet1", &excelize.SheetPropsOptions{
449-
// CodeName: &codeName,
450-
// }); err != nil {
451-
// fmt.Println(err)
452-
// }
447+
// codeName := "Sheet1"
448+
// if err := f.SetSheetProps("Sheet1", &excelize.SheetPropsOptions{
449+
// CodeName: &codeName,
450+
// }); err != nil {
451+
// fmt.Println(err)
452+
// }
453453
// if err := f.AddVBAProject("vbaProject.bin"); err != nil {
454454
// fmt.Println(err)
455455
// }

stream.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,14 @@ type StreamWriter struct {
5656
// if err != nil {
5757
// fmt.Println(err)
5858
// }
59-
// if err := streamWriter.SetRow("A1", []interface{}{excelize.Cell{StyleID: styleID, Value: "Data"}},
59+
// if err := streamWriter.SetRow("A1",
60+
// []interface{}{
61+
// excelize.Cell{StyleID: styleID, Value: "Data"},
62+
// []excelize.RichTextRun{
63+
// {Text: "Rich ", Font: &excelize.Font{Color: "2354e8"}},
64+
// {Text: "Text", Font: &excelize.Font{Color: "e83723"}},
65+
// },
66+
// },
6067
// excelize.RowOpts{Height: 45, Hidden: false}); err != nil {
6168
// fmt.Println(err)
6269
// }
@@ -433,7 +440,8 @@ func setCellFormula(c *xlsxC, formula string) {
433440
}
434441

435442
// setCellValFunc provides a function to set value of a cell.
436-
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) (err error) {
443+
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
444+
var err error
437445
switch val := val.(type) {
438446
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
439447
err = setCellIntFunc(c, val)
@@ -462,6 +470,9 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) (err error) {
462470
c.T, c.V = setCellBool(val)
463471
case nil:
464472
c.T, c.V, c.XMLSpace = setCellStr("")
473+
case []RichTextRun:
474+
c.T, c.IS = "inlineStr", &xlsxSI{}
475+
c.IS.R, err = setRichText(val)
465476
default:
466477
c.T, c.V, c.XMLSpace = setCellStr(fmt.Sprint(val))
467478
}
@@ -519,6 +530,12 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
519530
_ = xml.EscapeText(buf, []byte(c.V))
520531
_, _ = buf.WriteString(`</v>`)
521532
}
533+
if c.IS != nil {
534+
is, _ := xml.Marshal(c.IS.R)
535+
_, _ = buf.WriteString(`<is>`)
536+
_, _ = buf.Write(is)
537+
_, _ = buf.WriteString(`</is>`)
538+
}
522539
_, _ = buf.WriteString(`</c>`)
523540
}
524541

stream_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,14 @@ func TestStreamWriter(t *testing.T) {
5252
row[0] = []byte("Word")
5353
assert.NoError(t, streamWriter.SetRow("A3", row))
5454

55-
// Test set cell with style.
55+
// Test set cell with style and rich text.
5656
styleID, err := file.NewStyle(&Style{Font: &Font{Color: "#777777"}})
5757
assert.NoError(t, err)
5858
assert.NoError(t, streamWriter.SetRow("A4", []interface{}{Cell{StyleID: styleID}, Cell{Formula: "SUM(A10,B10)"}}, RowOpts{Height: 45, StyleID: styleID}))
59-
assert.NoError(t, streamWriter.SetRow("A5", []interface{}{&Cell{StyleID: styleID, Value: "cell"}, &Cell{Formula: "SUM(A10,B10)"}}))
59+
assert.NoError(t, streamWriter.SetRow("A5", []interface{}{&Cell{StyleID: styleID, Value: "cell"}, &Cell{Formula: "SUM(A10,B10)"}, []RichTextRun{
60+
{Text: "Rich ", Font: &Font{Color: "2354e8"}},
61+
{Text: "Text", Font: &Font{Color: "e83723"}},
62+
}}))
6063
assert.NoError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}))
6164
assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID}))
6265
assert.EqualError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: MaxRowHeight + 1}), ErrMaxRowHeight.Error())
@@ -128,7 +131,7 @@ func TestStreamWriter(t *testing.T) {
128131
cells += len(row)
129132
}
130133
assert.NoError(t, rows.Close())
131-
assert.Equal(t, 2559558, cells)
134+
assert.Equal(t, 2559559, cells)
132135
// Save spreadsheet with password.
133136
assert.NoError(t, file.SaveAs(filepath.Join("test", "EncryptionTestStreamWriter.xlsx"), Options{Password: "password"}))
134137
assert.NoError(t, file.Close())

xmlSharedStrings.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ type xlsxSI struct {
4646
// properties are defined in the rPr element, and the text displayed to the
4747
// user is defined in the Text (t) element.
4848
type xlsxR struct {
49-
RPr *xlsxRPr `xml:"rPr"`
50-
T *xlsxT `xml:"t"`
49+
XMLName xml.Name `xml:"r"`
50+
RPr *xlsxRPr `xml:"rPr"`
51+
T *xlsxT `xml:"t"`
5152
}
5253

5354
// xlsxT directly maps the t element in the run properties.

xmlWorksheet.go

+8-9
Original file line numberDiff line numberDiff line change
@@ -466,15 +466,14 @@ type xlsxC struct {
466466
XMLName xml.Name `xml:"c"`
467467
XMLSpace xml.Attr `xml:"space,attr,omitempty"`
468468
R string `xml:"r,attr,omitempty"` // Cell ID, e.g. A1
469-
S int `xml:"s,attr,omitempty"` // Style reference.
470-
// Str string `xml:"str,attr,omitempty"` // Style reference.
471-
T string `xml:"t,attr,omitempty"` // Type.
472-
Cm *uint `xml:"cm,attr,omitempty"` //
473-
Vm *uint `xml:"vm,attr,omitempty"` //
474-
Ph *bool `xml:"ph,attr,omitempty"` //
475-
F *xlsxF `xml:"f,omitempty"` // Formula
476-
V string `xml:"v,omitempty"` // Value
477-
IS *xlsxSI `xml:"is"`
469+
S int `xml:"s,attr,omitempty"` // Style reference
470+
T string `xml:"t,attr,omitempty"` // Type
471+
Cm *uint `xml:"cm,attr"`
472+
Vm *uint `xml:"vm,attr"`
473+
Ph *bool `xml:"ph,attr"`
474+
F *xlsxF `xml:"f"` // Formula
475+
V string `xml:"v,omitempty"` // Value
476+
IS *xlsxSI `xml:"is"`
478477
}
479478

480479
// xlsxF represents a formula for the cell. The formula expression is

0 commit comments

Comments
 (0)