add hyperlink and formula cell support
This commit is contained in:
parent
c7f56bc92a
commit
5544765b84
59
cell_range.go
Normal file
59
cell_range.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package xls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ranger interface {
|
||||||
|
FirstRow() uint16
|
||||||
|
LastRow() uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type CellRange struct {
|
||||||
|
FirstRowB uint16
|
||||||
|
LastRowB uint16
|
||||||
|
FristColB uint16
|
||||||
|
LastColB uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CellRange) FirstRow() uint16 {
|
||||||
|
return c.FirstRowB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CellRange) LastRow() uint16 {
|
||||||
|
return c.LastRowB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CellRange) FirstCol() uint16 {
|
||||||
|
return c.FristColB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CellRange) LastCol() uint16 {
|
||||||
|
return c.LastColB
|
||||||
|
}
|
||||||
|
|
||||||
|
type HyperLink struct {
|
||||||
|
CellRange
|
||||||
|
Description string
|
||||||
|
TextMark string
|
||||||
|
TargetFrame string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HyperLink) String(wb *WorkBook) []string {
|
||||||
|
res := make([]string, h.LastColB-h.FristColB+1)
|
||||||
|
str := fmt.Sprintf("%s(%s)", h.Description, h.Url)
|
||||||
|
for i := uint16(0); i < h.LastColB-h.FristColB+1; i++ {
|
||||||
|
res[i] = str
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (h *HyperLinkCR) ParseFrom(buf io.ReadSeeker) {
|
||||||
|
// if h.Flags&0x1 != 0 {
|
||||||
|
// binary.Read(buf, binary.LittleEndian, &richtext_num)
|
||||||
|
// }
|
||||||
|
// if h.Flags&0x4 != 0 {
|
||||||
|
// binary.Read(buf, binary.LittleEndian, &phonetic_size)
|
||||||
|
// }
|
||||||
|
// }
|
29
col.go
29
col.go
@ -5,16 +5,19 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ContentHandler interface {
|
||||||
|
String(*WorkBook) []string
|
||||||
|
FirstCol() uint16
|
||||||
|
LastCol() uint16
|
||||||
|
}
|
||||||
|
|
||||||
type Col struct {
|
type Col struct {
|
||||||
RowB uint16
|
RowB uint16
|
||||||
FirstColB uint16
|
FirstColB uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
type Coler interface {
|
type Coler interface {
|
||||||
String(*WorkBook) []string
|
|
||||||
Row() uint16
|
Row() uint16
|
||||||
FirstCol() uint16
|
|
||||||
LastCol() uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Col) Row() uint16 {
|
func (c *Col) Row() uint16 {
|
||||||
@ -30,7 +33,7 @@ func (c *Col) LastCol() uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Col) String(wb *WorkBook) []string {
|
func (c *Col) String(wb *WorkBook) []string {
|
||||||
return []string{""}
|
return []string{"default"}
|
||||||
}
|
}
|
||||||
|
|
||||||
type XfRk struct {
|
type XfRk struct {
|
||||||
@ -99,8 +102,20 @@ func (c *NumberCol) String(wb *WorkBook) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FormulaCol struct {
|
type FormulaCol struct {
|
||||||
Col
|
Header struct {
|
||||||
|
Col
|
||||||
|
IndexXf uint16
|
||||||
|
Result [8]byte
|
||||||
|
Flags uint16
|
||||||
|
_ uint32
|
||||||
|
}
|
||||||
|
Bts []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FormulaCol) String(wb *WorkBook) []string {
|
||||||
|
return []string{"FormulaCol"}
|
||||||
|
}
|
||||||
|
|
||||||
type RkCol struct {
|
type RkCol struct {
|
||||||
Col
|
Col
|
||||||
Xfrk XfRk
|
Xfrk XfRk
|
||||||
@ -124,3 +139,7 @@ type BlankCol struct {
|
|||||||
Col
|
Col
|
||||||
Xf uint16
|
Xf uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BlankCol) String(wb *WorkBook) []string {
|
||||||
|
return []string{""}
|
||||||
|
}
|
||||||
|
2
row.go
2
row.go
@ -12,5 +12,5 @@ type RowInfo struct {
|
|||||||
|
|
||||||
type Row struct {
|
type Row struct {
|
||||||
info *RowInfo
|
info *RowInfo
|
||||||
Cols map[uint16]Coler
|
Cols map[uint16]ContentHandler
|
||||||
}
|
}
|
||||||
|
42
workbook.go
42
workbook.go
@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -150,7 +149,6 @@ func (w *WorkBook) get_string(buf io.ReadSeeker, size uint16) string {
|
|||||||
var phonetic_size uint32
|
var phonetic_size uint32
|
||||||
var flag byte
|
var flag byte
|
||||||
binary.Read(buf, binary.LittleEndian, &flag)
|
binary.Read(buf, binary.LittleEndian, &flag)
|
||||||
log.Println(flag, size)
|
|
||||||
if flag&0x8 != 0 {
|
if flag&0x8 != 0 {
|
||||||
binary.Read(buf, binary.LittleEndian, &richtext_num)
|
binary.Read(buf, binary.LittleEndian, &richtext_num)
|
||||||
}
|
}
|
||||||
@ -200,27 +198,37 @@ func (w *WorkBook) PrepareSheet(sheet *WorkSheet) {
|
|||||||
sheet.Parse(w.rs)
|
sheet.Parse(w.rs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WorkBook) ReadAllCells() (res [][]string) {
|
func (w *WorkBook) ReadAllCells(max int) (res [][]string) {
|
||||||
|
res = make([][]string, 0)
|
||||||
for _, sheet := range w.Sheets {
|
for _, sheet := range w.Sheets {
|
||||||
w.PrepareSheet(sheet)
|
if len(res) < max {
|
||||||
if sheet.MaxRow != 0 {
|
max = max - len(res)
|
||||||
temp := make([][]string, sheet.MaxRow+1)
|
w.PrepareSheet(sheet)
|
||||||
for k, row := range sheet.Rows {
|
if sheet.MaxRow != 0 {
|
||||||
data := make([]string, 0)
|
leng := int(sheet.MaxRow) + 1
|
||||||
if len(row.Cols) > 0 {
|
if max < leng {
|
||||||
for _, col := range row.Cols {
|
leng = max
|
||||||
if uint16(len(data)) <= col.LastCol() {
|
}
|
||||||
data = append(data, make([]string, col.LastCol()-uint16(len(data))+1)...)
|
temp := make([][]string, leng)
|
||||||
|
for k, row := range sheet.Rows {
|
||||||
|
data := make([]string, 0)
|
||||||
|
if len(row.Cols) > 0 {
|
||||||
|
for _, col := range row.Cols {
|
||||||
|
if uint16(len(data)) <= col.LastCol() {
|
||||||
|
data = append(data, make([]string, col.LastCol()-uint16(len(data))+1)...)
|
||||||
|
}
|
||||||
|
str := col.String(w)
|
||||||
|
for i := uint16(0); i < col.LastCol()-col.FirstCol()+1; i++ {
|
||||||
|
data[col.FirstCol()+i] = str[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
str := col.String(w)
|
if leng > int(k) {
|
||||||
for i := uint16(0); i < col.LastCol()-col.FirstCol()+1; i++ {
|
temp[k] = data
|
||||||
data[col.FirstCol()+i] = str[i]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
temp[k] = data
|
|
||||||
}
|
}
|
||||||
|
res = append(res, temp...)
|
||||||
}
|
}
|
||||||
res = append(res, temp...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
106
worksheet.go
106
worksheet.go
@ -4,6 +4,8 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"unicode/utf16"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Boundsheet struct {
|
type Boundsheet struct {
|
||||||
@ -36,10 +38,10 @@ func (w *WorkSheet) Parse(buf io.ReadSeeker) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *WorkSheet) parseBof(buf io.ReadSeeker, bof *BOF, pre *BOF) *BOF {
|
func (w *WorkSheet) parseBof(buf io.ReadSeeker, bof *BOF, pre *BOF) *BOF {
|
||||||
var col Coler
|
var col interface{}
|
||||||
switch bof.Id {
|
switch bof.Id {
|
||||||
case 0x0E5: //MERGEDCELLS
|
// case 0x0E5: //MERGEDCELLS
|
||||||
// ws.mergedCells(buf)
|
// ws.mergedCells(buf)
|
||||||
case 0x208: //ROW
|
case 0x208: //ROW
|
||||||
r := new(RowInfo)
|
r := new(RowInfo)
|
||||||
binary.Read(buf, binary.LittleEndian, r)
|
binary.Read(buf, binary.LittleEndian, r)
|
||||||
@ -68,8 +70,11 @@ func (w *WorkSheet) parseBof(buf io.ReadSeeker, bof *BOF, pre *BOF) *BOF {
|
|||||||
col = new(NumberCol)
|
col = new(NumberCol)
|
||||||
binary.Read(buf, binary.LittleEndian, col)
|
binary.Read(buf, binary.LittleEndian, col)
|
||||||
case 0x06: //FORMULA
|
case 0x06: //FORMULA
|
||||||
col = new(FormulaCol)
|
c := new(FormulaCol)
|
||||||
binary.Read(buf, binary.LittleEndian, col)
|
binary.Read(buf, binary.LittleEndian, &c.Header)
|
||||||
|
c.Bts = make([]byte, bof.Size-20)
|
||||||
|
binary.Read(buf, binary.LittleEndian, &c.Bts)
|
||||||
|
col = c
|
||||||
case 0x27e: //RK
|
case 0x27e: //RK
|
||||||
col = new(RkCol)
|
col = new(RkCol)
|
||||||
binary.Read(buf, binary.LittleEndian, col)
|
binary.Read(buf, binary.LittleEndian, col)
|
||||||
@ -79,16 +84,86 @@ func (w *WorkSheet) parseBof(buf io.ReadSeeker, bof *BOF, pre *BOF) *BOF {
|
|||||||
case 0x201: //BLANK
|
case 0x201: //BLANK
|
||||||
col = new(BlankCol)
|
col = new(BlankCol)
|
||||||
binary.Read(buf, binary.LittleEndian, col)
|
binary.Read(buf, binary.LittleEndian, col)
|
||||||
|
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)
|
||||||
|
var bts = make([]uint16, count)
|
||||||
|
binary.Read(buf, binary.LittleEndian, &bts)
|
||||||
|
runes := utf16.Decode(bts[:len(bts)-1])
|
||||||
|
hy.Description = string(runes)
|
||||||
|
}
|
||||||
|
if flag&0x80 != 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.TargetFrame = string(runes)
|
||||||
|
}
|
||||||
|
if flag&0x1 != 0 {
|
||||||
|
var guid [2]uint64
|
||||||
|
binary.Read(buf, binary.BigEndian, &guid)
|
||||||
|
if guid[0] == 0xE0C9EA79F9BACE11 && guid[1] == 0x8C8200AA004BA90B { //URL
|
||||||
|
binary.Read(buf, binary.LittleEndian, &count)
|
||||||
|
var bts = make([]uint16, count/2)
|
||||||
|
binary.Read(buf, binary.LittleEndian, &bts)
|
||||||
|
runes := utf16.Decode(bts[:len(bts)-1])
|
||||||
|
hy.Url = string(runes)
|
||||||
|
} else {
|
||||||
|
log.Panicln("not support yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
//
|
||||||
|
// bts := make([]byte, 20)
|
||||||
|
// binary.Read(buf, binary.LittleEndian, bts)
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
// if flag&0x80 != 0 {
|
||||||
|
// binary.Read(buf, binary.LittleEndian, &count)
|
||||||
|
// var bts = make([]uint16, count)
|
||||||
|
// binary.Read(buf, binary.LittleEndian, &bts)
|
||||||
|
// runes := utf16.Decode(bts)
|
||||||
|
// hy.TargetFrame = string(runes)
|
||||||
|
// }
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
fmt.Printf("Unknow %X,%d\n", bof.Id, bof.Size)
|
||||||
buf.Seek(int64(bof.Size), 1)
|
buf.Seek(int64(bof.Size), 1)
|
||||||
}
|
}
|
||||||
if col != nil {
|
if col != nil {
|
||||||
w.addCell(col)
|
w.add(col)
|
||||||
}
|
}
|
||||||
return bof
|
return bof
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WorkSheet) addCell(col Coler) {
|
func (w *WorkSheet) add(content interface{}) {
|
||||||
|
if ch, ok := content.(ContentHandler); ok {
|
||||||
|
if col, ok := content.(Coler); ok {
|
||||||
|
w.addCell(col, ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WorkSheet) addCell(col Coler, ch ContentHandler) {
|
||||||
var row *Row
|
var row *Row
|
||||||
var ok bool
|
var ok bool
|
||||||
if row, ok = w.Rows[col.Row()]; !ok {
|
if row, ok = w.Rows[col.Row()]; !ok {
|
||||||
@ -96,7 +171,20 @@ func (w *WorkSheet) addCell(col Coler) {
|
|||||||
info.Index = col.Row()
|
info.Index = col.Row()
|
||||||
row = w.addRow(info)
|
row = w.addRow(info)
|
||||||
}
|
}
|
||||||
row.Cols[col.FirstCol()] = col
|
row.Cols[ch.FirstCol()] = ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WorkSheet) addRange(rang Ranger, ch ContentHandler) {
|
||||||
|
var row *Row
|
||||||
|
var ok bool
|
||||||
|
for i := rang.FirstRow(); i <= rang.LastRow(); i++ {
|
||||||
|
if row, ok = w.Rows[i]; !ok {
|
||||||
|
info := new(RowInfo)
|
||||||
|
info.Index = i
|
||||||
|
row = w.addRow(info)
|
||||||
|
}
|
||||||
|
row.Cols[ch.FirstCol()] = ch
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WorkSheet) addRow(info *RowInfo) (row *Row) {
|
func (w *WorkSheet) addRow(info *RowInfo) (row *Row) {
|
||||||
@ -107,7 +195,7 @@ func (w *WorkSheet) addRow(info *RowInfo) (row *Row) {
|
|||||||
if row, ok = w.Rows[info.Index]; ok {
|
if row, ok = w.Rows[info.Index]; ok {
|
||||||
row.info = info
|
row.info = info
|
||||||
} else {
|
} else {
|
||||||
row = &Row{info: info, Cols: make(map[uint16]Coler)}
|
row = &Row{info: info, Cols: make(map[uint16]ContentHandler)}
|
||||||
w.Rows[info.Index] = row
|
w.Rows[info.Index] = row
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestOpen(t *testing.T) {
|
func TestOpen(t *testing.T) {
|
||||||
wb, _ := Open("n201503061328.xls", "utf-8")
|
wb, _ := Open("n201502111031.xls", "utf-8")
|
||||||
fmt.Println(wb.ReadAllCells())
|
fmt.Println(wb.ReadAllCells(1000))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBof(t *testing.T) {
|
func TestBof(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user