rows.go 17.8 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 266
	var ht = defaultRowHeight
	ws, err := f.workSheetReader(sheet)
xurime's avatar
xurime 已提交
267
	if err != nil {
268 269 270 271
		return ht, err
	}
	if ws.SheetFormatPr != nil {
		ht = ws.SheetFormatPr.DefaultRowHeight
xurime's avatar
xurime 已提交
272
	}
273 274
	if row > len(ws.SheetData.Row) {
		return ht, nil // it will be better to use 0, but we take care with BC
275
	}
276
	for _, v := range ws.SheetData.Row {
277
		if v.R == row && v.Ht != 0 {
278
			return v.Ht, nil
279 280 281
		}
	}
	// Optimisation for when the row heights haven't changed.
282
	return ht, nil
283 284
}

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

290 291
	f.Lock()
	defer f.Unlock()
292 293
	if f.SharedStrings == nil {
		var sharedStrings xlsxSST
294
		ss := f.readXML("xl/sharedStrings.xml")
295 296 297 298
		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
			Decode(&sharedStrings); err != nil && err != io.EOF {
			log.Printf("xml decode error: %s", err)
		}
299 300 301
		if sharedStrings.UniqueCount == 0 {
			sharedStrings.UniqueCount = sharedStrings.Count
		}
302
		f.SharedStrings = &sharedStrings
303
		for i := range sharedStrings.SI {
304 305
			if sharedStrings.SI[i].T != nil {
				f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
306 307
			}
		}
308 309 310 311 312 313 314 315 316
		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", "")
317
	}
318

319
	return f.SharedStrings
A
ahmad 已提交
320 321
}

xurime's avatar
xurime 已提交
322 323 324
// 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.
325
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
326 327
	f.Lock()
	defer f.Unlock()
328 329
	switch xlsx.T {
	case "s":
330 331 332 333 334 335
		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
			}
336 337
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
338
	case "str":
339
		return f.formattedValue(xlsx.S, xlsx.V), nil
340
	case "inlineStr":
xurime's avatar
xurime 已提交
341 342 343 344
		if xlsx.IS != nil {
			return f.formattedValue(xlsx.S, xlsx.IS.String()), nil
		}
		return f.formattedValue(xlsx.S, xlsx.V), nil
345
	default:
346
		return f.formattedValue(xlsx.S, xlsx.V), nil
347
	}
A
ahmad 已提交
348
}
349

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

xurime's avatar
xurime 已提交
360 361 362 363
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
364 365
	prepareSheetXML(xlsx, 0, row)
	xlsx.SheetData.Row[row-1].Hidden = !visible
366
	return nil
367 368
}

369 370 371
// 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:
372
//
xurime's avatar
xurime 已提交
373
//    visible, err := f.GetRowVisible("Sheet1", 2)
374
//
375
func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
376
	if row < 1 {
377
		return false, newInvalidRowNumberError(row)
378 379
	}

xurime's avatar
xurime 已提交
380 381 382 383
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return false, err
	}
384
	if row > len(xlsx.SheetData.Row) {
385
		return false, nil
386
	}
387
	return !xlsx.SheetData.Row[row-1].Hidden, nil
388
}
389

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

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

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

xurime's avatar
xurime 已提交
446 447 448 449
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
450
	if row > len(xlsx.SheetData.Row) {
451
		return f.adjustHelper(sheet, rows, row, -1)
452
	}
xurime's avatar
xurime 已提交
453 454 455 456 457 458
	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++
459 460
		}
	}
xurime's avatar
xurime 已提交
461 462
	xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
	return f.adjustHelper(sheet, rows, row, -1)
463 464
}

465 466 467
// 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:
468
//
xurime's avatar
xurime 已提交
469
//    err := f.InsertRow("Sheet1", 3)
470
//
471 472 473 474
// 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.
475
func (f *File) InsertRow(sheet string, row int) error {
476
	if row < 1 {
477
		return newInvalidRowNumberError(row)
478
	}
479
	return f.adjustHelper(sheet, rows, row, 1)
480 481
}

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

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

xurime's avatar
xurime 已提交
508 509 510 511
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
512
	if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
513
		return nil
V
Veniamin Albaev 已提交
514 515
	}

516 517 518
	var ok bool
	var rowCopy xlsxRow

519
	for i, r := range xlsx.SheetData.Row {
V
Veniamin Albaev 已提交
520
		if r.R == row {
521
			rowCopy = xlsx.SheetData.Row[i]
522
			ok = true
V
Veniamin Albaev 已提交
523 524 525
			break
		}
	}
526
	if !ok {
527
		return nil
528
	}
V
Veniamin Albaev 已提交
529

530 531 532
	if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
		return err
	}
533 534

	idx2 := -1
535
	for i, r := range xlsx.SheetData.Row {
536 537 538 539 540
		if r.R == row2 {
			idx2 = i
			break
		}
	}
541
	if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
542
		return nil
V
Veniamin Albaev 已提交
543
	}
544 545 546 547

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

V
Veniamin Albaev 已提交
548
	if idx2 != -1 {
549
		xlsx.SheetData.Row[idx2] = rowCopy
V
Veniamin Albaev 已提交
550
	} else {
551
		xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
V
Veniamin Albaev 已提交
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 580 581 582 583 584 585 586
	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++
		}
	}
587
	return nil
V
Veniamin Albaev 已提交
588 589
}

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

618 619 620
		colCount := len(rowData.C)
		if colCount == 0 {
			continue
621
		}
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
		// 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)
		}
638 639 640 641
		lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
		if err != nil {
			return err
		}
642 643 644 645 646 647 648 649

		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++ {
650 651 652 653 654
				cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
				if err != nil {
					return err
				}
				newlist = append(newlist, xlsxC{R: cellName})
655 656 657 658 659 660
			}

			rowData.C = newlist

			for colIdx := range oldList {
				colData := &oldList[colIdx]
661 662 663 664
				colNum, _, err := CellNameToCoordinates(colData.R)
				if err != nil {
					return err
				}
665
				xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData
666 667 668
			}
		}
	}
669
	return nil
670 671
}

xurime's avatar
xurime 已提交
672 673 674
// 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.
675 676 677 678 679 680 681 682
func convertRowHeightToPixels(height float64) float64 {
	var pixels float64
	if height == 0 {
		return pixels
	}
	pixels = math.Ceil(4.0 / 3.0 * height)
	return pixels
}