cellmerged.go 5.4 KB
Newer Older
xurime's avatar
xurime 已提交
1
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
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.
9

xurime's avatar
xurime 已提交
10 11
package excelize

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
import (
	"fmt"
	"strings"
)

// MergeCell provides a function to merge cells by given coordinate area and
// sheet name. For example create a merged cell of D3:E9 on Sheet1:
//
//    err := f.MergeCell("Sheet1", "D3", "E9")
//
// If you create a merged cell that overlaps with another existing merged cell,
// those merged cells that already exist will be removed.
//
//                 B1(x1,y1)              D1(x2,y1)
//                +--------------------------------+
//                |                                |
//                |                                |
//     A4(x3,y3)  |        C4(x4,y3)               |
//    +-----------------------------+              |
//    |           |                 |              |
//    |           |                 |              |
//    |           |B5(x1,y2)        |     D5(x2,y2)|
//    |           +--------------------------------+
//    |                             |
//    |                             |
//    |A8(x3,y4)           C8(x4,y4)|
//    +-----------------------------+
//
func (f *File) MergeCell(sheet, hcell, vcell string) error {
	rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell)
	if err != nil {
		return err
	}
	// Correct the coordinate area, such correct C1:B3 to B1:C3.
xurime's avatar
xurime 已提交
46
	_ = sortCoordinates(rect1)
47 48 49

	hcell, _ = CoordinatesToCellName(rect1[0], rect1[1])
	vcell, _ = CoordinatesToCellName(rect1[2], rect1[3])
xurime's avatar
xurime 已提交
50 51 52

	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
53
		return err
xurime's avatar
xurime 已提交
54
	}
55
	ref := hcell + ":" + vcell
xurime's avatar
xurime 已提交
56
	if xlsx.MergeCells != nil {
57 58 59 60 61 62 63 64 65
		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
			cellData := xlsx.MergeCells.Cells[i]
			if cellData == nil {
				continue
			}
			cc := strings.Split(cellData.Ref, ":")
			if len(cc) != 2 {
				return fmt.Errorf("invalid area %q", cellData.Ref)
			}
xurime's avatar
xurime 已提交
66

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
			rect2, err := f.areaRefToCoordinates(cellData.Ref)
			if err != nil {
				return err
			}

			// Delete the merged cells of the overlapping area.
			if isOverlap(rect1, rect2) {
				xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
				i--

				if rect1[0] > rect2[0] {
					rect1[0], rect2[0] = rect2[0], rect1[0]
				}

				if rect1[2] < rect2[2] {
					rect1[2], rect2[2] = rect2[2], rect1[2]
				}

				if rect1[1] > rect2[1] {
					rect1[1], rect2[1] = rect2[1], rect1[1]
				}

				if rect1[3] < rect2[3] {
					rect1[3], rect2[3] = rect2[3], rect1[3]
				}
				hcell, _ = CoordinatesToCellName(rect1[0], rect1[1])
				vcell, _ = CoordinatesToCellName(rect1[2], rect1[3])
				ref = hcell + ":" + vcell
			}
xurime's avatar
xurime 已提交
96
		}
97 98 99
		xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &xlsxMergeCell{Ref: ref})
	} else {
		xlsx.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref}}}
xurime's avatar
xurime 已提交
100
	}
101
	return err
xurime's avatar
xurime 已提交
102 103
}

104 105 106 107 108 109 110 111 112 113 114
// UnmergeCell provides a function to unmerge a given coordinate area.
// For example unmerge area D3:E9 on Sheet1:
//
//    err := f.UnmergeCell("Sheet1", "D3", "E9")
//
// Attention: overlapped areas will also be unmerged.
func (f *File) UnmergeCell(sheet string, hcell, vcell string) error {
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
115
	rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell)
116 117 118 119
	if err != nil {
		return err
	}

xurime's avatar
xurime 已提交
120 121
	// Correct the coordinate area, such correct C1:B3 to B1:C3.
	_ = sortCoordinates(rect1)
122 123 124 125 126 127 128 129

	// return nil since no MergeCells in the sheet
	if xlsx.MergeCells == nil {
		return nil
	}

	i := 0
	for _, cellData := range xlsx.MergeCells.Cells {
130 131 132
		if cellData == nil {
			continue
		}
133
		cc := strings.Split(cellData.Ref, ":")
134 135 136 137 138 139 140 141 142 143
		if len(cc) != 2 {
			return fmt.Errorf("invalid area %q", cellData.Ref)
		}

		rect2, err := f.areaRefToCoordinates(cellData.Ref)
		if err != nil {
			return err
		}

		if isOverlap(rect1, rect2) {
144 145 146 147 148 149 150 151 152
			continue
		}
		xlsx.MergeCells.Cells[i] = cellData
		i++
	}
	xlsx.MergeCells.Cells = xlsx.MergeCells.Cells[:i]
	return nil
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
// GetMergeCells provides a function to get all merged cells from a worksheet
// currently.
func (f *File) GetMergeCells(sheet string) ([]MergeCell, error) {
	var mergeCells []MergeCell
	xlsx, err := f.workSheetReader(sheet)
	if err != nil {
		return mergeCells, err
	}
	if xlsx.MergeCells != nil {
		mergeCells = make([]MergeCell, 0, len(xlsx.MergeCells.Cells))

		for i := range xlsx.MergeCells.Cells {
			ref := xlsx.MergeCells.Cells[i].Ref
			axis := strings.Split(ref, ":")[0]
			val, _ := f.GetCellValue(sheet, axis)
			mergeCells = append(mergeCells, []string{ref, val})
		}
	}

	return mergeCells, err
}

xurime's avatar
xurime 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
// MergeCell define a merged cell data.
// It consists of the following structure.
// example: []string{"D4:E10", "cell value"}
type MergeCell []string

// GetCellValue returns merged cell value.
func (m *MergeCell) GetCellValue() string {
	return (*m)[1]
}

// GetStartAxis returns the merge start axis.
// example: "C2"
func (m *MergeCell) GetStartAxis() string {
	axis := strings.Split((*m)[0], ":")
	return axis[0]
}

// GetEndAxis returns the merge end axis.
// example: "D4"
func (m *MergeCell) GetEndAxis() string {
	axis := strings.Split((*m)[0], ":")
	return axis[1]
197
}