123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- package xlsx
- // Default column width in excel
- const ColWidth = 9.5
- const Excel2006MaxRowCount = 1048576
- const Excel2006MaxRowIndex = Excel2006MaxRowCount - 1
- type Col struct {
- Min int
- Max int
- Hidden bool
- Width float64
- Collapsed bool
- OutlineLevel uint8
- BestFit bool
- CustomWidth bool
- Phonetic bool
- numFmt string
- parsedNumFmt *parsedNumberFormat
- style *Style
- outXfID int
- }
- // NewColForRange return a pointer to a new Col, which will apply to
- // columns in the range min to max (inclusive). Note, in order for
- // this Col to do anything useful you must set some of its parameters
- // and then apply it to a Sheet by calling sheet.SetColParameters.
- func NewColForRange(min, max int) *Col {
- if max < min {
- // Nice try ;-)
- return &Col{Min: max, Max: min}
- }
- return &Col{Min: min, Max: max}
- }
- // SetWidth sets the width of columns that have this Col applied to
- // them. The width is expressed as the number of characters of the
- // maximum digit width of the numbers 0-9 as rendered in the normal
- // style's font.
- func (c *Col) SetWidth(width float64) {
- c.Width = width
- c.CustomWidth = true
- }
- // SetType will set the format string of a column based on the type that you want to set it to.
- // This function does not really make a lot of sense.
- func (c *Col) SetType(cellType CellType) {
- switch cellType {
- case CellTypeString:
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
- case CellTypeNumeric:
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
- case CellTypeBool:
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL] //TEMP
- case CellTypeInline:
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
- case CellTypeError:
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL] //TEMP
- case CellTypeDate:
- // Cells that are stored as dates are not properly supported in this library.
- // They should instead be stored as a Numeric with a date format.
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
- case CellTypeStringFormula:
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
- }
- }
- // GetStyle returns the Style associated with a Col
- func (c *Col) GetStyle() *Style {
- return c.style
- }
- // SetStyle sets the style of a Col
- func (c *Col) SetStyle(style *Style) {
- c.style = style
- }
- // SetStreamStyle sets the style and number format id to the ones specified in the given StreamStyle
- func (c *Col) SetStreamStyle(style StreamStyle) {
- c.style = style.style
- // TODO: `style.xNumFmtId` could be out of the range of the builtin map
- // returning "" which may not be a valid formatCode
- c.numFmt = builtInNumFmt[style.xNumFmtId]
- }
- func (c *Col) GetStreamStyle() StreamStyle {
- // TODO: Like `SetStreamStyle`, `numFmt` could be out of the range of the builtin inv map
- // returning 0 which maps to formatCode "general"
- return StreamStyle{builtInNumFmtInv[c.numFmt], c.style}
- }
- func (c *Col) SetOutlineLevel(outlineLevel uint8) {
- c.OutlineLevel = outlineLevel
- }
- // copyToRange is an internal convenience function to make a copy of a
- // Col with a different Min and Max value, it is not intended as a
- // general purpose Col copying function as you must still insert the
- // resulting Col into the Col Store.
- func (c *Col) copyToRange(min, max int) *Col {
- return &Col{
- Min: min,
- Max: max,
- Hidden: c.Hidden,
- Width: c.Width,
- Collapsed: c.Collapsed,
- OutlineLevel: c.OutlineLevel,
- BestFit: c.BestFit,
- CustomWidth: c.CustomWidth,
- Phonetic: c.Phonetic,
- numFmt: c.numFmt,
- parsedNumFmt: c.parsedNumFmt,
- style: c.style,
- }
- }
- type ColStoreNode struct {
- Col *Col
- Prev *ColStoreNode
- Next *ColStoreNode
- }
- //
- func (csn *ColStoreNode) findNodeForColNum(num int) *ColStoreNode {
- switch {
- case num >= csn.Col.Min && num <= csn.Col.Max:
- return csn
- case num < csn.Col.Min:
- if csn.Prev == nil {
- return nil
- }
- if csn.Prev.Col.Max < num {
- return nil
- }
- return csn.Prev.findNodeForColNum(num)
- case num > csn.Col.Max:
- if csn.Next == nil {
- return nil
- }
- if csn.Next.Col.Min > num {
- return nil
- }
- return csn.Next.findNodeForColNum(num)
- }
- return nil
- }
- // ColStore is the working store of Col definitions, it will simplify all Cols added to it, to ensure there ar no overlapping definitions.
- type ColStore struct {
- Root *ColStoreNode
- Len int
- }
- // Add a Col to the ColStore. If it overwrites all, or part of some
- // existing Col's range of columns the that Col will be adjusted
- // and/or split to make room for the new Col.
- func (cs *ColStore) Add(col *Col) *ColStoreNode {
- newNode := &ColStoreNode{Col: col}
- if cs.Root == nil {
- cs.Root = newNode
- cs.Len = 1
- return newNode
- }
- cs.makeWay(cs.Root, newNode)
- return newNode
- }
- func (cs *ColStore) FindColByIndex(index int) *Col {
- csn := cs.findNodeForColNum(index)
- if csn != nil {
- return csn.Col
- }
- return nil
- }
- func (cs *ColStore) findNodeForColNum(num int) *ColStoreNode {
- if cs.Root == nil {
- return nil
- }
- return cs.Root.findNodeForColNum(num)
- }
- func (cs *ColStore) removeNode(node *ColStoreNode) {
- if node.Prev != nil {
- if node.Next != nil {
- node.Prev.Next = node.Next
- } else {
- node.Prev.Next = nil
- }
- }
- if node.Next != nil {
- if node.Prev != nil {
- node.Next.Prev = node.Prev
- } else {
- node.Next.Prev = nil
- }
- }
- if cs.Root == node {
- switch {
- case node.Prev != nil:
- cs.Root = node.Prev
- case node.Next != nil:
- cs.Root = node.Next
- default:
- cs.Root = nil
- }
- }
- node.Next = nil
- node.Prev = nil
- cs.Len -= 1
- }
- // makeWay will adjust the Min and Max of this ColStoreNode's Col to
- // make way for a new ColStoreNode's Col. If necessary it will
- // generate an additional ColStoreNode with a new Col covering the
- // "tail" portion of this ColStoreNode's Col should the new node lay
- // completely within the range of this one, but without reaching its
- // maximum extent.
- func (cs *ColStore) makeWay(node1, node2 *ColStoreNode) {
- switch {
- case node1.Col.Max < node2.Col.Min:
- // The node2 starts after node1 ends, there's no overlap
- //
- // Node1 |----|
- // Node2 |----|
- if node1.Next != nil {
- if node1.Next.Col.Min <= node2.Col.Max {
- cs.makeWay(node1.Next, node2)
- return
- }
- cs.addNode(node1, node2, node1.Next)
- return
- }
- cs.addNode(node1, node2, nil)
- return
- case node1.Col.Min > node2.Col.Max:
- // Node2 ends before node1 begins, there's no overlap
- //
- // Node1 |-----|
- // Node2 |----|
- if node1.Prev != nil {
- if node1.Prev.Col.Max >= node2.Col.Min {
- cs.makeWay(node1.Prev, node2)
- return
- }
- cs.addNode(node1.Prev, node2, node1)
- return
- }
- cs.addNode(nil, node2, node1)
- return
- case node1.Col.Min == node2.Col.Min && node1.Col.Max == node2.Col.Max:
- // Exact match
- //
- // Node1 |xxx|
- // Node2 |---|
- prev := node1.Prev
- next := node1.Next
- cs.removeNode(node1)
- cs.addNode(prev, node2, next)
- // Remove node may have set the root to nil
- if cs.Root == nil {
- cs.Root = node2
- }
- return
- case node1.Col.Min > node2.Col.Min && node1.Col.Max < node2.Col.Max:
- // Node2 envelopes node1
- //
- // Node1 |xx|
- // Node2 |----|
- prev := node1.Prev
- next := node1.Next
- cs.removeNode(node1)
- switch {
- case prev == node2:
- node2.Next = next
- case next == node2:
- node2.Prev = prev
- default:
- cs.addNode(prev, node2, next)
- }
- if node2.Prev != nil && node2.Prev.Col.Max >= node2.Col.Min {
- cs.makeWay(prev, node2)
- }
- if node2.Next != nil && node2.Next.Col.Min <= node2.Col.Max {
- cs.makeWay(next, node2)
- }
- if cs.Root == nil {
- cs.Root = node2
- }
- case node1.Col.Min < node2.Col.Min && node1.Col.Max > node2.Col.Max:
- // Node2 bisects node1:
- //
- // Node1 |---xx---|
- // Node2 |--|
- newCol := node1.Col.copyToRange(node2.Col.Max+1, node1.Col.Max)
- newNode := &ColStoreNode{Col: newCol}
- cs.addNode(node1, newNode, node1.Next)
- node1.Col.Max = node2.Col.Min - 1
- cs.addNode(node1, node2, newNode)
- return
- case node1.Col.Max >= node2.Col.Min && node1.Col.Min < node2.Col.Min:
- // Node2 overlaps node1 at some point above it's minimum:
- //
- // Node1 |----xx|
- // Node2 |-------|
- next := node1.Next
- node1.Col.Max = node2.Col.Min - 1
- if next == node2 {
- return
- }
- cs.addNode(node1, node2, next)
- if next != nil && next.Col.Min <= node2.Col.Max {
- cs.makeWay(next, node2)
- }
- return
- case node1.Col.Min <= node2.Col.Max && node1.Col.Min > node2.Col.Min:
- // Node2 overlaps node1 at some point below it's maximum:
- //
- // Node1: |------|
- // Node2: |----xx|
- prev := node1.Prev
- node1.Col.Min = node2.Col.Max + 1
- if prev == node2 {
- return
- }
- cs.addNode(prev, node2, node1)
- if prev != nil && prev.Col.Max >= node2.Col.Min {
- cs.makeWay(node1.Prev, node2)
- }
- return
- }
- return
- }
- func (cs *ColStore) addNode(prev, this, next *ColStoreNode) {
- if prev != nil {
- prev.Next = this
- }
- this.Prev = prev
- this.Next = next
- if next != nil {
- next.Prev = this
- }
- cs.Len += 1
- }
- func (cs *ColStore) getOrMakeColsForRange(start *ColStoreNode, min, max int) []*Col {
- cols := []*Col{}
- var csn *ColStoreNode
- var newCol *Col
- switch {
- case start == nil:
- newCol = NewColForRange(min, max)
- csn = cs.Add(newCol)
- case start.Col.Min <= min && start.Col.Max >= min:
- csn = start
- case start.Col.Min < min && start.Col.Max < min:
- if start.Next != nil {
- return cs.getOrMakeColsForRange(start.Next, min, max)
- }
- newCol = NewColForRange(min, max)
- csn = cs.Add(newCol)
- case start.Col.Min > min:
- if start.Col.Min > max {
- newCol = NewColForRange(min, max)
- } else {
- newCol = NewColForRange(min, start.Col.Min-1)
- }
- csn = cs.Add(newCol)
- }
- cols = append(cols, csn.Col)
- if csn.Col.Max >= max {
- return cols
- }
- cols = append(cols, cs.getOrMakeColsForRange(csn.Next, csn.Col.Max+1, max)...)
- return cols
- }
- func chainOp(csn *ColStoreNode, fn func(idx int, col *Col)) {
- for csn.Prev != nil {
- csn = csn.Prev
- }
- var i int
- for i = 0; csn.Next != nil; i++ {
- fn(i, csn.Col)
- csn = csn.Next
- }
- fn(i+1, csn.Col)
- }
- // ForEach calls the function fn for each Col defined in the ColStore.
- func (cs *ColStore) ForEach(fn func(idx int, col *Col)) {
- if cs.Root == nil {
- return
- }
- chainOp(cs.Root, fn)
- }
|