rows.go 15.1 KB
Newer Older
xurime's avatar
xurime 已提交
1
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
xurime's avatar
xurime 已提交
2 3 4 5 6 7
// 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
8
// charts of XLSX. This library needs Go version 1.10 or later.
xurime's avatar
xurime 已提交
9

A
ahmad 已提交
10 11 12
package excelize

import (
13
	"bytes"
A
ahmad 已提交
14
	"encoding/xml"
xurime's avatar
xurime 已提交
15
	"errors"
L
Lunny Xiao 已提交
16
	"fmt"
17 18
	"io"
	"log"
19
	"math"
20
	"strconv"
A
ahmad 已提交
21 22
)

23 24
// GetRows return all the rows in a sheet by given worksheet name (case
// sensitive). For example:
25
//
xurime's avatar
xurime 已提交
26
//    rows, err := f.GetRows("Sheet1")
27
//    for _, row := range rows {
28 29 30 31 32 33
//        for _, colCell := range row {
//            fmt.Print(colCell, "\t")
//        }
//        fmt.Println()
//    }
//
34
func (f *File) GetRows(sheet string) ([][]string, error) {
H
Harris 已提交
35
	rows, err := f.Rows(sheet)
xurime's avatar
xurime 已提交
36 37 38
	if err != nil {
		return nil, err
	}
H
Harris 已提交
39 40 41
	results := make([][]string, 0, 64)
	for rows.Next() {
		if rows.Error() != nil {
42 43
			break
		}
H
Harris 已提交
44 45 46
		row, err := rows.Columns()
		if err != nil {
			break
47
		}
H
Harris 已提交
48
		results = append(results, row)
49
	}
H
Harris 已提交
50
	return results, nil
A
ahmad 已提交
51 52
}

L
Lunny Xiao 已提交
53 54
// Rows defines an iterator to a sheet
type Rows struct {
55 56 57 58 59 60 61
	err      error
	f        *File
	rows     []xlsxRow
	sheet    string
	curRow   int
	totalRow int
	decoder  *xml.Decoder
L
Lunny Xiao 已提交
62 63 64 65
}

// Next will return true if find the next row element.
func (rows *Rows) Next() bool {
D
ducquangkstn 已提交
66
	rows.curRow++
67
	return rows.curRow <= rows.totalRow
L
Lunny Xiao 已提交
68 69 70 71 72 73 74 75
}

// 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
76
func (rows *Rows) Columns() ([]string, error) {
77 78 79 80 81 82
	var (
		err          error
		inElement    string
		row, cellCol int
		columns      []string
	)
L
Lunny Xiao 已提交
83
	d := rows.f.sharedStringsReader()
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 121 122 123 124
	for {
		token, _ := rows.decoder.Token()
		if token == nil {
			break
		}
		switch startElement := token.(type) {
		case xml.StartElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
				for _, attr := range startElement.Attr {
					if attr.Name.Local == "r" {
						row, err = strconv.Atoi(attr.Value)
						if err != nil {
							return columns, err
						}
						if row > rows.curRow {
							return columns, err
						}
					}
				}
			}
			if inElement == "c" {
				colCell := xlsxC{}
				_ = rows.decoder.DecodeElement(&colCell, &startElement)
				cellCol, _, err = CellNameToCoordinates(colCell.R)
				if err != nil {
					return columns, err
				}
				blank := cellCol - len(columns)
				for i := 1; i < blank; i++ {
					columns = append(columns, "")
				}
				val, _ := colCell.getValueFrom(rows.f, d)
				columns = append(columns, val)
			}
		case xml.EndElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
				return columns, err
			}
		default:
125
		}
L
Lunny Xiao 已提交
126
	}
127
	return columns, err
L
Lunny Xiao 已提交
128 129 130 131 132 133 134 135
}

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

func (err ErrSheetNotExist) Error() string {
136
	return fmt.Sprintf("sheet %s is not exist", string(err.SheetName))
L
Lunny Xiao 已提交
137 138 139 140
}

