excelize.go 6.4 KB
Newer Older
xurime's avatar
xurime 已提交
1 2 3 4
package excelize

import (
	"archive/zip"
5
	"bytes"
xurime's avatar
xurime 已提交
6 7 8 9 10 11
	"encoding/xml"
	"fmt"
	"strconv"
	"strings"
)

12 13 14 15
// File define a populated xlsx.File struct.
type File struct {
	XLSX map[string]string
	Path string
xurime's avatar
xurime 已提交
16 17
}

xurime's avatar
xurime 已提交
18
// OpenFile take the name of an XLSX file and returns a populated
xurime's avatar
xurime 已提交
19
// xlsx.File struct for it.
20
func OpenFile(filename string) *File {
xurime's avatar
xurime 已提交
21
	var f *zip.ReadCloser
22 23 24 25 26 27
	file := make(map[string]string)
	f, _ = zip.OpenReader(filename)
	file, _ = ReadZip(f)
	return &File{
		XLSX: file,
		Path: filename,
xurime's avatar
xurime 已提交
28 29 30
	}
}

xurime's avatar
xurime 已提交
31
// SetCellInt provide function to set int type value of a cell
32
func (f *File) SetCellInt(sheet string, axis string, value int) {
xurime's avatar
xurime 已提交
33 34 35 36 37 38 39
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	col := getColIndex(axis)
	row := getRowIndex(axis)
	xAxis := row - 1
	yAxis := titleToNumber(col)

xurime's avatar
xurime 已提交
40
	name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
41
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
xurime's avatar
xurime 已提交
42 43 44 45 46 47 48 49 50 51 52

	rows := xAxis + 1
	cell := yAxis + 1

	xlsx = checkRow(xlsx)
	xlsx = completeRow(xlsx, rows, cell)
	xlsx = completeCol(xlsx, rows, cell)

	xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
	xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)

53
	output, err := xml.Marshal(xlsx)
xurime's avatar
xurime 已提交
54 55 56
	if err != nil {
		fmt.Println(err)
	}
57
	f.saveFileList(name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
xurime's avatar
xurime 已提交
58 59
}

xurime's avatar
xurime 已提交
60
// SetCellStr provide function to set string type value of a cell
61
func (f *File) SetCellStr(sheet string, axis string, value string) {
xurime's avatar
xurime 已提交
62 63 64 65 66 67 68
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	col := getColIndex(axis)
	row := getRowIndex(axis)
	xAxis := row - 1
	yAxis := titleToNumber(col)

xurime's avatar
xurime 已提交
69
	name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
70
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
xurime's avatar
xurime 已提交
71 72 73 74 75 76 77 78 79 80 81

	rows := xAxis + 1
	cell := yAxis + 1

	xlsx = checkRow(xlsx)
	xlsx = completeRow(xlsx, rows, cell)
	xlsx = completeCol(xlsx, rows, cell)

	xlsx.SheetData.Row[xAxis].C[yAxis].T = "str"
	xlsx.SheetData.Row[xAxis].C[yAxis].V = value

82
	output, err := xml.Marshal(xlsx)
xurime's avatar
xurime 已提交
83 84 85
	if err != nil {
		fmt.Println(err)
	}
86
	f.saveFileList(name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
xurime's avatar
xurime 已提交
87 88 89 90 91 92 93 94 95 96 97
}

// Completion column element tags of XML in a sheet
func completeCol(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet {
	if len(xlsx.SheetData.Row) < cell {
		for i := len(xlsx.SheetData.Row); i < cell; i++ {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
				R: i + 1,
			})
		}
	}
98
	buffer := bytes.Buffer{}
xurime's avatar
xurime 已提交
99 100 101 102
	for k, v := range xlsx.SheetData.Row {
		if len(v.C) < cell {
			start := len(v.C)
			for iii := start; iii < cell; iii++ {
103 104
				buffer.WriteString(toAlphaString(iii + 1))
				buffer.WriteString(strconv.Itoa(k + 1))
xurime's avatar
xurime 已提交
105
				xlsx.SheetData.Row[k].C = append(xlsx.SheetData.Row[k].C, xlsxC{
106
					R: buffer.String(),
xurime's avatar
xurime 已提交
107
				})
108
				buffer.Reset()
xurime's avatar
xurime 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122
			}
		}
	}
	return xlsx
}

