rows.go 17.7 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
// 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
xurime's avatar
xurime 已提交
6 7 8 9 10
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
// spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
// complex components by high compatibility, and provided streaming API for
// generating or reading data from a worksheet with huge amounts of data. This
// library needs Go version 1.10 or later.
xurime's avatar
xurime 已提交
11

A
ahmad 已提交
12 13 14
package excelize

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

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

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

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

72
// Error will return the error when the error occurs.
L
Lunny Xiao 已提交
73 74 75 76
func (rows *Rows) Error() error {
	return rows.err
}

77
// Columns return the current row's column values.
78
func (rows *Rows) Columns() ([]string, error) {
79 80 81 82 83 84
	var (
		err          error
		inElement    string
		row, cellCol int
		columns      []string
	)
85 86 87 88 89

	if rows.stashRow >= rows.curRow {
		return columns, err
	}

L
Lunny Xiao 已提交
90
	d := rows.f.sharedStringsReader()
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
	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 {
107
							rows.stashRow = row - 1
108 109 110 111 112 113
							return columns, err
						}
					}
				}
			}
			if inElement == "c" {
114
				cellCol++
115 116
				colCell := xlsxC{}
				_ = rows.decoder.DecodeElement(&colCell, &startElement)
117 118 119 120 121
				if colCell.R != "" {
					cellCol, _, err = CellNameToCoordinates(colCell.R)
					if err != nil {
						return columns, err
					}
122 123 124
				}
				blank := cellCol - len(columns)
				val, _ := colCell.getValueFrom(rows.f, d)
125
				columns = append(appendSpace(blank, columns), val)
126 127 128 129 130 131
			}
		case xml.EndElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
				return columns, err
			}
132
		}
L
Lunny Xiao 已提交
133
	}
134
	return columns, err
L
Lunny Xiao 已提交
135 136
}

137 138 139 140 141 142 143 144
// appendSpace append blank characters to slice by given length and source slice.
func appendSpace(l int, s []string) []string {
	for i := 1; i < l; i++ {
		s = append(s, "")
	}
	return s
}

L
Lunny Xiao 已提交
145 146 147 148 149 150
// ErrSheetNotExist defines an error of sheet is not exist
type ErrSheetNotExist struct {
	SheetName string
}

func (err ErrSheetNotExist) Error() string {
151
	return fmt.Sprintf("sheet %s is not exist", string(err.SheetName))
L
Lunny Xiao 已提交
152 153
}

xurime's avatar
xurime 已提交
154 155
// Rows returns a rows iterator, used for streaming reading data for a
// worksheet with a large data. For example:
L
Lunny Xiao 已提交
156
//
xurime's avatar
xurime 已提交
157
//    rows, err := f.Rows("Sheet1")
158
//    if err != nil {
xurime's avatar
xurime 已提交
159
//        fmt.Println(err)
160 161
//        return
//    }
L
Lunny Xiao 已提交
162
//    for rows.Next() {
xurime's avatar
xurime 已提交
163
//        row, err := rows.Columns()
164
//        if err != nil {
xurime's avatar
xurime 已提交
165
//            fmt.Println(err)
166
//        }
167
//        for _, colCell := range row {
xurime's avatar
xurime 已提交
168
//            fmt.Print(colCell, "\t")
L
Lunny Xiao 已提交
169
//        }
xurime's avatar
xurime 已提交
170
//        fmt.Println()
L
Lunny Xiao 已提交
171 172 173 174 175 176 177
//    }
//
func (f *File) Rows(sheet string) (*Rows, error) {
	name, ok := f.sheetMap[trimSheetName(sheet)]
	if !ok {
		return nil, ErrSheetNotExist{sheet}
	}
178 179 180
	if f.Sheet[name] != nil {
		// flush data
		output, _ := xml.Marshal(f.Sheet[name])
181
		f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
182 183
	}
	var (
184 185 186 187
		err       error
		inElement string
		row       int
		rows      Rows
188
	)
xurime's avatar
xurime 已提交
189
	decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
190 191 192 193 194 195 196 197 198
	for {
		token, _ := decoder.Token()
		if token == nil {
			break
		}
		switch startElement := token.(type) {
		case xml.StartElement:
			inElement = startElement.Name.Local
			if inElement == "row" {
199
				row++
200 201
				for _, attr := range startElement.Attr {
					if attr.Name.Local == "r" {
202
						row, err = strconv.Atoi(attr.Value)
203 204 205 206 207 208 209 210 211
						if err != nil {
							return &rows, err
						}
					}
				}
				rows.totalRow = row
			}
		default:
		}
L
Lunny Xiao 已提交
212
	}
213 214
	rows.f = f
	rows.sheet = name
xurime's avatar
xurime 已提交
215
	rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
216
	return &rows, nil
L
Lunny Xiao 已提交
217 218
}

