Skip to content
Fast and safe way to read/update your existing Excel xlsx files
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
format finalize conditionals May 5, 2019
internal finalize conditionals May 5, 2019
options ... Aug 8, 2018
test_files small fixes Apr 20, 2019
types added conditional prototype, May 5, 2019
.gitignore *updated tests, benchmarks and examples Jul 13, 2018
.travis.yml travis, update go version to 1.12 to support modules May 7, 2019
BENCHMARKS.md added benchmark results into readme, added benchmark for saving files Aug 27, 2017
LICENSE initial commit Aug 26, 2017
Makefile *refactored usage of types.Ref with types.Bounds internally Jul 16, 2018
README.md ... May 5, 2019
cell.go added conditional prototype, May 5, 2019
cell_test.go golint fixes Jul 29, 2018
col.go small fixes Apr 20, 2019
col_iterator.go *Sheet now is interface Jul 13, 2018
col_test.go added conditional prototype, May 5, 2019
columns.go refactored markup for lists to simplify logic and improve performance… May 5, 2019
columns_test.go refactored markup for lists to simplify logic and improve performance… May 5, 2019
conditional.go ... May 5, 2019
conditional_test.go ... May 5, 2019
example_test.go added conditional prototype, May 5, 2019
hyperlinks.go refactored markup for lists to simplify logic and improve performance… May 5, 2019
hyperlinks_test.go +prototype for hyperlinks Jul 31, 2018
linkname.s draft for hyperlinks, more merged cells functionality Nov 8, 2018
merged_cells.go refactored markup for lists to simplify logic and improve performance… May 5, 2019
merged_cells_test.go draft for hyperlinks, more merged cells functionality Nov 8, 2018
range.go removed SheetRead method, refactor Sheet method to support options May 3, 2019
range_iterator.go *fix for cell.Reset, now it preserve cell's ref Jul 30, 2018
range_test.go added conditional prototype, May 5, 2019
rich_text.go added resolving for rich texts in shared strings May 4, 2019
rich_text_test.go added conditional prototype, May 5, 2019
row.go small fixes Apr 20, 2019
row_iterator.go *Sheet now is interface Jul 13, 2018
row_test.go added conditional prototype, May 5, 2019
shared_strings.go some hash cleanups, more tests May 4, 2019
shared_strings_test.go added resolving for rich texts in shared strings May 4, 2019
sheet.go ... May 5, 2019
sheet_info.go fix for sheet's relationships file naming May 5, 2019
sheet_info_test.go refactored markup for lists to simplify logic and improve performance… May 5, 2019
sheet_iterator.go *Sheet now is interface Jul 13, 2018
sheet_readstream.go fix for stream read and cell with styles May 11, 2019
sheet_readstream_test.go added resolving for rich texts in shared strings May 4, 2019
sheet_readwrite.go fix for ignore dimension flag May 16, 2019
sheet_readwrite_test.go *bugfix for InsertRow Jul 30, 2018
spreadsheet.go added resolving for rich texts in shared strings May 4, 2019
spreadsheet_test.go added resolving for rich texts in shared strings May 4, 2019
style_sheet.go refactored markup for lists to simplify logic and improve performance… May 5, 2019
style_sheet_test.go stylesheet lists write optional count attribute to improve supporting… May 5, 2019
workbook.go fix for renamed 'ooxml' package name Aug 27, 2017
xlsx.go * refactored types and ml. Now types doesn't have any types - only tr… Aug 9, 2018
xlsx_test.go +more tests Jul 21, 2018

README.md

XLSX

Build Status Code Coverage Go Report Card GoDoc License FOSSA Status Donate

package main

import (
	"fmt"
	"github.com/plandem/xlsx"
	"github.com/plandem/xlsx/format"
	"github.com/plandem/xlsx/types"
)

