Stream writer support to set inline rich text cell (#1121)

Co-authored-by: zhengchao.deng <zhengchao.deng@meican.com>
This commit is contained in:
charles.deng 2022-10-10 00:11:18 +08:00 committed by GitHub
parent b1e776ee33
commit 2f5704b114
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 39 deletions

42
cell.go
View File

@ -885,6 +885,28 @@ func newRpr(fnt *Font) *xlsxRPr {
return &rpr
}
// setRichText provides a function to set rich text of a cell.
func setRichText(runs []RichTextRun) ([]xlsxR, error) {
var (
textRuns []xlsxR
totalCellChars int
)
for _, textRun := range runs {
totalCellChars += len(textRun.Text)
if totalCellChars > TotalCellChars {
return textRuns, ErrCellCharsLength
}
run := xlsxR{T: &xlsxT{}}
_, run.T.Val, run.T.Space = setCellStr(textRun.Text)
fnt := textRun.Font
if fnt != nil {
run.RPr = newRpr(fnt)
}
textRuns = append(textRuns, run)
}
return textRuns, nil
}
// SetCellRichText provides a function to set cell with rich text by given
// worksheet. For example, set rich text on the A1 cell of the worksheet named
// Sheet1:
@ -1016,24 +1038,10 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
return err
}
c.S = f.prepareCellStyle(ws, col, row, c.S)
si := xlsxSI{}
sst := f.sharedStringsReader()
var textRuns []xlsxR
totalCellChars := 0
for _, textRun := range runs {
totalCellChars += len(textRun.Text)
if totalCellChars > TotalCellChars {
return ErrCellCharsLength
}
run := xlsxR{T: &xlsxT{}}
_, run.T.Val, run.T.Space = setCellStr(textRun.Text)
fnt := textRun.Font
if fnt != nil {
run.RPr = newRpr(fnt)
}
textRuns = append(textRuns, run)
si, sst := xlsxSI{}, f.sharedStringsReader()
if si.R, err = setRichText(runs); err != nil {
return err
}
si.R = textRuns
for idx, strItem := range sst.SI {
if reflect.DeepEqual(strItem, si) {
c.T, c.V = "s", strconv.Itoa(idx)

View File

@ -444,12 +444,12 @@ func (f *File) UpdateLinkedValue() error {
// AddVBAProject provides the method to add vbaProject.bin file which contains
// functions and/or macros. The file extension should be .xlsm. For example:
//
// codeName := "Sheet1"
// if err := f.SetSheetProps("Sheet1", &excelize.SheetPropsOptions{
// CodeName: &codeName,
// }); err != nil {
// fmt.Println(err)
// }
// codeName := "Sheet1"
// if err := f.SetSheetProps("Sheet1", &excelize.SheetPropsOptions{
// CodeName: &codeName,
// }); err != nil {
// fmt.Println(err)
// }
// if err := f.AddVBAProject("vbaProject.bin"); err != nil {
// fmt.Println(err)
// }

View File

@ -56,7 +56,14 @@ type StreamWriter struct {
// if err != nil {
// fmt.Println(err)
// }
// if err := streamWriter.SetRow("A1", []interface{}{excelize.Cell{StyleID: styleID, Value: "Data"}},
// if err := streamWriter.SetRow("A1",
// []interface{}{
// excelize.Cell{StyleID: styleID, Value: "Data"},
// []excelize.RichTextRun{
// {Text: "Rich ", Font: &excelize.Font{Color: "2354e8"}},
// {Text: "Text", Font: &excelize.Font{Color: "e83723"}},
// },
// },
// excelize.RowOpts{Height: 45, Hidden: false}); err != nil {
// fmt.Println(err)
// }
@ -433,7 +440,8 @@ func setCellFormula(c *xlsxC, formula string) {
}
// setCellValFunc provides a function to set value of a cell.
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) (err error) {
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
var err error
switch val := val.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
err = setCellIntFunc(c, val)
@ -462,6 +470,9 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) (err error) {
c.T, c.V = setCellBool(val)
case nil:
c.T, c.V, c.XMLSpace = setCellStr("")
case []RichTextRun:
c.T, c.IS = "inlineStr", &xlsxSI{}
c.IS.R, err = setRichText(val)
default:
c.T, c.V, c.XMLSpace = setCellStr(fmt.Sprint(val))
}
@ -519,6 +530,12 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
_ = xml.EscapeText(buf, []byte(c.V))
_, _ = buf.WriteString(`</v>`)
}
if c.IS != nil {
is, _ := xml.Marshal(c.IS.R)
_, _ = buf.WriteString(`<is>`)
_, _ = buf.Write(is)
_, _ = buf.WriteString(`</is>`)
}
_, _ = buf.WriteString(`</c>`)
}

View File

@ -52,11 +52,14 @@ func TestStreamWriter(t *testing.T) {
row[0] = []byte("Word")
assert.NoError(t, streamWriter.SetRow("A3", row))
// Test set cell with style.
// Test set cell with style and rich text.
styleID, err := file.NewStyle(&Style{Font: &Font{Color: "#777777"}})
assert.NoError(t, err)
assert.NoError(t, streamWriter.SetRow("A4", []interface{}{Cell{StyleID: styleID}, Cell{Formula: "SUM(A10,B10)"}}, RowOpts{Height: 45, StyleID: styleID}))
assert.NoError(t, streamWriter.SetRow("A5", []interface{}{&Cell{StyleID: styleID, Value: "cell"}, &Cell{Formula: "SUM(A10,B10)"}}))
assert.NoError(t, streamWriter.SetRow("A5", []interface{}{&Cell{StyleID: styleID, Value: "cell"}, &Cell{Formula: "SUM(A10,B10)"}, []RichTextRun{
{Text: "Rich ", Font: &Font{Color: "2354e8"}},
{Text: "Text", Font: &Font{Color: "e83723"}},
}}))
assert.NoError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}))
assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID}))
assert.EqualError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: MaxRowHeight + 1}), ErrMaxRowHeight.Error())
@ -128,7 +131,7 @@ func TestStreamWriter(t *testing.T) {
cells += len(row)
}
assert.NoError(t, rows.Close())
assert.Equal(t, 2559558, cells)
assert.Equal(t, 2559559, cells)
// Save spreadsheet with password.
assert.NoError(t, file.SaveAs(filepath.Join("test", "EncryptionTestStreamWriter.xlsx"), Options{Password: "password"}))
assert.NoError(t, file.Close())