219 220
// 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 已提交
221
//
xurime's avatar
xurime 已提交
222
//    err := f.SetRowHeight("Sheet1", 1, 50)
N
Nikolas Silva 已提交
223
//
224
func (f *File) SetRowHeight(sheet string, row int, height float64) error {
225
	if row < 1 {
226
		return newInvalidRowNumberError(row)
227
	}
228

xurime's avatar
xurime 已提交
229 230 231 232
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
233 234 235

	prepareSheetXML(xlsx, 0, row)

236 237 238
	rowIdx := row - 1
	xlsx.SheetData.Row[rowIdx].Ht = height
	xlsx.SheetData.Row[rowIdx].CustomHeight = true
239
	return nil
N
Nikolas Silva 已提交
240 241
}

xurime's avatar
xurime 已提交
242
// getRowHeight provides a function to get row height in pixels by given sheet
243 244
// name and row index.
func (f *File) getRowHeight(sheet string, row int) int {
xurime's avatar
xurime 已提交
245
	xlsx, _ := f.workSheetReader(sheet)
X
xxb-at-julichina 已提交
246 247
	for i := range xlsx.SheetData.Row {
		v := &xlsx.SheetData.Row[i]
248 249 250 251 252 253 254 255
		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 已提交
256
// GetRowHeight provides a function to get row height by given worksheet name
257 258
// and row index. For example, get the height of the first row in Sheet1:
//
xurime's avatar
xurime 已提交
259
//    height, err := f.GetRowHeight("Sheet1", 1)
260
//
261
func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
262
	if row < 1 {
263
		return defaultRowHeightPixels, newInvalidRowNumberError(row)
264 265
	}

xurime's avatar
xurime 已提交
266 267 268 269
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return defaultRowHeightPixels, err
	}
270
	if row > len(xlsx.SheetData.Row) {
271
		return defaultRowHeightPixels, nil // it will be better to use 0, but we take care with BC
272
	}
273
	for _, v := range xlsx.SheetData.Row {
274
		if v.R == row && v.Ht != 0 {
275
			return v.Ht, nil
276 277 278
		}
	}
	// Optimisation for when the row heights haven't changed.
279
	return defaultRowHeightPixels, nil
280 281
}

xurime's avatar
xurime 已提交
282
// sharedStringsReader provides a function to get the pointer to the structure
283 284
// after deserialization of xl/sharedStrings.xml.
func (f *File) sharedStringsReader() *xlsxSST {
285 286
	var err error

287 288
	if f.SharedStrings == nil {
		var sharedStrings xlsxSST
289
		ss := f.readXML("xl/sharedStrings.xml")
290 291 292 293
		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
			Decode(&sharedStrings); err != nil && err != io.EOF {
			log.Printf("xml decode error: %s", err)
		}
294 295 296
		if sharedStrings.UniqueCount == 0 {
			sharedStrings.UniqueCount = sharedStrings.Count
		}
297
		f.SharedStrings = &sharedStrings
298
		for i := range sharedStrings.SI {
299 300
			if sharedStrings.SI[i].T != nil {
				f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
301 302
			}
		}
303 304 305 306 307 308 309 310 311
		f.addContentTypePart(0, "sharedStrings")
		rels := f.relsReader("xl/_rels/workbook.xml.rels")
		for _, rel := range rels.Relationships {
			if rel.Target == "sharedStrings.xml" {
				return f.SharedStrings
			}
		}
		// Update xl/_rels/workbook.xml.rels
		f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipSharedStrings, "sharedStrings.xml", "")
312
	}
313

314
	return f.SharedStrings
A
ahmad 已提交
315 316
}

xurime's avatar
xurime 已提交
317 318 319
// 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.
320
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
321 322
	switch xlsx.T {
	case "s":
323 324 325 326 327 328
		if xlsx.V != "" {
			xlsxSI := 0
			xlsxSI, _ = strconv.Atoi(xlsx.V)
			if len(d.SI) > xlsxSI {
				return f.formattedValue(xlsx.S, d.SI[xlsxSI].String()), nil
			}
329 330
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
331
	case "str":
332
		return f.formattedValue(xlsx.S, xlsx.V), nil
333
	case "inlineStr":
xurime's avatar
xurime 已提交
334 335 336 337
		if xlsx.IS != nil {
			return f.formattedValue(xlsx.S, xlsx.IS.String()), nil
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
338
	default:
339
		return f.formattedValue(xlsx.S, xlsx.V), nil
340
	}
A
ahmad 已提交
341
}
342

343
// SetRowVisible provides a function to set visible of a single row by given
344
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
345
//
xurime's avatar
xurime 已提交
346
//    err := f.SetRowVisible("Sheet1", 2, false)
347
//
348
func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
349
	if row < 1 {
350
		return newInvalidRowNumberError(row)
351
	}
352

xurime's avatar
xurime 已提交
353 354 355 356
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
357 358
	prepareSheetXML(xlsx, 0, row)
	xlsx.SheetData.Row[row-1].Hidden = !visible
359
	return nil
360 361
}

