2019-01-01 13:20:14 +08:00
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
2018-09-14 00:44:23 +08:00
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// 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.
2018-09-14 00:58:48 +08:00
2016-08-30 11:51:31 +08:00
package excelize
import (
"archive/zip"
"bytes"
"io"
"log"
2018-11-02 23:08:31 +08:00
"strconv"
"strings"
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.
2018-05-07 16:12:51 +08:00
func ReadZipReader ( r * zip . Reader ) ( map [ string ] [ ] byte , int , error ) {
fileList := make ( map [ string ] [ ] byte )
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
}
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 {
2016-09-05 16:37:15 +08:00
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
}
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 )
2018-05-27 11:25:55 +08:00
_ , _ = 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
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
}
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:
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 {
2019-02-02 11:05:01 +08:00
weight := 1
2016-08-30 11:51:31 +08:00
sum := 0
for i := len ( s ) - 1 ; i >= 0 ; i -- {
2019-02-02 11:05:01 +08:00
ch := s [ i ]
if ch >= 'a' && ch <= 'z' {
ch -= 32
2017-09-05 18:06:38 +08:00
}
2019-02-02 11:05:01 +08:00
sum += int ( ch - 'A' + 1 ) * weight
weight *= 26
2016-08-30 11:51:31 +08:00
}
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
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
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.
2018-01-19 17:32:54 +08:00
//
// 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
2018-01-19 17:32:54 +08:00
//
// 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 ( "{}" )
}
2018-10-17 00:28:31 +08:00
// namespaceStrictToTransitional provides a method to convert Strict and
// Transitional namespaces.
func namespaceStrictToTransitional ( content [ ] byte ) [ ] byte {
var namespaceTranslationDic = map [ string ] string {
StrictSourceRelationship : SourceRelationship ,
StrictSourceRelationshipChart : SourceRelationshipChart ,
StrictSourceRelationshipComments : SourceRelationshipComments ,
StrictSourceRelationshipImage : SourceRelationshipImage ,
StrictNameSpaceSpreadSheet : NameSpaceSpreadSheet ,
}
for s , n := range namespaceTranslationDic {
content = bytes . Replace ( content , [ ] byte ( s ) , [ ] byte ( n ) , - 1 )
}
return content
}
2018-11-02 23:08:31 +08:00
// genSheetPasswd provides a method to generate password for worksheet
// protection by given plaintext. When an Excel sheet is being protected with
// a password, a 16-bit (two byte) long hash is generated. To verify a
// password, it is compared to the hash. Obviously, if the input data volume
// is great, numerous passwords will match the same hash. Here is the
// algorithm to create the hash value:
//
// take the ASCII values of all characters shift left the first character 1 bit, the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits, the highest bit of the 16-bit value is always 0 [signed short])
// XOR all these values
// XOR the count of characters
// XOR the constant 0xCE4B
func genSheetPasswd ( plaintext string ) string {
var password int64 = 0x0000
var charPos uint = 1
for _ , v := range plaintext {
value := int64 ( v ) << charPos
charPos ++
rotatedBits := value >> 15 // rotated bits beyond bit 15
value &= 0x7fff // first 15 bits
password ^ = ( value | rotatedBits )
}
password ^ = int64 ( len ( plaintext ) )
password ^ = 0xCE4B
return strings . ToUpper ( strconv . FormatInt ( password , 16 ) )
}