[github-122] XWPF: Set table and table cell widths to percentage, twips, or auto. This closes #122

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1838449 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2018-08-20 18:12:37 +00:00
parent a611b66fd1
commit 179e81c4a4
5 changed files with 371 additions and 13 deletions

View File

@ -0,0 +1,54 @@
/* ====================================================================
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.usermodel;
import org.apache.poi.util.Internal;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth.Enum;
/**
* The width types for tables and table cells. Table width can be specified as "auto" (AUTO),
* an absolute value in 20ths of a point (DXA), or as a percentage (PCT).
* @since 4.0.0
*/
public enum TableWidthType {
AUTO(STTblWidth.AUTO), /* Width is determined by content. */
DXA(STTblWidth.DXA), /* Width is an integer number of 20ths of a point (twips) */
NIL(STTblWidth.NIL), /* No width value set */
PCT(STTblWidth.PCT); /* Width is a percentage, e.g. "33.3%" or 50 times percentage value, rounded to an integer, */
/* e.g. 2500 for 50% */
private Enum type = STTblWidth.NIL;
TableWidthType(STTblWidth.Enum type) {
this.type = type;
}
protected STTblWidth.Enum getSTWidthType() {
return this.type;
}
/**
* Get the underlying STTblWidth enum value.
*
* @return STTblWidth.Enum value
*/
@Internal
public STTblWidth.Enum getStWidthType() {
return this.type;
}
}

View File

