rows.go 14.9 KB
Newer Older
xurime's avatar
xurime 已提交
1
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
xurime's avatar
xurime 已提交
2 3 4 5 6 7 8
// 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.
xurime's avatar
xurime 已提交
9

A
ahmad 已提交
10 11 12
package excelize

import (
13
	"bytes"
A
ahmad 已提交
14
	"encoding/xml"
L
Lunny Xiao 已提交
15 16
	"fmt"
	"io"
17
	"math"
18
	"strconv"
A
ahmad 已提交
19 20 21
	"strings"
)

22 23
// GetRows return all the rows in a sheet by given worksheet name (case
// sensitive). For example:
24
//
25
//    for _, row := range xlsx.GetRows("Sheet1") {
26 27 28 29 30 31 32
//        for _, colCell := range row {
//            fmt.Print(colCell, "\t")
//        }
//        fmt.Println()
//    }
//
func (f *File) GetRows(sheet string) [][]string {
xurime's avatar
xurime 已提交
33
	xlsx := f.workSheetReader(sheet)
34 35
	name, ok := f.sheetMap[trimSheetName(sheet)]
	if !ok {
36
		return [][]string{}
37
	}
xurime's avatar
xurime 已提交
38 39
	if xlsx != nil {
		output, _ := xml.Marshal(f.Sheet[name])
40
		f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
A
ahmad 已提交
41
	}
42
	xml.NewDecoder(bytes.NewReader(f.readXML(name)))
43
	d := f.sharedStringsReader()
44
	var inElement string
xurime's avatar
xurime 已提交
45
	var r xlsxRow
46
	tr, tc := f.getTotalRowsCols(name)
47 48 49
	rows := make([][]string, tr)
	for i := range rows {
		rows[i] = make([]string, tc+1)
xurime's avatar
xurime 已提交
50
	}
51
	var row int
52
	decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name)))
53 54 55 56 57 58 59 60 61
	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 已提交
62
				r = xlsxRow{}
63
				_ = decoder.DecodeElement(&r, &startElement)
xurime's avatar
xurime 已提交
64
				cr := r.R - 1
65
				for _, colCell := range r.C {
66
					c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
67
					val, _ := colCell.getValueFrom(f, d)
xurime's avatar
xurime 已提交
68
					rows[cr][c] = val
69 70 71
					if val != "" {
						row = r.R
					}
72 73 74
				}
			}
		default:
75 76
		}
	}
77
	return rows[:row]
A
ahmad 已提交
78 79
}

L
Lunny Xiao 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
// Rows defines an iterator to a sheet
type Rows struct {
	decoder *xml.Decoder
	token   xml.Token
	err     error
	f       *File
}

// Next will return true if find the next row element.
func (rows *Rows) Next() bool {
	for {
		rows.token, rows.err = rows.decoder.Token()
		if rows.err == io.EOF {
			rows.err = nil
		}
		if rows.token == nil {
			return false
		}

		switch startElement := rows.token.(type) {
		case xml.StartElement:
			inElement := startElement.Name.Local
			if inElement == "row" {
				return true
			}
		}
	}
}

// Error will return the error when the find next row element
func (rows *Rows) Error() error {
	return rows.err
}

// Columns return the current row's column values
func (rows *Rows) Columns() []string {
	if rows.token == nil {
		return []string{}
	}
	startElement := rows.token.(xml.StartElement)
	r := xlsxRow{}
121
	_ = rows.decoder.DecodeElement(&r, &startElement)
L
Lunny Xiao 已提交
122
	d := rows.f.sharedStringsReader()
123
	row := make([]string, len(r.C))
L
Lunny Xiao 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	for _, colCell := range r.C {
		c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
		val, _ := colCell.getValueFrom(rows.f, d)
		row[c] = val
	}
	return row
}

// ErrSheetNotExist defines an error of sheet is not exist
type ErrSheetNotExist struct {
	SheetName string
}

func (err ErrSheetNotExist) Error() string {
	return fmt.Sprintf("Sheet %s is not exist", string(err.SheetName))
}

