2015-03-19 10:39:41 +01:00
|
|
|
package xls
|
|
|
|
|
|
|
|
import (
|
2019-04-02 22:38:51 +02:00
|
|
|
"bytes"
|
2015-03-19 10:39:41 +01:00
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2020-09-11 08:25:20 +02:00
|
|
|
"log"
|
2015-03-24 06:06:52 +01:00
|
|
|
"unicode/utf16"
|
2015-03-19 10:39:41 +01:00
|
|
|
)
|
|
|
|
|
2019-03-17 18:55:41 +01:00
|
|
|
type TWorkSheetVisibility byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
WorkSheetVisible TWorkSheetVisibility = 0
|
|
|
|
WorkSheetHidden TWorkSheetVisibility = 1
|
|
|
|
WorkSheetVeryHidden TWorkSheetVisibility = 2
|
|
|
|
)
|
|
|
|
|
2015-06-16 04:29:58 +02:00
|
|
|
type boundsheet struct {
|
2015-03-19 10:39:41 +01:00
|
|
|
Filepos uint32
|
2019-03-17 21:01:02 +01:00
|
|
|
Visible byte
|
2015-03-19 10:39:41 +01:00
|
|
|
Type byte
|
|
|
|
Name byte
|
|
|
|
}
|
|
|
|
|
2015-06-16 04:37:11 +02:00
|
|
|
//WorkSheet in one WorkBook
|
2015-03-19 10:39:41 +01:00
|
|
|
type WorkSheet struct {
|
2019-03-17 18:55:41 +01:00
|
|
|
bs *boundsheet
|
|
|
|
wb *WorkBook
|
|
|
|
Name string
|
|
|
|
Selected bool
|
|
|
|
Visibility TWorkSheetVisibility
|
|
|
|
rows map[uint16]*Row
|
2015-09-30 05:17:25 +02:00
|
|
|
//NOTICE: this is the max row number of the sheet, so it should be count -1
|
2019-03-17 18:55:41 +01:00
|
|
|
MaxRow uint16
|
|
|
|
parsed bool
|
|
|
|
rightToLeft bool
|
2020-09-11 08:25:20 +02:00
|
|
|
// NOTICE: get all merge cell
|
|
|
|
mergeCells *MergeCells
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
|
|
|
|
2017-02-21 09:27:01 +01:00
|
|
|
func (w *WorkSheet) Row(i int) *Row {
|
|
|
|
row := w.rows[uint16(i)]
|
2017-06-12 04:36:14 +02:00
|
|
|
if row != nil {
|
|
|
|
row.wb = w.wb
|
|
|
|
}
|
2017-02-21 09:27:01 +01:00
|
|
|
return row
|
|
|
|
}
|
|
|
|
|
2015-06-16 04:37:11 +02:00
|
|
|
func (w *WorkSheet) parse(buf io.ReadSeeker) {
|
2017-02-21 09:27:01 +01:00
|
|
|
w.rows = make(map[uint16]*Row)
|
2015-09-30 04:40:01 +02:00
|
|
|
b := new(bof)
|
|
|
|
var bof_pre *bof
|
2019-04-02 22:38:51 +02:00
|
|
|
var col_pre interface{}
|
2015-03-19 10:39:41 +01:00
|
|
|
for {
|
2015-09-30 04:40:01 +02:00
|
|
|
if err := binary.Read(buf, binary.LittleEndian, b); err == nil {
|
2019-04-02 22:38:51 +02:00
|
|
|
bof_pre, col_pre = w.parseBof(buf, b, bof_pre, col_pre)
|
2015-09-30 04:40:01 +02:00
|
|
|
if b.Id == 0xa {
|
2015-06-16 04:29:58 +02:00
|
|
|
break
|
|
|
|
}
|
2015-03-19 10:39:41 +01:00
|
|
|
} else {
|
|
|
|
fmt.Println(err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2016-01-21 06:14:37 +01:00
|
|
|
w.parsed = true
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
|
|
|
|
2019-04-02 22:38:51 +02:00
|
|
|
func (w *WorkSheet) parseBof(buf io.ReadSeeker, b *bof, pre *bof, col_pre interface{}) (*bof, interface{}) {
|
2015-03-24 06:06:52 +01:00
|
|
|
var col interface{}
|
2019-04-02 22:38:51 +02:00
|
|
|
var bts = make([]byte, b.Size)
|
|
|
|
binary.Read(buf, binary.LittleEndian, bts)
|
|
|
|
buf = bytes.NewReader(bts)
|
2015-09-30 04:40:01 +02:00
|
|
|
switch b.Id {
|
2020-09-11 08:25:20 +02:00
|
|
|
case 0x0E5: // MERGEDCELLS
|
|
|
|
mergeCells := new(MergeCells)
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &mergeCells.Count)
|
|
|
|
size := (b.Size - 2) / 8
|
|
|
|
mergeCells.Refs = make([]Ref8, size)
|
|
|
|
for i := uint16(0); i < size; i++ {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &mergeCells.Refs[i])
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
w.mergeCells = mergeCells
|
2019-03-17 18:55:41 +01:00
|
|
|
case 0x23E: // WINDOW2
|
|
|
|
var sheetOptions, firstVisibleRow, firstVisibleColumn uint16
|
|
|
|
binary.Read(buf, binary.LittleEndian, &sheetOptions)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &firstVisibleRow) // not valuable
|
|
|
|
binary.Read(buf, binary.LittleEndian, &firstVisibleColumn) // not valuable
|
2019-04-02 22:38:51 +02:00
|
|
|
//buf.Seek(int64(b.Size)-2*3, 1)
|
2019-03-17 18:55:41 +01:00
|
|
|
w.rightToLeft = (sheetOptions & 0x40) != 0
|
|
|
|
w.Selected = (sheetOptions & 0x400) != 0
|
2015-03-19 10:39:41 +01:00
|
|
|
case 0x208: //ROW
|
2016-09-30 05:46:51 +02:00
|
|
|
r := new(rowInfo)
|
2015-03-19 10:39:41 +01:00
|
|
|
binary.Read(buf, binary.LittleEndian, r)
|
|
|
|
w.addRow(r)
|
|
|
|
case 0x0BD: //MULRK
|
|
|
|
mc := new(MulrkCol)
|
2015-09-30 04:40:01 +02:00
|
|
|
size := (b.Size - 6) / 6
|
2015-03-19 10:39:41 +01:00
|
|
|
binary.Read(buf, binary.LittleEndian, &mc.Col)
|
|
|
|
mc.Xfrks = make([]XfRk, size)
|
|
|
|
for i := uint16(0); i < size; i++ {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &mc.Xfrks[i])
|
|
|
|
}
|
|
|
|
binary.Read(buf, binary.LittleEndian, &mc.LastColB)
|
|
|
|
col = mc
|
|
|
|
case 0x0BE: //MULBLANK
|
|
|
|
mc := new(MulBlankCol)
|
2015-09-30 04:40:01 +02:00
|
|
|
size := (b.Size - 6) / 2
|
2015-03-19 10:39:41 +01:00
|
|
|
binary.Read(buf, binary.LittleEndian, &mc.Col)
|
|
|
|
mc.Xfs = make([]uint16, size)
|
|
|
|
for i := uint16(0); i < size; i++ {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &mc.Xfs[i])
|
|
|
|
}
|
|
|
|
binary.Read(buf, binary.LittleEndian, &mc.LastColB)
|
|
|
|
col = mc
|
|
|
|
case 0x203: //NUMBER
|
|
|
|
col = new(NumberCol)
|
|
|
|
binary.Read(buf, binary.LittleEndian, col)
|
|
|
|
case 0x06: //FORMULA
|
2015-03-24 06:06:52 +01:00
|
|
|
c := new(FormulaCol)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &c.Header)
|
2015-09-30 04:40:01 +02:00
|
|
|
c.Bts = make([]byte, b.Size-20)
|
2015-03-24 06:06:52 +01:00
|
|
|
binary.Read(buf, binary.LittleEndian, &c.Bts)
|
|
|
|
col = c
|
2019-04-02 22:38:51 +02:00
|
|
|
case 0x207: //STRING = FORMULA-VALUE is expected right after FORMULA
|
|
|
|
if ch, ok := col_pre.(*FormulaCol); ok {
|
|
|
|
c := new(FormulaStringCol)
|
|
|
|
c.Col = ch.Header.Col
|
|
|
|
var cStringLen uint16
|
|
|
|
binary.Read(buf, binary.LittleEndian, &cStringLen)
|
|
|
|
str, err := w.wb.get_string(buf, cStringLen)
|
|
|
|
if nil == err {
|
|
|
|
c.RenderedValue = str
|
|
|
|
}
|
|
|
|
col = c
|
|
|
|
}
|
2015-03-19 10:39:41 +01:00
|
|
|
case 0x27e: //RK
|
|
|
|
col = new(RkCol)
|
|
|
|
binary.Read(buf, binary.LittleEndian, col)
|
|
|
|
case 0xFD: //LABELSST
|
|
|
|
col = new(LabelsstCol)
|
|
|
|
binary.Read(buf, binary.LittleEndian, col)
|
2016-05-28 04:39:46 +02:00
|
|
|
case 0x204:
|
|
|
|
c := new(labelCol)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &c.BlankCol)
|
|
|
|
var count uint16
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
2016-06-23 04:22:57 +02:00
|
|
|
c.Str, _ = w.wb.get_string(buf, count)
|
2016-05-28 04:39:46 +02:00
|
|
|
col = c
|
2015-03-19 10:39:41 +01:00
|
|
|
case 0x201: //BLANK
|
|
|
|
col = new(BlankCol)
|
|
|
|
binary.Read(buf, binary.LittleEndian, col)
|
2015-03-24 06:06:52 +01:00
|
|
|
case 0x1b8: //HYPERLINK
|
|
|
|
var hy HyperLink
|
|
|
|
binary.Read(buf, binary.LittleEndian, &hy.CellRange)
|
|
|
|
buf.Seek(20, 1)
|
|
|
|
var flag uint32
|
|
|
|
binary.Read(buf, binary.LittleEndian, &flag)
|
|
|
|
var count uint32
|
|
|
|
|
|
|
|
if flag&0x14 != 0 {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
2015-09-30 04:40:01 +02:00
|
|
|
hy.Description = b.utf16String(buf, count)
|
2015-03-24 06:06:52 +01:00
|
|
|
}
|
|
|
|
if flag&0x80 != 0 {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
2015-09-30 04:40:01 +02:00
|
|
|
hy.TargetFrame = b.utf16String(buf, count)
|
2015-03-24 06:06:52 +01:00
|
|
|
}
|
|
|
|
if flag&0x1 != 0 {
|
|
|
|
var guid [2]uint64
|
|
|
|
binary.Read(buf, binary.BigEndian, &guid)
|
|
|
|
if guid[0] == 0xE0C9EA79F9BACE11 && guid[1] == 0x8C8200AA004BA90B { //URL
|
2015-03-25 04:03:05 +01:00
|
|
|
hy.IsUrl = true
|
2015-03-24 06:06:52 +01:00
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
2015-09-30 04:40:01 +02:00
|
|
|
hy.Url = b.utf16String(buf, count/2)
|
2015-03-25 04:03:05 +01:00
|
|
|
} else if guid[0] == 0x303000000000000 && guid[1] == 0xC000000000000046 { //URL{
|
|
|
|
var upCount uint16
|
|
|
|
binary.Read(buf, binary.LittleEndian, &upCount)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
|
|
|
bts := make([]byte, count)
|
2015-03-24 06:06:52 +01:00
|
|
|
binary.Read(buf, binary.LittleEndian, &bts)
|
2015-03-25 04:03:05 +01:00
|
|
|
hy.ShortedFilePath = string(bts)
|
|
|
|
buf.Seek(24, 1)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
|
|
|
if count > 0 {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
|
|
|
buf.Seek(2, 1)
|
2015-09-30 04:40:01 +02:00
|
|
|
hy.ExtendedFilePath = b.utf16String(buf, count/2+1)
|
2015-03-25 04:03:05 +01:00
|
|
|
}
|
2015-03-24 06:06:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if flag&0x8 != 0 {
|
|
|
|
binary.Read(buf, binary.LittleEndian, &count)
|
|
|
|
var bts = make([]uint16, count)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &bts)
|
|
|
|
runes := utf16.Decode(bts[:len(bts)-1])
|
|
|
|
hy.TextMark = string(runes)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.addRange(&hy.CellRange, &hy)
|
2015-06-16 04:29:58 +02:00
|
|
|
case 0x809:
|
2015-09-30 04:40:01 +02:00
|
|
|
buf.Seek(int64(b.Size), 1)
|
2015-06-16 04:29:58 +02:00
|
|
|
case 0xa:
|
2015-03-19 10:39:41 +01:00
|
|
|
default:
|
2015-09-30 04:40:01 +02:00
|
|
|
// log.Printf("Unknow %X,%d\n", b.Id, b.Size)
|
|
|
|
buf.Seek(int64(b.Size), 1)
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
|
|
|
if col != nil {
|
2015-03-24 06:06:52 +01:00
|
|
|
w.add(col)
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
2019-04-02 22:38:51 +02:00
|
|
|
return b, col
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
|
|
|
|
2015-03-24 06:06:52 +01:00
|
|
|
func (w *WorkSheet) add(content interface{}) {
|
2015-09-30 04:40:01 +02:00
|
|
|
if ch, ok := content.(contentHandler); ok {
|
2015-03-24 06:06:52 +01:00
|
|
|
if col, ok := content.(Coler); ok {
|
|
|
|
w.addCell(col, ch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-09-30 04:40:01 +02:00
|
|
|
func (w *WorkSheet) addCell(col Coler, ch contentHandler) {
|
2015-06-16 04:29:58 +02:00
|
|
|
w.addContent(col.Row(), ch)
|
2015-03-24 06:06:52 +01:00
|
|
|
}
|
|
|
|
|
2015-09-30 04:40:01 +02:00
|
|
|
func (w *WorkSheet) addRange(rang Ranger, ch contentHandler) {
|
2015-06-16 04:29:58 +02:00
|
|
|
|
|
|
|
for i := rang.FirstRow(); i <= rang.LastRow(); i++ {
|
|
|
|
w.addContent(i, ch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 04:40:01 +02:00
|
|
|
func (w *WorkSheet) addContent(row_num uint16, ch contentHandler) {
|
2015-03-24 06:06:52 +01:00
|
|
|
var row *Row
|
|
|
|
var ok bool
|
2017-02-21 09:27:01 +01:00
|
|
|
if row, ok = w.rows[row_num]; !ok {
|
2016-09-30 05:46:51 +02:00
|
|
|
info := new(rowInfo)
|
2015-06-16 04:29:58 +02:00
|
|
|
info.Index = row_num
|
|
|
|
row = w.addRow(info)
|
2015-03-24 06:06:52 +01:00
|
|
|
}
|
2019-03-17 20:42:58 +01:00
|
|
|
if row.info.Lcell < ch.LastCol() {
|
|
|
|
row.info.Lcell = ch.LastCol()
|
|
|
|
}
|
2017-02-21 09:27:01 +01:00
|
|
|
row.cols[ch.FirstCol()] = ch
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
|
|
|
|
2016-09-30 05:46:51 +02:00
|
|
|
func (w *WorkSheet) addRow(info *rowInfo) (row *Row) {
|
2015-03-19 10:39:41 +01:00
|
|
|
if info.Index > w.MaxRow {
|
|
|
|
w.MaxRow = info.Index
|
|
|
|
}
|
|
|
|
var ok bool
|
2017-02-21 09:27:01 +01:00
|
|
|
if row, ok = w.rows[info.Index]; ok {
|
2015-03-19 10:39:41 +01:00
|
|
|
row.info = info
|
|
|
|
} else {
|
2017-02-21 09:27:01 +01:00
|
|
|
row = &Row{info: info, cols: make(map[uint16]contentHandler)}
|
|
|
|
w.rows[info.Index] = row
|
2015-03-19 10:39:41 +01:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|