001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: RtfTableCell.java 492769 2007-01-04 21:54:38Z jeremias $ */
019:
020: package org.apache.fop.render.rtf.rtflib.rtfdoc;
021:
022: /*
023: * This file is part of the RTF library of the FOP project, which was originally
024: * created by Bertrand Delacretaz <bdelacretaz@codeconsult.ch> and by other
025: * contributors to the jfor project (www.jfor.org), who agreed to donate jfor to
026: * the FOP project.
027: */
028:
029: import java.io.Writer;
030: import java.io.IOException;
031: import java.util.Iterator;
032:
033: /** A cell in an RTF table, container for paragraphs, lists, etc.
034: * @author Bertrand Delacretaz bdelacretaz@codeconsult.ch
035: */
036:
037: public class RtfTableCell extends RtfContainer implements
038: IRtfParagraphContainer, IRtfListContainer, IRtfTableContainer,
039: IRtfExternalGraphicContainer, IRtfTextrunContainer {
040: private RtfParagraph paragraph;
041: private RtfList list;
042: private RtfTable table;
043: private RtfExternalGraphic externalGraphic;
044: private final RtfTableRow parentRow;
045: private boolean setCenter;
046: private boolean setRight;
047: private int id;
048:
049: /** default cell width (in twips ??) */
050: public static final int DEFAULT_CELL_WIDTH = 2000;
051:
052: /** cell width in twips */
053: private int cellWidth;
054: private int widthOffset;
055:
056: /** cell merging has three states */
057: private int vMerge = NO_MERGE;
058: private int hMerge = NO_MERGE;
059:
060: /** cell merging: this cell is not merged */
061: public static final int NO_MERGE = 0;
062:
063: /** cell merging: this cell is the start of a range of merged cells */
064: public static final int MERGE_START = 1;
065:
066: /** cell merging: this cell is part of (but not the start of) a range of merged cells */
067: public static final int MERGE_WITH_PREVIOUS = 2;
068:
069: /** Create an RTF element as a child of given container */
070: RtfTableCell(RtfTableRow parent, Writer w, int cellWidth, int idNum)
071: throws IOException {
072: super (parent, w);
073: id = idNum;
074: parentRow = parent;
075: this .cellWidth = cellWidth;
076: setCenter = false;
077: setRight = false;
078:
079: }
080:
081: /** Create an RTF element as a child of given container */
082: RtfTableCell(RtfTableRow parent, Writer w, int cellWidth,
083: RtfAttributes attrs, int idNum) throws IOException {
084: super (parent, w, attrs);
085: id = idNum;
086: parentRow = parent;
087: this .cellWidth = cellWidth;
088: }
089:
090: /**
091: * Start a new paragraph after closing current current paragraph, list and table
092: * @param attrs attributes of new RtfParagraph
093: * @return new RtfParagraph object
094: * @throws IOException for I/O problems
095: */
096: public RtfParagraph newParagraph(RtfAttributes attrs)
097: throws IOException {
098: closeAll();
099:
100: // in tables, RtfParagraph must have the intbl attribute
101: if (attrs == null) {
102: attrs = new RtfAttributes();
103: }
104:
105: attrs.set("intbl");
106:
107: paragraph = new RtfParagraph(this , writer, attrs);
108:
109: if (paragraph.attrib.isSet("qc")) {
110: setCenter = true;
111: attrs.set("qc");
112: } else if (paragraph.attrib.isSet("qr")) {
113: setRight = true;
114: attrs.set("qr");
115: } else {
116: attrs.set("ql");
117: }
118: attrs.set("intbl");
119:
120: //lines modified by Chris Scott, Westinghouse
121: return paragraph;
122: }
123:
124: /**
125: * Start a new external graphic after closing current paragraph, list and table
126: * @throws IOException for I/O problems
127: * @return new RtfExternalGraphic object
128: */
129: public RtfExternalGraphic newImage() throws IOException {
130: closeAll();
131: externalGraphic = new RtfExternalGraphic(this , writer);
132: return externalGraphic;
133: }
134:
135: /**
136: * Start a new paragraph with default attributes after closing current
137: * paragraph, list and table
138: * @return new RtfParagraph object
139: * @throws IOException for I/O problems
140: */
141: public RtfParagraph newParagraph() throws IOException {
142: return newParagraph(null);
143: }
144:
145: /**
146: * Start a new list after closing current paragraph, list and table
147: * @param attrib attributes for new RtfList
148: * @return new RtfList object
149: * @throws IOException for I/O problems
150: */
151: public RtfList newList(RtfAttributes attrib) throws IOException {
152: closeAll();
153: list = new RtfList(this , writer, attrib);
154: return list;
155: }
156:
157: /**
158: * Start a new nested table after closing current paragraph, list and table
159: * @param tc table column info for new RtfTable
160: * @return new RtfTable object
161: * @throws IOException for I/O problems
162: */
163: public RtfTable newTable(ITableColumnsInfo tc) throws IOException {
164: closeAll();
165: table = new RtfTable(this , writer, tc);
166: return table;
167: }
168:
169: /**
170: * Start a new nested table after closing current paragraph, list and table
171: * @param attrs attributes of new RtfTable
172: * @param tc table column info for new RtfTable
173: * @return new RtfTable object
174: * @throws IOException for I/O problems
175: */
176: // Modified by Boris Poudérous on 07/22/2002
177: public RtfTable newTable(RtfAttributes attrs, ITableColumnsInfo tc)
178: throws IOException {
179: closeAll();
180: table = new RtfTable(this , writer, attrs, tc); // Added tc Boris Poudérous 07/22/2002
181: return table;
182: }
183:
184: /** used by RtfTableRow to write the <celldef> cell definition control words
185: * @param widthOffset sum of the widths of preceeding cells in same row
186: * @return widthOffset + width of this cell
187: */
188: int writeCellDef(int offset) throws IOException {
189: /*
190: * Don't write \clmgf or \clmrg. Instead add the widths
191: * of all spanned columns and create a single wider cell,
192: * because \clmgf and \clmrg won't work in last row of a
193: * table (Word2000 seems to do the same).
194: * Cause of this, dont't write horizontally merged cells.
195: * They just exist as placeholders in TableContext class,
196: * and are never written to RTF file.
197: */
198: // horizontal cell merge codes
199: if (hMerge == MERGE_WITH_PREVIOUS) {
200: return offset;
201: }
202:
203: newLine();
204: this .widthOffset = offset;
205:
206: // vertical cell merge codes
207: if (vMerge == MERGE_START) {
208: writeControlWord("clvmgf");
209: } else if (vMerge == MERGE_WITH_PREVIOUS) {
210: writeControlWord("clvmrg");
211: }
212:
213: /**
214: * Added by Boris POUDEROUS on 2002/06/26
215: */
216: // Cell background color processing :
217: writeAttributes(attrib, ITableAttributes.CELL_COLOR);
218: /** - end - */
219:
220: writeAttributes(attrib, ITableAttributes.ATTRIB_CELL_PADDING);
221: writeAttributes(attrib, ITableAttributes.CELL_BORDER);
222: writeAttributes(attrib, IBorderAttributes.BORDERS);
223:
224: // determine cell width
225: int iCurrentWidth = this .cellWidth;
226: if (attrib.getValue("number-columns-spanned") != null) {
227: // Get the number of columns spanned
228: int nbMergedCells = ((Integer) attrib
229: .getValue("number-columns-spanned")).intValue();
230:
231: RtfTable tab = getRow().getTable();
232:
233: // Get the context of the current table in order to get the width of each column
234: ITableColumnsInfo tableColumnsInfo = tab
235: .getITableColumnsInfo();
236:
237: tableColumnsInfo.selectFirstColumn();
238:
239: // Reach the column index in table context corresponding to the current column cell
240: // id is the index of the current cell (it begins at 1)
241: // getColumnIndex() is the index of the current column in table context (it begins at 0)
242: // => so we must widthdraw 1 when comparing these two variables.
243: while ((this .id - 1) != tableColumnsInfo.getColumnIndex()) {
244: tableColumnsInfo.selectNextColumn();
245: }
246:
247: // We widthdraw one cell because the first cell is already created
248: // (it's the current cell) !
249: int i = nbMergedCells - 1;
250: while (i > 0) {
251: tableColumnsInfo.selectNextColumn();
252: iCurrentWidth += (int) tableColumnsInfo
253: .getColumnWidth();
254:
255: i--;
256: }
257: }
258: final int xPos = offset + iCurrentWidth;
259:
260: //these lines added by Chris Scott, Westinghouse
261: //some attributes need to be writting before opening block
262: if (setCenter) {
263: writeControlWord("qc");
264: } else if (setRight) {
265: writeControlWord("qr");
266: } else {
267: writeControlWord("ql");
268: }
269: writeAttributes(attrib, ITableAttributes.CELL_VERT_ALIGN);
270:
271: writeControlWord("cellx" + xPos);
272:
273: //TODO Why is this here, right after an alignment command is written (see above)?
274: writeControlWord("ql");
275:
276: return xPos;
277:
278: }
279:
280: /**
281: * Overriden to avoid writing any it's a merged cell.
282: * @throws IOException for I/O problems
283: */
284: protected void writeRtfContent() throws IOException {
285: // Never write horizontally merged cells.
286: if (hMerge == MERGE_WITH_PREVIOUS) {
287: return;
288: }
289:
290: super .writeRtfContent();
291: }
292:
293: /**
294: * Called before writeRtfContent; overriden to avoid writing
295: * any it's a merged cell.
296: * @throws IOException for I/O problems
297: */
298: protected void writeRtfPrefix() throws IOException {
299: // Never write horizontally merged cells.
300: if (hMerge == MERGE_WITH_PREVIOUS) {
301: return;
302: }
303:
304: super .writeRtfPrefix();
305: }
306:
307: /**
308: * The "cell" control word marks the end of a cell
309: * @throws IOException for I/O problems
310: */
311: protected void writeRtfSuffix() throws IOException {
312: // Never write horizontally merged cells.
313: if (hMerge == MERGE_WITH_PREVIOUS) {
314: return;
315: }
316:
317: if (getRow().getTable().isNestedTable()) {
318: //nested table
319: writeControlWordNS("nestcell");
320: writeGroupMark(true);
321: writeControlWord("nonesttables");
322: writeControlWord("par");
323: writeGroupMark(false);
324: } else {
325: // word97 hangs if cell does not contain at least one "par" control word
326: // TODO this is what causes the extra spaces in nested table of test
327: // 004-spacing-in-tables.fo,
328: // but if is not here we generate invalid RTF for word97
329:
330: if (setCenter) {
331: writeControlWord("qc");
332: } else if (setRight) {
333: writeControlWord("qr");
334: } else {
335: RtfElement lastChild = null;
336:
337: if (getChildren().size() > 0) {
338: lastChild = (RtfElement) getChildren().get(
339: getChildren().size() - 1);
340: }
341:
342: if (lastChild != null
343: && lastChild instanceof RtfTextrun) {
344: //Don't write \ql in order to allow for example a right aligned paragraph
345: //in a not right aligned table-cell to write its \qr.
346: } else {
347: writeControlWord("ql");
348: }
349: }
350:
351: if (!containsText()) {
352: writeControlWord("intbl");
353:
354: //R.Marra this create useless paragraph
355: //Seem working into Word97 with the "intbl" only
356: //writeControlWord("par");
357: }
358:
359: writeControlWord("cell");
360: }
361: }
362:
363: //modified by Chris Scott, Westinghouse
364: private void closeCurrentParagraph() throws IOException {
365: if (paragraph != null) {
366: paragraph.close();
367: }
368: }
369:
370: private void closeCurrentList() throws IOException {
371: if (list != null) {
372: list.close();
373: }
374: }
375:
376: private void closeCurrentTable() throws IOException {
377: if (table != null) {
378: table.close();
379: }
380: }
381:
382: private void closeCurrentExternalGraphic() throws IOException {
383: if (externalGraphic != null) {
384: externalGraphic.close();
385: }
386: }
387:
388: private void closeAll() throws IOException {
389: closeCurrentTable();
390: closeCurrentParagraph();
391: closeCurrentList();
392: closeCurrentExternalGraphic();
393: }
394:
395: /**
396: * @param mergeStatus vertical cell merging status to set
397: */
398: public void setVMerge(int mergeStatus) {
399: this .vMerge = mergeStatus;
400: }
401:
402: /**
403: * @return vertical cell merging status
404: */
405: public int getVMerge() {
406: return this .vMerge;
407: }
408:
409: /**
410: * Set horizontal cell merging status
411: * @param mergeStatus mergeStatus to set
412: */
413: public void setHMerge(int mergeStatus) {
414: this .hMerge = mergeStatus;
415: }
416:
417: /**
418: * @return horizontal cell merging status
419: */
420: public int getHMerge() {
421: return this .hMerge;
422: }
423:
424: /** get cell width */
425: int getCellWidth() {
426: return this .cellWidth;
427: }
428:
429: /**
430: * Overridden so that nested tables cause extra rows to be added after the row
431: * that contains this cell
432: * disabled for V0.3 - nested table support is not done yet
433: * @throws IOException for I/O problems
434: */
435: /*
436: protected void writeRtfContent()
437: throws IOException {
438: int extraRowIndex = 0;
439: RtfTableCell extraCell = null;
440:
441: for (Iterator it = getChildren().iterator(); it.hasNext();) {
442: final RtfElement e = (RtfElement)it.next();
443: if (e instanceof RtfTable) {
444: // nested table - render its cells in supplementary rows after current row,
445: // and put the remaining content of this cell in a new cell after nested table
446: // Line added by Boris Poudérous
447: parentRow.getExtraRowSet().setParentITableColumnsInfo(
448: ((RtfTable)this.getParentOfClass(e.getClass())).getITableColumnsInfo());
449: extraRowIndex = parentRow.getExtraRowSet().addTable((RtfTable)e,
450: extraRowIndex, widthOffset);
451: // Boris Poudérous added the passing of the current cell
452: // attributes to the new cells (in order not to have cell without
453: // border for example)
454: extraCell = parentRow.getExtraRowSet().createExtraCell(extraRowIndex,
455: widthOffset, this.getCellWidth(), attrib);
456: extraRowIndex++;
457:
458: } else if (extraCell != null) {
459: // we are after a nested table, add elements to the extra cell created for them
460: extraCell.addChild(e);
461:
462: } else {
463: // before a nested table, normal rendering
464: e.writeRtf();
465: }
466: }
467: }*/
468:
469: /**
470: * A table cell always contains "useful" content, as it is here to take some
471: * space in a row.
472: * Use containsText() to find out if there is really some useful content in the cell.
473: * TODO: containsText could use the original isEmpty implementation?
474: * @return false (always)
475: */
476: public boolean isEmpty() {
477: return false;
478: }
479:
480: /** true if the "par" control word must be written for given RtfParagraph
481: * (which is not the case for the last non-empty paragraph of the cell)
482: */
483: boolean paragraphNeedsPar(RtfParagraph p) {
484: // true if there is at least one non-empty paragraph after p in our children
485: boolean pFound = false;
486: boolean result = false;
487: for (Iterator it = getChildren().iterator(); it.hasNext();) {
488: final Object o = it.next();
489: if (!pFound) {
490: // set pFound when p is found in the list
491: pFound = (o == p);
492: } else {
493: if (o instanceof RtfParagraph) {
494: final RtfParagraph p2 = (RtfParagraph) o;
495: if (!p2.isEmpty()) {
496: // found a non-empty paragraph after p
497: result = true;
498: break;
499: }
500: } else if (o instanceof RtfTable) {
501: break;
502: }
503: }
504: }
505: return result;
506: }
507:
508: /**
509: * Returns the current RtfTextrun object.
510: * Opens a new one if necessary.
511: * @return The RtfTextrun object
512: * @throws IOException Thrown when an IO-problem occurs
513: */
514: public RtfTextrun getTextrun() throws IOException {
515: RtfAttributes attrs = new RtfAttributes();
516:
517: if (!getRow().getTable().isNestedTable()) {
518: attrs.set("intbl");
519: }
520:
521: RtfTextrun textrun = RtfTextrun.getTextrun(this , writer, attrs);
522:
523: //Suppress the very last \par, because the closing \cell applies the
524: //paragraph attributes.
525: textrun.setSuppressLastPar(true);
526:
527: return textrun;
528: }
529:
530: /**
531: * Get the parent row.
532: * @return The parent row.
533: */
534: public RtfTableRow getRow() {
535: RtfElement e = this ;
536: while (e.parent != null) {
537: if (e.parent instanceof RtfTableRow) {
538: return (RtfTableRow) e.parent;
539: }
540:
541: e = e.parent;
542: }
543:
544: return null;
545: }
546: }
|