// Rows return a rows iterator. For example:
//
xurime's avatar
xurime 已提交
143
//    rows, err := xlsx.Rows("Sheet1")
L
Lunny Xiao 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157
//    for rows.Next() {
//        for _, colCell := range rows.Columns() {
//            fmt.Print(colCell, "\t")
//        }
//        fmt.Println()
//    }
//
func (f *File) Rows(sheet string) (*Rows, error) {
	xlsx := f.workSheetReader(sheet)
	name, ok := f.sheetMap[trimSheetName(sheet)]
	if !ok {
		return nil, ErrSheetNotExist{sheet}
	}
	if xlsx != nil {
158
		output, _ := xml.Marshal(f.Sheet[name])
159
		f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
L
Lunny Xiao 已提交
160 161 162
	}
	return &Rows{
		f:       f,
163
		decoder: xml.NewDecoder(bytes.NewReader(f.readXML(name))),
L
Lunny Xiao 已提交
164 165 166
	}, nil
}

xurime's avatar
xurime 已提交
167
// getTotalRowsCols provides a function to get total columns and rows in a
168
// worksheet.
169
func (f *File) getTotalRowsCols(name string) (int, int) {
170
	decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name)))
xurime's avatar
xurime 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183
	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{}
184
				_ = decoder.DecodeElement(&r, &startElement)
xurime's avatar
xurime 已提交
185 186
				tr = r.R
				for _, colCell := range r.C {
187
					col := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
xurime's avatar
xurime 已提交
188 189 190 191 192 193 194 195 196 197 198
					if col > tc {
						tc = col
					}
				}
			}
		default:
		}
	}
	return tr, tc
}

199 200
// SetRowHeight provides a function to set the height of a single row. For
// example, set the height of the first row in Sheet1:
N
Nikolas Silva 已提交
201
//
202
//    xlsx.SetRowHeight("Sheet1", 1, 50)
N
Nikolas Silva 已提交
203
//
204
func (f *File) SetRowHeight(sheet string, row int, height float64) {
xurime's avatar
xurime 已提交
205
	xlsx := f.workSheetReader(sheet)
206 207 208
	if row < 1 {
		return
	}
N
Nikolas Silva 已提交
209
	cells := 0
210 211 212 213
	rowIdx := row - 1
	completeRow(xlsx, row, cells)
	xlsx.SheetData.Row[rowIdx].Ht = height
	xlsx.SheetData.Row[rowIdx].CustomHeight = true
N
Nikolas Silva 已提交
214 215
}

xurime's avatar
xurime 已提交
216
// getRowHeight provides a function to get row height in pixels by given sheet
217 218 219 220 221 222 223 224 225 226 227 228
// 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)
}

xurime's avatar
xurime 已提交
229
// GetRowHeight provides a function to get row height by given worksheet name
230 231 232 233
// and row index. For example, get the height of the first row in Sheet1:
//
//    xlsx.GetRowHeight("Sheet1", 1)
//
234 235
func (f *File) GetRowHeight(sheet string, row int) float64 {
	xlsx := f.workSheetReader(sheet)
236 237 238
	if row < 1 || row > len(xlsx.SheetData.Row) {
		return defaultRowHeightPixels // it will be better to use 0, but we take care with BC
	}
239
	for _, v := range xlsx.SheetData.Row {
240
		if v.R == row && v.Ht != 0 {
241 242 243 244 245 246 247
			return v.Ht
		}
	}
	// Optimisation for when the row heights haven't changed.
	return defaultRowHeightPixels
}

xurime's avatar
xurime 已提交
248
// sharedStringsReader provides a function to get the pointer to the structure
249 250 251 252
// after deserialization of xl/sharedStrings.xml.
func (f *File) sharedStringsReader() *xlsxSST {
	if f.SharedStrings == nil {
		var sharedStrings xlsxSST
253
		ss := f.readXML("xl/sharedStrings.xml")
254
		if len(ss) == 0 {
255 256
			ss = f.readXML("xl/SharedStrings.xml")
		}
257
		_ = xml.Unmarshal(namespaceStrictToTransitional(ss), &sharedStrings)
258 259 260
		f.SharedStrings = &sharedStrings
	}
	return f.SharedStrings
A
ahmad 已提交
261 262
}

