已验证 提交 d133dc12 编写于 作者: xurime's avatar xurime

Support format cell value with fraction number format code

- Fix delete incorrect image files when picture deleting pictures
- Update the unit test and dependencies modules
上级 87a00e4f
......@@ -741,10 +741,10 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
idxTbl := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
value := []string{"37947.7500001", "-37947.7500001", "0.007", "2.1", "String"}
expected := [][]string{
{"37947.7500001", "37948", "37947.75", "37,948", "37,947.75", "3794775%", "3794775.00%", "3.79E+04", "37947.7500001", "37947.7500001", "11-22-03", "22-Nov-03", "22-Nov", "Nov-03", "6:00 PM", "6:00:00 PM", "18:00", "18:00:00", "11/22/03 18:00", "37,948 ", "37,948 ", "37,947.75 ", "37,947.75 ", "37,948", "$37,948", "37,947.75", "$37,947.75", "00:00", "910746:00:00", "00:00.0", "37947.7500001", "37947.7500001"},
{"-37947.7500001", "-37948", "-37947.75", "-37,948", "-37,947.75", "-3794775%", "-3794775.00%", "-3.79E+04", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "(37,948)", "(37,948)", "(37,947.75)", "(37,947.75)", "(37,948)", "$(37,948)", "(37,947.75)", "$(37,947.75)", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001"},
{"0.007", "0", "0.01", "0", "0.01", "1%", "0.70%", "7.00E-03", "0.007", "0.007", "12-30-99", "30-Dec-99", "30-Dec", "Dec-99", "12:10 AM", "12:10:05 AM", "00:10", "00:10:05", "12/30/99 00:10", "0 ", "0 ", "0.01 ", "0.01 ", "0", "$0", "0.01", "$0.01", "10:05", "0:10:05", "10:04.8", "0.007", "0.007"},
{"2.1", "2", "2.10", "2", "2.10", "210%", "210.00%", "2.10E+00", "2.1", "2.1", "01-01-00", "1-Jan-00", "1-Jan", "Jan-00", "2:24 AM", "2:24:00 AM", "02:24", "02:24:00", "1/1/00 02:24", "2 ", "2 ", "2.10 ", "2.10 ", "2", "$2", "2.10", "$2.10", "24:00", "50:24:00", "24:00.0", "2.1", "2.1"},
{"37947.7500001", "37948", "37947.75", "37,948", "37,947.75", "3794775%", "3794775.00%", "3.79E+04", "37947 3/4", "37947 3/4", "11-22-03", "22-Nov-03", "22-Nov", "Nov-03", "6:00 PM", "6:00:00 PM", "18:00", "18:00:00", "11/22/03 18:00", "37,948 ", "37,948 ", "37,947.75 ", "37,947.75 ", "37,948", "$37,948", "37,947.75", "$37,947.75", "00:00", "910746:00:00", "00:00.0", "37947.7500001", "37947.7500001"},
{"-37947.7500001", "-37948", "-37947.75", "-37,948", "-37,947.75", "-3794775%", "-3794775.00%", "-3.79E+04", "-37947 3/4", "-37947 3/4", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "(37,948)", "(37,948)", "(37,947.75)", "(37,947.75)", "(37,948)", "$(37,948)", "(37,947.75)", "$(37,947.75)", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001"},
{"0.007", "0", "0.01", "0", "0.01", "1%", "0.70%", "7.00E-03", "0 ", "0 ", "12-30-99", "30-Dec-99", "30-Dec", "Dec-99", "12:10 AM", "12:10:05 AM", "00:10", "00:10:05", "12/30/99 00:10", "0 ", "0 ", "0.01 ", "0.01 ", "0", "$0", "0.01", "$0.01", "10:05", "0:10:05", "10:04.8", "0.007", "0.007"},
{"2.1", "2", "2.10", "2", "2.10", "210%", "210.00%", "2.10E+00", "2 1/9", "2 1/10", "01-01-00", "1-Jan-00", "1-Jan", "Jan-00", "2:24 AM", "2:24:00 AM", "02:24", "02:24:00", "1/1/00 02:24", "2 ", "2 ", "2.10 ", "2.10 ", "2", "$2", "2.10", "$2.10", "24:00", "50:24:00", "24:00.0", "2.1", "2.1"},
{"String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String"},
}
......
......@@ -8,9 +8,9 @@ require (
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/stretchr/testify v1.8.0
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca
github.com/xuri/nfp v0.0.0-20230918160701-e5a3f5b24785
golang.org/x/crypto v0.13.0
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05
golang.org/x/crypto v0.14.0
golang.org/x/image v0.11.0
golang.org/x/net v0.15.0
golang.org/x/net v0.16.0
golang.org/x/text v0.13.0
)
......@@ -17,13 +17,13 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca h1:uvPMDVyP7PXMMioYdyPH+0O+Ta/UO1WFfNYMO3Wz0eg=
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/nfp v0.0.0-20230918160701-e5a3f5b24785 h1:FG9hcK7lhf3w/Y2NRUKy/mopsH0Oy6P1rib1KWXAie0=
github.com/xuri/nfp v0.0.0-20230918160701-e5a3f5b24785/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
......@@ -33,8 +33,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
......@@ -45,12 +45,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
......
......@@ -18,6 +18,7 @@ import (
"encoding/xml"
"fmt"
"io"
"math"
"math/big"
"os"
"regexp"
......@@ -825,6 +826,30 @@ func bstrMarshal(s string) (result string) {
return result
}
// newRat converts decimals to rational fractions with the required precision.
func newRat(n float64, iterations int64, prec float64) *big.Rat {
x := int64(math.Floor(n))
y := n - float64(x)
rat := continuedFraction(y, 1, iterations, prec)
return rat.Add(rat, new(big.Rat).SetInt64(x))
}
// continuedFraction returns rational from decimal with the continued fraction
// algorithm.
func continuedFraction(n float64, i int64, limit int64, prec float64) *big.Rat {
if i >= limit || n <= prec {
return big.NewRat(0, 1)
}
inverted := 1 / n
y := int64(math.Floor(inverted))
x := inverted - float64(y)
ratY := new(big.Rat).SetInt64(y)
ratNext := continuedFraction(x, i+1, limit, prec)
res := ratY.Add(ratY, ratNext)
res = res.Inv(res)
return res
}
// Stack defined an abstract data type that serves as a collection of elements.
type Stack struct {
list *list.List
......
......@@ -33,18 +33,18 @@ type languageInfo struct {
// numberFormat directly maps the number format parser runtime required
// fields.
type numberFormat struct {
opts *Options
cellType CellType
section []nfp.Section
t time.Time
sectionIdx int
date1904, isNumeric, hours, seconds, useMillisecond, useGannen bool
number float64
ap, localCode, result, value, valueSectionType string
switchArgument, currencyString string
fracHolder, fracPadding, intHolder, intPadding, expBaseLen int
percent int
useCommaSep, usePointer, usePositive, useScientificNotation bool
opts *Options
cellType CellType
section []nfp.Section
t time.Time
sectionIdx int
date1904, isNumeric, hours, seconds, useMillisecond, useGannen bool
number float64
ap, localCode, result, value, valueSectionType string
switchArgument, currencyString string
fracHolder, fracPadding, intHolder, intPadding, expBaseLen int
percent int
useCommaSep, useFraction, usePointer, usePositive, useScientificNotation bool
}
// CultureName is the type of supported language country codes types for apply
......@@ -688,8 +688,11 @@ var (
nfp.TokenTypeCurrencyLanguage,
nfp.TokenTypeDateTimes,
nfp.TokenTypeDecimalPoint,
nfp.TokenTypeDenominator,
nfp.TokenTypeDigitalPlaceHolder,
nfp.TokenTypeElapsedDateTimes,
nfp.TokenTypeExponential,
nfp.TokenTypeFraction,
nfp.TokenTypeGeneral,
nfp.TokenTypeHashPlaceHolder,
nfp.TokenTypeLiteral,
......@@ -702,7 +705,10 @@ var (
}
// supportedNumberTokenTypes list the supported number token types.
supportedNumberTokenTypes = []string{
nfp.TokenTypeDenominator,
nfp.TokenTypeDigitalPlaceHolder,
nfp.TokenTypeExponential,
nfp.TokenTypeFraction,
nfp.TokenTypeHashPlaceHolder,
nfp.TokenTypePercent,
nfp.TokenTypeZeroPlaceHolder,
......@@ -4775,6 +4781,9 @@ func (nf *numberFormat) getNumberFmtConf() {
if token.TType == nfp.TokenTypeDecimalPoint {
nf.usePointer = true
}
if token.TType == nfp.TokenTypeFraction {
nf.useFraction = true
}
if token.TType == nfp.TokenTypeSwitchArgument {
nf.switchArgument = token.TValue
}
......@@ -4795,8 +4804,11 @@ func (nf *numberFormat) getNumberFmtConf() {
// printNumberLiteral apply literal tokens for the pre-formatted text.
func (nf *numberFormat) printNumberLiteral(text string) string {
var result string
var useLiteral, usePlaceHolder bool
var (
result string
frac float64
useFraction, useLiteral, usePlaceHolder bool
)
if nf.usePositive {
result += "-"
}
......@@ -4822,10 +4834,41 @@ func (nf *numberFormat) printNumberLiteral(text string) string {
result += text
}
}
if token.TType == nfp.TokenTypeFraction {
_, frac = math.Modf(nf.number)
frac, useFraction = math.Abs(frac), true
}
if useFraction {
result += nf.fractionHandler(frac, token)
}
}
return nf.printSwitchArgument(result)
}
// fractionHandler handling fraction number format expression for positive and
// negative numeric.
func (nf *numberFormat) fractionHandler(frac float64, token nfp.Token) string {
var rat, result string
if token.TType == nfp.TokenTypeDigitalPlaceHolder {
fracPlaceHolder := len(token.TValue)
for i := 0; i < 5000; i++ {
if r := newRat(frac, int64(i), 0); len(r.Denom().String()) <= fracPlaceHolder {
if rat = r.String(); strings.HasPrefix(rat, "0/") {
rat = strings.Repeat(" ", 3)
}
continue
}
break
}
result += rat
}
if token.TType == nfp.TokenTypeDenominator {
denom, _ := strconv.ParseFloat(token.TValue, 64)
result += fmt.Sprintf("%d/%d", int(math.Round(frac*denom)), int(math.Round(denom)))
}
return result
}
// printCommaSep format number with thousands separator.
func printCommaSep(text string) string {
var (
......@@ -4929,6 +4972,9 @@ func (nf *numberFormat) numberHandler() string {
if nf.percent > 0 {
num *= math.Pow(100, float64(nf.percent))
}
if nf.useFraction {
num = math.Floor(math.Abs(num))
}
if result = fmt.Sprintf(fmtCode, math.Abs(num)); nf.useCommaSep {
result = printCommaSep(result)
}
......
......@@ -3543,6 +3543,16 @@ func TestNumFmt(t *testing.T) {
{"1.234E-16", "0.000000000000000000", "0.000000000000000123"},
{"1.234E-16", "0.000000000000000000%", "0.000000000000012340%"},
{"1.234E-16", "0.000000000000000000%%%%", "0.000000000000012340%"},
{"-123.4567", "# ?/?", "-123 1/2"},
{"123.4567", "# ??/??", "123 37/81"},
{"123.4567", "#\\ ???/???", "123 58/127"},
{"123.4567", "#\\ ?/2", "123 1/2"},
{"123.4567", "#\\ ?/4", "123 2/4"},
{"123.4567", "#\\ ?/8", "123 4/8"},
{"123.4567", "#\\ ?/16", "123 7/16"},
{"123.4567", "#\\ ?/10", "123 5/10"},
{"-123.4567", "#\\ ?/100", "-123 46/100"},
{"123.4567", "#\\ ?/1000", "123 457/1000"},
{"1234.5678", "[$$-409]#,##0.00", "$1,234.57"},
// Unsupported number format
{"37947.7500001", "0.00000000E+000", "37947.7500001"},
......
......@@ -231,7 +231,18 @@ func (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error {
drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
mediaStr := ".." + strings.TrimPrefix(f.addMedia(pic.File, ext), "xl")
drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
var drawingRID int
if rels, _ := f.relsReader(drawingRels); rels != nil {
for _, rel := range rels.Relationships {
if rel.Type == SourceRelationshipImage && rel.Target == mediaStr {
drawingRID, _ = strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
break
}
}
}
if drawingRID == 0 {
drawingRID = f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
}
// Add picture with hyperlink.
if options.Hyperlink != "" && options.HyperlinkType != "" {
if options.HyperlinkType == "External" {
......@@ -494,8 +505,8 @@ func (f *File) DeletePicture(sheet, cell string) error {
return err
}
var used bool
f.Pkg.Range(func(k, v interface{}) bool {
if strings.Contains(k.(string), "xl/drawings/_rels/") {
checkPicRef := func(k, v interface{}) bool {
if strings.Contains(k.(string), "xl/drawings/_rels/drawing") {
r, err := f.relsReader(k.(string))
if err != nil {
return true
......@@ -508,7 +519,9 @@ func (f *File) DeletePicture(sheet, cell string) error {
}
}
return true
})
}
f.Relationships.Range(checkPicRef)
f.Pkg.Range(checkPicRef)
if !used {
f.Pkg.Delete(strings.Replace(rels.Target, "../", "xl/", -1))
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册