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

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

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

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

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

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

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

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

// Rows return a rows iterator. For example:
//
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 287
			ss = f.readXML("xl/SharedStrings.xml")
		}
288 289 290 291
		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
			Decode(&sharedStrings); err != nil && err != io.EOF {
			log.Printf("xml decode error: %s", err)
		}
292 293
		f.SharedStrings = &sharedStrings
	}
294

295
	return f.SharedStrings
A
ahmad 已提交
296 297
}

xurime's avatar
xurime 已提交
298 299 300
// 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.
301
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
302 303
	switch xlsx.T {
	case "s":
304 305 306 307 308 309
		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
			}
310 311
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
312
	case "str":
313
		return f.formattedValue(xlsx.S, xlsx.V), nil
314
	case "inlineStr":
xurime's avatar
xurime 已提交
315 316 317 318
		if xlsx.IS != nil {
			return f.formattedValue(xlsx.S, xlsx.IS.String()), nil
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
319
	default:
320
		return f.formattedValue(xlsx.S, xlsx.V), nil
321
	}
A
ahmad 已提交
322
}
323

324
// SetRowVisible provides a function to set visible of a single row by given
325
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
326
//
xurime's avatar
xurime 已提交
327
//    err := f.SetRowVisible("Sheet1", 2, false)
328
//
329
func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
330
	if row < 1 {
331
		return newInvalidRowNumberError(row)
332
	}
333

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

343 344 345
// 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:
346
//
xurime's avatar
xurime 已提交
347
//    visible, err := f.GetRowVisible("Sheet1", 2)
348
//
349
func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
350
	if row < 1 {
351
		return false, newInvalidRowNumberError(row)
352 353
	}

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

364
// SetRowOutlineLevel provides a function to set outline level number of a
xurime's avatar
xurime 已提交
365 366
// 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:
367
//
xurime's avatar
xurime 已提交
368
//    err := f.SetRowOutlineLevel("Sheet1", 2, 1)
369
//
370
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
371
	if row < 1 {
372
		return newInvalidRowNumberError(row)
373
	}
xurime's avatar
xurime 已提交
374 375 376
	if level > 7 || level < 1 {
		return errors.New("invalid outline level")
	}
xurime's avatar
xurime 已提交
377 378 379 380
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
381
	prepareSheetXML(xlsx, 0, row)
382
	xlsx.SheetData.Row[row-1].OutlineLevel = level
383
	return nil
384 385
}

xurime's avatar
xurime 已提交
386
// GetRowOutlineLevel provides a function to get outline level number of a
xurime's avatar
xurime 已提交
387 388
// single row by given worksheet name and Excel row number. For example, get
// outline number of row 2 in Sheet1:
389
//
xurime's avatar
xurime 已提交
390
//    level, err := f.GetRowOutlineLevel("Sheet1", 2)
391
//
392
func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {
393
	if row < 1 {
394
		return 0, newInvalidRowNumberError(row)
395
	}
xurime's avatar
xurime 已提交
396 397 398 399
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return 0, err
	}
400
	if row > len(xlsx.SheetData.Row) {
401
		return 0, nil
402
	}
403
	return xlsx.SheetData.Row[row-1].OutlineLevel, nil
404 405
}

C
caozhiyi 已提交
406
// RemoveRow provides a function to remove single row by given worksheet name
407
// and Excel row number. For example, remove row 3 in Sheet1:
408
//
xurime's avatar
xurime 已提交
409
//    err := f.RemoveRow("Sheet1", 3)
410
//
411 412 413 414
// 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.
415
func (f *File) RemoveRow(sheet string, row int) error {
416
	if row < 1 {
417
		return newInvalidRowNumberError(row)
418 419
	}

xurime's avatar
xurime 已提交
420 421 422 423
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
424
	if row > len(xlsx.SheetData.Row) {
425
		return f.adjustHelper(sheet, rows, row, -1)
426
	}
xurime's avatar
xurime 已提交
427 428 429 430 431 432
	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++
433 434
		}
	}
