changeset 5344:7df6062a1371 extented-report

XLSX: Implemented correct handling of merged cells.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 18 Jun 2021 21:27:41 +0200
parents bb6761abd81d
children 95dafb72a288
files pkg/xlsx/templater.go
diffstat 1 files changed, 127 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/xlsx/templater.go	Fri Jun 18 13:43:21 2021 +0200
+++ b/pkg/xlsx/templater.go	Fri Jun 18 21:27:41 2021 +0200
@@ -57,6 +57,38 @@
 	frames           []frame
 }
 
+type area struct {
+	x1 int
+	y1 int
+	x2 int
+	y2 int
+	mc excelize.MergeCell
+}
+
+func mergeCellToArea(mc excelize.MergeCell) (area, error) {
+	sa := mc.GetStartAxis()
+	x1, y1, err := excelize.CellNameToCoordinates(sa)
+	if err != nil {
+		return area{}, err
+	}
+	ea := mc.GetEndAxis()
+	x2, y2, err := excelize.CellNameToCoordinates(ea)
+	if err != nil {
+		return area{}, err
+	}
+	return area{
+		x1: x1,
+		y1: y1,
+		x2: x2,
+		y2: y2,
+		mc: mc,
+	}, nil
+}
+
+func (a *area) contains(x, y int) bool {
+	return x >= a.x1 && x <= a.x2 && y >= a.y1 && y <= a.y2
+}
+
 func ActionFromFile(filename string) (*Action, error) {
 	f, err := os.Open(filename)
 	if err != nil {
@@ -161,6 +193,13 @@
 	return e.actions(action)
 }
 
+func order(a, b int) (int, int) {
+	if a < b {
+		return a, b
+	}
+	return b, a
+}
+
 func (e *executor) copy(action *Action) error {
 	if n := len(action.Location); !(n == 1 || n == 2) {
 		return fmt.Errorf("length location = %d (expect 1 or 2)",
@@ -179,22 +218,12 @@
 	split := func(s string) (int, int) {
 		var x, y int
 		if err == nil {
-			var cell string
-			if cell, y, err = excelize.SplitCellName(s); err == nil {
-				x, err = excelize.ColumnNameToNumber(cell)
-			}
+			x, y, err = excelize.CellNameToCoordinates(s)
 		}
 		return x, y
 	}
-	order := func(a, b int) (int, int) {
-		if a > b {
-			return b, a
-		}
-		return a, b
-	}
 
 	var location []string
-
 	if len(action.Location) == 1 {
 		location = []string{action.Location[0], action.Location[0]}
 	} else {
@@ -202,7 +231,6 @@
 	}
 
 	var destination string
-
 	if action.Destination == "" {
 		destination = location[0]
 	} else {
@@ -221,26 +249,101 @@
 		return err
 	}
 	sx1, sx2 = order(sx1, sx2)
-	sy1, sy1 = order(sy1, sy2)
+	sy1, sy2 = order(sy1, sy2)
+
+	var areas []area
 
-	//log.Printf("%s/%s -> %s\n", sFrom, sTo, dTo)
+	//log.Println("merged cells")
+	if mcs, err := e.template.GetMergeCells(e.sourceSheet); err == nil {
+		areas = make([]area, 0, len(mcs))
+		for _, mc := range mcs {
+			if a, err := mergeCellToArea(mc); err == nil {
+				areas = append(areas, a)
+			}
+		}
+	}
 
 	for y, i := sy1, 0; y <= sy2; y, i = y+1, i+1 {
+	nextX:
 		for x, j := sx1, 0; x <= sx2; x, j = x+1, j+1 {
-			src, err1 := excelize.CoordinatesToCellName(x, y)
-			dst, err2 := excelize.CoordinatesToCellName(dx1+j, dy1+i)
-			if err1 != nil || err2 != nil {
+
+			// check if cell is part of a merged cell
+			for k := range areas {
+				area := &areas[k]
+
+				if area.contains(x, y) {
+					ofsX := x - area.x1
+					ofsY := y - area.y1
+
+					sx := dx1 + j - ofsX
+					sy := dy1 + i - ofsY
+					ex := sx + (area.x2 - area.x1)
+					ey := sy + (area.y2 - area.y1)
+
+					// Copy over attributes
+					for l := 0; l <= area.x2-area.x1; l++ {
+						for m := 0; m <= area.y2-area.y1; m++ {
+							src, err1 := excelize.CoordinatesToCellName(area.x1+l, area.y1+m)
+							dst, err2 := excelize.CoordinatesToCellName(sx+l, sy+m)
+							if err1 != nil || err2 != nil {
+								continue
+							}
+							if s, err := e.template.GetCellStyle(e.sourceSheet, src); err == nil {
+								e.template.SetCellStyle(e.destinationSheet, dst, dst, s)
+							}
+							if s, err := e.template.GetCellFormula(e.sourceSheet, src); err == nil {
+								e.template.SetCellFormula(e.destinationSheet, dst, s)
+							}
+						}
+					}
+
+					dst, err := excelize.CoordinatesToCellName(sx, sy)
+					if err != nil {
+						continue nextX
+					}
+
+					// Copy over expanded text
+					if s, err := e.expand(area.mc.GetCellValue(), vars); err == nil {
+						e.template.SetCellStr(e.destinationSheet, dst, s)
+					}
+
+					// Finally merge the cells
+					if end, err := excelize.CoordinatesToCellName(ex, ey); err == nil {
+						e.template.MergeCell(e.destinationSheet, dst, end)
+					}
+
+					continue nextX
+				}
+			}
+
+			// Regular cell
+
+			src, err := excelize.CoordinatesToCellName(x, y)
+			if err != nil {
+				continue
+			}
+			dst, err := excelize.CoordinatesToCellName(dx1+j, dy1+i)
+			if err != nil {
 				continue
 			}
 
-			sc, sr, err1 := excelize.SplitCellName(src)
-			dc, dr, err2 := excelize.SplitCellName(dst)
-			if err1 != nil || err2 != nil {
+			cn, err := excelize.ColumnNumberToName(x)
+			if err != nil {
+				continue
+			}
+
+			cw, err := e.template.GetColWidth(e.sourceSheet, cn)
+			if err != nil {
 				continue
 			}
-			cw, err1 := e.template.GetColWidth(e.sourceSheet, sc)
-			rh, err1 := e.template.GetRowHeight(e.sourceSheet, sr)
-			if err1 != nil || err2 != nil {
+
+			rh, err := e.template.GetRowHeight(e.sourceSheet, y)
+			if err != nil {
+				continue
+			}
+
+			dc, err := excelize.ColumnNumberToName(dx1 + j)
+			if err != nil {
 				continue
 			}
 
@@ -248,7 +351,7 @@
 				continue
 			}
 
-			if e.template.SetRowHeight(e.destinationSheet, dr, rh) != nil {
+			if e.template.SetRowHeight(e.destinationSheet, dy1+i, rh) != nil {
 				continue
 			}