Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
     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 org.docx4j.convert.out.common.writer;
 
 import java.util.List;
 
 
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.
The algorithm is as follows,
  • When a cell is added, its colspan is set. Even a dummy cell can have a colspan, the same value as its upper has.
  • When a new cell has a colspan greater than 1, the required extra dummy cells are also added
  • When a docx dummy cell is encountered (one with a vMerge continue attribute), the rowspan is incremented in its upper neighbors until a real cell is found.
This model captures: - whether the table layout is fixed or auto (Word usually does auto) - whether conflict resolution is required on cell borders (Word usually does conflict resolution)

Author(s):
Adam Schmideg
Alberto Zerolo
Jason Harrop
 
 public class AbstractTableWriterModel {
 	private final static Logger log = LoggerFactory.getLogger(AbstractTableWriterModel.class);
 
 		 = -1;
 	}
 
 	// TODO, retire this
 	private final static int DEFAULT_PAGE_WIDTH_TWIPS = 12240;  // LETTER; A4 would be 11907
 	
A list of rows
	private int headerMaxRow;
	private int row;
	private int col;
	private int width = -1;
	private boolean drawTableBorder = true;
	protected String styleId

Returns:
the table's style, if any
	public String getStyleId() {
		return ;
	}

Returns:
the table's effective Style
	}

Table properties are represented using the docx model.
	protected TblPr tblPr;

Returns:
the w:tblPr
	public TblPr getTblPr() {
		return ;
	}
	protected TblGrid tblGrid;

Returns:
the w:tblGrid
	public TblGrid getTblGrid() {
		return ;
	}
	// We don't need this in our table model,
	// at least for HTML. (PropertyFactory takes care of it)
//	boolean tableLayoutFixed = false; // default to auto
//	/**
//	 * @return isTableLayoutFixed
//	 */
//	public boolean isTableLayoutFixed() {
//		return tableLayoutFixed;
//	}
If borderConflictResolutionRequired is required, we need to set this explicitly, because in CSS, 'separate' is the default. We need to avoid incorrectly overruling an inherited value (ie where TblCellSpacing is set), so we do borderConflictResolutionRequired here, as an explicit \@style value, rather than that in conjunction with \@class.

Returns:
borderConflictResolutionRequired
	}
	/*
	 * @since 3.0.0
	 */
	public boolean isDrawTableBorders() {
	}
	//Table width in twips, -1 = undefined
	public int getTableWidth() {
		return ;
	}

Reset row and col.
	public void resetIndexes() {
		 = -1;
		 = -1;
	}
	public void startRow(Tr tr) {
		++;
		 = -1;
	}

Add a new cell to this table and copy processed content of tc to it.
	public void addCell(Tc tcNode content) {
		addCell(new AbstractTableWriterModelCell(this, ++tccontent));
	}
	private void addDummyCell() {
	}
	private void addDummyCell(int colSpan) {
		if (colSpan > 0) {
			cell.colspan = colSpan;
		}
		addCell(cell);
	}
	private void addCell(AbstractTableWriterModelCell cell) {
		.get().add(cell);
	}
	public AbstractTableWriterModelCell getCell(int rowint col) {
		return .get(row).get(col);
	}

Returns:
"colX" where X is a 1-based index
	public String getColName(int col) {
		return "col" + String.valueOf(col + 1);
	}
	public int getColCount() {
		return .get(0).size();
	}
		return ;
	}
    public int getHeaderMaxRow() {
    	return ;
    }
    