// Rows return a rows iterator. For example:
//
xurime's avatar
xurime 已提交
141
//    rows, err := f.Rows("Sheet1")
L
Lunny Xiao 已提交
142
//    for rows.Next() {
xurime's avatar
xurime 已提交
143
//        row, err := rows.Columns()
144
//        for _, colCell := range row {
L
Lunny Xiao 已提交
145 146 147 148 149 150 151 152 153 154
//            fmt.Print(colCell, "\t")
//        }
//        fmt.Println()
//    }
//
func (f *File) Rows(sheet string) (*Rows, error) {
	name, ok := f.sheetMap[trimSheetName(sheet)]
	if !ok {
		return nil, ErrSheetNotExist{sheet}
	}
155 156 157 158 159 160 161 162 163 164 165
	if f.Sheet[name] != nil {
		// flush data
		output, _ := xml.Marshal(f.Sheet[name])
		f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
	}
	var (
		err       error
		inElement string
		row       int
		rows      Rows
	)
xurime's avatar
xurime 已提交
166
	decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	for {
		token, _ := decoder.Token()
		if token == nil {
			break
		}
		switch startElement := token.(type) {
		case xml.StartElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
				for _, attr := range startElement.Attr {
					if attr.Name.Local == "r" {
						row, err = strconv.Atoi(attr.Value)
						if err != nil {
							return &rows, err
						}
					}
				}
				rows.totalRow = row
			}
		default:
		}
L
Lunny Xiao 已提交
188
	}
189 190
	rows.f = f
	rows.sheet = name
xurime's avatar
xurime 已提交
191
	rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
192
	return &rows, nil
L
Lunny Xiao 已提交
193 194
}

195 196
// 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 已提交
197
//
xurime's avatar
xurime 已提交
198
//    err := f.SetRowHeight("Sheet1", 1, 50)
N
Nikolas Silva 已提交
199
//
200
func (f *File) SetRowHeight(sheet string, row int, height float64) error {
201
	if row < 1 {
202
		return newInvalidRowNumberError(row)
203
	}
204

xurime's avatar
xurime 已提交
205 206 207 208
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
209 210 211

	prepareSheetXML(xlsx, 0, row)

212 213 214
	rowIdx := row - 1
	xlsx.SheetData.Row[rowIdx].Ht = height
	xlsx.SheetData.Row[rowIdx].CustomHeight = true
215
	return nil
N
Nikolas Silva 已提交
216 217
}

xurime's avatar
xurime 已提交
218
// getRowHeight provides a function to get row height in pixels by given sheet
219 220
// name and row index.
func (f *File) getRowHeight(sheet string, row int) int {
xurime's avatar
xurime 已提交
221
	xlsx, _ := f.workSheetReader(sheet)
222 223 224 225 226 227 228 229 230
	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 已提交
231
// GetRowHeight provides a function to get row height by given worksheet name
232 233
// and row index. For example, get the height of the first row in Sheet1:
//
xurime's avatar
xurime 已提交
234
//    height, err := f.GetRowHeight("Sheet1", 1)
235
//
236
func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
237
	if row < 1 {
238
		return defaultRowHeightPixels, newInvalidRowNumberError(row)
239 240
	}

xurime's avatar
xurime 已提交
241 242 243 244
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return defaultRowHeightPixels, err
	}
245
	if row > len(xlsx.SheetData.Row) {
246
		return defaultRowHeightPixels, nil // it will be better to use 0, but we take care with BC
247
	}
248
	for _, v := range xlsx.SheetData.Row {
249
		if v.R == row && v.Ht != 0 {
250
			return v.Ht, nil
251 252 253
		}
	}
	// Optimisation for when the row heights haven't changed.
254
	return defaultRowHeightPixels, nil
255 256
}

xurime's avatar
xurime 已提交
257
// sharedStringsReader provides a function to get the pointer to the structure
258 259
// after deserialization of xl/sharedStrings.xml.
func (f *File) sharedStringsReader() *xlsxSST {
260 261
	var err error

262 263
	if f.SharedStrings == nil {
		var sharedStrings xlsxSST
264
		ss := f.readXML("xl/sharedStrings.xml")
265
		if len(ss) == 0 {
266 267
			ss = f.readXML("xl/SharedStrings.xml")
		}
268 269 270 271
		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
			Decode(&sharedStrings); err != nil && err != io.EOF {
			log.Printf("xml decode error: %s", err)
		}
272 273
		f.SharedStrings = &sharedStrings
	}
274

275
	return f.SharedStrings
A
ahmad 已提交
276 277
}