func main() {
	xl, err := xlsx.Open("./test_files/example_simple.xlsx")
	if err != nil {
		panic(err)
	}

	defer func() {
		_ = xl.Close()
	}()

	redBoldYellow := xl.AddFormatting(
		format.NewStyles(
			format.Font.Bold,
			format.Font.Color("#ff0000"),
			format.Fill.Type(format.PatternTypeSolid),
			format.Fill.Color("#FFFF00"),
		),
	)

	//iterating via indexes
	sheet := xl.Sheet(0)
	iMaxCol, iMaxRow := sheet.Dimension()
	for iRow := 0; iRow < iMaxRow; iRow++ {
		for iCol := 0; iCol < iMaxCol; iCol++ {
			if iRow % 2 == 0 && iCol % 2 == 0 {
				cell := sheet.Cell(iCol, iRow)
				cell.SetFormatting(redBoldYellow)
			}
		}
	}

	//iterating via iterators
	for rows := sheet.Rows(); rows.HasNext(); {
		_, row := rows.Next()
		
		for cells := row.Cells(); cells.HasNext(); {
			iCol, iRow, cell := cells.Next()
			if iRow % 2 == 0 && iCol % 2 == 0 {
				cell.SetFormatting(redBoldYellow)
			}
		}
	}
    	
	//walk through the range's cells
	for rows := sheet.Rows(); rows.HasNext(); {
		_, row := rows.Next()
		row.Walk(func(idx, iCol, iRow int, cell *xlsx.Cell) {
			if iRow % 2 == 0 && iCol % 2 == 0 {
				cell.SetFormatting(redBoldYellow)
			}
		})
 	}

	//Add hyperlink and set value same time
	_ = sheet.CellByRef("A1").SetValueWithHyperlink("Link To Google", "http://google.com")
	
	//Add hyperlink as string
	_ = sheet.Range("B1:C3").SetHyperlink("spam@spam.it")	

	//Add hyperlink via helper type for advanced settings
	_ = sheet.CellByRef("A7").SetHyperlink(types.NewHyperlink(
		types.Hyperlink.ToFile("./example_simple.xlsx"),
		types.Hyperlink.ToRef("C3", "Sheet1"),
		types.Hyperlink.Tooltip("That's a tooltip"),
		types.Hyperlink.Display("Something to display"), //Cell still holds own value
		types.Hyperlink.Formatting(redBoldYellow),
	))

	sheet.CellByRef("A1").RemoveHyperlink()
	
	//Merged Cells
	_= sheet.Range("A1:C3").Merge()
	sheet.Range("A1:C3").Split()
	
	//Rich Text
	_= sheet.CellByRef("F10").SetText(
		"plain text", 
		format.NewStyles(
			format.Font.Bold,
			format.Font.Color("#ff0000"),
		),
		"red bold text",
		"another plain text",
	)
	
	//Conditional formatting
	_= sheet.AddConditional(format.NewConditions(
		format.Conditions.Rule(
			format.Condition.Type(format.ConditionTypeCellIs),
			format.Condition.Operator(format.ConditionOperatorLessThanOrEqual),
			format.Condition.Priority(2),
			format.Condition.Formula("500"),
			format.Condition.Style(format.NewStyles(
				format.Font.Bold,
				format.Font.Color("#FF0000"),
			)),
		),
	), "A1:A10", "B2", "C1:C10")
    	
	_= xl.SaveAs("test1.xlsx")
}

Introduction

Why another library to work with Excel XLSX in GO?

Truth be told, developing of any library starts with some personal goals of author. Someone wants simple library to read Excel files, someone wants to create a new file, other wants to add charts.

So what were the goals that time? It's a great pity, but I could not get a library that:

  1. respects existing data/formatting - no corrupted files or lost formatting

What if I need to open a well formatted file created with my favorite desktop application and update only one value?! I must get almost same file with just one updated value. None of existing library was able to do it. Corrupted file or lost formatting is common issue.

  1. works with big files - reasonable speed and memory footprint

Same here, someone could not open, others took forever to open with anomaly memory usage.

  1. consistent and as small API as possible with enough features set to do most common tasks - learning curve means something

Why?! Because it's not rocket science - open/create file, create/read/update/delete sheets/rows/cols and use styles. XLSX is quite simple format to read/write and GO has quite powerful xml encoder/decoder, so the hardest part - that API.

  1. easy to read/understand source code, easy to maintain, easy to contribute - no shadow places/hacks/magic, just read and understand

I was trying to contribute to existing libraries, but...actually it's much faster to create it from ground zero than to refactor existing and get satisfied results or fix some issues.

Benchmarks

It was not a goal to make best of the best, but the same time it's interesting to know pros/cons. For some cases this library is second, for other - best, but in case of reading huge files - the only.

tealeg excelize xlsx
RandomGet 1! 3 2
RandomSet 1! 3 2
RandomSetStyle 1! 3 2
ReadBigFile 2 3 1
UpdateBigFile 2!! 3 1
ReadHugeFile - - 1
UpdateHugeFile - - 1
  • ! - does not mutate information directly, so faster get/set, but slower read/write files - sometimes it can take forever to open file.
  • !! - corrupted file after saving, lost styles/formatting

Benchmarks report

Documentation and Examples

For more detailed documentation and examples you can check godoc.org

Roadmap

  • sheet: copy
  • sheet: read as stream
  • sheet: write as stream
  • merged cells: merge/split for ranges, cols, rows
  • hyperlinks: for cells, ranges, cols, rows
  • range: copy
  • row: copy
  • col: copy
  • cell: comments
  • cell: formulas
  • cell: typed getter/setter for values
  • other: conditional formatting
  • other: rich texts
  • other: drawing
  • other: unpack package to temp folder to reduce memory usage
  • other: more tests

Contribution

  • To prevent mess, sources have strict separation of markup and functionality. Document that describes OOXML is quite huge (about 6K pages), but the same time - functionality is not.
  • All markup resides inside of 'ml' folders, only marshal/unmarshal is allowed here, no any functionality.
  • Not every 'ml object' has related 'functional object' and vice versa.
  • If you want some functionality, then wrap 'ml object' and do what you want.

OOXML edition

XML is compliant with part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML

License

FOSSA Status

You can’t perform that action at this time.