Skip to content

Commit

Permalink
Fixed Bug that Merge Cells were not parsed
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderJohr committed Apr 7, 2024
1 parent 7aa1fc1 commit 5739a2f
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 18 deletions.
60 changes: 42 additions & 18 deletions lib/src/parser/parse.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,25 @@ class Parser {
});
}

/// Parses and processes merged cells within the spreadsheet.
///
/// This method identifies merged cell regions in each sheet of the spreadsheet
/// and handles them accordingly. It removes all cells within a merged cell region
/// except for the top-left cell, preserving its content.
void _parseMergedCells() {
Map spannedCells = <String, List<String>>{};
_excel._sheets.forEach((sheetName, node) {
_excel._availSheet(sheetName);
XmlElement elementNode = node as XmlElement;
XmlElement sheetDataNode = node as XmlElement;
List spanList = <String>[];
final sheet = _excel._sheetMap[sheetName]!;

elementNode.findAllElements('mergeCell').forEach((elemen) {
String? ref = elemen.getAttribute('ref');
final worksheetNode = sheetDataNode.parent;
worksheetNode!.findAllElements('mergeCell').forEach((element) {
String? ref = element.getAttribute('ref');
if (ref != null && ref.contains(':') && ref.split(':').length == 2) {
if (!_excel._sheetMap[sheetName]!._spannedItems.contains(ref)) {
_excel._sheetMap[sheetName]!._spannedItems.add(ref);
if (!sheet._spannedItems.contains(ref)) {
sheet._spannedItems.add(ref);
}

String startCell = ref.split(':')[0], endCell = ref.split(':')[1];
Expand All @@ -193,26 +200,43 @@ class Parser {
start: startIndex,
end: endIndex,
);
if (!_excel._sheetMap[sheetName]!._spanList.contains(spanObj)) {
_excel._sheetMap[sheetName]!._spanList.add(spanObj);
if (!sheet._spanList.contains(spanObj)) {
sheet._spanList.add(spanObj);

_deleteAllButTopLeftCellsOfSpanObj(spanObj, sheet);
}
_excel._mergeChangeLookup = sheetName;
}
});
});
}

// Remove those cells which are present inside the
_excel._sheetMap.forEach((sheetName, sheetObject) {
if (spannedCells.containsKey(sheetName)) {
sheetObject._sheetData.forEach((row, columnMap) {
columnMap.forEach((column, dataObject) {
if (!(spannedCells[sheetName].contains(getCellId(column, row)))) {
_excel[sheetName]._sheetData[row]?.remove(column);
}
});
});
/// Deletes all cells within the span of the given [_Span] object
/// except for the top-left cell.
///
/// This method is used internally by [_parseMergedCells] to remove
/// cells within merged cell regions.
///
/// Parameters:
/// - [spanObj]: The span object representing the merged cell region.
/// - [sheet]: The sheet object from which cells are to be removed.
void _deleteAllButTopLeftCellsOfSpanObj(_Span spanObj, Sheet sheet) {
final columnSpanStart = spanObj.columnSpanStart;
final columnSpanEnd = spanObj.columnSpanEnd;
final rowSpanStart = spanObj.rowSpanStart;
final rowSpanEnd = spanObj.rowSpanEnd;

for (var columnI = columnSpanStart; columnI <= columnSpanEnd; columnI++) {
for (var rowI = rowSpanStart; rowI <= rowSpanEnd; rowI++) {
bool isTopLeftCellThatShouldNotBeDeleted =
columnI == columnSpanStart && rowI == rowSpanStart;

if (isTopLeftCellThatShouldNotBeDeleted) {
continue;
}
sheet._removeCell(rowI, columnI);
}
});
}
}

// Reading the styles from the excel file.
Expand Down
24 changes: 24 additions & 0 deletions lib/src/sheet/sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,30 @@ class Sheet {
_countRowsAndColumns();
}

/// Removes a cell from the specified [rowIndex] and [columnIndex].
///
/// If the specified [rowIndex] or [columnIndex] does not exist,
/// no action is taken.
///
/// If the removal of the cell results in an empty row, the entire row is removed.
///
/// Parameters:
/// - [rowIndex]: The index of the row from which to remove the cell.
/// - [columnIndex]: The index of the column from which to remove the cell.
///
/// Example:
/// ```dart
/// final sheet = Spreadsheet();
/// sheet.removeCell(1, 2);
/// ```
void _removeCell(int rowIndex, int columnIndex) {
_sheetData[rowIndex]?.remove(columnIndex);
final rowIsEmptyAfterRemovalOfCell = _sheetData[rowIndex]?.isEmpty == true;
if (rowIsEmptyAfterRemovalOfCell) {
_sheetData.remove(rowIndex);
}
}

///
/// returns `true` is this sheet is `right-to-left` other-wise `false`
///
Expand Down
57 changes: 57 additions & 0 deletions test/excel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -971,4 +971,61 @@ void main() {
});
});
});

group('Spanned Items', () {
test("read spanned items", () {
var file = './test/test_resources/spannedItemExample.xlsx';
var bytes = File(file).readAsBytesSync();
var excel = Excel.decodeBytes(bytes);

Sheet? sheet = excel.tables["Spanned Items"]!;

testSpannedItemsSheetValues(Sheet sheet) {
final cells =
sheet.rows.expand((r) => r.where((c) => c != null)).toList();

expect(cells[0]?.value, equals(TextCellValue('spanned item A1:B1')));
expect(cells[0]?.cellIndex,
equals(CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 0)));

expect(cells[1]?.value, equals(TextCellValue('spanned item A2:A3')));
expect(cells[1]?.cellIndex,
equals(CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 1)));

expect(cells[2]?.value, equals(TextCellValue('spanned item A4:B5')));
expect(cells[2]?.cellIndex,
equals(CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 3)));
}

testSpannedItemsList(Sheet sheet) {
List<String> spannedItems = sheet.spannedItems;

expect(spannedItems[0], equals('A1:B1'));
expect(spannedItems[1], equals('A2:A3'));
expect(spannedItems[2], equals('A4:B5'));
}

testSpannedItemsList(sheet);

testSpannedItemsSheetValues(sheet);

var fileBytes = excel.encode();
if (fileBytes != null) {
File(Directory.current.path + '/tmp/spannedItemExampleOut.xlsx')
..createSync(recursive: true)
..writeAsBytesSync(fileBytes);
}
var newFile = './tmp/spannedItemExampleOut.xlsx';
var newFileBytes = File(newFile).readAsBytesSync();
var newExcel = Excel.decodeBytes(newFileBytes);
// delete tmp folder
new Directory('./tmp').delete(recursive: true);

Sheet? newSheet = newExcel.tables["Spanned Items"]!;

testSpannedItemsList(newSheet);

testSpannedItemsSheetValues(newSheet);
});
});
}
Binary file added test/test_resources/spannedItemExample.xlsx
Binary file not shown.

0 comments on commit 5739a2f

Please sign in to comment.