362 363 364
// 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:
365
//
xurime's avatar
xurime 已提交
366
//    visible, err := f.GetRowVisible("Sheet1", 2)
367
//
368
func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
369
	if row < 1 {
370
		return false, newInvalidRowNumberError(row)
371 372
	}

xurime's avatar
xurime 已提交
373 374 375 376
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return false, err
	}
377
	if row > len(xlsx.SheetData.Row) {
378
		return false, nil
379
	}
380
	return !xlsx.SheetData.Row[row-1].Hidden, nil
381
}
382

383
// SetRowOutlineLevel provides a function to set outline level number of a
xurime's avatar
xurime 已提交
384 385
// 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:
386
//
xurime's avatar
xurime 已提交
387
//    err := f.SetRowOutlineLevel("Sheet1", 2, 1)
388
//
389
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
390
	if row < 1 {
391
		return newInvalidRowNumberError(row)
392
	}
xurime's avatar
xurime 已提交
393 394 395
	if level > 7 || level < 1 {
		return errors.New("invalid outline level")
	}
xurime's avatar
xurime 已提交
396 397 398 399
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
400
	prepareSheetXML(xlsx, 0, row)
401
	xlsx.SheetData.Row[row-1].OutlineLevel = level
402
	return nil
403 404
}

xurime's avatar
xurime 已提交
405
// GetRowOutlineLevel provides a function to get outline level number of a
xurime's avatar
xurime 已提交
406 407
// single row by given worksheet name and Excel row number. For example, get
// outline number of row 2 in Sheet1:
408
//
xurime's avatar
xurime 已提交
409
//    level, err := f.GetRowOutlineLevel("Sheet1", 2)
410
//
411
func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {
412
	if row < 1 {
413
		return 0, newInvalidRowNumberError(row)
414
	}
xurime's avatar
xurime 已提交
415 416 417 418
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return 0, err
	}
419
	if row > len(xlsx.SheetData.Row) {
420
		return 0, nil
421
	}
422
	return xlsx.SheetData.Row[row-1].OutlineLevel, nil
423 424
}

C
caozhiyi 已提交
425
// RemoveRow provides a function to remove single row by given worksheet name
426
// and Excel row number. For example, remove row 3 in Sheet1:
427
//
xurime's avatar
xurime 已提交
428
//    err := f.RemoveRow("Sheet1", 3)
429
//
430 431 432 433
// 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.
434
func (f *File) RemoveRow(sheet string, row int) error {
435
	if row < 1 {
436
		return newInvalidRowNumberError(row)
437 438
	}

xurime's avatar
xurime 已提交
439 440 441 442
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
443
	if row > len(xlsx.SheetData.Row) {
444
		return f.adjustHelper(sheet, rows, row, -1)
445
	}
xurime's avatar
xurime 已提交
446 447 448 449 450 451
	keep := 0
	for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
		v := &xlsx.SheetData.Row[rowIdx]
		if v.R != row {
			xlsx.SheetData.Row[keep] = *v
			keep++
452 453
		}
	}
xurime's avatar
xurime 已提交
454 455
	xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
	return f.adjustHelper(sheet, rows, row, -1)
456 457
}

458 459 460
// 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:
461
//
xurime's avatar
xurime 已提交
462
//    err := f.InsertRow("Sheet1", 3)
463
//
464 465 466 467
// 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.
468
func (f *File) InsertRow(sheet string, row int) error {
469
	if row < 1 {
470
		return newInvalidRowNumberError(row)
471
	}
472
	return f.adjustHelper(sheet, rows, row, 1)
473 474
}

475
// DuplicateRow inserts a copy of specified row (by its Excel row number) below
V
Veniamin Albaev 已提交
476
//
xurime's avatar
xurime 已提交
477
//    err := f.DuplicateRow("Sheet1", 2)
V
Veniamin Albaev 已提交
478
//
479 480 481 482
// 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.
483 484
func (f *File) DuplicateRow(sheet string, row int) error {
	return f.DuplicateRowTo(sheet, row, row+1)
485 486
}