xurime's avatar
xurime 已提交
263 264 265
// 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.
266
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
267 268 269 270
	switch xlsx.T {
	case "s":
		xlsxSI := 0
		xlsxSI, _ = strconv.Atoi(xlsx.V)
xurime's avatar
xurime 已提交
271 272 273 274 275 276 277
		if len(d.SI[xlsxSI].R) > 0 {
			value := ""
			for _, v := range d.SI[xlsxSI].R {
				value += v.T
			}
			return value, nil
		}
278
		return f.formattedValue(xlsx.S, d.SI[xlsxSI].T), nil
279
	case "str":
280
		return f.formattedValue(xlsx.S, xlsx.V), nil
281 282
	case "inlineStr":
		return f.formattedValue(xlsx.S, xlsx.IS.T), nil
283
	default:
284
		return f.formattedValue(xlsx.S, xlsx.V), nil
285
	}
A
ahmad 已提交
286
}
287

288 289
// SetRowVisible2 provides a function to set visible of a single row by given
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
290
//
291
//    xlsx.SetRowVisible("Sheet1", 2, false)
292
//
293
func (f *File) SetRowVisible(sheet string, row int, visible bool) {
294
	xlsx := f.workSheetReader(sheet)
295 296 297
	if row < 1 {
		return
	}
298
	cells := 0
299 300
	completeRow(xlsx, row, cells)
	rowIdx := row - 1
301
	if visible {
302
		xlsx.SheetData.Row[rowIdx].Hidden = false
303 304
		return
	}
305
	xlsx.SheetData.Row[rowIdx].Hidden = true
306 307
}

308 309 310
// GetRowVisible2 provides a function to get visible of a single row by given
// worksheet name and Excel row number.
// For example, get visible state of row 2 in
311
// Sheet1:
312
//
313
//    xlsx.GetRowVisible("Sheet1", 2)
314
//
315
func (f *File) GetRowVisible(sheet string, row int) bool {
316
	xlsx := f.workSheetReader(sheet)
317 318 319 320
	if row < 1 || row > len(xlsx.SheetData.Row) {
		return false
	}
	rowIndex := row - 1
321
	cells := 0
322
	completeRow(xlsx, row, cells)
323
	return !xlsx.SheetData.Row[rowIndex].Hidden
324
}
325

326
// SetRowOutlineLevel provides a function to set outline level number of a
327
// single row by given worksheet name and Excel row number. For example, outline row
328
// 2 in Sheet1 to level 1:
329 330 331
//
//    xlsx.SetRowOutlineLevel("Sheet1", 2, 1)
//
332
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) {
333
	xlsx := f.workSheetReader(sheet)
334 335 336
	if row < 1 {
		return
	}
337
	cells := 0
338 339
	completeRow(xlsx, row, cells)
	xlsx.SheetData.Row[row-1].OutlineLevel = level
340 341
}

xurime's avatar
xurime 已提交
342
// GetRowOutlineLevel provides a function to get outline level number of a
343 344
// single row by given worksheet name and Exce row number.
// For example, get outline number of row 2 in Sheet1:
345 346 347
//
//    xlsx.GetRowOutlineLevel("Sheet1", 2)
//
348
func (f *File) GetRowOutlineLevel(sheet string, row int) uint8 {
349
	xlsx := f.workSheetReader(sheet)
350 351 352 353
	if row < 1 || row > len(xlsx.SheetData.Row) {
		return 0
	}
	return xlsx.SheetData.Row[row-1].OutlineLevel
354 355
}

356 357
// RemoveRow2 provides a function to remove single row by given worksheet name
// and Excel row number. For example, remove row 3 in Sheet1:
358
//
359
//    xlsx.RemoveRow("Sheet1", 3)
360
//
361 362 363 364
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
365
func (f *File) RemoveRow(sheet string, row int) {
366 367
	xlsx := f.workSheetReader(sheet)
	if row < 1 || row > len(xlsx.SheetData.Row) {
368 369 370
		return
	}
	for i, r := range xlsx.SheetData.Row {
xurime's avatar
xurime 已提交
371 372 373 374
		if r.R == row {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row[:i], xlsx.SheetData.Row[i+1:]...)
			f.adjustHelper(sheet, -1, row, -1)
			return
375 376 377 378
		}
	}
}