@ -18,13 +18,12 @@ package org.apache.poi.xwpf.usermodel;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Collections;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
@ -54,6 +53,11 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class XWPFTable implements IBodyElement, ISDTContents { public class XWPFTable implements IBodyElement, ISDTContents {
public static final String REGEX_PERCENTAGE = "[0-9]+(\\.[0-9]+)?%";
public static final String DEFAULT_PERCENTAGE_WIDTH = "100%";
static final String NS_OOXML_WP_MAIN = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
public static final String REGEX_WIDTH_VALUE = "auto|[0-9]+|" + REGEX_PERCENTAGE;
// Create a map from this XWPF-level enum to the STBorder.Enum values // Create a map from this XWPF-level enum to the STBorder.Enum values
public enum XWPFBorderType { public enum XWPFBorderType {
NIL, NONE, SINGLE, THICK, DOUBLE, DOTTED, DASHED, DOT_DASH, DOT_DOT_DASH, TRIPLE, NIL, NONE, SINGLE, THICK, DOUBLE, DOTTED, DASHED, DOT_DASH, DOT_DOT_DASH, TRIPLE,
@ -282,7 +286,10 @@ public class XWPFTable implements IBodyElement, ISDTContents {
} }
/** /**
* @return width value * Get the width value as an integer.
* <p>If the width type is AUTO, DXA, or NIL, the value is 20ths of a point. If
* the width type is PCT, the value is the percentage times 50 (e.g., 2500 for 50%).</p>
* @return width value as an integer
*/ */
public int getWidth() { public int getWidth() {
CTTblPr tblPr = getTblPr(); CTTblPr tblPr = getTblPr();
@ -290,12 +297,14 @@ public class XWPFTable implements IBodyElement, ISDTContents {
} }
/** /**
* @param width * Set the width in 20ths of a point (twips).
* @param width Width value (20ths of a point)
*/ */
public void setWidth(int width) { public void setWidth(int width) {
CTTblPr tblPr = getTblPr(); CTTblPr tblPr = getTblPr();
CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW(); CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
tblWidth.setW(new BigInteger(Integer.toString(width))); tblWidth.setW(new BigInteger(Integer.toString(width)));
tblWidth.setType(STTblWidth.DXA);
} }
/** /**
@ -1125,4 +1134,168 @@ public class XWPFTable implements IBodyElement, ISDTContents {
} }
return null; return null;
} }
/**
* Get the table width as a decimal value.
* <p>If the width type is DXA or AUTO, then the value will always have
* a fractional part of zero (because these values are really integers).
* If the with type is percentage, then value may have a non-zero fractional
* part.
*
* @return Width value as a double-precision decimal.
* @since 4.0.0
*/
public double getWidthDecimal() {
return getWidthDecimal(getTblPr().getTblW());
}
/**
* Get the width as a decimal value.
* <p>This method is also used by table cells.
* @param ctWidth Width value to evaluate.
* @return Width value as a decimal
* @since 4.0.0
*/
protected static double getWidthDecimal(CTTblWidth ctWidth) {
double result = 0.0;
STTblWidth.Enum typeValue = ctWidth.getType();
if (typeValue == STTblWidth.DXA
|| typeValue == STTblWidth.AUTO
|| typeValue == STTblWidth.NIL) {
result = 0.0 + ctWidth.getW().intValue();
} else if (typeValue == STTblWidth.PCT) {
// Percentage values are stored as integers that are 50 times
// percentage.
result = ctWidth.getW().intValue() / 50.0;
} else {
// Should never get here
}
return result;
}
/**
* Get the width type for the table, as an {@link STTblWidth.Enum} value.
* A table width can be specified as an absolute measurement (an integer
* number of twips), a percentage, or the value "AUTO".
*
* @return The width type.
* @since 4.0.0
*/
public TableWidthType getWidthType() {
return getWidthType(getTblPr().getTblW());
}
/**
* Get the width type from the width value
* @param ctWidth CTTblWidth to evalute
* @return The table width type
* @since 4.0.0
*/
protected static TableWidthType getWidthType(CTTblWidth ctWidth) {
STTblWidth.Enum typeValue = ctWidth.getType();
if (typeValue == null) {
typeValue = STTblWidth.NIL;
ctWidth.setType(typeValue);
}
switch (typeValue.intValue()) {
case STTblWidth.INT_NIL:
return TableWidthType.NIL;
case STTblWidth.INT_AUTO:
return TableWidthType.AUTO;
case STTblWidth.INT_DXA:
return TableWidthType.DXA;
case STTblWidth.INT_PCT:
return TableWidthType.PCT;
default:
// Should never get here
return TableWidthType.AUTO;
}
}
/**
* Set the width to the value "auto", an integer value (20ths of a point), or a percentage ("nn.nn%").
*
* @param widthValue String matching one of "auto", [0-9]+, or [0-9]+(\.[0-9]+)%.
* @since 4.0.0
*/
public void setWidth(String widthValue) {
setWidthValue(widthValue, getTblPr().getTblW());
}
/**
* Set the width value from a string
* @param widthValue The width value string
* @param ctWidth CTTblWidth to set the value on.
*/
protected static void setWidthValue(String widthValue, CTTblWidth ctWidth) {
if (!widthValue.matches(REGEX_WIDTH_VALUE)) {
throw new RuntimeException("Table width value \"" + widthValue + "\" "
+ "must match regular expression \"" + REGEX_WIDTH_VALUE + "\".");
}
if (widthValue.matches("auto")) {
ctWidth.setType(STTblWidth.AUTO);
ctWidth.setW(BigInteger.ZERO);
} else if (widthValue.matches(REGEX_PERCENTAGE)) {
setWidthPercentage(ctWidth, widthValue);
} else {
// Must be an integer
ctWidth.setW(new BigInteger(widthValue));
ctWidth.setType(STTblWidth.DXA);
}
}
/**
* Set the underlying table width value to a percentage value.
* @param ctWidth The CTTblWidth to set the value on
* @param widthValue String width value in form "33.3%" or an integer that is 50 times desired percentage value (e.g,
* 2500 for 50%)
* @since 4.0.0
*/
protected static void setWidthPercentage(CTTblWidth ctWidth, String widthValue) {
ctWidth.setType(STTblWidth.PCT);
if (widthValue.matches(REGEX_PERCENTAGE)) {
String numberPart = widthValue.substring(0, widthValue.length() - 1);
double percentage = Double.parseDouble(numberPart) * 50;
long intValue = Math.round(percentage);
ctWidth.setW(BigInteger.valueOf(intValue));
} else if (widthValue.matches("[0-9]+")) {
ctWidth.setW(new BigInteger(widthValue));
} else {
throw new RuntimeException("setWidthPercentage(): Width value must be a percentage (\"33.3%\" or an integer, was \"" + widthValue + "\"");
}
}
/**
* Set the width value type for the table.
* <p>If the width type is changed from the current type and the currently-set value
* is not consistent with the new width type, the value is reset to the default value
* for the specified width type.</p>
*
* @param widthType Width type
* @since 4.0.0
*/
public void setWidthType(TableWidthType widthType) {
setWidthType(widthType, getTblPr().getTblW());
}
/**
* Set the width type if different from current width type
* @param widthType The new width type
* @param ctWidth CTTblWidth to set the type on
* @since 4.0.0
*/
protected static void setWidthType(TableWidthType widthType, CTTblWidth ctWidth) {
TableWidthType currentType = getWidthType(ctWidth);
if (!currentType.equals(widthType)) {
STTblWidth.Enum stWidthType = widthType.getSTWidthType();
ctWidth.setType(stWidthType);
switch (stWidthType.intValue()) {
case STTblWidth.INT_PCT:
setWidthPercentage(ctWidth, DEFAULT_PERCENTAGE_WIDTH);
break;
default:
ctWidth.setW(BigInteger.ZERO);
}
}
}
} }

View File

@ -32,10 +32,12 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVerticalJc; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVerticalJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
/** /**
@ -216,7 +218,7 @@ public class XWPFTableCell implements IBody, ICell {
* @param rgbStr - the desired cell color, in the hex form "RRGGBB". * @param rgbStr - the desired cell color, in the hex form "RRGGBB".
*/ */
public void setColor(String rgbStr) { public void setColor(String rgbStr) {
CTTcPr tcpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr(); CTTcPr tcpr = getTcPr();
CTShd ctshd = tcpr.isSetShd() ? tcpr.getShd() : tcpr.addNewShd(); CTShd ctshd = tcpr.isSetShd() ? tcpr.getShd() : tcpr.addNewShd();
ctshd.setColor("auto"); ctshd.setColor("auto");
ctshd.setVal(STShd.CLEAR); ctshd.setVal(STShd.CLEAR);
@ -247,7 +249,7 @@ public class XWPFTableCell implements IBody, ICell {
* @param vAlign - the desired alignment enum value * @param vAlign - the desired alignment enum value
*/ */
public void setVerticalAlignment(XWPFVertAlign vAlign) { public void setVerticalAlignment(XWPFVertAlign vAlign) {
CTTcPr tcpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr(); CTTcPr tcpr = getTcPr();
CTVerticalJc va = tcpr.addNewVAlign(); CTVerticalJc va = tcpr.addNewVAlign();
va.setVal(alignMap.get(vAlign)); va.setVal(alignMap.get(vAlign));
} }
@ -407,7 +409,7 @@ public class XWPFTableCell implements IBody, ICell {
public void insertTable(int pos, XWPFTable table) { public void insertTable(int pos, XWPFTable table) {
bodyElements.add(pos, table); bodyElements.add(pos, table);
int i = 0; int i = 0;
for (CTTbl tbl : ctTc.getTblArray()) { for (CTTbl tbl : ctTc.getTblList()) {
if (tbl == table.getCTTbl()) { if (tbl == table.getCTTbl()) {
break; break;
} }
@ -510,4 +512,71 @@ public class XWPFTableCell implements IBody, ICell {
public enum XWPFVertAlign { public enum XWPFVertAlign {
TOP, CENTER, BOTH, BOTTOM TOP, CENTER, BOTH, BOTTOM
} }
/**
* Get the table width as a decimal value.
* <p>If the width type is DXA or AUTO, then the value will always have
* a fractional part of zero (because these values are really integers).
* If the with type is percentage, then value may have a non-zero fractional
* part.
*
* @return Width value as a double-precision decimal.
* @since 4.0.0
*/
public double getWidthDecimal() {
return XWPFTable.getWidthDecimal(getTcWidth());
}
/**
* Get the width type for the table, as an {@link STTblWidth.Enum} value.
* A table width can be specified as an absolute measurement (an integer
* number of twips), a percentage, or the value "AUTO".
*
* @return The width type.
* @since 4.0.0
*/
public TableWidthType getWidthType() {
return XWPFTable.getWidthType(getTcWidth());
}
/**
* Set the width to the value "auto", an integer value (20ths of a point), or a percentage ("nn.nn%").
*
* @param widthValue String matching one of "auto", [0-9]+, or [0-9]+(\.[0-9]+)%.
* @since 4.0.0
*/
public void setWidth(String widthValue) {
XWPFTable.setWidthValue(widthValue, getTcWidth());
}
private CTTblWidth getTcWidth() {
CTTcPr tcPr = getTcPr();
return tcPr.isSetTcW() ? tcPr.getTcW() : tcPr.addNewTcW();
}
/**
* Get the cell properties for the cell.
* @return The cell properties
* @since 4.0.0
*/
protected CTTcPr getTcPr() {
return ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
}
/**
* Set the width value type for the table.
* <p>If the width type is changed from the current type and the currently-set value
* is not consistent with the new width type, the value is reset to the default value
* for the specified width type.</p>
*
* @param widthType Width type
* @since 4.0.0
*/
public void setWidthType(TableWidthType widthType) {
XWPFTable.setWidthType(widthType, getTcWidth());
}
public int getWidth() {
return getTcWidth().getW().intValue();
}
} }

View File

@ -146,15 +146,63 @@ public class TestXWPFTable {
public void testSetGetWidth() { public void testSetGetWidth() {
XWPFDocument doc = new XWPFDocument(); XWPFDocument doc = new XWPFDocument();
CTTbl table = CTTbl.Factory.newInstance(); XWPFTable xtab = doc.createTable();
table.addNewTblPr().addNewTblW().setW(new BigInteger("1000"));
XWPFTable xtab = new XWPFTable(table, doc);
assertEquals(0, xtab.getWidth());
assertEquals(TableWidthType.AUTO, xtab.getWidthType());
xtab.setWidth(1000);
assertEquals(TableWidthType.DXA, xtab.getWidthType());
assertEquals(1000, xtab.getWidth()); assertEquals(1000, xtab.getWidth());
xtab.setWidth("auto");
assertEquals(TableWidthType.AUTO, xtab.getWidthType());
assertEquals(0, xtab.getWidth());
assertEquals(0.0, xtab.getWidthDecimal(), 0.01);
xtab.setWidth("999");
assertEquals(TableWidthType.DXA, xtab.getWidthType());
assertEquals(999, xtab.getWidth());
xtab.setWidth("50.5%");
assertEquals(TableWidthType.PCT, xtab.getWidthType());
assertEquals(50.5, xtab.getWidthDecimal(), 0.01);
// Test effect of setting width type to a new value
// From PCT to NIL:
xtab.setWidthType(TableWidthType.NIL);
assertEquals(TableWidthType.NIL, xtab.getWidthType());
assertEquals(0, xtab.getWidth());
xtab.setWidth("999"); // Sets type to DXA
assertEquals(TableWidthType.DXA, xtab.getWidthType());
// From DXA to AUTO:
xtab.setWidthType(TableWidthType.AUTO);
assertEquals(TableWidthType.AUTO, xtab.getWidthType());
assertEquals(0, xtab.getWidth());
xtab.setWidthType(TableWidthType.PCT);
assertEquals(TableWidthType.PCT, xtab.getWidthType());
// From PCT to DXA:
xtab.setWidth("33.3%");
xtab.setWidthType(TableWidthType.DXA);
assertEquals(TableWidthType.DXA, xtab.getWidthType());
assertEquals(0, xtab.getWidth());
// From DXA to DXA: (value should be unchanged)
xtab.setWidth("999");
xtab.setWidthType(TableWidthType.DXA);
assertEquals(TableWidthType.DXA, xtab.getWidthType());
assertEquals(999, xtab.getWidth());
// From DXA to PCT:
xtab.setWidthType(TableWidthType.PCT);
assertEquals(TableWidthType.PCT, xtab.getWidthType());
assertEquals(100.0, xtab.getWidthDecimal(), 0.0);
xtab.setWidth(100);
assertEquals(100, table.getTblPr().getTblW().getW().intValue());
try { try {
doc.close(); doc.close();
} catch (IOException e) { } catch (IOException e) {

View File

@ -133,4 +133,18 @@ public class TestXWPFTableCell {
} }
} }
} }
@Test
public void testCellGetSetWidth() throws Exception {
XWPFDocument doc = new XWPFDocument();
XWPFTable table = doc.createTable();
XWPFTableRow tr = table.createRow();
XWPFTableCell cell = tr.addNewTableCell();
cell.setWidth("50%");
assertEquals(TableWidthType.PCT, cell.getWidthType());
assertEquals(50.0, cell.getWidthDecimal(), 0.0);
assertEquals(2500, cell.getWidth());
doc.close();
}
} }