Просмотр исходного кода

Merge pull request #9 from zhcy/master

Thanks for your work!
Geoff Teale 13 лет назад
Родитель
Сommit
9220224285
5 измененных файлов с 215 добавлено и 43 удалено
  1. 0 1
      doc.go
  2. 114 19
      lib.go
  3. 5 10
      sharedstrings.go
  4. 85 0
      style.go
  5. 11 13
      worksheet.go

+ 0 - 1
doc.go

@@ -6,4 +6,3 @@
 // the source for xlsx2csv here: https://github.com/tealeg/xlsx2csv
 
 package xlsx
-

+ 114 - 19
lib.go

@@ -26,7 +26,9 @@ func (e *XLSXReaderError) Error() string {
 // Cell is a high level structure intended to provide user access to
 // the contents of Cell within an xlsx.Row.
 type Cell struct {
-	Value string
+	Value      string
+	styleIndex int
+	styles     *xlsxStyles
 }
 
 // CellInterface defines the public API of the Cell.
@@ -38,6 +40,27 @@ func (c *Cell) String() string {
 	return c.Value
 }
 
+func (c *Cell) GetStyle() *Style {
+	style := new(Style)
+	if c.styleIndex > 0 && c.styleIndex < len(c.styles.CellXfs) {
+		xf := c.styles.CellXfs[c.styleIndex]
+		if xf.ApplyBorder != "0" {
+			var border Border
+			border.Left = c.styles.Borders[xf.BorderId].Left.Style
+			border.Right = c.styles.Borders[xf.BorderId].Right.Style
+			border.Top = c.styles.Borders[xf.BorderId].Top.Style
+			border.Bottom = c.styles.Borders[xf.BorderId].Bottom.Style
+			style.Boders = border
+		}
+		if xf.ApplyFill != "0" {
+			var fill Fill
+			fill.BgColorIndex = c.styles.Fills[xf.FillId].BgColorIndex
+			style.Fills = fill
+		}
+	}
+	return style
+}
+
 // Row is a high level structure indended to provide user access to a
 // row within a xlsx.Sheet.  An xlsx.Row contains a slice of xlsx.Cell.
 type Row struct {
@@ -47,18 +70,42 @@ type Row struct {
 // Sheet is a high level structure intended to provide user access to
 // the contents of a particular sheet within an XLSX file.
 type Sheet struct {
-	Rows []*Row
+	Rows   []*Row
 	MaxRow int
 	MaxCol int
 }
 
+// Style is a high level structure intended to provide user access to
+// the contents of Style within an XLSX file.
+type Style struct {
+	Boders Border
+	Fills  Fill
+}
+
+// Border is a high level structure intended to provide user access to
+// the contents of Border Style within an Sheet.
+type Border struct {
+	Left   string
+	Right  string
+	Top    string
+	Bottom string
+}
+
+// Fill is a high level structure intended to provide user access to
+// the contents of background and foreground color index within an Sheet.
+type Fill struct {
+	BgColorIndex  string
+	FgColorIndex  string
+}
+
 // File is a high level structure providing a slice of Sheet structs
 // to the user.
 type File struct {
 	worksheets     map[string]*zip.File
 	referenceTable []string
-	Sheets         []*Sheet // sheet access by index
-	Sheet map[string]*Sheet // sheet access by name
+	styles         *xlsxStyles
+	Sheets         []*Sheet          // sheet access by index
+	Sheet          map[string]*Sheet // sheet access by name
 }
 
 // getRangeFromString is an internal helper function that converts
@@ -217,7 +264,7 @@ func makeRowFromRaw(rawrow xlsxRow) *Row {
 		if error != nil {
 			panic(fmt.Sprintf("Invalid Cell Coord, %s\n", rawcell.R))
 		}
-		if x  > upper {
+		if x > upper {
 			upper = x
 		}
 	}
@@ -254,33 +301,53 @@ func getValueFromCellData(rawcell xlsxC, reftable []string) string {
 // readRowsFromSheet is an internal helper function that extracts the
 // rows from a XSLXWorksheet, poulates them with Cells and resolves
 // the value references from the reference table and stores them in
-func readRowsFromSheet(Worksheet *xlsxWorksheet, reftable []string) ([]*Row ,int) {
+func readRowsFromSheet(Worksheet *xlsxWorksheet, file *File) ([]*Row, int, int) {
 	var rows []*Row
 	var row *Row
 	var maxCol int
+	var maxRow int
+	var reftable []string
 
-	rows = make([]*Row, len(Worksheet.SheetData.Row))
+	reftable = file.referenceTable
 	maxCol = 0
-	for i, rawrow := range Worksheet.SheetData.Row {
+	maxRow = 0
+	for _, rawrow := range Worksheet.SheetData.Row {
+		for _, rawcell := range rawrow.C {
+			x, y, error := getCoordsFromCellIDString(rawcell.R)
+			if error != nil {
+				panic(fmt.Sprintf("Invalid Cell Coord, %s\n", rawcell.R))
+			}
+			if x > maxCol {
+				maxCol = x
+			}
+			if y > maxRow {
+				maxRow = y
+			}
+		}
+	}
+	maxCol += 1
+	maxRow += 1
+	rows = make([]*Row, maxRow)
+	for _, rawrow := range Worksheet.SheetData.Row {
 		// range is not empty
 		if len(rawrow.Spans) != 0 {
 			row = makeRowFromSpan(rawrow.Spans)
 		} else {
 			row = makeRowFromRaw(rawrow)
 		}
+		rowno := 0
 		for _, rawcell := range rawrow.C {
-			x, _, error := getCoordsFromCellIDString(rawcell.R)
-			if error != nil {
-				panic(fmt.Sprintf("Invalid Cell Coord, %s\n", rawcell.R))
-			}
-			if x > maxCol {
-				maxCol = x
+			x, y, _ := getCoordsFromCellIDString(rawcell.R)
+			if y != 0 && rowno == 0{
+				rowno = y
 			}
 			row.Cells[x].Value = getValueFromCellData(rawcell, reftable)
+			row.Cells[x].styleIndex = rawcell.S
+			row.Cells[x].styles = file.styles
 		}
-		rows[i] = row
+		rows[rowno] = row
 	}
-	return rows,maxCol
+	return rows, maxCol, maxRow
 }
 
 // readSheetsFromZipFile is an internal helper function that loops
@@ -309,9 +376,8 @@ func readSheetsFromZipFile(f *zip.File, file *File) ([]*Sheet, []string, error)
 			return nil, nil, error
 		}
 		sheet := new(Sheet)
-		sheet.Rows,sheet.MaxCol = readRowsFromSheet(worksheet, file.referenceTable)
+		sheet.Rows, sheet.MaxCol, sheet.MaxRow = readRowsFromSheet(worksheet, file)
 		sheets[i] = sheet
-		sheet.MaxRow = len(sheet.Rows)
 		names[i] = rawsheet.Name
 	}
 	return sheets, names, nil
@@ -340,6 +406,27 @@ func readSharedStringsFromZipFile(f *zip.File) ([]string, error) {
 	return reftable, nil
 }
 
+// readStylesFromZipFile() is an internal helper function to
+// extract a style table from the style.xml file within
+// the XLSX zip file.
+func readStylesFromZipFile(f *zip.File) (*xlsxStyles, error) {
+	var style *xlsxStyles
+	var error error
+	var rc io.ReadCloser
+	var decoder *xml.Decoder
+	rc, error = f.Open()
+	if error != nil {
+		return nil, error
+	}
+	style = new(xlsxStyles)
+	decoder = xml.NewDecoder(rc)
+	error = decoder.Decode(style)
+	if error != nil {
+		return nil, error
+	}
+	return style, nil
+}
+
 // OpenFile() take the name of an XLSX file and returns a populated
 // xlsx.File struct for it.
 func OpenFile(filename string) (x *File, e error) {
@@ -348,6 +435,7 @@ func OpenFile(filename string) (x *File, e error) {
 	var file *File
 	var v *zip.File
 	var workbook *zip.File
+	var styles *zip.File
 	var sharedStrings *zip.File
 	var reftable []string
 	var worksheets map[string]*zip.File
@@ -365,6 +453,8 @@ func OpenFile(filename string) (x *File, e error) {
 			sharedStrings = v
 		case "xl/workbook.xml":
 			workbook = v
+		case "xl/styles.xml":
+			styles = v
 		default:
 			if len(v.Name) > 12 {
 				if v.Name[0:13] == "xl/worksheets" {
@@ -384,6 +474,11 @@ func OpenFile(filename string) (x *File, e error) {
 		return nil, error
 	}
 	file.referenceTable = reftable
+	style, error := readStylesFromZipFile(styles)
+	if error != nil {
+		return nil, error
+	}
+	file.styles = style
 	sheets, names, error := readSheetsFromZipFile(workbook, file)
 	if error != nil {
 		return nil, error
@@ -394,7 +489,7 @@ func OpenFile(filename string) (x *File, e error) {
 		return nil, error
 	}
 	file.Sheets = sheets
-	sheetMap = make(map[string]*Sheet,len(names))
+	sheetMap = make(map[string]*Sheet, len(names))
 	for i := 0; i < len(names); i++ {
 		sheetMap[names[i]] = sheets[i]
 	}

+ 5 - 10
sharedstrings.go

@@ -1,23 +1,21 @@
 package xlsx
 
-
 // xlsxSST directly maps the sst element from the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main currently
 // I have not checked this for completeness - it does as much as need.
 type xlsxSST struct {
-	Count       string    `xml:"count,attr"`
-	UniqueCount string    `xml:"uniqueCount,attr"`
-	SI          []xlsxSI  `xml:"si"`
+	Count       string   `xml:"count,attr"`
+	UniqueCount string   `xml:"uniqueCount,attr"`
+	SI          []xlsxSI `xml:"si"`
 }
 
-
 // xlsxSI directly maps the si element from the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked this for completeness - it does as
 // much as I need.
 type xlsxSI struct {
-	T string `xml:"t"` 
-	R        []xlsxR `xml:"r"`
+	T string  `xml:"t"`
+	R []xlsxR `xml:"r"`
 }
 
 // xlsxR directly maps the r element from the namespace
@@ -36,7 +34,6 @@ type xlsxR struct {
 // 	Data string `xml:"chardata"`
 // }
 
-
 // MakeSharedStringRefTable() takes an xlsxSST struct and converts
 // it's contents to an slice of strings used to refer to string values
 // by numeric index - this is the model used within XLSX worksheet (a
@@ -62,5 +59,3 @@ func MakeSharedStringRefTable(source *xlsxSST) []string {
 func ResolveSharedString(reftable []string, index int) string {
 	return reftable[index]
 }
-
-

+ 85 - 0
style.go

@@ -0,0 +1,85 @@
+// xslx is a package designed to help with reading data from
+// spreadsheets stored in the XLSX format used in recent versions of
+// Microsoft's Excel spreadsheet.
+//
+// For a concise example of how to use this library why not check out
+// the source for xlsx2csv here: https://github.com/tealeg/xlsx2csv
+
+package xlsx
+
+// xlsxStyle directly maps the style element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+type xlsxStyles struct {
+	Fonts        []xlsxFont   `xml:"fonts>font"`
+	Fills        []xlsxFill   `xml:"fills>fill"`
+	Borders      []xlsxBorder `xml:"borders>border"`
+	CellStyleXfs []xlsxXf     `xml:"cellStyleXfs>xf"`
+	CellXfs      []xlsxXf     `xml:"cellXfs>xf"`
+}
+
+// xlsxFont directly maps the font element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+type xlsxFont struct {
+	Sz      xlsxVal `xml:"sz"`
+	Name    xlsxVal `xml:"name"`
+	Family  xlsxVal `xml:"family"`
+	Charset xlsxVal `xml:"charset"`
+}
+
+// xlsxVal directly maps the val element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+type xlsxVal struct {
+	Val string `xml:"val,attr"`
+}
+
+// xlsxFill directly maps the fill element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+// ColorIndex ARGBValue
+// 0          00000000
+// 1          00FFFFFF
+// 2          00FF0000
+// 3          0000FF00
+// ...............
+// ...............
+type xlsxFill struct {
+	FgColorIndex string  `xml:"patternFill>fgColor>indexed,attr"`
+	BgColorIndex string  `xml:"patternFill>bgColor>indexed,attr"`
+}
+
+// xlsxBorder directly maps the border element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+type xlsxBorder struct {
+	Left   xlsxLine `xml:"left"`
+	Right  xlsxLine `xml:"right"`
+	Top    xlsxLine `xml:"top"`
+	Bottom xlsxLine `xml:"bottom"`
+}
+
+// xlsxLine directly maps the line style element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+type xlsxLine struct {
+	Style string `xml:"style,attr"`
+}
+
+// xlsxXf directly maps the xf element in the namespace
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// currently I have not checked it for completeness - it does as much
+// as I need.
+type xlsxXf struct {
+	ApplyBorder string `xml:"applyBorder,attr"`
+	BorderId    int    `xml:"borderId,attr"`
+	ApplyFill string `xml:"applyFill,attr"`
+	FillId int `xml:"fillId,attr"`
+}

+ 11 - 13
worksheet.go

@@ -5,10 +5,10 @@ package xlsx
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxWorksheet struct {
-	Dimension     xlsxDimension      `xml:"dimension"`
-	SheetViews    xlsxSheetViews     `xml:"sheetViews"`
-	SheetFormatPr xlsxSheetFormatPr  `xml:"sheetFormatPr"`
-	SheetData     xlsxSheetData      `xml:"sheetData"`
+	Dimension     xlsxDimension     `xml:"dimension"`
+	SheetViews    xlsxSheetViews    `xml:"sheetViews"`
+	SheetFormatPr xlsxSheetFormatPr `xml:"sheetFormatPr"`
+	SheetData     xlsxSheetData     `xml:"sheetData"`
 }
 
 // xlsxDimension directly maps the dimension element in the namespace
@@ -32,12 +32,11 @@ type xlsxSheetViews struct {
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxSheetView struct {
-	TabSelected    string `xml:"tabSelected,attr"`
-	WorkbookViewID string `xml:"workbookViewId,attr"`
+	TabSelected    string        `xml:"tabSelected,attr"`
+	WorkbookViewID string        `xml:"workbookViewId,attr"`
 	Selection      xlsxSelection `xml:"selection"`
 }
 
-
 // xlsxSelection directly maps the selection element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much
@@ -62,7 +61,7 @@ type xlsxSheetFormatPr struct {
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxSheetData struct {
-	Row []xlsxRow  `xml:"row"`
+	Row []xlsxRow `xml:"row"`
 }
 
 // xlsxRow directly maps the row element in the namespace
@@ -81,11 +80,11 @@ type xlsxRow struct {
 // as I need.
 type xlsxC struct {
 	R string `xml:"r,attr"`
+	S int    `xml:"s,attr"`
 	T string `xml:"t,attr"`
-	V string  `xml:"v"`
+	V string `xml:"v"`
 }
 
-
 // xlsxV directly maps the v element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much
@@ -95,11 +94,10 @@ type xlsxC struct {
 // }
 
 // get cell
-func (sh *Sheet) Cell(row,col int) *Cell {
+func (sh *Sheet) Cell(row, col int) *Cell {
 
-	if len(sh.Rows) > row && len(sh.Rows[row].Cells) > col {
+	if len(sh.Rows) > row && sh.Rows[row] != nil && len(sh.Rows[row].Cells) > col {
 		return sh.Rows[row].Cells[col]
 	}
 	return new(Cell)
 }
-