2016-08-30 11:51:31 +08:00
|
|
|
package excelize
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"bytes"
|
2017-08-19 13:37:15 +08:00
|
|
|
"encoding/gob"
|
2016-08-30 11:51:31 +08:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"math"
|
2018-01-19 17:32:54 +08:00
|
|
|
"unicode"
|
2016-08-30 11:51:31 +08:00
|
|
|
)
|
|
|
|
|
2017-01-18 16:05:01 +08:00
|
|
|
// ReadZipReader can be used to read an XLSX in memory without touching the
|
|
|
|
// filesystem.
|
2016-09-06 21:20:24 +08:00
|
|
|
func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
|
2016-09-05 10:44:32 +08:00
|
|
|
fileList := make(map[string]string)
|
2016-09-06 21:20:24 +08:00
|
|
|
worksheets := 0
|
2016-08-30 11:51:31 +08:00
|
|
|
for _, v := range r.File {
|
2016-09-05 10:44:32 +08:00
|
|
|
fileList[v.Name] = readFile(v)
|
2016-09-06 21:20:24 +08:00
|
|
|
if len(v.Name) > 18 {
|
2017-01-18 14:47:23 +08:00
|
|
|
if v.Name[0:19] == "xl/worksheets/sheet" {
|
2016-09-06 21:20:24 +08:00
|
|
|
worksheets++
|
|
|
|
}
|
|
|
|
}
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2016-09-06 21:20:24 +08:00
|
|
|
return fileList, worksheets, nil
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
|
2017-01-24 18:29:02 +08:00
|
|
|
// readXML provides function to read XML content as string.
|
2016-09-05 16:37:15 +08:00
|
|
|
func (f *File) readXML(name string) string {
|
|
|
|
if content, ok := f.XLSX[name]; ok {
|
2016-12-26 23:55:59 +08:00
|
|
|
return content
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2017-01-23 16:15:01 +08:00
|
|
|
return ""
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
|
2017-01-24 18:29:02 +08:00
|
|
|
// saveFileList provides function to update given file content in file list of
|
|
|
|
// XLSX.
|
2017-03-06 12:05:41 +08:00
|
|
|
func (f *File) saveFileList(name, content string) {
|
2016-09-05 16:37:15 +08:00
|
|
|
f.XLSX[name] = XMLHeader + content
|
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.
|
2016-08-30 11:51:31 +08:00
|
|
|
func readFile(file *zip.File) string {
|
|
|
|
rc, err := file.Open()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
buff := bytes.NewBuffer(nil)
|
|
|
|
io.Copy(buff, rc)
|
|
|
|
rc.Close()
|
|
|
|
return string(buff.Bytes())
|
|
|
|
}
|
|
|
|
|
2017-06-26 18:44:19 +08:00
|
|
|
// ToAlphaString provides function to convert integer to Excel sheet column
|
2017-06-27 17:53:06 +08:00
|
|
|
// title. For example convert 36 to column title AK:
|
2017-06-26 18:44:19 +08:00
|
|
|
//
|
2017-06-27 17:53:06 +08:00
|
|
|
// excelize.ToAlphaString(36)
|
2017-06-26 18:44:19 +08:00
|
|
|
//
|
|
|
|
func ToAlphaString(value int) string {
|
2016-08-30 11:51:31 +08:00
|
|
|
if value < 0 {
|
2017-01-23 16:15:01 +08:00
|
|
|
return ""
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
var ans string
|
2017-06-27 17:53:06 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-06-27 17:53:06 +08:00
|
|
|
// TitleToNumber provides function to convert Excel sheet column title to int
|
2017-09-06 12:16:39 +08:00
|
|
|
// (this function doesn't do value check currently). For example convert AK
|
|
|
|
// and ak to column title 36:
|
2017-06-27 17:53:06 +08:00
|
|
|
//
|
|
|
|
// excelize.TitleToNumber("AK")
|
2017-09-05 18:26:47 +08:00
|
|
|
// excelize.TitleToNumber("ak")
|
2017-06-27 17:53:06 +08:00
|
|
|
//
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-01-18 16:05:01 +08:00
|
|
|
// letterOnlyMapF is used in conjunction with strings.Map to return only the
|
|
|
|
// characters A-Z and a-z in a string.
|
2016-09-12 17:37:06 +08:00
|
|
|
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
|
|
|
}
|
2016-09-12 17:37:06 +08:00
|
|
|
return -1
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
|
2017-01-18 16:05:01 +08:00
|
|
|
// intOnlyMapF is used in conjunction with strings.Map to return only the
|
|
|
|
// numeric portions of a string.
|
2016-09-12 17:37:06 +08:00
|
|
|
func intOnlyMapF(rune rune) rune {
|
|
|
|
if rune >= 48 && rune < 58 {
|
|
|
|
return rune
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2016-09-12 17:37:06 +08:00
|
|
|
return -1
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2017-08-19 13:37:15 +08:00
|
|
|
|
|
|
|
// deepCopy provides method to creates a deep copy of whatever is passed to it
|
|
|
|
// and returns the copy in an interface. The returned value will need to be
|
|
|
|
// asserted to the correct type.
|
|
|
|
func deepCopy(dst, src interface{}) error {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
|
|
|
|
}
|
2017-11-05 09:16:41 +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-01-19 17:32:54 +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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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, ""
|
|
|
|
}
|