xurime's avatar
xurime 已提交
278 279 280
// 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.
281
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
282 283 284 285
	switch xlsx.T {
	case "s":
		xlsxSI := 0
		xlsxSI, _ = strconv.Atoi(xlsx.V)
286 287 288 289
		if len(d.SI) > xlsxSI {
			return f.formattedValue(xlsx.S, d.SI[xlsxSI].String()), nil
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
290
	case "str":
291
		return f.formattedValue(xlsx.S, xlsx.V), nil
292
	case "inlineStr":
xurime's avatar
xurime 已提交
293 294 295 296
		if xlsx.IS != nil {
			return f.formattedValue(xlsx.S, xlsx.IS.String()), nil
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
297
	default:
298
		return f.formattedValue(xlsx.S, xlsx.V), nil
299
	}
A
ahmad 已提交
300
}
301

302
// SetRowVisible provides a function to set visible of a single row by given
303
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
304
//
xurime's avatar
xurime 已提交
305
//    err := f.SetRowVisible("Sheet1", 2, false)
306
//
307
func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
308
	if row < 1 {
309
		return newInvalidRowNumberError(row)
310
	}
311

xurime's avatar
xurime 已提交
312 313 314 315
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
316 317
	prepareSheetXML(xlsx, 0, row)
	xlsx.SheetData.Row[row-1].Hidden = !visible
318
	return nil
319 320
}

321 322 323
// GetRowVisible 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 Sheet1:
324
//
xurime's avatar
xurime 已提交
325
//    visible, err := f.GetRowVisible("Sheet1", 2)
326
//
327
func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
328
	if row < 1 {
329
		return false, newInvalidRowNumberError(row)
330 331
	}

xurime's avatar
xurime 已提交
332 333 334 335
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return false, err
	}
336
	if row > len(xlsx.SheetData.Row) {
337
		return false, nil
338
	}
339
	return !xlsx.SheetData.Row[row-1].Hidden, nil
340
}
341

342
// SetRowOutlineLevel provides a function to set outline level number of a
xurime's avatar
xurime 已提交
343 344
// single row by given worksheet name and Excel row number. The value of
// parameter 'level' is 1-7. For example, outline row 2 in Sheet1 to level 1:
345
//
xurime's avatar
xurime 已提交
346
//    err := f.SetRowOutlineLevel("Sheet1", 2, 1)
347
//
348
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
349
	if row < 1 {
350
		return newInvalidRowNumberError(row)
351
	}
xurime's avatar
xurime 已提交
352 353 354
	if level > 7 || level < 1 {
		return errors.New("invalid outline level")
	}
xurime's avatar
xurime 已提交
355 356 357 358
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
359
	prepareSheetXML(xlsx, 0, row)
360
	xlsx.SheetData.Row[row-1].OutlineLevel = level
361
	return nil
362 363
}

xurime's avatar
xurime 已提交
364
// GetRowOutlineLevel provides a function to get outline level number of a
xurime's avatar
xurime 已提交
365 366
// single row by given worksheet name and Excel row number. For example, get
// outline number of row 2 in Sheet1:
367
//
xurime's avatar
xurime 已提交
368
//    level, err := f.GetRowOutlineLevel("Sheet1", 2)
369
//
370
func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {
371
	if row < 1 {
372
		return 0, newInvalidRowNumberError(row)
373
	}
xurime's avatar
xurime 已提交
374 375 376 377
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return 0, err
	}
378
	if row > len(xlsx.SheetData.Row) {
379
		return 0, nil
380
	}
381
	return xlsx.SheetData.Row[row-1].OutlineLevel, nil
382 383
}

C
caozhiyi 已提交
384
// RemoveRow provides a function to remove single row by given worksheet name
385
// and Excel row number. For example, remove row 3 in Sheet1:
386
//
xurime's avatar
xurime 已提交
387
//    err := f.RemoveRow("Sheet1", 3)
388
//
389 390 391 392
// 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.
393
func (f *File) RemoveRow(sheet string, row int) error {
394
	if row < 1 {
395
		return newInvalidRowNumberError(row)
396 397
	}

xurime's avatar
xurime 已提交
398 399 400 401
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
402
	if row > len(xlsx.SheetData.Row) {
403
		return f.adjustHelper(sheet, rows, row, -1)
404
	}
405 406 407 408
	for rowIdx := range xlsx.SheetData.Row {
		if xlsx.SheetData.Row[rowIdx].R == row {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx],
				xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1]
409
			return f.adjustHelper(sheet, rows, row, -1)
410 411
		}
	}
