mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
spreadsheet: add helpers and example for number/date/time formatting
This commit is contained in:
parent
70051ae509
commit
10498401d6
60
_examples/spreadsheet/number-date-time-formats/main.go
Normal file
60
_examples/spreadsheet/number-date-time-formats/main.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2017 Baliance. All rights reserved.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"baliance.com/gooxml/spreadsheet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ss := spreadsheet.New()
|
||||
sheet := ss.AddSheet()
|
||||
|
||||
row := sheet.AddRow()
|
||||
cell := row.AddCell()
|
||||
|
||||
// If no formatting/styles are applied, then the 'general' format is used.
|
||||
cell.SetNumber(1.234)
|
||||
|
||||
// You can also apply a format at the same time you are setting a number.
|
||||
cell = row.AddCell()
|
||||
cell.SetNumberWithStyle(0.95, spreadsheet.StandardFormatPercent)
|
||||
|
||||
// But that involves a few lookups, so if you're creating many, many cells
|
||||
// it wil be faster to
|
||||
cell = row.AddCell()
|
||||
// create the style
|
||||
dateStyle := ss.StyleSheet.AddCellStyle()
|
||||
// set its format
|
||||
dateStyle.SetNumberFormatStandard(spreadsheet.StandardFormatDate)
|
||||
// and apply it to a cell
|
||||
cell.SetDate(time.Now())
|
||||
cell.SetStyle(dateStyle)
|
||||
|
||||
// It's even faster if repeatedly applying a style to apply the style index
|
||||
// directly. This is probably not worth the hassle most of the time, and
|
||||
// will generate the same content as calling setXWithStyle
|
||||
cs := ss.StyleSheet.AddCellStyle()
|
||||
cs.SetNumberFormatStandard(spreadsheet.StandardFormatTime)
|
||||
idx := cs.Index()
|
||||
for i := 0; i < 5; i++ {
|
||||
cell = row.AddCell()
|
||||
cell.SetDate(time.Now())
|
||||
cell.SetStyleIndex(idx)
|
||||
}
|
||||
|
||||
// completely custom number formats can also be used
|
||||
customStyle := ss.StyleSheet.AddCellStyle()
|
||||
customStyle.SetNumberFormat("$#,##0.00")
|
||||
cell = row.AddCell()
|
||||
cell.SetNumber(1.234)
|
||||
cell.SetStyle(customStyle)
|
||||
|
||||
if err := ss.Validate(); err != nil {
|
||||
log.Fatalf("error validating sheet: %s", err)
|
||||
}
|
||||
|
||||
ss.SaveToFile("number-date-time-formats.xlsx")
|
||||
}
|
Binary file not shown.
@ -69,6 +69,12 @@ func (c Cell) SetNumber(v float64) {
|
||||
c.x.TAttr = sml.ST_CellTypeN
|
||||
}
|
||||
|
||||
// SetNumberWithStyle sets a number and applies a standard format to the cell.
|
||||
func (c Cell) SetNumberWithStyle(v float64, f StandardFormat) {
|
||||
c.SetNumber(v)
|
||||
c.SetStyle(c.w.StyleSheet.GetOrCreateStandardNumberFormat(f))
|
||||
}
|
||||
|
||||
// SetBool sets the cell type to boolean and the value to the given boolean
|
||||
// value.
|
||||
func (c Cell) SetBool(v bool) {
|
||||
@ -108,7 +114,8 @@ func (c Cell) SetTime(d time.Time) {
|
||||
// SetDate sets the cell value to a date. It's stored as the number of days past
|
||||
// th sheet epoch. When we support v5 strict, we can store an ISO 8601 date
|
||||
// string directly, however that's not allowed with v5 transitional (even
|
||||
// though it works in Excel).
|
||||
// though it works in Excel). The cell is not styled via this method, so it will
|
||||
// display as a number. SetDateWithStyle should normally be used instead.
|
||||
func (c Cell) SetDate(d time.Time) {
|
||||
d = asUTC(d)
|
||||
epoch := c.w.Epoch()
|
||||
@ -122,10 +129,32 @@ func (c Cell) SetDate(d time.Time) {
|
||||
c.x.V = gooxml.Stringf("%d", int(delta.Hours()/24))
|
||||
}
|
||||
|
||||
// SetStyle applies a style to the cell. This style is referenced in the generated XML
|
||||
// via CellStyle.Index().
|
||||
// SetDateWithStyle sets a date with the default date style applied.
|
||||
func (c Cell) SetDateWithStyle(d time.Time) {
|
||||
c.SetDate(d)
|
||||
for _, cs := range c.w.StyleSheet.CellStyles() {
|
||||
// found an existing number format
|
||||
if cs.HasNumberFormat() && cs.NumberFormat() == uint32(StandardFormatDate) {
|
||||
c.SetStyle(cs)
|
||||
return
|
||||
}
|
||||
}
|
||||
// need to create a new format
|
||||
cs := c.w.StyleSheet.AddCellStyle()
|
||||
cs.SetNumberFormatStandard(StandardFormatDate)
|
||||
c.SetStyle(cs)
|
||||
}
|
||||
|
||||
// SetStyle applies a style to the cell. This style is referenced in the
|
||||
// generated XML via CellStyle.Index().
|
||||
func (c Cell) SetStyle(cs CellStyle) {
|
||||
c.x.SAttr = gooxml.Uint32(cs.Index())
|
||||
c.SetStyleIndex(cs.Index())
|
||||
}
|
||||
|
||||
// SetStyleIndex directly sets a style index to the cell. This should only be
|
||||
// called with an index retrieved from CellStyle.Index()
|
||||
func (c Cell) SetStyleIndex(idx uint32) {
|
||||
c.x.SAttr = gooxml.Uint32(idx)
|
||||
}
|
||||
|
||||
func (c Cell) GetValue() (string, error) {
|
||||
|
@ -15,10 +15,26 @@ import (
|
||||
// CellStyle is a formatting style for a cell. CellStyles are spreadsheet global
|
||||
// and can be applied to cells across sheets.
|
||||
type CellStyle struct {
|
||||
wb *Workbook
|
||||
xf *sml.CT_Xf
|
||||
xfs *sml.CT_CellXfs
|
||||
}
|
||||
|
||||
// HasNumberFormat returns true if the cell style has a number format applied.
|
||||
func (cs CellStyle) HasNumberFormat() bool {
|
||||
return cs.xf.NumFmtIdAttr != nil && cs.xf.ApplyNumberFormatAttr != nil &&
|
||||
*cs.xf.ApplyNumberFormatAttr
|
||||
}
|
||||
|
||||
// NumberFormat returns the number format that the cell style uses, or zero if
|
||||
// it is not set.
|
||||
func (cs CellStyle) NumberFormat() uint32 {
|
||||
if cs.xf.NumFmtIdAttr == nil {
|
||||
return 0
|
||||
}
|
||||
return *cs.xf.NumFmtIdAttr
|
||||
}
|
||||
|
||||
// ClearNumberFormat removes any number formatting from the style.
|
||||
func (cs CellStyle) ClearNumberFormat() {
|
||||
cs.xf.NumFmtIdAttr = nil
|
||||
@ -32,6 +48,15 @@ func (cs CellStyle) SetNumberFormatStandard(s StandardFormat) {
|
||||
cs.xf.ApplyNumberFormatAttr = gooxml.Bool(true)
|
||||
}
|
||||
|
||||
func (cs CellStyle) SetNumberFormat(s string) {
|
||||
|
||||
//SetNumberFormat
|
||||
nf := cs.wb.StyleSheet.AddNumberFormat()
|
||||
nf.SetCode(s)
|
||||
cs.xf.ApplyNumberFormatAttr = gooxml.Bool(true)
|
||||
cs.xf.NumFmtIdAttr = gooxml.Uint32(nf.ID())
|
||||
}
|
||||
|
||||
// Wrapped returns true if the cell will wrap text.
|
||||
func (cs CellStyle) Wrapped() bool {
|
||||
if cs.xf.Alignment == nil {
|
||||
|
@ -7,74 +7,42 @@
|
||||
|
||||
package spreadsheet
|
||||
|
||||
// Extracted from ECMA-376 Part 1 Section 18.8.30
|
||||
/*
|
||||
0 General
|
||||
1 0
|
||||
2 0.00
|
||||
3 #,##0
|
||||
4 #,##0.00
|
||||
9 0%
|
||||
10 0.00%
|
||||
11 0.00E+00
|
||||
12 # ?/?
|
||||
13 # ??/??
|
||||
14 mm-dd-yy
|
||||
15 d-mmm-yy
|
||||
16 d-mmm
|
||||
17 mmm-yy
|
||||
18 h:mm AM/PM
|
||||
19 h:mm:ss AM/PM
|
||||
20 h:mm
|
||||
21 h:mm:ss
|
||||
22 m/d/yy h:mm
|
||||
37 #,##0 ;(#,##0)
|
||||
38 #,##0 ;[Red](#,##0)
|
||||
39 #,##0.00;(#,##0.00)
|
||||
40 #,##0.00;[Red](#,##0.00)
|
||||
45 mm:ss
|
||||
46 [h]:mm:ss
|
||||
47 mmss.0
|
||||
48 ##0.0E+0
|
||||
49 @
|
||||
*/
|
||||
|
||||
// StandardFormat is a standard ECMA 376 number format.
|
||||
type StandardFormat uint32
|
||||
|
||||
// StandardFormat constants
|
||||
// StandardFormat constants, extracted from ECMA-376 Part 1 Section 18.8.30
|
||||
const (
|
||||
StandardFormatGeneral StandardFormat = 0
|
||||
StandardFormat0 StandardFormat = 0
|
||||
StandardFormat1 StandardFormat = 1
|
||||
StandardFormat2 StandardFormat = 2
|
||||
StandardFormat3 StandardFormat = 3
|
||||
StandardFormat4 StandardFormat = 4
|
||||
StandardFormatPercent StandardFormat = 9
|
||||
StandardFormat9 StandardFormat = 9
|
||||
StandardFormat10 StandardFormat = 10
|
||||
StandardFormat11 StandardFormat = 11
|
||||
StandardFormat12 StandardFormat = 12
|
||||
StandardFormat13 StandardFormat = 13
|
||||
StandardFormatDate StandardFormat = 14
|
||||
StandardFormat14 StandardFormat = 14
|
||||
StandardFormat15 StandardFormat = 15
|
||||
StandardFormat16 StandardFormat = 16
|
||||
StandardFormat17 StandardFormat = 17
|
||||
StandardFormat18 StandardFormat = 18
|
||||
StandardFormatTime StandardFormat = 19
|
||||
StandardFormat19 StandardFormat = 19
|
||||
StandardFormat20 StandardFormat = 20
|
||||
StandardFormat21 StandardFormat = 21
|
||||
StandardFormatDateTime StandardFormat = 22
|
||||
StandardFormat22 StandardFormat = 22
|
||||
StandardFormat37 StandardFormat = 37
|
||||
StandardFormat38 StandardFormat = 38
|
||||
StandardFormat39 StandardFormat = 39
|
||||
StandardFormat40 StandardFormat = 40
|
||||
StandardFormat45 StandardFormat = 45
|
||||
StandardFormat46 StandardFormat = 46
|
||||
StandardFormat47 StandardFormat = 47
|
||||
StandardFormat48 StandardFormat = 48
|
||||
StandardFormat49 StandardFormat = 49
|
||||
StandardFormatGeneral StandardFormat = 0 // General
|
||||
StandardFormat0 StandardFormat = 0 // General
|
||||
StandardFormat1 StandardFormat = 1 // 0
|
||||
StandardFormat2 StandardFormat = 2 // 0.00
|
||||
StandardFormat3 StandardFormat = 3 // #,##0
|
||||
StandardFormat4 StandardFormat = 4 // #,##0.00
|
||||
StandardFormatPercent StandardFormat = 9 // 0%
|
||||
StandardFormat9 StandardFormat = 9 // 0%
|
||||
StandardFormat10 StandardFormat = 10 // 0.00%
|
||||
StandardFormat11 StandardFormat = 11 // 0.00E+00
|
||||
StandardFormat12 StandardFormat = 12 // # ?/?
|
||||
StandardFormat13 StandardFormat = 13 // # ??/??
|
||||
StandardFormatDate StandardFormat = 14 // mm-dd-yy
|
||||
StandardFormat14 StandardFormat = 14 // mm-dd-yy
|
||||
StandardFormat15 StandardFormat = 15 // d-mmm-yy
|
||||
StandardFormat16 StandardFormat = 16 // d-mmm
|
||||
StandardFormat17 StandardFormat = 17 // mmm-yy
|
||||
StandardFormat18 StandardFormat = 18 // h:mm AM/PM
|
||||
StandardFormatTime StandardFormat = 19 // h:mm:ss AM/PM
|
||||
StandardFormat19 StandardFormat = 19 // h:mm:ss AM/PM
|
||||
StandardFormat20 StandardFormat = 20 // h:mm
|
||||
StandardFormat21 StandardFormat = 21 // h:mm:ss
|
||||
StandardFormatDateTime StandardFormat = 22 // m/d/yy h:mm
|
||||
StandardFormat22 StandardFormat = 22 // m/d/yy h:mm
|
||||
StandardFormat37 StandardFormat = 37 // #,##0 ;(#,##0)
|
||||
StandardFormat38 StandardFormat = 38 // #,##0 ;[Red](#,##0)
|
||||
StandardFormat39 StandardFormat = 39 // #,##0.00;(#,##0.00)
|
||||
StandardFormat40 StandardFormat = 40 // #,##0.00;[Red](#,##0.00)
|
||||
StandardFormat45 StandardFormat = 45 // mm:ss
|
||||
StandardFormat46 StandardFormat = 46 // [h]:mm:ss
|
||||
StandardFormat47 StandardFormat = 47 // mmss.0
|
||||
StandardFormat48 StandardFormat = 48 // ##0.0E+0
|
||||
StandardFormat49 StandardFormat = 49 // @
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ func New() *Workbook {
|
||||
|
||||
wb.AppProperties = common.NewAppProperties()
|
||||
wb.CoreProperties = common.NewCoreProperties()
|
||||
wb.StyleSheet = NewStyleSheet()
|
||||
wb.StyleSheet = NewStyleSheet(wb)
|
||||
|
||||
wb.Rels = common.NewRelationships()
|
||||
wb.wbRels = common.NewRelationships()
|
||||
|
34
spreadsheet/numberformat.go
Normal file
34
spreadsheet/numberformat.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017 Baliance. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by the terms of the Affero GNU General
|
||||
// Public License version 3.0 as published by the Free Software Foundation and
|
||||
// appearing in the file LICENSE included in the packaging of this file. A
|
||||
// commercial license can be purchased by contacting sales@baliance.com.
|
||||
|
||||
package spreadsheet
|
||||
|
||||
import sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
|
||||
|
||||
// NumberFormat is a number formatting string that can be applied to a cell
|
||||
// style.
|
||||
type NumberFormat struct {
|
||||
wb *Workbook
|
||||
x *sml.CT_NumFmt
|
||||
}
|
||||
|
||||
// X returns the inner wrapped XML type.
|
||||
func (n NumberFormat) X() *sml.CT_NumFmt {
|
||||
return n.x
|
||||
}
|
||||
|
||||
// SetCode sets the number format code.
|
||||
func (n NumberFormat) SetCode(f string) {
|
||||
n.x.FormatCodeAttr = f
|
||||
}
|
||||
|
||||
// ID returns the number format ID. This is not an index as there are some
|
||||
// predefined number formats which can be used in cell styles and don't need a
|
||||
// corresponding NumberFormat.
|
||||
func (n NumberFormat) ID() uint32 {
|
||||
return n.x.NumFmtIdAttr
|
||||
}
|
@ -16,11 +16,12 @@ import (
|
||||
|
||||
// StyleSheet is a document style sheet.
|
||||
type StyleSheet struct {
|
||||
x *sml.StyleSheet
|
||||
wb *Workbook
|
||||
x *sml.StyleSheet
|
||||
}
|
||||
|
||||
// NewStyleSheet constructs a new default stylesheet.
|
||||
func NewStyleSheet() StyleSheet {
|
||||
func NewStyleSheet(wb *Workbook) StyleSheet {
|
||||
ss := sml.NewStyleSheet()
|
||||
b := NewBorders()
|
||||
ss.Borders = b.X()
|
||||
@ -52,7 +53,7 @@ func NewStyleSheet() StyleSheet {
|
||||
|
||||
ss.Fonts = sml.NewCT_Fonts()
|
||||
|
||||
s := StyleSheet{ss}
|
||||
s := StyleSheet{wb, ss}
|
||||
fnt := s.AddFont()
|
||||
fnt.SetName("Calibri")
|
||||
fnt.SetSize(11)
|
||||
@ -66,7 +67,7 @@ func NewStyleSheet() StyleSheet {
|
||||
return s
|
||||
}
|
||||
|
||||
// X returns the innter XML entity for a stylesheet.
|
||||
// X returns the inner XML entity for a stylesheet.
|
||||
func (s StyleSheet) X() *sml.StyleSheet {
|
||||
return s.x
|
||||
}
|
||||
@ -106,10 +107,61 @@ func (s StyleSheet) AddCellStyle() CellStyle {
|
||||
xf := sml.NewCT_Xf()
|
||||
s.x.CellXfs.Xf = append(s.x.CellXfs.Xf, xf)
|
||||
s.x.CellXfs.CountAttr = gooxml.Uint32(uint32(len(s.x.CellXfs.Xf)))
|
||||
return CellStyle{xf, s.x.CellXfs}
|
||||
return CellStyle{s.wb, xf, s.x.CellXfs}
|
||||
}
|
||||
|
||||
// CellStyles returns the list of defined cell styles
|
||||
func (s StyleSheet) CellStyles() []CellStyle {
|
||||
ret := []CellStyle{}
|
||||
for _, xf := range s.x.CellXfs.Xf {
|
||||
ret = append(ret, CellStyle{s.wb, xf, s.x.CellXfs})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetOrCreateStandardNumberFormat gets or creates a cell style with a given
|
||||
// standard format. This should only be used when you want to perform
|
||||
// number/date/time formatting only. Manipulating the style returned will cause
|
||||
// all cells using style returned from this for a given format to be formatted.
|
||||
func (s StyleSheet) GetOrCreateStandardNumberFormat(f StandardFormat) CellStyle {
|
||||
for _, cs := range s.CellStyles() {
|
||||
// found an existing number format
|
||||
if cs.HasNumberFormat() && cs.NumberFormat() == uint32(f) {
|
||||
return cs
|
||||
}
|
||||
}
|
||||
// need to create a new format
|
||||
cs := s.AddCellStyle()
|
||||
cs.SetNumberFormatStandard(f)
|
||||
return cs
|
||||
}
|
||||
|
||||
// AddNumberFormat adds a new blank number format to the stylesheet.
|
||||
func (s StyleSheet) AddNumberFormat() NumberFormat {
|
||||
if s.x.NumFmts == nil {
|
||||
s.x.NumFmts = sml.NewCT_NumFmts()
|
||||
}
|
||||
nf := sml.NewCT_NumFmt()
|
||||
// start our IDs at 200 so we can ensure we don't conflict with any
|
||||
// pre-defined formats
|
||||
nf.NumFmtIdAttr = uint32(200 + len(s.x.NumFmts.NumFmt))
|
||||
s.x.NumFmts.NumFmt = append(s.x.NumFmts.NumFmt, nf)
|
||||
s.x.NumFmts.CountAttr = gooxml.Uint32(uint32(len(s.x.NumFmts.NumFmt)))
|
||||
return NumberFormat{s.wb, nf}
|
||||
}
|
||||
|
||||
// Fills returns a Fills object that can be used to add/create/edit fills.
|
||||
func (s StyleSheet) Fills() Fills {
|
||||
return Fills{s.x.Fills}
|
||||
}
|
||||
|
||||
/*
|
||||
func (s StyleSheet) NumberFormats() {
|
||||
if s.x.NumFmts == nil {
|
||||
return
|
||||
}
|
||||
for _, nfmt := range s.x.NumFmts.NumFmt {
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -25,7 +25,7 @@ func TestStyleSheetUnmarshal(t *testing.T) {
|
||||
t.Fatalf("error reading styles.xml")
|
||||
}
|
||||
dec := xml.NewDecoder(f)
|
||||
r := spreadsheet.NewStyleSheet()
|
||||
r := spreadsheet.NewStyleSheet(nil)
|
||||
if err := dec.Decode(r.X()); err != nil {
|
||||
t.Errorf("error decoding styles.xml: %s", err)
|
||||
}
|
||||
@ -40,7 +40,7 @@ func TestStyleSheetUnmarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStyleSheetFonts(t *testing.T) {
|
||||
ss := spreadsheet.NewStyleSheet()
|
||||
ss := spreadsheet.NewStyleSheet(nil)
|
||||
fc := len(ss.Fonts())
|
||||
ft := ss.AddFont()
|
||||
|
||||
|
@ -267,7 +267,7 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.xws))
|
||||
|
||||
case gooxml.StylesType:
|
||||
wb.StyleSheet = NewStyleSheet()
|
||||
wb.StyleSheet = NewStyleSheet(wb)
|
||||
decMap.AddTarget(target, wb.StyleSheet.X())
|
||||
|
||||
case gooxml.ThemeType:
|
||||
|
Loading…
x
Reference in New Issue
Block a user