View File

@ -46,8 +46,9 @@ type xlsxSI struct {
// properties are defined in the rPr element, and the text displayed to the
// user is defined in the Text (t) element.
type xlsxR struct {
RPr *xlsxRPr `xml:"rPr"`
T *xlsxT `xml:"t"`
XMLName xml.Name `xml:"r"`
RPr *xlsxRPr `xml:"rPr"`
T *xlsxT `xml:"t"`
}
// xlsxT directly maps the t element in the run properties.

View File

@ -466,15 +466,14 @@ type xlsxC struct {
XMLName xml.Name `xml:"c"`
XMLSpace xml.Attr `xml:"space,attr,omitempty"`
R string `xml:"r,attr,omitempty"` // 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.
Cm *uint `xml:"cm,attr,omitempty"` //
Vm *uint `xml:"vm,attr,omitempty"` //
Ph *bool `xml:"ph,attr,omitempty"` //
F *xlsxF `xml:"f,omitempty"` // Formula
V string `xml:"v,omitempty"` // Value
IS *xlsxSI `xml:"is"`
S int `xml:"s,attr,omitempty"` // Style reference
T string `xml:"t,attr,omitempty"` // Type
Cm *uint `xml:"cm,attr"`
Vm *uint `xml:"vm,attr"`
Ph *bool `xml:"ph,attr"`
F *xlsxF `xml:"f"` // Formula
V string `xml:"v,omitempty"` // Value
IS *xlsxSI `xml:"is"`
}
// xlsxF represents a formula for the cell. The formula expression is