From 5544765b84b4332948786b345fdb8d456aba1d4b Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 24 Mar 2015 13:06:52 +0800 Subject: [PATCH] add hyperlink and formula cell support --- cell_range.go | 59 ++++++++++++++++++++++++++++ col.go | 29 +++++++++++--- row.go | 2 +- workbook.go | 42 ++++++++++++-------- worksheet.go | 106 +++++++++++++++++++++++++++++++++++++++++++++----- xls_test.go | 4 +- 6 files changed, 208 insertions(+), 34 deletions(-) create mode 100644 cell_range.go diff --git a/cell_range.go b/cell_range.go new file mode 100644 index 0000000..1145dd8 --- /dev/null +++ b/cell_range.go @@ -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) +// } +// } diff --git a/col.go b/col.go index d38a2f0..ee1ff93 100644 --- a/col.go +++ b/col.go @@ -5,16 +5,19 @@ import ( "math" ) +type ContentHandler interface { + String(*WorkBook) []string + FirstCol() uint16 + LastCol() uint16 +} + type Col struct { RowB uint16 FirstColB uint16 } type Coler interface { - String(*WorkBook) []string Row() uint16 - FirstCol() uint16 - LastCol() uint16 } func (c *Col) Row() uint16 { @@ -30,7 +33,7 @@ func (c *Col) LastCol() uint16 { } func (c *Col) String(wb *WorkBook) []string { - return []string{""} + return []string{"default"} } type XfRk struct { @@ -99,8 +102,20 @@ func (c *NumberCol) String(wb *WorkBook) []string { } 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 { Col Xfrk XfRk @@ -124,3 +139,7 @@ type BlankCol struct { Col Xf uint16 } + +func (c *BlankCol) String(wb *WorkBook) []string { + return []string{""} +} diff --git a/row.go b/row.go index 72c0203..ebe6cf1 100644 --- a/row.go +++ b/row.go @@ -12,5 +12,5 @@ type RowInfo struct { type Row struct { info *RowInfo - Cols map[uint16]Coler + Cols map[uint16]ContentHandler } diff --git a/workbook.go b/workbook.go index 30cab85..42484d8 100644 --- a/workbook.go +++ b/workbook.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/binary" "io" - "log" "unicode/utf16" ) @@ -150,7 +149,6 @@ func (w *WorkBook) get_string(buf io.ReadSeeker, size uint16) string { var phonetic_size uint32 var flag byte binary.Read(buf, binary.LittleEndian, &flag) - log.Println(flag, size) if flag&0x8 != 0 { binary.Read(buf, binary.LittleEndian, &richtext_num) } @@ -200,27 +198,37 @@ func (w *WorkBook) PrepareSheet(sheet *WorkSheet) { 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 { - w.PrepareSheet(sheet) - if sheet.MaxRow != 0 { - temp := make([][]string, sheet.MaxRow+1) - 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)...) + if len(res) < max { + max = max - len(res) + w.PrepareSheet(sheet) + if sheet.MaxRow != 0 { + leng := int(sheet.MaxRow) + 1 + if max < leng { + leng = max + } + 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) - for i := uint16(0); i < col.LastCol()-col.FirstCol()+1; i++ { - data[col.FirstCol()+i] = str[i] + if leng > int(k) { + temp[k] = data } } - temp[k] = data } + res = append(res, temp...) } - res = append(res, temp...) } } return diff --git a/worksheet.go b/worksheet.go index 065ab5c..8c441e2 100644 --- a/worksheet.go +++ b/worksheet.go @@ -4,6 +4,8 @@ import ( "encoding/binary" "fmt" "io" + "log" + "unicode/utf16" ) 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 { - var col Coler + var col interface{} switch bof.Id { - case 0x0E5: //MERGEDCELLS - // ws.mergedCells(buf) + // case 0x0E5: //MERGEDCELLS + // ws.mergedCells(buf) case 0x208: //ROW r := new(RowInfo) 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) binary.Read(buf, binary.LittleEndian, col) case 0x06: //FORMULA - col = new(FormulaCol) - binary.Read(buf, binary.LittleEndian, col) + c := new(FormulaCol) + 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 col = new(RkCol) 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 col = new(BlankCol) 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: + fmt.Printf("Unknow %X,%d\n", bof.Id, bof.Size) buf.Seek(int64(bof.Size), 1) } if col != nil { - w.addCell(col) + w.add(col) } 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 ok bool if row, ok = w.Rows[col.Row()]; !ok { @@ -96,7 +171,20 @@ func (w *WorkSheet) addCell(col Coler) { info.Index = col.Row() 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) { @@ -107,7 +195,7 @@ func (w *WorkSheet) addRow(info *RowInfo) (row *Row) { if row, ok = w.Rows[info.Index]; ok { row.info = info } else { - row = &Row{info: info, Cols: make(map[uint16]Coler)} + row = &Row{info: info, Cols: make(map[uint16]ContentHandler)} w.Rows[info.Index] = row } return diff --git a/xls_test.go b/xls_test.go index 1b6271c..c899ca3 100644 --- a/xls_test.go +++ b/xls_test.go @@ -7,8 +7,8 @@ import ( ) func TestOpen(t *testing.T) { - wb, _ := Open("n201503061328.xls", "utf-8") - fmt.Println(wb.ReadAllCells()) + wb, _ := Open("n201502111031.xls", "utf-8") + fmt.Println(wb.ReadAllCells(1000)) } func TestBof(t *testing.T) {