diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java b/src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java new file mode 100644 index 000000000..7c30d21b0 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xwpf/model/WMLHelper.java @@ -0,0 +1,33 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xwpf.model; + +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff; + +public final class WMLHelper { + + public static boolean STOnOffToBoolean (STOnOff.Enum value) { + if (value == STOnOff.TRUE || value == STOnOff.ON || value == STOnOff.X_1) { + return true; + } + return false; + } + + public static STOnOff.Enum BooleanToSTOnOff (boolean value) { + return (value ? STOnOff.TRUE : STOnOff.FALSE); + } +} diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java index eb1ec7775..3f530df76 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import org.apache.poi.util.Internal; +import org.apache.poi.xwpf.model.WMLHelper; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHeight; @@ -190,57 +191,76 @@ public class XWPFTableRow { * * @return true if rows can't be split, false otherwise. */ - public boolean isCantSplitRow() { - boolean isCant = false; - CTTrPr trpr = getTrPr(); - if (trpr.sizeOfCantSplitArray() > 0) { - CTOnOff onoff = trpr.getCantSplitArray(0); - isCant = onoff.getVal().equals(STOnOff.ON); - } - return isCant; + public boolean isCantSplitRow() { + boolean isCant = false; + if (ctRow.isSetTrPr()) { + CTTrPr trpr = getTrPr(); + if (trpr.sizeOfCantSplitArray() > 0) { + CTOnOff onoff = trpr.getCantSplitArray(0); + isCant = (onoff.isSetVal() ? WMLHelper.STOnOffToBoolean(onoff.getVal()) : true); + } + } + return isCant; } /** - * This attribute controls whether to allow table rows to split across pages. + * Controls whether to allow this table row to split across pages. * The logic for this attribute is a little unusual: a true value means * DON'T allow rows to split, false means allow rows to split. * - * @param split - if true, don't allow rows to be split. If false, allow - * rows to be split. + * @param split - if true, don't allow row to be split. If false, allow + * row to be split. */ public void setCantSplitRow(boolean split) { CTTrPr trpr = getTrPr(); - CTOnOff onoff = trpr.addNewCantSplit(); - onoff.setVal(split ? STOnOff.ON : STOnOff.OFF); + CTOnOff onoff = (trpr.sizeOfCantSplitArray() > 0 ? trpr.getCantSplitArray(0) : trpr.addNewCantSplit()); + onoff.setVal(WMLHelper.BooleanToSTOnOff(split)); } /** * Return true if a table's header row should be repeated at the top of a - * table split across pages. + * table split across pages. NOTE - Word will not repeat a table row unless + * all preceding rows of the table are also repeated. This function returns + * false if the row will not be repeated even if the repeat tag is present + * for this row. * * @return true if table's header row should be repeated at the top of each * page of table, false otherwise. */ public boolean isRepeatHeader() { - boolean repeat = false; - CTTrPr trpr = getTrPr(); - if (trpr.sizeOfTblHeaderArray() > 0) { - CTOnOff rpt = trpr.getTblHeaderArray(0); - repeat = rpt.getVal().equals(STOnOff.ON); + boolean repeat = false; + for (XWPFTableRow row : table.getRows()) { + repeat = row.getRepeat(); + if (row == this || !repeat) { + break; + } } return repeat; + } + + private boolean getRepeat() { + boolean repeat = false; + if (ctRow.isSetTrPr()) { + CTTrPr trpr = getTrPr(); + if (trpr.sizeOfTblHeaderArray() > 0) { + CTOnOff rpt = trpr.getTblHeaderArray(0); + repeat = (rpt.isSetVal() ? WMLHelper.STOnOffToBoolean(rpt.getVal()) : true); + } + } + return repeat; } /** * This attribute controls whether to repeat a table's header row at the top - * of a table split across pages. + * of a table split across pages. NOTE - for a row to be repeated, all preceding + * rows in the table must also be repeated. * * @param repeat - if TRUE, repeat header row at the top of each page of table; * if FALSE, don't repeat header row. */ public void setRepeatHeader(boolean repeat) { CTTrPr trpr = getTrPr(); - CTOnOff onoff = trpr.addNewTblHeader(); - onoff.setVal(repeat ? STOnOff.ON : STOnOff.OFF); + CTOnOff onoff = (trpr.sizeOfTblHeaderArray() > 0 ? trpr.getTblHeaderArray(0) : trpr.addNewTblHeader()); + onoff.setVal(WMLHelper.BooleanToSTOnOff(repeat)); } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java index 0f625fbd9..c9d12bddc 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java @@ -17,21 +17,26 @@ package org.apache.poi.xwpf.usermodel; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; -import junit.framework.TestCase; import org.apache.poi.xwpf.XWPFTestDataSamples; +import org.junit.Ignore; +import org.junit.Test; -public final class TestXWPFSDT extends TestCase { +public final class TestXWPFSDT { /** * Test simple tag and title extraction from SDT * * @throws Exception */ + @Test public void testTagTitle() throws Exception { XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx"); String tag = null; @@ -51,7 +56,7 @@ public final class TestXWPFSDT extends TestCase { assertEquals("title", "MyTitle", title); } - + @Test public void testGetSDTs() throws Exception { String[] contents = new String[]{ "header_rich_text", @@ -83,6 +88,7 @@ public final class TestXWPFSDT extends TestCase { /** * POI-54771 and TIKA-1317 */ + @Test public void testSDTAsCell() throws Exception { //Bug54771a.docx and Bug54771b.docx test slightly //different recursion patterns. Keep both! @@ -110,6 +116,7 @@ public final class TestXWPFSDT extends TestCase { /** * POI-55142 and Tika 1130 */ + @Test public void testNewLinesBetweenRuns() throws Exception { XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug55142.docx"); List sdts = extractAllSDTs(doc); @@ -132,6 +139,7 @@ public final class TestXWPFSDT extends TestCase { } } + @Ignore public void test60341() throws IOException { //handle sdtbody without an sdtpr XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug60341.docx"); diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java index b22f1a0cb..57ed43532 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableRow.java @@ -17,51 +17,124 @@ package org.apache.poi.xwpf.usermodel; -import junit.framework.TestCase; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; -public class TestXWPFTableRow extends TestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); +import java.io.IOException; + +import org.apache.poi.xwpf.XWPFTestDataSamples; +import org.junit.Test; + +public class TestXWPFTableRow { + + @Test + public void testCreateRow() throws IOException { + XWPFDocument doc = new XWPFDocument(); + XWPFTable table = doc.createTable(1, 1); + XWPFTableRow tr = table.createRow(); + assertNotNull(tr); + doc.close(); } - public void testCreateRow() throws Exception { - CTRow ctRow = CTRow.Factory.newInstance(); - assertNotNull(ctRow); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - public void testSetGetCantSplitRow() { + @Test + public void testSetGetCantSplitRow() throws IOException { // create a table XWPFDocument doc = new XWPFDocument(); - CTTbl ctTable = CTTbl.Factory.newInstance(); - XWPFTable table = new XWPFTable(ctTable, doc); + XWPFTable table = doc.createTable(1, 1); // table has a single row by default; grab it XWPFTableRow tr = table.getRow(0); assertNotNull(tr); + // Assert the repeat header is false by default + boolean isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + // Repeat the header tr.setCantSplitRow(true); - boolean isCant = tr.isCantSplitRow(); - assert (isCant); + isCantSplit = tr.isCantSplitRow(); + assertTrue(isCantSplit); + + // Make the header no longer repeating + tr.setCantSplitRow(false); + isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + doc.close(); } - public void testSetGetRepeatHeader() { + @Test + public void testSetGetRepeatHeader() throws IOException { // create a table XWPFDocument doc = new XWPFDocument(); - CTTbl ctTable = CTTbl.Factory.newInstance(); - XWPFTable table = new XWPFTable(ctTable, doc); + XWPFTable table = doc.createTable(3, 1); // table has a single row by default; grab it XWPFTableRow tr = table.getRow(0); assertNotNull(tr); - - tr.setRepeatHeader(true); + + // Assert the repeat header is false by default boolean isRpt = tr.isRepeatHeader(); - assert (isRpt); + assertFalse(isRpt); + + // Repeat the header + tr.setRepeatHeader(true); + isRpt = tr.isRepeatHeader(); + assertTrue(isRpt); + + // Make the header no longer repeating + tr.setRepeatHeader(false); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + // If the third row is set to repeat, but not the second, + // isRepeatHeader should report false because Word will + // ignore it. + tr = table.getRow(2); + tr.setRepeatHeader(true); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + doc.close(); + } + + // Test that validates the table header value can be parsed from a document + // generated in Word + @Test + public void testIsRepeatHeader() throws Exception { + XWPFDocument doc = XWPFTestDataSamples + .openSampleDocument("Bug60337.docx"); + XWPFTable table = doc.getTables().get(0); + XWPFTableRow tr = table.getRow(0); + boolean isRpt = tr.isRepeatHeader(); + assertTrue(isRpt); + + tr = table.getRow(1); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + + tr = table.getRow(2); + isRpt = tr.isRepeatHeader(); + assertFalse(isRpt); + } + + + // Test that validates the table header value can be parsed from a document + // generated in Word + @Test + public void testIsCantSplit() throws Exception { + XWPFDocument doc = XWPFTestDataSamples + .openSampleDocument("Bug60337.docx"); + XWPFTable table = doc.getTables().get(0); + XWPFTableRow tr = table.getRow(0); + boolean isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + tr = table.getRow(1); + isCantSplit = tr.isCantSplitRow(); + assertFalse(isCantSplit); + + tr = table.getRow(2); + isCantSplit = tr.isCantSplitRow(); + assertTrue(isCantSplit); } } diff --git a/test-data/document/Bug60337.docx b/test-data/document/Bug60337.docx new file mode 100644 index 000000000..06d0339fc Binary files /dev/null and b/test-data/document/Bug60337.docx differ