Source code: org/jfor/jfor/rtflib/rtfdoc/RtfExtraRowSet.java
1 package org.jfor.jfor.rtflib.rtfdoc;
2
3 import java.io.*;
4 import java.util.*;
5 import org.jfor.jfor.interfaces.ITableColumnsInfo;
6
7 /*-----------------------------------------------------------------------------
8 * jfor - Open-Source XSL-FO to RTF converter - see www.jfor.org
9 *
10 * ====================================================================
11 * jfor Apache-Style Software License.
12 * Copyright (c) 2002 by the jfor project. All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in
23 * the documentation and/or other materials provided with the
24 * distribution.
25 *
26 * 3. The end-user documentation included with the redistribution,
27 * if any, must include the following acknowledgment:
28 * "This product includes software developed
29 * by the jfor project (http://www.jfor.org)."
30 * Alternately, this acknowledgment may appear in the software itself,
31 * if and wherever such third-party acknowledgments normally appear.
32 *
33 * 4. The name "jfor" must not be used to endorse
34 * or promote products derived from this software without prior written
35 * permission. For written permission, please contact info@jfor.org.
36 *
37 * 5. Products derived from this software may not be called "jfor",
38 * nor may "jfor" appear in their name, without prior written
39 * permission of info@jfor.org.
40 *
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 * DISCLAIMED. IN NO EVENT SHALL THE JFOR PROJECT OR ITS CONTRIBUTORS BE
45 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
48 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
49 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
50 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
51 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 * ====================================================================
53 * Contributor(s):
54 -----------------------------------------------------------------------------*/
55
56 /** Used to add extra table rows after a row that contains a nested table:
57 * <li> created by RtfTableRow before generating RTF code
58 * <li> an RtfTableCell that contains a nested table can ask this to put
59 * some of its children in extra rows that after the current row
60 * <li> once RtfTableRow is done rendering its children, it renders this,
61 * causing extra rows to be generated, with content that can come
62 * from several RtfTableCells
63 *
64 * @see org.jfor.jfor.rtflib.testdoc.NestedTable
65 * @author Bertrand Delacretaz bdelacretaz@codeconsult.ch
66 */
67
68 //------------------------------------------------------------------------------
69 // $Id: RtfExtraRowSet.java,v 1.5 2002/08/12 09:40:03 bdelacretaz Exp $
70 // $Log: RtfExtraRowSet.java,v $
71 // Revision 1.5 2002/08/12 09:40:03 bdelacretaz
72 // V0.7.1dev-e, contributions from Boris Poudérous for number-columns-spanned
73 // and vertical merging of tables cells.
74 //
75 // Revision 1.4 2002/07/12 08:08:31 bdelacretaz
76 // License changed to jfor Apache-style license
77 //
78 // Revision 1.3 2001/12/28 17:08:50 bdelacretaz
79 // V0.5.2 - first integration of Chris Scott's changes
80 //
81 // Revision 1.2 2001/08/31 07:51:01 bdelacretaz
82 // MPL license text added + javadoc class comments corrected
83 //
84 // Revision 1.1 2001/08/29 13:27:51 bdelacretaz
85 // V0.4.1 - base package name changed to org.jfor.jfor
86 //
87 // Revision 1.4 2001/08/27 15:56:02 bdelacretaz
88 // V0.3.10 - isEmpty() added to RtfElement to avoid rendering empty containers
89 //
90 // Revision 1.3 2001/08/20 12:26:01 bdelacretaz
91 // V0.3.1, bug fixes:
92 // -single-space whitespace elements were not handled properly
93 // -missing list items after nested tables disappeared from RTF output
94 // -SectionBuilder now correctly processes fo:page-sequence instead of fo:root
95 //
96 // Revision 1.2 2001/08/16 14:12:12 bdelacretaz
97 // V0.3 - first version with nested tables support
98 //
99 // Revision 1.1 2001/08/16 11:54:49 bdelacretaz
100 // V0.2.3 - fo:external-graphic support added (written by Andreas Putz of skynamics)
101 //
102 //------------------------------------------------------------------------------
103
104 public class RtfExtraRowSet extends RtfContainer {
105 // TODO what is idnum?
106 final int DEFAULT_IDNUM = 0;
107
108 /** Parent table context
109 * (added by Boris Poudérous on july 2002 in order to process nested tables)
110 */
111 private ITableColumnsInfo parentITableColumnsInfo = null;
112
113 /** While a top-level RtfTableRow is being rendered, we build a list of
114 * RtfTableCells that must be rendered in extra rows.
115 * This holds a cell with positioning information
116 */
117 private final List m_cells = new LinkedList();
118 private static class PositionedCell
119 implements Comparable {
120 final RtfTableCell cell;
121 final int xOffset;
122 final int rowIndex;
123
124 PositionedCell(RtfTableCell c,int index,int offset) {
125 cell = c;
126 xOffset = offset;
127 rowIndex = index;
128 }
129
130 /** debugging dump */
131 public String toString() {
132 return "PositionedCell: row " + rowIndex + ", offset " + xOffset;
133 }
134
135 /** cells need to be sorted by row index and then by x offset */
136 public int compareTo(Object o) {
137 int result = 0;
138 if(o==null) {
139 result = 1;
140 } else if( !(o instanceof PositionedCell)) {
141 result = 1;
142 } else {
143 final PositionedCell pc = (PositionedCell)o;
144 if(this.rowIndex < pc.rowIndex) {
145 result = -1;
146 } else if(this.rowIndex > pc.rowIndex) {
147 result = 1;
148 } else if(this.xOffset < pc.xOffset) {
149 result = -1;
150 } else if(this.xOffset > pc.xOffset) {
151 result = 1;
152 }
153 }
154
155 return result;
156 }
157
158 public boolean equals(Object o) {
159 return o!=null && this.compareTo(o) == 0;
160 }
161 }
162
163 /** our maximum row index */
164 private int m_maxRowIndex;
165
166 /** an RtfExtraRowSet has no parent, it is only used temporary during
167 * generation of RTF for an RtfTableRow
168 */
169 RtfExtraRowSet(Writer w)
170 throws IOException {
171 super(null,w);
172 }
173
174 /** Add all cells of given Table to this set for later rendering in extra rows
175 * @return index of extra row to use for elements that follow this table in the same cell
176 * @param rowIndex index of first extra row to create to hold cells of tbl
177 * @param xOffset horizontal position of left edge of first column of tbl
178 */
179 int addTable(RtfTable tbl,int rowIndex,int xOffset) {
180 // process all rows of the table
181 for(Iterator it = tbl.getChildren().iterator(); it.hasNext(); ) {
182 final RtfElement e = (RtfElement)it.next();
183 if(e instanceof RtfTableRow) {
184 addRow((RtfTableRow)e,rowIndex,xOffset);
185 rowIndex++;
186 m_maxRowIndex = Math.max(rowIndex,m_maxRowIndex);
187 }
188 }
189 return rowIndex;
190 }
191
192 /** add all cells of given row to this set */
193 private void addRow(RtfTableRow row,int rowIndex,int xOffset) {
194 for(Iterator it = row.getChildren().iterator(); it.hasNext(); ) {
195 final RtfElement e = (RtfElement)it.next();
196 if(e instanceof RtfTableCell) {
197 final RtfTableCell c = (RtfTableCell)e;
198 m_cells.add(new PositionedCell(c,rowIndex,xOffset));
199 xOffset += c.getCellWidth();
200 }
201 }
202 }
203
204 /** create an extra cell to hold content that comes after a nested table in a cell
205 * Modified by Boris Poudérous in order to permit the extra cell to have the attributes of its parent cell
206 */
207 RtfTableCell createExtraCell(int rowIndex,int xOffset,int cellWidth,RtfAttributes parentCellAttributes)
208 throws IOException {
209 final RtfTableCell c = new RtfTableCell(null,m_writer,cellWidth,parentCellAttributes,DEFAULT_IDNUM);
210 m_cells.add(new PositionedCell(c,rowIndex,xOffset));
211 return c;
212 }
213
214 /** render extra RtfTableRows containing all the extra RtfTableCells that we contain */
215 protected void writeRtfContent() throws IOException {
216 // sort cells by rowIndex and xOffset
217 Collections.sort(m_cells);
218
219 // process all extra cells by rendering them into extra rows
220 List rowCells = null;
221 int rowIndex = -1;
222 for(Iterator it = m_cells.iterator(); it.hasNext(); ) {
223 final PositionedCell pc = (PositionedCell)it.next();
224 if(pc.rowIndex != rowIndex) {
225 // starting a new row, render previous one
226 if(rowCells!=null) writeRow(rowCells);
227 rowIndex = pc.rowIndex;
228 rowCells = new LinkedList();
229 }
230 rowCells.add(pc);
231 }
232
233 // render last row
234 if(rowCells!=null) writeRow(rowCells);
235 }
236
237 /** write one RtfTableRow containing given PositionedCells */
238 private void writeRow(List cells)
239 throws IOException {
240 if(allCellsEmpty(cells)) return;
241
242 final RtfTableRow row = new RtfTableRow(null,m_writer,DEFAULT_IDNUM);
243 int cellIndex = 0;
244
245 // Get the context of the table that holds the nested table
246 ITableColumnsInfo parentITableColumnsInfo = getParentITableColumnsInfo();
247 parentITableColumnsInfo.selectFirstColumn();
248
249 // X offset of the current empty cell to add
250 float xOffset = 0;
251 float xOffsetOfLastPositionedCell = 0;
252
253 for(Iterator it = cells.iterator(); it.hasNext(); ) {
254 final PositionedCell pc = (PositionedCell)it.next();
255
256 // if first cell is not at offset 0, add placeholder cell
257 // TODO should be merged with the cell that is above it
258 if(cellIndex==0 && pc.xOffset > 0) {
259 /**
260 * Added by Boris Poudérous
261 */
262 // Add empty cells merged vertically with the cells above and with the same widths
263 // (BEFORE the cell that contains the nested table)
264 for (int i = 0; (xOffset < pc.xOffset) && (i < parentITableColumnsInfo.getNumberOfColumns()); i++) {
265 // Get the width of the cell above
266 xOffset += parentITableColumnsInfo.getColumnWidth();
267 // Create the empty cell merged vertically
268 row.newTableCellMergedVertically((int)parentITableColumnsInfo.getColumnWidth(), pc.cell.m_attrib);
269 // Select next column in order to have its width
270 parentITableColumnsInfo.selectNextColumn();
271 }
272 }
273
274 row.addChild(pc.cell);
275 // Line added by Boris Poudérous
276 xOffsetOfLastPositionedCell = pc.xOffset + pc.cell.getCellWidth();
277 cellIndex++;
278 }
279
280 /**
281 * Added by Boris Poudérous
282 */
283 // Add empty cells merged vertically with the cells above AFTER the cell that contains the nested table
284 // The cells added have the same widths than the cells above.
285 if (parentITableColumnsInfo.getColumnIndex() < (parentITableColumnsInfo.getNumberOfColumns() - 1))
286 {
287 parentITableColumnsInfo.selectNextColumn();
288
289 while (parentITableColumnsInfo.getColumnIndex() < parentITableColumnsInfo.getNumberOfColumns())
290 {
291 // Create the empty cell merged vertically
292 // TODO : the new cells after the extra cell don't have its attributes as we did for the previous cells.
293 // => in fact the m_attrib below (last argument) is empty => should be the attributes of the above cells.
294 row.newTableCellMergedVertically((int)parentITableColumnsInfo.getColumnWidth(), m_attrib);
295 // Select next column in order to have its width
296 parentITableColumnsInfo.selectNextColumn();
297 }
298 }
299
300 row.writeRtf();
301 }
302
303 /** true if all cells of given list are empty
304 * @param cells List of PositionedCell objects
305 */
306 private static boolean allCellsEmpty(List cells) {
307 boolean empty = true;
308 for(Iterator it = cells.iterator(); it.hasNext(); ) {
309 final PositionedCell pc = (PositionedCell)it.next();
310 if(pc.cell.containsText()) {
311 empty = false;
312 break;
313 }
314 }
315 return empty;
316 }
317
318 /** As this contains cells from several rows, we say that it's empty
319 * only if we have no cells.
320 * writeRow makes the decision about rendering specific rows
321 */
322 public boolean isEmpty() {
323 return false;
324 }
325
326 /**
327 * @return The table context of the parent table
328 * Added by Boris Poudérous on july 2002 in order to process nested tables
329 */
330 public ITableColumnsInfo getParentITableColumnsInfo()
331 {
332 return this.parentITableColumnsInfo;
333 }
334
335 public void setParentITableColumnsInfo (ITableColumnsInfo parentITableColumnsInfo)
336 {
337 this.parentITableColumnsInfo = parentITableColumnsInfo;
338 }
339 /** - end - */
340 }