487 488
// DuplicateRowTo inserts a copy of specified row by it Excel number
// to specified row position moving down exists rows after target position
489
//
xurime's avatar
xurime 已提交
490
//    err := f.DuplicateRowTo("Sheet1", 2, 7)
491
//
492 493 494 495
// 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.
496
func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
497
	if row < 1 {
498
		return newInvalidRowNumberError(row)
499
	}
500

xurime's avatar
xurime 已提交
501 502 503 504
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
505
	if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
506
		return nil
V
Veniamin Albaev 已提交
507 508
	}

509 510 511
	var ok bool
	var rowCopy xlsxRow

512
	for i, r := range xlsx.SheetData.Row {
V
Veniamin Albaev 已提交
513
		if r.R == row {
514
			rowCopy = xlsx.SheetData.Row[i]
515
			ok = true
V
Veniamin Albaev 已提交
516 517 518
			break
		}
	}
519
	if !ok {
520
		return nil
521
	}
V
Veniamin Albaev 已提交
522

523 524 525
	if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
		return err
	}
526 527

	idx2 := -1
528
	for i, r := range xlsx.SheetData.Row {
529 530 531 532 533
		if r.R == row2 {
			idx2 = i
			break
		}
	}
534
	if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
535
		return nil
V
Veniamin Albaev 已提交
536
	}
537 538 539 540

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

V
Veniamin Albaev 已提交
541
	if idx2 != -1 {
542
		xlsx.SheetData.Row[idx2] = rowCopy
V
Veniamin Albaev 已提交
543
	} else {
544
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
V
Veniamin Albaev 已提交
545
	}
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	return f.duplicateMergeCells(sheet, xlsx, row, row2)
}

// duplicateMergeCells merge cells in the destination row if there are single
// row merged cells in the copied row.
func (f *File) duplicateMergeCells(sheet string, xlsx *xlsxWorksheet, row, row2 int) error {
	if xlsx.MergeCells == nil {
		return nil
	}
	if row > row2 {
		row++
	}
	for _, rng := range xlsx.MergeCells.Cells {
		coordinates, err := f.areaRefToCoordinates(rng.Ref)
		if err != nil {
			return err
		}
		if coordinates[1] < row2 && row2 < coordinates[3] {
			return nil
		}
	}
	for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
		areaData := xlsx.MergeCells.Cells[i]
		coordinates, _ := f.areaRefToCoordinates(areaData.Ref)
		x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
		if y1 == y2 && y1 == row {
			from, _ := CoordinatesToCellName(x1, row2)
			to, _ := CoordinatesToCellName(x2, row2)
			if err := f.MergeCell(sheet, from, to); err != nil {
				return err
			}
			i++
		}
	}
580
	return nil
V
Veniamin Albaev 已提交
581 582
}

xurime's avatar
xurime 已提交
583 584
// 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:
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
//
//    <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).
607
func checkRow(xlsx *xlsxWorksheet) error {
608 609
	for rowIdx := range xlsx.SheetData.Row {
		rowData := &xlsx.SheetData.Row[rowIdx]
610

611 612 613
		colCount := len(rowData.C)
		if colCount == 0 {
			continue
614
		}
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
		// check and fill the cell without r attribute in a row element
		rCount := 0
		for idx, cell := range rowData.C {
			rCount++
			if cell.R != "" {
				lastR, _, err := CellNameToCoordinates(cell.R)
				if err != nil {
					return err
				}
				if lastR > rCount {
					rCount = lastR
				}
				continue
			}
			rowData.C[idx].R, _ = CoordinatesToCellName(rCount, rowIdx+1)
		}
631 632 633 634
		lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
		if err != nil {
			return err
		}
635 636 637 638 639 640 641 642

		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++ {
643 644 645 646 647
				cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
				if err != nil {
					return err
				}
				newlist = append(newlist, xlsxC{R: cellName})
648 649 650 651 652 653
			}

			rowData.C = newlist

			for colIdx := range oldList {
				colData := &oldList[colIdx]
654 655 656 657
				colNum, _, err := CellNameToCoordinates(colData.R)
				if err != nil {
					return err
				}
658
				xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData
659 660 661
			}
		}
	}
662
	return nil
663 664
}

xurime's avatar
xurime 已提交
665 666 667
// 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.
668 669 670 671 672 673 674 675
func convertRowHeightToPixels(height float64) float64 {
	var pixels float64
	if height == 0 {
		return pixels
	}
	pixels = math.Ceil(4.0 / 3.0 * height)
	return pixels
}