/*
Licensed to Plutext Pty Ltd under one or more contributor license agreements.
* This file is part of docx4j.
docx4j is licensed 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 com.plutext.editor;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.List;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import org.apache.log4j.Logger;
import org.docx4j.UnitsOfMeasurement;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.Converter;
import org.docx4j.jaxb.Context;
import org.docx4j.model.table.Cell;
import org.docx4j.model.table.TableModel;
import org.docx4j.wml.CTBorder;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.STBorder;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.TblBorders;
import org.docx4j.wml.TblWidth;
import org.docx4j.wml.CTTblPrBase.TblStyle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* There are different ways to represent a table with possibly merged
* cells.
* - In html, both vertically and horizontally merged cells are
* represented by one cell only that has a colspan and rowspan
* attribute. No dummy cells are used.
*
- In docx, horizontally merged cells are represented by one cell
* with a gridSpan attribute; while vertically merged cells are
* represented as a top cell containing the actual content and a series
* of dummy cells having a vMerge tag with "continue" attribute.
*
- This table is a regular matrix, dummy cells are added for both
* merge directions.
*
*
* This class builds a table from an HTML table.
* A cell can hold three things:
* - a real cell,
* - a dummy cell (implied by a rowspan or colspan)
* - null (an entry in the grid which exists, but is not populated
* by either a real cell or a dummy cell)
*
* See also TableElementInfo in src/share/classes/javax/swing/text/html/AccessibleHTML
* for a similar model (though the implementation is different)
*
* @author Jason Harrop
* Based in part on docx TableModel by Adam Schmideg
*
*/
public class TableModelFromHtml extends TableModel {
private final static Logger logger = Logger.getLogger(TableModelFromHtml.class);
private int colNum=-1;
private int rowNum=-1;
public TableModelFromHtml() {
resetIndexes();
cells = new Vector>();
}
private void ensureSufficientColumns(int requiredNumber ) {
if (getColCount() row = cells.get(i);
((Vector)row).setSize(requiredNumber); // adds null entries
logger.debug("For row " + i + ", setSize to " + requiredNumber);
}
}
}
private int getInsertionColumn(int r, int c) throws ArrayIndexOutOfBoundsException {
for (int i=c; i
// follows it
// Make the correct number of rows
// Find the tbody node
NodeList kids = node.getChildNodes();
Node tbody = null;
for (int i=0; i.
NodeList rows = tbody.getChildNodes();
for (int i=0; i());
logger.debug("row added");
rowNum++;
} else {
logger.warn("Unexpected " + rows.item(i).getLocalName());
}
}
NodeList cellContents = children.item(0).getChildNodes(); // the w:tr
// now process in each |
int r = -1;
for (int i=0; i.size() is 1-based
int rowspan = 1;
if (td.getAttributeNodeNS(null, "rowspan")!=null) {
rowspan = Integer.parseInt(td.getAttribute("rowspan") );
logger.debug( "(" + r + "," + j + ") has rowspan " + rowspan );
}
// Sanity check
if (r + (rowspan-1) > rowNum) {
// rowspan extends beyond bottom row,
// so truncate it
logger.warn("rowspan extends beyond bottom row, so truncating rowspan from " + rowspan);
rowspan = (rowNum + 1)-r;
logger.warn(".. to " + rowspan);
}
// create dummy cells for rows/cols spanned
for (int ry = r ; ry <= (r + rowspan -1); ry++) {
for (int cx = insertionCol ; cx <= (insertionCol + colspan -1); cx++) {
Cell newCell;
if ((ry==r) && (cx==insertionCol)) {
// special case
Node contents = wtrNode.getChildNodes().item(sourceTdPos);
newCell = new CellFromHtml(this, r, insertionCol, rowspan, colspan, contents); // TODO: content
} else {
newCell = new CellFromHtml(this, ry, cx);
}
logger.debug("Getting row " + ry + ", setting col " + cx);
cells.get(ry).set(cx, newCell);
}
}
}
}
}
}
// Set tblPr
ObjectFactory factory = Context.getWmlObjectFactory();
tblPr = factory.createTblPr();
// Default to page width
TblWidth tblWidth = factory.createTblWidth();
tblWidth.setW(BigInteger.valueOf(UnitsOfMeasurement.DEFAULT_PAGE_WIDTH_TWIPS));
tblWidth.setType("dxa"); // twips
tblPr.setTblW(tblWidth);
// TODO: borders
// TblBorders tblBorders = factory.createTblBorders();
if ( ((Element)node).getAttribute("border")!=null ) {
// CTBorder left = factory.createCTBorder();
TblStyle tblStyle = factory.createCTTblPrBaseTblStyle();
tblStyle.setVal("TableGrid");
tblPr.setTblStyle(tblStyle);
}
}
// private void setupBorder(CTBorder border ) {
// border.setVal(STBorder.SINGLE);
// border.setSz(BigInteger.valueOf(2));
// border.setSpace(BigInteger.valueOf(2));
// border.setColor("auto");
// }
/**
* A cell in the table holding its own content, too
*/
public class CellFromHtml extends Cell {
/**
* Create a dummy cell without content
*/
public CellFromHtml(TableModel table, int row, int col) {
super(table, row, col);
}
public CellFromHtml(TableModelFromHtml table, int row, int col, int rowspan, int colspan, Node content) {
this(table, row, col);
dummy = false;
this.rowspan = rowspan;
this.colspan = colspan;
this.content = content;
logger.debug("Cell content: "
+ XmlUtils.w3CDomNodeToString(content));
}
}
public static void main(String[] args) throws Exception {
// Simple table 2x3 - no spans
// String table="";
// 2x3 table - with rowspan
// String table="";
// 2x3 table - with rowspan, and 3 explicit cells in 2nd row; result should be 4 cols
// String table="";
// 2 x 5 table (colspan)
// String table="";
String table="";
Document doc;
// System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
javax.xml.parsers.DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true); // otherwise getLocalName() returns null!
DocumentBuilder db = dbf.newDocumentBuilder();
// ByteArrayInputStream is = new ByteArrayInputStream(table.getBytes());
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(table));
doc = db.parse(is);
// logger.debug(XmlUtils.w3CDomNodeToString(doc) );
TableModelFromHtml tm = new TableModelFromHtml();
tm.build( doc.getDocumentElement(), null);
logger.debug(tm.debugStr());
// Can we round trip it?
Tbl tbl = (Tbl)tm.toJAXB();
logger.debug(XmlUtils.marshaltoString(tbl, true));
}
}
|