rows.go 9.1 KB
Newer Older
A
ahmad 已提交
1 2 3
package excelize

import (
4
	"bytes"
A
ahmad 已提交
5
	"encoding/xml"
6
	"math"
7
	"strconv"
A
ahmad 已提交
8 9 10
	"strings"
)

11 12
// GetRows return all the rows in a sheet by given worksheet name (case
// sensitive). For example:
13
//
14
//    for _, row := range xlsx.GetRows("Sheet1") {
15 16 17 18 19 20 21
//        for _, colCell := range row {
//            fmt.Print(colCell, "\t")
//        }
//        fmt.Println()
//    }
//
func (f *File) GetRows(sheet string) [][]string {
xurime's avatar
xurime 已提交
22
	xlsx := f.workSheetReader(sheet)
23
	rows := [][]string{}
24
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xurime's avatar
xurime 已提交
25 26 27
	if xlsx != nil {
		output, _ := xml.Marshal(f.Sheet[name])
		f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
A
ahmad 已提交
28
	}
xurime's avatar
xurime 已提交
29
	decoder := xml.NewDecoder(strings.NewReader(f.readXML(name)))
30
	d := f.sharedStringsReader()
31
	var inElement string
xurime's avatar
xurime 已提交
32
	var r xlsxRow
33
	var row []string
xurime's avatar
xurime 已提交
34 35 36 37 38 39 40 41 42
	tr, tc := f.getTotalRowsCols(sheet)
	for i := 0; i < tr; i++ {
		row = []string{}
		for j := 0; j <= tc; j++ {
			row = append(row, "")
		}
		rows = append(rows, row)
	}
	decoder = xml.NewDecoder(strings.NewReader(f.readXML(name)))
43 44 45 46 47 48 49 50 51
	for {
		token, _ := decoder.Token()
		if token == nil {
			break
		}
		switch startElement := token.(type) {
		case xml.StartElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
xurime's avatar
xurime 已提交
52
				r = xlsxRow{}
53
				decoder.DecodeElement(&r, &startElement)
xurime's avatar
xurime 已提交
54
				cr := r.R - 1
55
				for _, colCell := range r.C {
56
					c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
57
					val, _ := colCell.getValueFrom(f, d)
xurime's avatar
xurime 已提交
58
					rows[cr][c] = val
59 60 61
				}
			}
		default:
62 63
		}
	}
64
	return rows
A
ahmad 已提交
65 66
}

xurime's avatar
xurime 已提交
67
// getTotalRowsCols provides a function to get total columns and rows in a
68
// worksheet.
xurime's avatar
xurime 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
func (f *File) getTotalRowsCols(sheet string) (int, int) {
	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
	decoder := xml.NewDecoder(strings.NewReader(f.readXML(name)))
	var inElement string
	var r xlsxRow
	var tr, tc int
	for {
		token, _ := decoder.Token()
		if token == nil {
			break
		}
		switch startElement := token.(type) {
		case xml.StartElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
				r = xlsxRow{}
				decoder.DecodeElement(&r, &startElement)
				tr = r.R
				for _, colCell := range r.C {
88
					col := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
xurime's avatar
xurime 已提交
89 90 91 92 93 94 95 96 97 98 99
					if col > tc {
						tc = col
					}
				}
			}
		default:
		}
	}
	return tr, tc
}

N
Nikolas Silva 已提交
100 101 102
// SetRowHeight provides a function to set the height of a single row.
// For example:
//
103
//    xlsx := excelize.NewFile()
N
Nikolas Silva 已提交
104 105 106 107 108 109 110 111
//    xlsx.SetRowHeight("Sheet1", 0, 50)
//    err := xlsx.Save()
//    if err != nil {
//        fmt.Println(err)
//        os.Exit(1)
//    }
//
func (f *File) SetRowHeight(sheet string, rowIndex int, height float64) {
xurime's avatar
xurime 已提交
112
	xlsx := f.workSheetReader(sheet)
N
Nikolas Silva 已提交
113 114
	rows := rowIndex + 1
	cells := 0
xurime's avatar
xurime 已提交
115
	completeRow(xlsx, rows, cells)
116
	xlsx.SheetData.Row[rowIndex].Ht = height
N
Nikolas Silva 已提交
117 118 119
	xlsx.SheetData.Row[rowIndex].CustomHeight = true
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
// getRowHeight provides function to get row height in pixels by given sheet
// name and row index.
func (f *File) getRowHeight(sheet string, row int) int {
	xlsx := f.workSheetReader(sheet)
	for _, v := range xlsx.SheetData.Row {
		if v.R == row+1 && v.Ht != 0 {
			return int(convertRowHeightToPixels(v.Ht))
		}
	}
	// Optimisation for when the row heights haven't changed.
	return int(defaultRowHeightPixels)
}

// GetRowHeight provides function to get row height by given worksheet name and
// row index.
func (f *File) GetRowHeight(sheet string, row int) float64 {
	xlsx := f.workSheetReader(sheet)
	for _, v := range xlsx.SheetData.Row {
		if v.R == row+1 && v.Ht != 0 {
			return v.Ht
		}
	}
	// Optimisation for when the row heights haven't changed.
	return defaultRowHeightPixels
}

146 147 148 149 150 151 152 153 154
// sharedStringsReader provides function to get the pointer to the structure
// after deserialization of xl/sharedStrings.xml.
func (f *File) sharedStringsReader() *xlsxSST {
	if f.SharedStrings == nil {
		var sharedStrings xlsxSST
		xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &sharedStrings)
		f.SharedStrings = &sharedStrings
	}
	return f.SharedStrings
A
ahmad 已提交
155 156
}