xurime's avatar
xurime 已提交
435 436
	xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
	return f.adjustHelper(sheet, rows, row, -1)
437 438
}

439 440 441
// 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:
442
//
xurime's avatar
xurime 已提交
443
//    err := f.InsertRow("Sheet1", 3)
444
//
445 446 447 448
// 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.
449
func (f *File) InsertRow(sheet string, row int) error {
450
	if row < 1 {
451
		return newInvalidRowNumberError(row)
452
	}
453
	return f.adjustHelper(sheet, rows, row, 1)
454 455
}

456
// DuplicateRow inserts a copy of specified row (by its Excel row number) below
V
Veniamin Albaev 已提交
457
//
xurime's avatar
xurime 已提交
458
//    err := f.DuplicateRow("Sheet1", 2)
V
Veniamin Albaev 已提交
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 465
func (f *File) DuplicateRow(sheet string, row int) error {
	return f.DuplicateRowTo(sheet, row, row+1)
466 467
}

468 469
// DuplicateRowTo inserts a copy of specified row by it Excel number
// to specified row position moving down exists rows after target position
470
//
xurime's avatar
xurime 已提交
471
//    err := f.DuplicateRowTo("Sheet1", 2, 7)
472
//
473 474 475 476
// 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.
477
func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
478
	if row < 1 {
479
		return newInvalidRowNumberError(row)
480
	}
481

xurime's avatar
xurime 已提交
482 483 484 485
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
486
	if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
487
		return nil
V
Veniamin Albaev 已提交
488 489
	}

490 491 492
	var ok bool
	var rowCopy xlsxRow

493
	for i, r := range xlsx.SheetData.Row {
V
Veniamin Albaev 已提交
494
		if r.R == row {
495
			rowCopy = xlsx.SheetData.Row[i]
496
			ok = true
V
Veniamin Albaev 已提交
497 498 499
			break
		}
	}
500
	if !ok {
501
		return nil
502
	}
V
Veniamin Albaev 已提交
503

504 505 506
	if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
		return err
	}
507 508

	idx2 := -1
509
	for i, r := range xlsx.SheetData.Row {
510 511 512 513 514
		if r.R == row2 {
			idx2 = i
			break
		}
	}
515
	if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
516
		return nil
V
Veniamin Albaev 已提交
517
	}
518 519 520 521

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

V
Veniamin Albaev 已提交
522
	if idx2 != -1 {
523
		xlsx.SheetData.Row[idx2] = rowCopy
V
Veniamin Albaev 已提交
524
	} else {
525
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
V
Veniamin Albaev 已提交
526
	}
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
	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++
		}
	}
561
	return nil
V
Veniamin Albaev 已提交
562 563
}

xurime's avatar
xurime 已提交
564 565
// 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:
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
//
//    <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).
588
func checkRow(xlsx *xlsxWorksheet) error {
589 590
	for rowIdx := range xlsx.SheetData.Row {
		rowData := &xlsx.SheetData.Row[rowIdx]
591

592 593 594
		colCount := len(rowData.C)
		if colCount == 0 {
			continue
595
		}
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
		// 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)
		}
612 613 614 615
		lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
		if err != nil {
			return err
		}
616 617 618 619 620 621 622 623

		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++ {
624 625 626 627 628
				cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
				if err != nil {
					return err
				}
				newlist = append(newlist, xlsxC{R: cellName})
629 630 631 632 633 634
			}

			rowData.C = newlist

			for colIdx := range oldList {
				colData := &oldList[colIdx]
635 636 637 638
				colNum, _, err := CellNameToCoordinates(colData.R)
				if err != nil {
					return err
				}
639
				xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData
640 641 642
			}
		}
	}
643
	return nil
644 645
}

xurime's avatar
xurime 已提交
646 647 648
// 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.
649 650 651 652 653 654 655 656
func convertRowHeightToPixels(height float64) float64 {
	var pixels float64
	if height == 0 {
		return pixels
	}
	pixels = math.Ceil(4.0 / 3.0 * height)
	return pixels
}