cell.go 8.1 KB
Newer Older
xurime's avatar
xurime 已提交
1 2 3 4 5 6 7 8
package excelize

import (
	"encoding/xml"
	"strconv"
	"strings"
)

9
// GetCellValue provides function to get value from cell by given sheet index
10
// and axis in XLSX file.
11
func (f *File) GetCellValue(sheet, axis string) string {
xurime's avatar
xurime 已提交
12 13
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
14
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
15
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
16 17 18 19 20 21 22 23 24
	if xlsx.MergeCells != nil {
		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
				axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
			}
		}
	}
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
xurime's avatar
xurime 已提交
25
	rows := len(xlsx.SheetData.Row)
xurime's avatar
xurime 已提交
26 27 28 29 30 31
	if rows > 1 {
		lastRow := xlsx.SheetData.Row[rows-1].R
		if lastRow >= rows {
			rows = lastRow
		}
	}
32
	if rows < xAxis {
33
		return ""
xurime's avatar
xurime 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47
	}
	for _, v := range xlsx.SheetData.Row {
		if v.R != row {
			continue
		}
		for _, r := range v.C {
			if axis != r.R {
				continue
			}
			switch r.T {
			case "s":
				shardStrings := xlsxSST{}
				xlsxSI := 0
				xlsxSI, _ = strconv.Atoi(r.V)
48
				xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings)
xurime's avatar
xurime 已提交
49 50 51 52 53 54 55 56
				return shardStrings.SI[xlsxSI].T
			case "str":
				return r.V
			default:
				return r.V
			}
		}
	}
57
	return ""
xurime's avatar
xurime 已提交
58 59
}

60 61
// GetCellFormula provides function to get formula from cell by given sheet
// index and axis in XLSX file.
62
func (f *File) GetCellFormula(sheet, axis string) string {
xurime's avatar
xurime 已提交
63 64
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
65
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xurime's avatar
xurime 已提交
66
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
67 68 69 70 71 72 73 74 75
	if xlsx.MergeCells != nil {
		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
				axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
			}
		}
	}
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
xurime's avatar
xurime 已提交
76 77 78 79 80 81 82
	rows := len(xlsx.SheetData.Row)
	if rows > 1 {
		lastRow := xlsx.SheetData.Row[rows-1].R
		if lastRow >= rows {
			rows = lastRow
		}
	}
83
	if rows < xAxis {
84
		return ""
xurime's avatar
xurime 已提交
85
	}
xurime's avatar
xurime 已提交
86 87 88 89 90 91 92 93 94 95
	for _, v := range xlsx.SheetData.Row {
		if v.R != row {
			continue
		}
		for _, f := range v.C {
			if axis != f.R {
				continue
			}
			if f.F != nil {
				return f.F.Content
xurime's avatar
xurime 已提交
96 97 98
			}
		}
	}
99
	return ""
xurime's avatar
xurime 已提交
100
}
101 102 103 104 105 106

// SetCellFormula provides function to set cell formula by given string and
// sheet index.
func (f *File) SetCellFormula(sheet, axis, formula string) {
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
107 108
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
109 110 111 112 113
	if f.checked == nil {
		f.checked = make(map[string]bool)
	}
	ok := f.checked[name]
	if !ok {
114
		checkRow(&xlsx)
115 116
		f.checked[name] = true
	}
117 118 119 120 121 122 123
	if xlsx.MergeCells != nil {
		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
				axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
			}
		}
	}
124 125 126 127 128 129 130 131
	col := string(strings.Map(letterOnlyMapF, axis))
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
	yAxis := titleToNumber(col)

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

132 133
	completeRow(&xlsx, rows, cell)
	completeCol(&xlsx, rows, cell)
134 135 136 137 138 139 140 141 142 143 144 145

	if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil {
		xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula
	} else {
		f := xlsxF{
			Content: formula,
		}
		xlsx.SheetData.Row[xAxis].C[yAxis].F = &f
	}
	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}
146 147 148 149 150 151 152 153

