21
0

add hyperlink and formula cell support

This commit is contained in:
Liu Ming 2015-03-24 13:06:52 +08:00
parent c7f56bc92a
commit 5544765b84
6 changed files with 208 additions and 34 deletions

59
cell_range.go Normal file
View 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
View File

@ -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
View File

@ -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
} }

View File

@ -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

View File

@ -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

View File

@ -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) {