rows.go 17.6 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 29
//    rows, err := f.Rows("Sheet1")
//    if err != nil {
xurime's avatar
xurime 已提交
30
//        fmt.Println(err)
31 32 33 34 35
//        return
//    }
//    for rows.Next() {
//        row, err := rows.Columns()
//        if err != nil {
xurime's avatar
xurime 已提交
36
//            fmt.Println(err)
37
//        }
38
//        for _, colCell := range row {
xurime's avatar
xurime 已提交
39
//            fmt.Print(colCell, "\t")
40
//        }
xurime's avatar
xurime 已提交
41
//        fmt.Println()
42 43
//    }
//
44
func (f *File) GetRows(sheet string) ([][]string, error) {
H
Harris 已提交
45
	rows, err := f.Rows(sheet)
xurime's avatar
xurime 已提交
46 47 48
	if err != nil {
		return nil, err
	}
H
Harris 已提交
49 50 51 52 53
	results := make([][]string, 0, 64)
	for rows.Next() {
		row, err := rows.Columns()
		if err != nil {
			break
54
		}
H
Harris 已提交
55
		results = append(results, row)
56
	}
H
Harris 已提交
57
	return results, nil
A
ahmad 已提交
58 59
}

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

// Next will return true if find the next row element.
func (rows *Rows) Next() bool {
D
ducquangkstn 已提交
72
	rows.curRow++
73
	return rows.curRow <= rows.totalRow
L
Lunny Xiao 已提交
74 75 76 77 78 79 80 81
}

// 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
82
func (rows *Rows) Columns() ([]string, error) {
83 84 85 86 87 88
	var (
		err          error
		inElement    string
		row, cellCol int
		columns      []string
	)
89 90 91 92 93

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

L
Lunny Xiao 已提交
94
	d := rows.f.sharedStringsReader()
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	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 {
111
							rows.stashRow = row - 1
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
							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
			}
136
		}
L
Lunny Xiao 已提交
137
	}
138
	return columns, err
L
Lunny Xiao 已提交
139 140 141 142 143 144 145 146
}

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

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

xurime's avatar
xurime 已提交
150 151
// Rows returns a rows iterator, used for streaming reading data for a
// worksheet with a large data. For example:
L
Lunny Xiao 已提交
152
//
xurime's avatar
xurime 已提交
153
//    rows, err := f.Rows("Sheet1")
154
//    if err != nil {
xurime's avatar
xurime 已提交
155
//        fmt.Println(err)
156 157
//        return
//    }
L
Lunny Xiao 已提交
158
//    for rows.Next() {
xurime's avatar
xurime 已提交
159
//        row, err := rows.Columns()
160
//        if err != nil {
xurime's avatar
xurime 已提交
161
//            fmt.Println(err)
162
//        }
163
//        for _, colCell := range row {
xurime's avatar
xurime 已提交
164
//            fmt.Print(colCell, "\t")
L
Lunny Xiao 已提交
165
//        }
xurime's avatar
xurime 已提交
166
//        fmt.Println()
L
Lunny Xiao 已提交
167 168 169 170 171 172 173
//    }
//
func (f *File) Rows(sheet string) (*Rows, error) {
	name, ok := f.sheetMap[trimSheetName(sheet)]
	if !ok {
		return nil, ErrSheetNotExist{sheet}
	}
174 175 176
	if f.Sheet[name] != nil {
		// flush data
		output, _ := xml.Marshal(f.Sheet[name])
177
		f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
178 179 180 181 182 183 184
	}
	var (
		err       error
		inElement string
		row       int
		rows      Rows
	)
xurime's avatar
xurime 已提交
185
	decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
	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 已提交
207
	}
208 209
	rows.f = f
	rows.sheet = name
xurime's avatar
xurime 已提交
210
	rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
211
	return &rows, nil
L
Lunny Xiao 已提交
212 213
}

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

xurime's avatar
xurime 已提交
224 225 226 227
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
228 229 230

	prepareSheetXML(xlsx, 0, row)

231 232 233
	rowIdx := row - 1
	xlsx.SheetData.Row[rowIdx].Ht = height
	xlsx.SheetData.Row[rowIdx].CustomHeight = true
234
	return nil
N
Nikolas Silva 已提交
235 236
}

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

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

xurime's avatar
xurime 已提交
277
// sharedStringsReader provides a function to get the pointer to the structure
278 279
// after deserialization of xl/sharedStrings.xml.
func (f *File) sharedStringsReader() *xlsxSST {
280 281
	var err error

282 283
	if f.SharedStrings == nil {
		var sharedStrings xlsxSST
284
		ss := f.readXML("xl/sharedStrings.xml")
285
		if len(ss) == 0 {
286
			ss = f.readXML("xl/SharedStrings.xml")
287
			delete(f.XLSX, "xl/SharedStrings.xml")
288
		}
289 290 291 292
		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
			Decode(&sharedStrings); err != nil && err != io.EOF {
			log.Printf("xml decode error: %s", err)
		}
293
		f.SharedStrings = &sharedStrings
294 295 296 297 298
		for i := range sharedStrings.SI {
			if sharedStrings.SI[i].T != "" {
				f.sharedStringsMap[sharedStrings.SI[i].T] = i
			}
		}
299 300 301 302 303 304 305 306 307
		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", "")
308
	}
309

310
	return f.SharedStrings
A
ahmad 已提交
311 312
}

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

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

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

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

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

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

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

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

xurime's avatar
xurime 已提交
435 436 437 438
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
439
	if row > len(xlsx.SheetData.Row) {
440
		return f.adjustHelper(sheet, rows, row, -1)
441
	}
xurime's avatar
xurime 已提交
442 443 444 445 446 447
	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++
448 449
		}
	}
xurime's avatar
xurime 已提交
450 451
	xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
	return f.adjustHelper(sheet, rows, row, -1)
452 453
}

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

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

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

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

505 506 507
	var ok bool
	var rowCopy xlsxRow

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

519 520 521
	if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
		return err
	}
522 523

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

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

V
Veniamin Albaev 已提交
537
	if idx2 != -1 {
538
		xlsx.SheetData.Row[idx2] = rowCopy
V
Veniamin Albaev 已提交
539
	} else {
540
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
V
Veniamin Albaev 已提交
541
	}
542 543 544 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
	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++
		}
	}
576
	return nil
V
Veniamin Albaev 已提交
577 578
}

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

607 608 609
		colCount := len(rowData.C)
		if colCount == 0 {
			continue
610
		}
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
		// 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)
		}
627 628 629 630
		lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
		if err != nil {
			return err
		}
631 632 633 634 635 636 637 638

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

			rowData.C = newlist

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

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