]> source.dussan.org Git - poi.git/commitdiff
[github-122] XWPF: Set table and table cell widths to percentage, twips, or auto...
authorPJ Fanning <fanningpj@apache.org>
Mon, 20 Aug 2018 18:12:37 +0000 (18:12 +0000)
committerPJ Fanning <fanningpj@apache.org>
Mon, 20 Aug 2018 18:12:37 +0000 (18:12 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1838449 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/xwpf/usermodel/TableWidthType.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTable.java
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFTableCell.java

diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/TableWidthType.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/TableWidthType.java
new file mode 100644 (file)
index 0000000..34a1f2a
--- /dev/null
@@ -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;
+    }
+}
index ad93ed375b58a0d0cc197ab278bd770453d333cc..0caca8c6ddd048441e178c4da834944f25a1b52d 100644 (file)
@@ -18,13 +18,12 @@ package org.apache.poi.xwpf.usermodel;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Collections;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Supplier;
 
 import org.apache.poi.ooxml.POIXMLDocumentPart;
 import org.apache.poi.util.Internal;
@@ -54,6 +53,11 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
 @SuppressWarnings("WeakerAccess")
 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
     public enum XWPFBorderType {
         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() {
         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) {
         CTTblPr tblPr = getTblPr();
         CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
         tblWidth.setW(new BigInteger(Integer.toString(width)));
+        tblWidth.setType(STTblWidth.DXA);
     }
 
     /**
@@ -1125,4 +1134,168 @@ public class XWPFTable implements IBodyElement, ISDTContents {
         }
         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);
+            }
+        }
+    }
 }
index 169aef21d615296d432ea5db9a4f829264a432f8..745f5b2879d0b7a22f5c7240df8e69066c6e96a3 100644 (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.CTShd;
 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.CTTcPr;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVerticalJc;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
 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".
      */
     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.setColor("auto");
         ctshd.setVal(STShd.CLEAR);
@@ -247,7 +249,7 @@ public class XWPFTableCell implements IBody, ICell {
      * @param vAlign - the desired alignment enum value
      */
     public void setVerticalAlignment(XWPFVertAlign vAlign) {
-        CTTcPr tcpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
+        CTTcPr tcpr = getTcPr();
         CTVerticalJc va = tcpr.addNewVAlign();
         va.setVal(alignMap.get(vAlign));
     }
@@ -407,7 +409,7 @@ public class XWPFTableCell implements IBody, ICell {
     public void insertTable(int pos, XWPFTable table) {
         bodyElements.add(pos, table);
         int i = 0;
-        for (CTTbl tbl : ctTc.getTblArray()) {
+        for (CTTbl tbl : ctTc.getTblList()) {
             if (tbl == table.getCTTbl()) {
                 break;
             }
@@ -510,4 +512,71 @@ public class XWPFTableCell implements IBody, ICell {
     public enum XWPFVertAlign {
         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();
+    }
 }
index 96e7b227170cb8e717c0fab03a8936c381af4fa8..77ba00e0f2da56a53bbc5cc42341f871b8ad14e3 100644 (file)
@@ -146,15 +146,63 @@ public class TestXWPFTable {
     public void testSetGetWidth() {
         XWPFDocument doc = new XWPFDocument();
 
-        CTTbl table = CTTbl.Factory.newInstance();
-        table.addNewTblPr().addNewTblW().setW(new BigInteger("1000"));
-
-        XWPFTable xtab = new XWPFTable(table, doc);
+        XWPFTable xtab = doc.createTable();
 
+        assertEquals(0, xtab.getWidth());
+        assertEquals(TableWidthType.AUTO, xtab.getWidthType());
+        
+        xtab.setWidth(1000);
+        assertEquals(TableWidthType.DXA, xtab.getWidthType());
         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 {
             doc.close();
         } catch (IOException e) {
index d1cf5b2e907b2a71845f272b6beced1865f12942..4b711c6a77376d5bce64b1b5ff676e7fefe477a0 100644 (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();
+    }
 }