From 5e46a9f1e31f3c515a986487d03a66a820677044 Mon Sep 17 00:00:00 2001 From: Igor Bolic Date: Tue, 18 Nov 2025 15:01:11 +0100 Subject: [PATCH] Keep track of row hidden/visible state Resolves: https://github.com/dhatim/fastexcel/issues/566 --- .../java/org/dhatim/fastexcel/reader/Row.java | 13 ++++- .../fastexcel/reader/RowSpliterator.java | 3 +- .../org/dhatim/fastexcel/reader/RowTest.java | 49 ++++++++++++++++++ .../xlsx/simple-with-hidden-rows.xlsx | Bin 0 -> 6647 bytes 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 fastexcel-reader/src/test/resources/xlsx/simple-with-hidden-rows.xlsx diff --git a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/Row.java b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/Row.java index 9c9a66ee..0e3ae4fb 100644 --- a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/Row.java +++ b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/Row.java @@ -25,11 +25,13 @@ public class Row implements Iterable { private final int rowNum; private final List cells; private final int physicalCellCount; + private final boolean isHidden; - Row(int rowNum, int physicalCellCount, List cells) { + Row(int rowNum, int physicalCellCount, List cells, boolean isHidden) { this.rowNum = rowNum; this.physicalCellCount = physicalCellCount; this.cells = cells; + this.isHidden = isHidden; } /** @@ -81,6 +83,15 @@ public int getPhysicalCellCount() { return physicalCellCount; } + /** + * Indicates whether this row is hidden in the worksheet. + * + * @return {@code true} if the row is hidden; {@code false} otherwise + */ + public boolean isHidden() { + return isHidden; + } + @Override public String toString() { return "Row " + rowNum + ' ' + cells; diff --git a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java index 18ed4aad..6be28bac 100644 --- a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java +++ b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java @@ -86,6 +86,7 @@ private Row next() throws XMLStreamException { int trackedColIndex = 0; int rowIndex = getRowIndexWithFallback(++trackedRowIndex); + boolean isHidden = "1".equals(r.getAttribute("hidden")); List cells = new ArrayList<>(rowCapacity); int physicalCellCount = 0; @@ -103,7 +104,7 @@ private Row next() throws XMLStreamException { physicalCellCount++; } rowCapacity = Math.max(rowCapacity, cells.size()); - return new Row(rowIndex, physicalCellCount, cells); + return new Row(rowIndex, physicalCellCount, cells, isHidden); } private int getRowIndexWithFallback(int fallbackRowIndex) { diff --git a/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowTest.java b/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowTest.java index e5c694ce..e49cf898 100644 --- a/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowTest.java +++ b/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowTest.java @@ -1,10 +1,14 @@ package org.dhatim.fastexcel.reader; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; +import static org.assertj.core.api.Assertions.assertThat; import static org.dhatim.fastexcel.reader.Resources.open; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -25,4 +29,49 @@ public void testGetCellByAddress() throws IOException { assertEquals(cellIndex0, cellA1); } } + + @ParameterizedTest + @CsvSource({ + "0, false, 1, Lorem, 43101, 1+A1, true", + "1, true, 2, ipsum, 43102, 1+A2, false", + "2, true, 3, dolor, 43103, 1+A3, true", + "3, false, 4, sit, 43104, 1+A4, false", + "4, false, 5, amet, 43105, 1+A5, true", + "5, false, 6, consectetur, 43106, 1+A6, false", + "6, true, 7, adipiscing, 43107, 1+A7, true", + "7, true, 8, elit, 43108, 1+A8, false", + "8, true, 9, Ut, 43109, 1+A9, true", + "9, false, 10, nec, 43110, 1+A10, false" + }) + public void shouldGetVisibleAndHiddenRows(int index, boolean isHidden, String cellIndex0, String cellIndex1, int cellIndex2, String cellIndex3, + boolean cellIndex4) throws IOException { + try (InputStream inputStream = open("/xlsx/simple-with-hidden-rows.xlsx"); + ReadableWorkbook excel = new ReadableWorkbook(inputStream)) { + Sheet firstSheet = excel.getFirstSheet(); + + Row row = firstSheet.read().get(index); + + assertThat(row.isHidden()).isEqualTo(isHidden); + assertThat(row.getCell(0)).satisfies(cell -> { + assertThat(cell.getType()).isEqualTo(CellType.NUMBER); + assertThat(cell.asNumber()).isEqualTo(new BigDecimal(cellIndex0)); + }); + assertThat(row.getCell(1)).satisfies(cell -> { + assertThat(cell.getType()).isEqualTo(CellType.STRING); + assertThat(cell.asString()).isEqualTo(cellIndex1); + }); + assertThat(row.getCell(2)).satisfies(cell -> { + assertThat(cell.getType()).isEqualTo(CellType.NUMBER); + assertThat(cell.asNumber()).isEqualTo(new BigDecimal(cellIndex2)); + }); + assertThat(row.getCell(3)).satisfies(cell -> { + assertThat(cell.getType()).isEqualTo(CellType.FORMULA); + assertThat(cell.getFormula()).isEqualTo(cellIndex3); + }); + assertThat(row.getCell(4)).satisfies(cell -> { + assertThat(cell.getType()).isEqualTo(CellType.BOOLEAN); + assertThat(cell.asBoolean()).isEqualTo(cellIndex4); + }); + } + } } diff --git a/fastexcel-reader/src/test/resources/xlsx/simple-with-hidden-rows.xlsx b/fastexcel-reader/src/test/resources/xlsx/simple-with-hidden-rows.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..97000fd578830a606b8573e9098d2f93b7460e44 GIT binary patch literal 6647 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 c0Dyl