excelize/lib.go

187 lines
4.4 KiB
Go
Raw Normal View History

2018-09-14 00:35:47 +08:00
/*
Package excelize providing a set of functions that allow you to write to
and read from XLSX files. Support reads and writes XLSX file generated by
Microsoft Excel 2007 and later. Support save file without losing original
charts of XLSX. This library needs Go version 1.8 or later.
Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
this source code is governed by a BSD-style license that can be found in
the LICENSE file.
*/
2016-08-30 11:51:31 +08:00
package excelize
import (
"archive/zip"
"bytes"
"io"
"log"
"math"
"unicode"
2016-08-30 11:51:31 +08:00
)
// ReadZipReader can be used to read an XLSX in memory without touching the
// filesystem.
2018-05-07 16:12:51 +08:00
func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
fileList := make(map[string][]byte)
worksheets := 0
2016-08-30 11:51:31 +08:00
for _, v := range r.File {
fileList[v.Name] = readFile(v)
if len(v.Name) > 18 {
if v.Name[0:19] == "xl/worksheets/sheet" {
worksheets++
}
}
2016-08-30 11:51:31 +08:00
}
return fileList, worksheets, nil
2016-08-30 11:51:31 +08:00
}
2018-08-06 10:21:24 +08:00
// readXML provides a function to read XML content as string.
2018-05-07 16:12:51 +08:00
func (f *File) readXML(name string) []byte {
if content, ok := f.XLSX[name]; ok {
return content
2016-08-30 11:51:31 +08:00
}
2018-05-07 16:12:51 +08:00
return []byte{}
2016-08-30 11:51:31 +08:00
}
2018-08-06 10:21:24 +08:00
// saveFileList provides a function to update given file content in file list
// of XLSX.
2018-05-07 16:12:51 +08:00
func (f *File) saveFileList(name string, content []byte) {
newContent := make([]byte, 0, len(XMLHeader)+len(content))
newContent = append(newContent, []byte(XMLHeader)...)
newContent = append(newContent, content...)
f.XLSX[name] = newContent
2016-08-30 11:51:31 +08:00
}
2016-10-19 20:39:44 +08:00
// Read file content as string in a archive file.
2018-05-07 16:12:51 +08:00
func readFile(file *zip.File) []byte {
2016-08-30 11:51:31 +08:00
rc, err := file.Open()
if err != nil {
log.Fatal(err)
}
buff := bytes.NewBuffer(nil)
_, _ = io.Copy(buff, rc)
2016-08-30 11:51:31 +08:00
rc.Close()
2018-05-07 16:12:51 +08:00
return buff.Bytes()
2016-08-30 11:51:31 +08:00
}
2018-08-06 10:21:24 +08:00
// ToAlphaString provides a function to convert integer to Excel sheet column
// title. For example convert 36 to column title AK:
//
// excelize.ToAlphaString(36)
//
func ToAlphaString(value int) string {
2016-08-30 11:51:31 +08:00
if value < 0 {
return ""
2016-08-30 11:51:31 +08:00
}
var ans string
i := value + 1
2016-08-30 11:51:31 +08:00
for i > 0 {
ans = string((i-1)%26+65) + ans
i = (i - 1) / 26
}
return ans
}
2018-08-06 10:21:24 +08:00
// TitleToNumber provides a function to convert Excel sheet column title to
// int (this function doesn't do value check currently). For example convert
// AK and ak to column title 36:
//
// excelize.TitleToNumber("AK")
2017-09-05 18:26:47 +08:00
// excelize.TitleToNumber("ak")
//
func TitleToNumber(s string) int {
2016-08-30 11:51:31 +08:00
weight := 0.0
sum := 0
for i := len(s) - 1; i >= 0; i-- {
2017-09-06 12:16:39 +08:00
ch := int(s[i])
2017-09-05 18:06:38 +08:00
if int(s[i]) >= int('a') && int(s[i]) <= int('z') {
ch = int(s[i]) - 32
}
sum = sum + (ch-int('A')+1)*int(math.Pow(26, weight))
2016-08-30 11:51:31 +08:00
weight++
}
return sum - 1
}
// letterOnlyMapF is used in conjunction with strings.Map to return only the
// characters A-Z and a-z in a string.
func letterOnlyMapF(rune rune) rune {
switch {
case 'A' <= rune && rune <= 'Z':
return rune
case 'a' <= rune && rune <= 'z':
return rune - 32
2016-08-30 11:51:31 +08:00
}
return -1
2016-08-30 11:51:31 +08:00
}
// intOnlyMapF is used in conjunction with strings.Map to return only the
// numeric portions of a string.
func intOnlyMapF(rune rune) rune {
if rune >= 48 && rune < 58 {
return rune
2016-08-30 11:51:31 +08:00
}
return -1
2016-08-30 11:51:31 +08:00
}
// boolPtr returns a pointer to a bool with the given value.
func boolPtr(b bool) *bool { return &b }
// defaultTrue returns true if b is nil, or the pointed value.
func defaultTrue(b *bool) bool {
if b == nil {
return true
}
return *b
}
2018-08-06 10:21:24 +08:00
// axisLowerOrEqualThan returns true if axis1 <= axis2 axis1/axis2 can be
// either a column or a row axis, e.g. "A", "AAE", "42", "1", etc.
//
// For instance, the following comparisons are all true:
//
// "A" <= "B"
// "A" <= "AA"
// "B" <= "AA"
// "BC" <= "ABCD" (in a XLSX sheet, the BC col comes before the ABCD col)
// "1" <= "2"
// "2" <= "11" (in a XLSX sheet, the row 2 comes before the row 11)
// and so on
func axisLowerOrEqualThan(axis1, axis2 string) bool {
if len(axis1) < len(axis2) {
return true
} else if len(axis1) > len(axis2) {
return false
} else {
return axis1 <= axis2
}
}
2018-08-06 10:21:24 +08:00
// getCellColRow returns the two parts of a cell identifier (its col and row)
// as strings
//
// For instance:
//
// "C220" => "C", "220"
// "aaef42" => "aaef", "42"
// "" => "", ""
func getCellColRow(cell string) (col, row string) {
for index, rune := range cell {
if unicode.IsDigit(rune) {
return cell[:index], cell[index:]
}
}
return cell, ""
}
2018-07-13 17:40:47 +08:00
// parseFormatSet provides a method to convert format string to []byte and
// handle empty string.
func parseFormatSet(formatSet string) []byte {
if formatSet != "" {
return []byte(formatSet)
}
return []byte("{}")
}