// SetCellHyperLink provides function to set cell hyperlink by given sheet index
// and link URL address. Only support external link currently.
func (f *File) SetCellHyperLink(sheet, axis, link string) {
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
154 155 156 157 158
	if f.checked == nil {
		f.checked = make(map[string]bool)
	}
	ok := f.checked[name]
	if !ok {
159
		checkRow(&xlsx)
160 161
		f.checked[name] = true
	}
162 163 164 165 166 167 168
	if xlsx.MergeCells != nil {
		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
				axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
			}
		}
	}
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	rID := f.addSheetRelationships(sheet, SourceRelationshipHyperLink, link, "External")
	hyperlink := xlsxHyperlink{
		Ref: axis,
		RID: "rId" + strconv.Itoa(rID),
	}
	if xlsx.Hyperlinks != nil {
		xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
	} else {
		hyperlinks := xlsxHyperlinks{}
		hyperlinks.Hyperlink = append(hyperlinks.Hyperlink, hyperlink)
		xlsx.Hyperlinks = &hyperlinks
	}
	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}
xurime's avatar
xurime 已提交
184

185 186
// MergeCell provides function to merge cells by given coordinate area and sheet
// name. For example create a merged cell of D3:E9 on Sheet1:
xurime's avatar
xurime 已提交
187
//
188
//    xlsx.MergeCell("sheet1", "D3", "E9")
xurime's avatar
xurime 已提交
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
//
// If you create a merged cell that overlaps with another existing merged cell,
// those merged cells that already exist will be removed.
func (f *File) MergeCell(sheet, hcell, vcell string) {
	if hcell == vcell {
		return
	}

	hcell = strings.ToUpper(hcell)
	vcell = strings.ToUpper(vcell)

	// Coordinate conversion, convert C1:B3 to 2,0,1,2.
	hcol := string(strings.Map(letterOnlyMapF, hcell))
	hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
	hyAxis := hrow - 1
	hxAxis := titleToNumber(hcol)

	vcol := string(strings.Map(letterOnlyMapF, vcell))
	vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
	vyAxis := vrow - 1
	vxAxis := titleToNumber(vcol)

	if vxAxis < hxAxis {
		hcell, vcell = vcell, hcell
		vxAxis, hxAxis = hxAxis, vxAxis
	}

	if vyAxis < hyAxis {
		hcell, vcell = vcell, hcell
		vyAxis, hyAxis = hyAxis, vyAxis
	}

	var xlsx xlsxWorksheet
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
224 225 226 227 228
	if f.checked == nil {
		f.checked = make(map[string]bool)
	}
	ok := f.checked[name]
	if !ok {
229
		checkRow(&xlsx)
230 231
		f.checked[name] = true
	}
xurime's avatar
xurime 已提交
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	if xlsx.MergeCells != nil {
		mergeCell := xlsxMergeCell{}
		// Correct the coordinate area, such correct C1:B3 to B1:C3.
		mergeCell.Ref = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1) + ":" + toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
		// Delete the merged cells of the overlapping area.
		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
			if checkCellInArea(hcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0], mergeCell.Ref) {
				xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
			} else if checkCellInArea(vcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[1], mergeCell.Ref) {
				xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
			}
		}
		xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &mergeCell)
	} else {
		mergeCell := xlsxMergeCell{}
		// Correct the coordinate area, such correct C1:B3 to B1:C3.
		mergeCell.Ref = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1) + ":" + toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
		mergeCells := xlsxMergeCells{}
		mergeCells.Cells = append(mergeCells.Cells, &mergeCell)
		xlsx.MergeCells = &mergeCells
	}
	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

// checkCellInArea provides function to determine if a given coordinate is
// within an area.
func checkCellInArea(cell, area string) bool {
	result := false
	cell = strings.ToUpper(cell)
	col := string(strings.Map(letterOnlyMapF, cell))
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
	xAxis := row - 1
	yAxis := titleToNumber(col)

	ref := strings.Split(area, ":")
	hCol := string(strings.Map(letterOnlyMapF, ref[0]))
	hRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[0]))
	hyAxis := hRow - 1
	hxAxis := titleToNumber(hCol)

	vCol := string(strings.Map(letterOnlyMapF, ref[1]))
	vRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[1]))
	vyAxis := vRow - 1
	vxAxis := titleToNumber(vCol)

	if hxAxis <= yAxis && yAxis <= vxAxis && hyAxis <= xAxis && xAxis <= vyAxis {
		result = true
	}

	return result
}