org.apache.fop.render.rtf.rtflib.rtfdoc.RtfExtraRowSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fop Show documentation
Show all versions of fop Show documentation
Apache FOP (Formatting Objects Processor) is the world's first print formatter driven by XSL formatting objects (XSL-FO) and the world's first output independent formatter. It is a Java application that reads a formatting object (FO) tree and renders the resulting pages to a specified output. Output formats currently supported include PDF, PCL, PS, AFP, TIFF, PNG, SVG, XML (area tree representation), Print, AWT and TXT. The primary output target is PDF.
/*
* 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.
*/
/* $Id: RtfExtraRowSet.java 1761020 2016-09-16 11:17:35Z ssteiner $ */
package org.apache.fop.render.rtf.rtflib.rtfdoc;
/*
* This file is part of the RTF library of the FOP project, which was originally
* created by Bertrand Delacretaz and by other
* contributors to the jfor project (www.jfor.org), who agreed to donate jfor to
* the FOP project.
*/
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Used to add extra table rows after a row that contains a nested table:
*
* - created by RtfTableRow before generating RTF code
*
- an RtfTableCell that contains a nested table can ask this to put
* some of its children in extra rows that after the current row
*
- once RtfTableRow is done rendering its children, it renders this,
* causing extra rows to be generated, with content that can come
* from several RtfTableCells
*
*
* See org.apache.fop.rtf.rtflib.testdocs.NestedTable for an example of
* usage.
*
* This work was authored by Bertrand Delacretaz ([email protected]).
*/
public class RtfExtraRowSet extends RtfContainer {
// TODO what is idnum?
static final int DEFAULT_IDNUM = 0;
/** Parent table context
* (added by Boris Poudérous on july 2002 in order to process nested tables)
*/
private ITableColumnsInfo parentITableColumnsInfo;
/** While a top-level RtfTableRow is being rendered, we build a list of
* RtfTableCells that must be rendered in extra rows.
* This holds a cell with positioning information
*/
private final List cells = new LinkedList();
private static class PositionedCell
implements Comparable {
private final RtfTableCell cell;
private final int xOffset;
private final int rowIndex;
PositionedCell(RtfTableCell c, int index, int offset) {
cell = c;
xOffset = offset;
rowIndex = index;
}
/** debugging dump */
public String toString() {
return "PositionedCell: row " + rowIndex + ", offset " + xOffset;
}
/** cells need to be sorted by row index and then by x offset */
public int compareTo(Object o) {
int result = 0;
if (o == null) {
result = 1;
} else if (!(o instanceof PositionedCell)) {
result = 1;
} else {
final PositionedCell pc = (PositionedCell)o;
if (this.rowIndex < pc.rowIndex) {
result = -1;
} else if (this.rowIndex > pc.rowIndex) {
result = 1;
} else if (this.xOffset < pc.xOffset) {
result = -1;
} else if (this.xOffset > pc.xOffset) {
result = 1;
}
}
return result;
}
public int hashCode() {
int hc = super.hashCode();
hc ^= (hc * 11) + xOffset;
hc ^= (hc * 19) + rowIndex;
return hc;
}
public boolean equals(Object o) {
if (o instanceof PositionedCell) {
PositionedCell pc = (PositionedCell) o;
return (pc.rowIndex == rowIndex) && (pc.xOffset == xOffset);
} else {
return false;
}
}
}
/** our maximum row index */
private int maxRowIndex;
/** an RtfExtraRowSet has no parent, it is only used temporary during
* generation of RTF for an RtfTableRow
*/
RtfExtraRowSet(Writer w)
throws IOException {
super(null, w);
}
/** Add all cells of given Table to this set for later rendering in extra rows
* @return index of extra row to use for elements that follow this table in the same cell
* @param rowIndex index of first extra row to create to hold cells of tbl
* @param xOffset horizontal position of left edge of first column of tbl
*/
int addTable(RtfTable tbl, int rowIndex, int xOffset) {
// process all rows of the table
for (Object o : tbl.getChildren()) {
final RtfElement e = (RtfElement) o;
if (e instanceof RtfTableRow) {
addRow((RtfTableRow) e, rowIndex, xOffset);
rowIndex++;
maxRowIndex = Math.max(rowIndex, maxRowIndex);
}
}
return rowIndex;
}
/** add all cells of given row to this set */
private void addRow(RtfTableRow row, int rowIndex, int xOffset) {
for (Object o : row.getChildren()) {
final RtfElement e = (RtfElement) o;
if (e instanceof RtfTableCell) {
final RtfTableCell c = (RtfTableCell) e;
cells.add(new PositionedCell(c, rowIndex, xOffset));
xOffset += c.getCellWidth();
}
}
}
/** create an extra cell to hold content that comes after a nested table in a cell
* Modified by Boris Poudérous in order to permit the extra cell to have
* the attributes of its parent cell
*/
RtfTableCell createExtraCell(int rowIndex, int xOffset, int cellWidth,
RtfAttributes parentCellAttributes)
throws IOException {
final RtfTableCell c = new RtfTableCell(null, writer, cellWidth,
parentCellAttributes, DEFAULT_IDNUM);
cells.add(new PositionedCell(c, rowIndex, xOffset));
return c;
}
/**
* render extra RtfTableRows containing all the extra RtfTableCells that we
* contain
* @throws IOException for I/O problems
*/
protected void writeRtfContent() throws IOException {
// sort cells by rowIndex and xOffset
Collections.sort(cells);
// process all extra cells by rendering them into extra rows
List rowCells = null;
int rowIndex = -1;
for (Object cell : cells) {
final PositionedCell pc = (PositionedCell) cell;
if (pc.rowIndex != rowIndex) {
// starting a new row, render previous one
if (rowCells != null) {
writeRow(rowCells);
}
rowIndex = pc.rowIndex;
rowCells = new LinkedList();
}
rowCells.add(pc);
}
// render last row
if (rowCells != null) {
writeRow(rowCells);
}
}
/** write one RtfTableRow containing given PositionedCells */
private void writeRow(List cells)
throws IOException {
if (allCellsEmpty(cells)) {
return;
}
final RtfTableRow row = new RtfTableRow(null, writer, DEFAULT_IDNUM);
int cellIndex = 0;
// Get the context of the table that holds the nested table
ITableColumnsInfo parentITableColumnsInfo = getParentITableColumnsInfo();
parentITableColumnsInfo.selectFirstColumn();
// X offset of the current empty cell to add
float xOffset = 0;
float xOffsetOfLastPositionedCell = 0;
for (Object cell : cells) {
final PositionedCell pc = (PositionedCell) cell;
// if first cell is not at offset 0, add placeholder cell
// TODO should be merged with the cell that is above it
if (cellIndex == 0 && pc.xOffset > 0) {
/**
* Added by Boris Poudérous
*/
// Add empty cells merged vertically with the cells above and with the same widths
// (BEFORE the cell that contains the nested table)
for (int i = 0; (xOffset < pc.xOffset)
&& (i < parentITableColumnsInfo.getNumberOfColumns()); i++) {
// Get the width of the cell above
xOffset += parentITableColumnsInfo.getColumnWidth();
// Create the empty cell merged vertically
row.newTableCellMergedVertically((int) parentITableColumnsInfo.getColumnWidth(),
pc.cell.attrib);
// Select next column in order to have its width
parentITableColumnsInfo.selectNextColumn();
}
}
row.addChild(pc.cell);
// Line added by Boris Poudérous
xOffsetOfLastPositionedCell = pc.xOffset + pc.cell.getCellWidth();
cellIndex++;
}
/**
* Added by Boris Poudérous
*/
// Add empty cells merged vertically with the cells above AFTER the cell
// that contains the nested table
// The cells added have the same widths than the cells above.
if (parentITableColumnsInfo.getColumnIndex()
< (parentITableColumnsInfo.getNumberOfColumns() - 1)) {
parentITableColumnsInfo.selectNextColumn();
while (parentITableColumnsInfo.getColumnIndex()
< parentITableColumnsInfo.getNumberOfColumns()) {
// Create the empty cell merged vertically
// TODO : the new cells after the extra cell don't have its
// attributes as we did for the previous cells.
// => in fact the m_attrib below (last argument) is
// empty => should be the attributes of the above cells.
row.newTableCellMergedVertically((int)parentITableColumnsInfo.getColumnWidth(),
attrib);
// Select next column in order to have its width
parentITableColumnsInfo.selectNextColumn();
}
}
row.writeRtf();
}
/** true if all cells of given list are empty
* @param cells List of PositionedCell objects
*/
private static boolean allCellsEmpty(List cells) {
boolean empty = true;
for (Object cell : cells) {
final PositionedCell pc = (PositionedCell) cell;
if (pc.cell.containsText()) {
empty = false;
break;
}
}
return empty;
}
/**
* As this contains cells from several rows, we say that it's empty
* only if we have no cells.
* writeRow makes the decision about rendering specific rows
* @return false (always)
*/
public boolean isEmpty() {
return false;
}
/**
* @return The table context of the parent table
* Added by Boris Poudérous on july 2002 in order to process nested tables
*/
public ITableColumnsInfo getParentITableColumnsInfo() {
return this.parentITableColumnsInfo;
}
/**
*
* @param parentITableColumnsInfo table context to set
*/
public void setParentITableColumnsInfo(ITableColumnsInfo parentITableColumnsInfo) {
this.parentITableColumnsInfo = parentITableColumnsInfo;
}
/** - end - */
}