379 380
// InsertRow2 provides a function to insert a new row after given Excel row number
// starting from 1. For example, create a new row before row 3 in Sheet1:
381
//
382
//    xlsx.InsertRow("Sheet1", 3)
383 384
//
func (f *File) InsertRow(sheet string, row int) {
385
	if row < 1 {
386 387 388 389 390
		return
	}
	f.adjustHelper(sheet, -1, row, 1)
}

391
// DuplicateRow inserts a copy of specified row (by it Excel row number) below
V
Veniamin Albaev 已提交
392 393 394
//
//    xlsx.DuplicateRow("Sheet1", 2)
//
395 396 397 398
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
V
Veniamin Albaev 已提交
399
func (f *File) DuplicateRow(sheet string, row int) {
400 401 402
	f.DuplicateRowTo(sheet, row, row+1)
}

403 404
// DuplicateRowTo inserts a copy of specified row by it Excel number
// to specified row position moving down exists rows after target position
405 406 407
//
//    xlsx.DuplicateRowTo("Sheet1", 2, 7)
//
408 409 410 411
// Use this method with caution, which will affect changes in references such
// as formulas, charts, and so on. If there is any referenced value of the
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
412
func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
413 414 415
	xlsx := f.workSheetReader(sheet)

	if row < 1 || row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
V
Veniamin Albaev 已提交
416 417 418
		return
	}

419 420 421
	var ok bool
	var rowCopy xlsxRow

422
	for i, r := range xlsx.SheetData.Row {
V
Veniamin Albaev 已提交
423
		if r.R == row {
424
			rowCopy = xlsx.SheetData.Row[i]
425
			ok = true
V
Veniamin Albaev 已提交
426 427 428
			break
		}
	}
429 430 431
	if !ok {
		return
	}
V
Veniamin Albaev 已提交
432

433 434 435
	f.adjustHelper(sheet, -1, row2, 1)

	idx2 := -1
436
	for i, r := range xlsx.SheetData.Row {
437 438 439 440 441
		if r.R == row2 {
			idx2 = i
			break
		}
	}
442
	if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
V
Veniamin Albaev 已提交
443 444
		return
	}
445 446 447 448

	rowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)
	f.ajustSingleRowDimensions(&rowCopy, row2)

V
Veniamin Albaev 已提交
449
	if idx2 != -1 {
450
		xlsx.SheetData.Row[idx2] = rowCopy
V
Veniamin Albaev 已提交
451
	} else {
452
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
V
Veniamin Albaev 已提交
453 454 455
	}
}

xurime's avatar
xurime 已提交
456 457
// checkRow provides a function to check and fill each column element for all
// rows and make that is continuous in a worksheet of XML. For example:
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
//
//    <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{}
482 483
	for k := range xlsx.SheetData.Row {
		lenCol := len(xlsx.SheetData.Row[k].C)
xurime's avatar
xurime 已提交
484
		if lenCol > 0 {
485 486
			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 已提交
487 488 489 490
			endCol := TitleToNumber(endR) + 1
			if lenCol < endCol {
				oldRow := xlsx.SheetData.Row[k].C
				xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
491
				var tmp []xlsxC
492
				for i := 0; i < endCol; i++ {
xurime's avatar
xurime 已提交
493 494 495 496 497 498 499 500 501 502 503 504
					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
				}
505 506 507 508 509
			}
		}
	}
}

xurime's avatar
xurime 已提交
510
// completeRow provides a function to check and fill each column element for a
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
// 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()
			}
		}
	}
}

xurime's avatar
xurime 已提交
542 543 544
// convertRowHeightToPixels provides a 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.
545 546 547 548 549 550 551 552
func convertRowHeightToPixels(height float64) float64 {
	var pixels float64
	if height == 0 {
		return pixels
	}
	pixels = math.Ceil(4.0 / 3.0 * height)
	return pixels
}