412
	return nil
413 414
}

415 416 417
// InsertRow 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:
418
//
xurime's avatar
xurime 已提交
419
//    err := f.InsertRow("Sheet1", 3)
420
//
421 422 423 424
// 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.
425
func (f *File) InsertRow(sheet string, row int) error {
426
	if row < 1 {
427
		return newInvalidRowNumberError(row)
428
	}
429
	return f.adjustHelper(sheet, rows, row, 1)
430 431
}

432
// DuplicateRow inserts a copy of specified row (by its Excel row number) below
V
Veniamin Albaev 已提交
433
//
xurime's avatar
xurime 已提交
434
//    err := f.DuplicateRow("Sheet1", 2)
V
Veniamin Albaev 已提交
435
//
436 437 438 439
// 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.
440 441
func (f *File) DuplicateRow(sheet string, row int) error {
	return f.DuplicateRowTo(sheet, row, row+1)
442 443
}

444 445
// DuplicateRowTo inserts a copy of specified row by it Excel number
// to specified row position moving down exists rows after target position
446
//
xurime's avatar
xurime 已提交
447
//    err := f.DuplicateRowTo("Sheet1", 2, 7)
448
//
449 450 451 452
// 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.
453
func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
454
	if row < 1 {
455
		return newInvalidRowNumberError(row)
456
	}
457

xurime's avatar
xurime 已提交
458 459 460 461
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
462
	if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
463
		return nil
V
Veniamin Albaev 已提交
464 465
	}

466 467 468
	var ok bool
	var rowCopy xlsxRow

469
	for i, r := range xlsx.SheetData.Row {
V
Veniamin Albaev 已提交
470
		if r.R == row {
471
			rowCopy = xlsx.SheetData.Row[i]
472
			ok = true
V
Veniamin Albaev 已提交
473 474 475
			break
		}
	}
476
	if !ok {
477
		return nil
478
	}
V
Veniamin Albaev 已提交
479

480 481 482
	if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
		return err
	}
483 484

	idx2 := -1
485
	for i, r := range xlsx.SheetData.Row {
486 487 488 489 490
		if r.R == row2 {
			idx2 = i
			break
		}
	}
491
	if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
492
		return nil
V
Veniamin Albaev 已提交
493
	}
494 495 496 497

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

V
Veniamin Albaev 已提交
498
	if idx2 != -1 {
499
		xlsx.SheetData.Row[idx2] = rowCopy
V
Veniamin Albaev 已提交
500
	} else {
501
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
V
Veniamin Albaev 已提交
502
	}
503
	return nil
V
Veniamin Albaev 已提交
504 505
}

xurime's avatar
xurime 已提交
506 507
// 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:
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
//
//    <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).
530
func checkRow(xlsx *xlsxWorksheet) error {
531 532
	for rowIdx := range xlsx.SheetData.Row {
		rowData := &xlsx.SheetData.Row[rowIdx]
533

534 535 536
		colCount := len(rowData.C)
		if colCount == 0 {
			continue
537
		}
538 539 540 541
		lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
		if err != nil {
			return err
		}
542 543 544 545 546 547 548 549

		if colCount < lastCol {
			oldList := rowData.C
			newlist := make([]xlsxC, 0, lastCol)

			rowData.C = xlsx.SheetData.Row[rowIdx].C[:0]

			for colIdx := 0; colIdx < lastCol; colIdx++ {
550 551 552 553 554
				cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
				if err != nil {
					return err
				}
				newlist = append(newlist, xlsxC{R: cellName})
555 556 557 558 559 560
			}

			rowData.C = newlist

			for colIdx := range oldList {
				colData := &oldList[colIdx]
561 562 563 564
				colNum, _, err := CellNameToCoordinates(colData.R)
				if err != nil {
					return err
				}
565
				xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData
566 567 568
			}
		}
	}
569
	return nil
570 571
}

xurime's avatar
xurime 已提交
572 573 574
// 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.
575 576 577 578 579 580 581 582
func convertRowHeightToPixels(height float64) float64 {
	var pixels float64
	if height == 0 {
		return pixels
	}
	pixels = math.Ceil(4.0 / 3.0 * height)
	return pixels
}