Build a table representation from a tbl instance. Remember to set wordMLPackage before using this method!
	public void build(AbstractWmlConversionContext conversionContextObject nodeNode contentthrows TransformerException {
		Tbl tbl = null;
		try {
			tbl = (Tbl)node;
catch (ClassCastException e) {
			throw new TransformerException("Node is not of the type Tbl it is " + node.getClass().getName());
		}
		if (tbl.getTblPr()!=null
				&& tbl.getTblPr().getTblStyle()!=null) {
		}
		this. = tbl.getTblGrid();
		this. = tbl.getTblPr();
		PropertyResolver pr = conversionContext.getPropertyResolver();
//	    if (tblPr!=null
//	    		&& tblPr.getTblW()!=null) {
//	    	if (tblPr.getTblW().getType()!=null 
//	    			&& (tblPr.getTblW().getType().equals("auto")
//	    					|| tblPr.getTblW().getType().equals("nil") )) {
//	    		// @w:type
//	    					// nil, per Word 2007 implementation note
//	    		tableLayoutFixed = false;
//	    	} else if (tblPr.getTblW().getW()!=null ){
//	    		// @w:w
//	    		if (tblPr.getTblW().getW() == BigInteger.ZERO) {
//	    			// Word 2007 implementation note
//	    			tableLayoutFixed = false;
//	    		} else {
//	    			tableLayoutFixed = true;
//	    		}
//	    	} else {
//	    		// no attributes!!
//	    		tableLayoutFixed = false;
//	    	}
//	    } else {
//	    	// element omitted, so type is auto (2.4.61)
//	    	tableLayoutFixed = false;
//	    }
		NodeList cellContents = content.getChildNodes(); // the w:tr
		TrFinder trFinder = new TrFinder();
		new TraversalUtil(tbltrFinder);
		ensureFoTableBody(trFinder.trList); // this is currently applied to HTML etc as well
		int r = 0;
		for (Tr tr : trFinder.trList) {
				handleRow(cellContentstrr);
				r++;
					--;
					r--;
				}
		}
		if (tblPr != null) {
			if (tblPr.getTblCellSpacing()!=null) {
			}
		}
	}

"fo:table" content model is: (marker*,table-column*,table-header?,table-footer?,table-body+) ie table-header (if any) must precede table-body The first requirement is that there is a table-body. Since the docx format doesn't have any equivalent to table-footer, we can always treat the last row as table-body. The second requirement is that there is no table-header after table-body. We could either treat each t-h after a t-b as t-b, or we could treat all t-b before t-h as t-h. If the docx has normal rows before the a t-h row, the user should split the table into two. Since they can do that, we'll treat all rows before last t-h row as t-h rows
	private void ensureFoTableBody(List<Trrows) {
		int numRows = rows.size();
		if (numRows==0) {
			.warn("Encountered table with no rows");
			return;
		}
		// Req 1: Make sure the last row is not a header row
		Tr lastRow = rows.get(numRows-1);
		if (isHeaderRow(lastRow)) {
			List<JAXBElement<?>> cnfStyleOrDivIdOrGridBefore = lastRow.getTrPr().getCnfStyleOrDivIdOrGridBefore();
			JAXBElement tblHeader = getElement(cnfStyleOrDivIdOrGridBefore"tblHeader");
			cnfStyleOrDivIdOrGridBefore.remove(tblHeader);
		}
		// Req 2: All rows before last header row become header rows
		// .. find last header row
		int indexOfLastHeaderRow=-1;
		for (int i = rows.size(); i>0; i--) {
			Tr tr = rows.get(i-1);
			if (isHeaderRow(tr)) {
				indexOfLastHeaderRow = i-1;
				break;
			}
		}
		// .. now convert all rows up to that one
		for (int i = 0; i<indexOfLastHeaderRowi++) {
			Tr tr = rows.get(i);
			if (!isHeaderRow(tr)) {
				// make it so...
				TrPr trpr = null;
				if (tr.getTrPr() == null) {
					trpr = Context.getWmlObjectFactory().createTrPr(); 
				    tr.setTrPr(trpr); 
				}
		        // Create object for tblHeader (wrapped in JAXBElement) 
		        BooleanDefaultTrue booleandefaulttrue = Context.getWmlObjectFactory().createBooleanDefaultTrue(); 
		        JAXBElement<org.docx4j.wml.BooleanDefaultTruebooleandefaulttrueWrapped 
		        	= Context.getWmlObjectFactory().createCTTrPrBaseTblHeader(booleandefaulttrue); 
		        trpr.getCnfStyleOrDivIdOrGridBefore().addbooleandefaulttrueWrapped); 					
			}
		}
	}
	static class TrFinder extends CallbackImpl {
		List<TrtrList = new ArrayList<Tr>();  
		public List<Objectapply(Object o) {
			if (o instanceof Tr ) {
			}			
			return null
		}
		public boolean shouldTraverse(Object o) {
			// Yes, unless its a nested Tbl
			return !(o instanceof Tbl); 
		}
	}
	/*
	 * TrFinder and TcFinder can find tr and tc in a complex
	 * case such as the following:
	 * 
		    <w:tbl>
		      <w:tblPr>
		        <w:tblStyle w:val="TableGrid"/>
		        <w:tblW w:type="auto" w:w="0"/>
		        <w:tblLook w:val="04A0"/>
		      </w:tblPr>
		      <w:tblGrid>
		        <w:gridCol w:w="4621"/>
		        <w:gridCol w:w="4621"/>
		      </w:tblGrid>
		      <w:tr w:rsidTr="005051A3" w:rsidR="005051A3">
		        <w:tc>
		          <w:tcPr>
		            <w:tcW w:type="dxa" w:w="4621"/>
		          </w:tcPr>
		          <w:p w:rsidRDefault="005051A3" w:rsidR="005051A3">
		            <w:r>
		              <w:t>Desscription</w:t>
		            </w:r>
		          </w:p>
		        </w:tc>
		        <w:tc>
		          <w:tcPr>
		            <w:tcW w:type="dxa" w:w="4621"/>
		          </w:tcPr>
		          <w:p w:rsidRDefault="005051A3" w:rsidR="005051A3">
		            <w:r>
		              <w:t>Price</w:t>
		            </w:r>
		          </w:p>
		        </w:tc>
		      </w:tr>
		      <w:sdt>
		        <w:sdtPr>
		          <w:alias w:val="REPEAT tr"/>
		          <w:tag w:val="od:rptd=hU9bp&amp;od:RptInst=2"/>
		          <w:id w:val="65990884"/>
		        </w:sdtPr>
		        <w:sdtContent>
		          <w:tr w:rsidTr="005051A3" w:rsidR="005051A3">
		            <w:sdt>
		              <w:sdtPr>
		                <w:alias w:val="desc"/>
		                <w:tag w:val="od:xpath=NFSsi_0"/>
		                <w:dataBinding w:storeItemID="{80261315-781E-4194-879C-F78D116D6A5E}" w:xpath="/oda:answers/oda:repeat[@qref='tr_oB']/oda:row[1][1]/oda:answer[@id='desc_UM']" w:prefixMappings="xmlns:oda='http://opendope.org/answers'"/>
		                <w:text w:multiLine="true"/>
		                <w:id w:val="1529259979"/>
		              </w:sdtPr>
		              <w:sdtContent>
		                <w:tc>
		                  <w:tcPr>
		                    <w:tcW w:type="dxa" w:w="4621"/>
		                  </w:tcPr>
		                  <w:p>
		                    <w:r>
		                      <w:t>banana</w:t>
		                    </w:r>
		                  </w:p>
		                </w:tc>
		              </w:sdtContent>
		            </w:sdt>
		            <w:bookmarkStart w:name="_GoBack" w:displacedByCustomXml="next" w:id="0"/>
		            <w:bookmarkEnd w:displacedByCustomXml="next" w:id="0"/>
		            <w:sdt>
		              <w:sdtPr>
		                <w:alias w:val="price"/>
		                <w:tag w:val="od:xpath=OhZO5_0"/>
		                <w:dataBinding w:storeItemID="{80261315-781E-4194-879C-F78D116D6A5E}" w:xpath="/oda:answers/oda:repeat[@qref='tr_oB']/oda:row[1][1]/oda:answer[@id='price_a7']" w:prefixMappings="xmlns:oda='http://opendope.org/answers'"/>
		                <w:text w:multiLine="true"/>
		                <w:id w:val="494684987"/>
		              </w:sdtPr>
		              <w:sdtContent>
		                <w:tc>
		                  <w:tcPr>
		                    <w:tcW w:type="dxa" w:w="4621"/>
		                  </w:tcPr>
		                  <w:p>
		                    <w:r>
		                      <w:t>10</w:t>
		                    </w:r>
		                  </w:p>
		                </w:tc>
		              </w:sdtContent>
		            </w:sdt>
		          </w:tr>
		        </w:sdtContent>
		      </w:sdt>
		      <w:sdt>
		        <w:sdtPr>
		          <w:alias w:val="REPEAT tr"/>
		          <w:tag w:val="od:rptd=hU9bp&amp;od:RptInst=2"/>
		          <w:id w:val="351087912"/>
		        </w:sdtPr>
		        <w:sdtContent>
		          <w:tr w:rsidTr="005051A3" w:rsidR="005051A3">
		            <w:sdt>
		              <w:sdtPr>
		                <w:alias w:val="desc"/>
		                <w:tag w:val="od:xpath=NFSsi_1"/>
		                <w:dataBinding w:storeItemID="{80261315-781E-4194-879C-F78D116D6A5E}" w:xpath="/oda:answers/oda:repeat[@qref='tr_oB']/oda:row[2][1]/oda:answer[@id='desc_UM']" w:prefixMappings="xmlns:oda='http://opendope.org/answers'"/>
		                <w:text w:multiLine="true"/>
		                <w:id w:val="178426324"/>
		              </w:sdtPr>
		              <w:sdtContent>
		                <w:tc>
		                  <w:tcPr>
		                    <w:tcW w:type="dxa" w:w="4621"/>
		                  </w:tcPr>
		                  <w:p>
		                    <w:r>
		                      <w:t>apple</w:t>
		                    </w:r>
		                  </w:p>
		                </w:tc>
		              </w:sdtContent>
		            </w:sdt>
		            <w:bookmarkStart w:name="_GoBack" w:displacedByCustomXml="next" w:id="0"/>
		            <w:bookmarkEnd w:displacedByCustomXml="next" w:id="0"/>
		            <w:sdt>
		              <w:sdtPr>
		                <w:alias w:val="price"/>
		                <w:tag w:val="od:xpath=OhZO5_1"/>
		                <w:dataBinding w:storeItemID="{80261315-781E-4194-879C-F78D116D6A5E}" w:xpath="/oda:answers/oda:repeat[@qref='tr_oB']/oda:row[2][1]/oda:answer[@id='price_a7']" w:prefixMappings="xmlns:oda='http://opendope.org/answers'"/>
		                <w:text w:multiLine="true"/>
		                <w:id w:val="1236330390"/>
		              </w:sdtPr>
		              <w:sdtContent>
		                <w:tc>
		                  <w:tcPr>
		                    <w:tcW w:type="dxa" w:w="4621"/>
		                  </w:tcPr>
		                  <w:p>
		                    <w:r>
		                      <w:t>20</w:t>
		                    </w:r>
		                  </w:p>
		                </w:tc>
		              </w:sdtContent>
		            </w:sdt>
		          </w:tr>
		        </w:sdtContent>
		      </w:sdt>
		    </w:tbl>
	 */
	private void handleRow(NodeList cellContentsTr trint r) {
		int gridAfter = getGridAfter(tr);
		int gridBefore = getGridBefore(tr);
		boolean headerRow = isHeaderRow(tr);
		.debug("Processing r " + r);
				&& tr.getTblPrEx().getTblCellSpacing() != null) {
		}
		if (headerRow && ( < r)) {
		}
			 = (gridBefore == 0) && (gridAfter == 0);
		}
		TcFinder tcFinder = new TcFinder();
		new TraversalUtil(trtcFinder);
		//add dummy cell for gridBefore
		if (gridBefore > 0) {
			addDummyCell(gridBefore);
		}
		//List<Object> cells = tr.getEGContentCellContent();
		int c = 0;
		.debug("Processing c " + c);
		for (Tc tc : tcFinder.tcList) {
			Node wtrNode = cellContents.item(r); // w:tr
			if (wtrNode==null ) {
				.warn("Couldn't find item " + r);
			}
			addCell(tcgetTc(wtrNodecnew IntRef(0))); // the cell content
			// addCell(tc, cellContents.item(i));
			// i++;
			c++;
		}
		//add dummy cell for gridAfter
		if (gridAfter > 0) {
			addDummyCell(gridAfter);
		}
	}
	protected boolean isHeaderRow(Tr tr) {
		List<JAXBElement<?>> cnfStyleOrDivIdOrGridBefore = (tr.getTrPr() != null ? tr.getTrPr().getCnfStyleOrDivIdOrGridBefore() : null);
		JAXBElement element = getElement(cnfStyleOrDivIdOrGridBefore"tblHeader");
		BooleanDefaultTrue boolVal = (element != null ? (BooleanDefaultTrue)element.getValue() : null);
		return (boolVal != null ? boolVal.isVal() : false);
	}
	protected int getGridAfter(Tr tr) {
	List<JAXBElement<?>> cnfStyleOrDivIdOrGridBefore = (tr.getTrPr() != null ? tr.getTrPr().getCnfStyleOrDivIdOrGridBefore() : null);
	JAXBElement element = getElement(cnfStyleOrDivIdOrGridBefore"gridAfter");
	CTTrPrBase.GridAfter gridAfter = (element != null ? (CTTrPrBase.GridAfter)element.getValue() : null);
	BigInteger val = (gridAfter != null ? gridAfter.getVal() : null);
		return (val != null ? val.intValue() : 0);
	}
	protected int getGridBefore(Tr tr) {
	List<JAXBElement<?>> cnfStyleOrDivIdOrGridBefore = (tr.getTrPr() != null ? tr.getTrPr().getCnfStyleOrDivIdOrGridBefore() : null);
	JAXBElement element = getElement(cnfStyleOrDivIdOrGridBefore"gridBefore");
	CTTrPrBase.GridBefore gridBefore = (element != null ? (CTTrPrBase.GridBefore)element.getValue() : null);
	BigInteger val = (gridBefore != null ? gridBefore.getVal() : null);
		return (val != null ? val.intValue() : 0);
	}
	protected JAXBElement<?> getElement(List<JAXBElement<?>> cnfStyleOrDivIdOrGridBeforeString localName) {
		JAXBElement<?> element = null;
		if ((cnfStyleOrDivIdOrGridBefore != null) && (!cnfStyleOrDivIdOrGridBefore.isEmpty())) {
			for (int i=0; i<cnfStyleOrDivIdOrGridBefore.size(); i++) {
				element = cnfStyleOrDivIdOrGridBefore.get(i);
				if (localName.equals(element.getName().getLocalPart())) {
					return element;
				}
			}
		}
		return null;
	}
	protected int calcTableWidth() {
	int ret = -1;
	List<TblGridColgridCols = (getTblGrid() != null ? getTblGrid().getGridCol() : null);
		//The calculation is done the way it was done in the TableWriter. This isn't necesarily correct,
	    //as cell-widths may override column widths.
    	if ((gridCols != null) && (!gridCols.isEmpty())) {
    		ret = 0;
	    	for(int i=0; i<gridCols.size(); i++) {   
	    		ret += gridCols.get(i).getW().intValue();
	    	}
    	}
    	return ret;
	}

The tc could be inside something else, so find it recursively.

Parameters:
wtrNode
wanted
current
Returns:
	private Node getTc(Node wtrNodeint wantedIntRef current) {
		for (int i=0; i<wtrNode.getChildNodes().getLength(); i++ ) {
			Node thisChild = wtrNode.getChildNodes().item(i);
			.debug("Looking at " + thisChild.getLocalName() + "; have encountered " + current.i);
			if (thisChild.getLocalName().equals("tc") ) {
				if (current.i==wantedreturn thisChild;
				current.increment();
else {
				// could be inside
				Node n = getTc(thisChildwantedcurrent);
				if (n!=nullreturn n;
			}
		}
		.error("Couldn't find tc in: " + XmlUtils.w3CDomNodeToString(wtrNode));
		return null;
	}
	static class IntRef {
		IntRef(int i) {
			this. = i;
		}
		int i;
		void increment() {
			++;
		}
	}
	public String debugStr() {
		StringBuffer buf = new StringBuffer();
			for (AbstractTableWriterModelCell c : rowContents) {
				if (c==null) {
					buf.append("null     ");
else {
					buf.append(c.debugStr());
				}
			}
			buf.append("\n");
		}
		return buf.toString();
	}
New to GrepCode? Check out our FAQ X