157 158
// getValueFrom return a value from a column/row cell, this function is inteded
// to be used with for range on rows an argument with the xlsx opened file.
159
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
160 161 162 163
	switch xlsx.T {
	case "s":
		xlsxSI := 0
		xlsxSI, _ = strconv.Atoi(xlsx.V)
xurime's avatar
xurime 已提交
164 165 166 167 168 169 170
		if len(d.SI[xlsxSI].R) > 0 {
			value := ""
			for _, v := range d.SI[xlsxSI].R {
				value += v.T
			}
			return value, nil
		}
171
		return f.formattedValue(xlsx.S, d.SI[xlsxSI].T), nil
172
	case "str":
173
		return f.formattedValue(xlsx.S, xlsx.V), nil
174
	default:
175
		return f.formattedValue(xlsx.S, xlsx.V), nil
176
	}
A
ahmad 已提交
177
}
178

179
// SetRowVisible provides a function to set visible of a single row by given
180
// worksheet name and row index. For example, hide row 3 in Sheet1:
181
//
182
//    xlsx.SetRowVisible("Sheet1", 2, false)
183
//
184
func (f *File) SetRowVisible(sheet string, rowIndex int, visible bool) {
185 186 187 188
	xlsx := f.workSheetReader(sheet)
	rows := rowIndex + 1
	cells := 0
	completeRow(xlsx, rows, cells)
189 190 191 192
	if visible {
		xlsx.SheetData.Row[rowIndex].Hidden = false
		return
	}
193 194 195
	xlsx.SheetData.Row[rowIndex].Hidden = true
}

196
// GetRowVisible provides a function to get visible of a single row by given
197
// worksheet name and row index. For example, get visible state of row 3 in
198
// Sheet1:
199
//
200
//    xlsx.GetRowVisible("Sheet1", 2)
201
//
202
func (f *File) GetRowVisible(sheet string, rowIndex int) bool {
203 204 205 206
	xlsx := f.workSheetReader(sheet)
	rows := rowIndex + 1
	cells := 0
	completeRow(xlsx, rows, cells)
207
	return !xlsx.SheetData.Row[rowIndex].Hidden
208
}
209

210
// RemoveRow provides function to remove single row by given worksheet name and
211 212 213 214 215 216 217 218 219 220 221
// row index. For example, remove row 3 in Sheet1:
//
//    xlsx.RemoveRow("Sheet1", 2)
//
func (f *File) RemoveRow(sheet string, row int) {
	if row < 0 {
		return
	}
	xlsx := f.workSheetReader(sheet)
	row++
	for i, r := range xlsx.SheetData.Row {
xurime's avatar
xurime 已提交
222 223 224 225
		if r.R == row {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row[:i], xlsx.SheetData.Row[i+1:]...)
			f.adjustHelper(sheet, -1, row, -1)
			return
226 227 228 229 230 231 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
		}
	}
}

// InsertRow provides function to insert a new row before given row index. For
// example, create a new row before row 3 in Sheet1:
//
//    xlsx.InsertRow("Sheet1", 2)
//
func (f *File) InsertRow(sheet string, row int) {
	if row < 0 {
		return
	}
	row++
	f.adjustHelper(sheet, -1, row, 1)
}

// checkRow provides function to check and fill each column element for all rows
// and make that is continuous in a worksheet of XML. 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>
//
// Noteice: this method could be very slow for large spreadsheets (more than
// 3000 rows one sheet).
func checkRow(xlsx *xlsxWorksheet) {
	buffer := bytes.Buffer{}
269 270
	for k := range xlsx.SheetData.Row {
		lenCol := len(xlsx.SheetData.Row[k].C)
xurime's avatar
xurime 已提交
271
		if lenCol > 0 {
272 273
			endR := string(strings.Map(letterOnlyMapF, xlsx.SheetData.Row[k].C[lenCol-1].R))
			endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, xlsx.SheetData.Row[k].C[lenCol-1].R))
xurime's avatar
xurime 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
			endCol := TitleToNumber(endR) + 1
			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++ {
					buffer.WriteString(ToAlphaString(i))
					buffer.WriteString(strconv.Itoa(endRow))
					tmp = append(tmp, xlsxC{
						R: buffer.String(),
					})
					buffer.Reset()
				}
				xlsx.SheetData.Row[k].C = tmp
				for _, y := range oldRow {
					colAxis := TitleToNumber(string(strings.Map(letterOnlyMapF, y.R)))
					xlsx.SheetData.Row[k].C[colAxis] = y
				}
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
			}
		}
	}
}

// completeRow provides function to check and fill each column element for a
// single row and make that is continuous in a worksheet of XML by given row
// index and axis.
func completeRow(xlsx *xlsxWorksheet, row, cell int) {
	currentRows := len(xlsx.SheetData.Row)
	if currentRows > 1 {
		lastRow := xlsx.SheetData.Row[currentRows-1].R
		if lastRow >= row {
			row = lastRow
		}
	}
	for i := currentRows; i < row; i++ {
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
			R: i + 1,
		})
	}
	buffer := bytes.Buffer{}
	for ii := currentRows; ii < row; ii++ {
		start := len(xlsx.SheetData.Row[ii].C)
		if start == 0 {
			for iii := start; iii < cell; iii++ {
				buffer.WriteString(ToAlphaString(iii))
				buffer.WriteString(strconv.Itoa(ii + 1))
				xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
		}
	}
}

// convertRowHeightToPixels provides function to convert the height of a cell
// from user's units to pixels. If the height hasn't been set by the user we use
// the default value. If the row is hidden it has a value of zero.
func convertRowHeightToPixels(height float64) float64 {
	var pixels float64
	if height == 0 {
		return pixels
	}
	pixels = math.Ceil(4.0 / 3.0 * height)
	return pixels
}