// Completion row element tags of XML in a sheet
func completeRow(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet {
	if len(xlsx.SheetData.Row) < row {
		for i := len(xlsx.SheetData.Row); i < row; i++ {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
				R: i + 1,
			})
		}
123
		buffer := bytes.Buffer{}
xurime's avatar
xurime 已提交
124 125 126 127
		for ii := 0; ii < row; ii++ {
			start := len(xlsx.SheetData.Row[ii].C)
			if start == 0 {
				for iii := start; iii < cell; iii++ {
128 129
					buffer.WriteString(toAlphaString(iii + 1))
					buffer.WriteString(strconv.Itoa(ii + 1))
xurime's avatar
xurime 已提交
130
					xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{
131
						R: buffer.String(),
xurime's avatar
xurime 已提交
132
					})
133
					buffer.Reset()
xurime's avatar
xurime 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
				}
			}
		}
	}
	return xlsx
}

// Replace xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Office Excel 2007
func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string {
	oldXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
	newXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">`
	workbookMarshal = strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></sheetPr>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></dimension>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></selection>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></sheetFormatPr>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></printOptions>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></pageSetup>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></pageMargins>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></headerFooter>`, ` />`, -1)
	workbookMarshal = strings.Replace(workbookMarshal, `></drawing>`, ` />`, -1)
	return workbookMarshal
}

// Check XML tags and fix discontinuous case, for example:
//
//  <row r="15" spans="1:22" x14ac:dyDescent="0.2">
//      <c r="A15" s="2" />
//      <c r="B15" s="2" />
//      <c r="F15" s="1" />
//      <c r="G15" s="1" />
//  </row>
//
// in this case, we should to change it to
//
//  <row r="15" spans="1:22" x14ac:dyDescent="0.2">
//      <c r="A15" s="2" />
//      <c r="B15" s="2" />
//      <c r="C15" s="2" />
//      <c r="D15" s="2" />
//      <c r="E15" s="2" />
//      <c r="F15" s="1" />
//      <c r="G15" s="1" />
//  </row>
//
func checkRow(xlsx xlsxWorksheet) xlsxWorksheet {
180
	buffer := bytes.Buffer{}
xurime's avatar
xurime 已提交
181 182
	for k, v := range xlsx.SheetData.Row {
		lenCol := len(v.C)
xurime's avatar
xurime 已提交
183 184 185
		if lenCol < 1 {
			continue
		}
xurime's avatar
xurime 已提交
186 187 188 189 190 191 192 193
		endR := getColIndex(v.C[lenCol-1].R)
		endRow := getRowIndex(v.C[lenCol-1].R)
		endCol := titleToNumber(endR)
		if lenCol < endCol {
			oldRow := xlsx.SheetData.Row[k].C
			xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
			tmp := []xlsxC{}
			for i := 0; i <= endCol; i++ {
194 195
				buffer.WriteString(toAlphaString(i + 1))
				buffer.WriteString(strconv.Itoa(endRow))
xurime's avatar
xurime 已提交
196
				tmp = append(tmp, xlsxC{
197
					R: buffer.String(),
xurime's avatar
xurime 已提交
198
				})
199
				buffer.Reset()
xurime's avatar
xurime 已提交
200 201 202 203 204 205 206 207 208 209
			}
			xlsx.SheetData.Row[k].C = tmp
			for _, y := range oldRow {
				colAxis := titleToNumber(getColIndex(y.R))
				xlsx.SheetData.Row[k].C[colAxis] = y
			}
		}
	}
	return xlsx
}