You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RtfTableCell.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.rtf.rtflib.rtfdoc;
  19. /*
  20. * This file is part of the RTF library of the FOP project, which was originally
  21. * created by Bertrand Delacretaz <bdelacretaz@codeconsult.ch> and by other
  22. * contributors to the jfor project (www.jfor.org), who agreed to donate jfor to
  23. * the FOP project.
  24. */
  25. import java.io.IOException;
  26. import java.io.Writer;
  27. import java.util.Iterator;
  28. /** A cell in an RTF table, container for paragraphs, lists, etc.
  29. * @author Bertrand Delacretaz bdelacretaz@codeconsult.ch
  30. */
  31. public class RtfTableCell
  32. extends RtfContainer
  33. implements IRtfParagraphContainer, IRtfListContainer, IRtfTableContainer,
  34. IRtfExternalGraphicContainer, IRtfTextrunContainer {
  35. private RtfParagraph paragraph;
  36. private RtfList list;
  37. private RtfTable table;
  38. private RtfExternalGraphic externalGraphic;
  39. private final RtfTableRow parentRow;
  40. private boolean setCenter;
  41. private boolean setRight;
  42. private int id;
  43. /** default cell width (in twips ??) */
  44. public static final int DEFAULT_CELL_WIDTH = 2000;
  45. /** cell width in twips */
  46. private int cellWidth;
  47. private int widthOffset;
  48. /** cell merging has three states */
  49. private int vMerge = NO_MERGE;
  50. private int hMerge = NO_MERGE;
  51. /** cell merging: this cell is not merged */
  52. public static final int NO_MERGE = 0;
  53. /** cell merging: this cell is the start of a range of merged cells */
  54. public static final int MERGE_START = 1;
  55. /** cell merging: this cell is part of (but not the start of) a range of merged cells */
  56. public static final int MERGE_WITH_PREVIOUS = 2;
  57. /** Create an RTF element as a child of given container */
  58. RtfTableCell(RtfTableRow parent, Writer w, int cellWidth, int idNum) throws IOException {
  59. super(parent, w);
  60. id = idNum;
  61. parentRow = parent;
  62. this.cellWidth = cellWidth;
  63. setCenter = false;
  64. setRight = false;
  65. }
  66. /** Create an RTF element as a child of given container */
  67. RtfTableCell(RtfTableRow parent, Writer w, int cellWidth, RtfAttributes attrs,
  68. int idNum) throws IOException {
  69. super(parent, w, attrs);
  70. id = idNum;
  71. parentRow = parent;
  72. this.cellWidth = cellWidth;
  73. }
  74. /**
  75. * Start a new paragraph after closing current current paragraph, list and table
  76. * @param attrs attributes of new RtfParagraph
  77. * @return new RtfParagraph object
  78. * @throws IOException for I/O problems
  79. */
  80. public RtfParagraph newParagraph(RtfAttributes attrs) throws IOException {
  81. closeAll();
  82. // in tables, RtfParagraph must have the intbl attribute
  83. if (attrs == null) {
  84. attrs = new RtfAttributes();
  85. }
  86. attrs.set("intbl");
  87. paragraph = new RtfParagraph(this, writer, attrs);
  88. if (paragraph.attrib.isSet("qc")) {
  89. setCenter = true;
  90. attrs.set("qc");
  91. } else if (paragraph.attrib.isSet("qr")) {
  92. setRight = true;
  93. attrs.set("qr");
  94. } else {
  95. attrs.set("ql");
  96. }
  97. attrs.set("intbl");
  98. //lines modified by Chris Scott, Westinghouse
  99. return paragraph;
  100. }
  101. /**
  102. * Start a new external graphic after closing current paragraph, list and table
  103. * @throws IOException for I/O problems
  104. * @return new RtfExternalGraphic object
  105. */
  106. public RtfExternalGraphic newImage() throws IOException {
  107. closeAll();
  108. externalGraphic = new RtfExternalGraphic(this, writer);
  109. return externalGraphic;
  110. }
  111. /**
  112. * Start a new paragraph with default attributes after closing current
  113. * paragraph, list and table
  114. * @return new RtfParagraph object
  115. * @throws IOException for I/O problems
  116. */
  117. public RtfParagraph newParagraph() throws IOException {
  118. return newParagraph(null);
  119. }
  120. /**
  121. * Start a new list after closing current paragraph, list and table
  122. * @param attrib attributes for new RtfList
  123. * @return new RtfList object
  124. * @throws IOException for I/O problems
  125. */
  126. public RtfList newList(RtfAttributes attrib) throws IOException {
  127. closeAll();
  128. list = new RtfList(this, writer, attrib);
  129. return list;
  130. }
  131. /**
  132. * Start a new nested table after closing current paragraph, list and table
  133. * @param tc table column info for new RtfTable
  134. * @return new RtfTable object
  135. * @throws IOException for I/O problems
  136. */
  137. public RtfTable newTable(ITableColumnsInfo tc) throws IOException {
  138. closeAll();
  139. table = new RtfTable(this, writer, tc);
  140. return table;
  141. }
  142. /**
  143. * Start a new nested table after closing current paragraph, list and table
  144. * @param attrs attributes of new RtfTable
  145. * @param tc table column info for new RtfTable
  146. * @return new RtfTable object
  147. * @throws IOException for I/O problems
  148. */
  149. // Modified by Boris Poudérous on 07/22/2002
  150. public RtfTable newTable(RtfAttributes attrs, ITableColumnsInfo tc) throws IOException {
  151. closeAll();
  152. table = new RtfTable(this, writer, attrs, tc); // Added tc Boris Poudérous 07/22/2002
  153. return table;
  154. }
  155. /** used by RtfTableRow to write the <celldef> cell definition control words
  156. * @param widthOffset sum of the widths of preceeding cells in same row
  157. * @return widthOffset + width of this cell
  158. */
  159. int writeCellDef(int offset) throws IOException {
  160. /*
  161. * Don't write \clmgf or \clmrg. Instead add the widths
  162. * of all spanned columns and create a single wider cell,
  163. * because \clmgf and \clmrg won't work in last row of a
  164. * table (Word2000 seems to do the same).
  165. * Cause of this, dont't write horizontally merged cells.
  166. * They just exist as placeholders in TableContext class,
  167. * and are never written to RTF file.
  168. */
  169. // horizontal cell merge codes
  170. if (hMerge == MERGE_WITH_PREVIOUS) {
  171. return offset;
  172. }
  173. newLine();
  174. this.widthOffset = offset;
  175. // vertical cell merge codes
  176. if (vMerge == MERGE_START) {
  177. writeControlWord("clvmgf");
  178. } else if (vMerge == MERGE_WITH_PREVIOUS) {
  179. writeControlWord("clvmrg");
  180. }
  181. /**
  182. * Added by Boris POUDEROUS on 2002/06/26
  183. */
  184. // Cell background color processing :
  185. writeAttributes (attrib, ITableAttributes.CELL_COLOR);
  186. /** - end - */
  187. writeAttributes (attrib, ITableAttributes.ATTRIB_CELL_PADDING);
  188. writeAttributes (attrib, ITableAttributes.CELL_BORDER);
  189. writeAttributes (attrib, IBorderAttributes.BORDERS);
  190. // determine cell width
  191. int iCurrentWidth = this.cellWidth;
  192. if (attrib.getValue("number-columns-spanned") != null) {
  193. // Get the number of columns spanned
  194. int nbMergedCells = ((Integer)attrib.getValue("number-columns-spanned")).intValue();
  195. RtfTable tab = getRow().getTable();
  196. // Get the context of the current table in order to get the width of each column
  197. ITableColumnsInfo tableColumnsInfo
  198. = tab.getITableColumnsInfo();
  199. tableColumnsInfo.selectFirstColumn();
  200. // Reach the column index in table context corresponding to the current column cell
  201. // id is the index of the current cell (it begins at 1)
  202. // getColumnIndex() is the index of the current column in table context (it begins at 0)
  203. // => so we must withdraw 1 when comparing these two variables.
  204. while ((this.id - 1) != tableColumnsInfo.getColumnIndex()) {
  205. tableColumnsInfo.selectNextColumn();
  206. }
  207. // We withdraw one cell because the first cell is already created
  208. // (it's the current cell) !
  209. int i = nbMergedCells - 1;
  210. while (i > 0) {
  211. tableColumnsInfo.selectNextColumn();
  212. iCurrentWidth += (int)tableColumnsInfo.getColumnWidth();
  213. i--;
  214. }
  215. }
  216. final int xPos = offset + iCurrentWidth;
  217. //these lines added by Chris Scott, Westinghouse
  218. //some attributes need to be written before opening block
  219. if (setCenter) {
  220. writeControlWord("trqc");
  221. } else if (setRight) {
  222. writeControlWord("trqr");
  223. } else {
  224. writeControlWord("trql");
  225. }
  226. writeAttributes (attrib, ITableAttributes.CELL_VERT_ALIGN);
  227. writeControlWord("cellx" + xPos);
  228. return xPos;
  229. }
  230. /**
  231. * Overriden to avoid writing any it's a merged cell.
  232. * @throws IOException for I/O problems
  233. */
  234. protected void writeRtfContent() throws IOException {
  235. // Never write horizontally merged cells.
  236. if (hMerge == MERGE_WITH_PREVIOUS) {
  237. return;
  238. }
  239. super.writeRtfContent();
  240. }
  241. /**
  242. * Called before writeRtfContent; overriden to avoid writing
  243. * any it's a merged cell.
  244. * @throws IOException for I/O problems
  245. */
  246. protected void writeRtfPrefix() throws IOException {
  247. // Never write horizontally merged cells.
  248. if (hMerge == MERGE_WITH_PREVIOUS) {
  249. return;
  250. }
  251. super.writeRtfPrefix();
  252. }
  253. /**
  254. * The "cell" control word marks the end of a cell
  255. * @throws IOException for I/O problems
  256. */
  257. protected void writeRtfSuffix() throws IOException {
  258. // Never write horizontally merged cells.
  259. if (hMerge == MERGE_WITH_PREVIOUS) {
  260. return;
  261. }
  262. if (getRow().getTable().isNestedTable()) {
  263. //nested table
  264. writeControlWordNS("nestcell");
  265. writeGroupMark(true);
  266. writeControlWord("nonesttables");
  267. writeControlWord("par");
  268. writeGroupMark(false);
  269. } else {
  270. // word97 hangs if cell does not contain at least one "par" control word
  271. // TODO this is what causes the extra spaces in nested table of test
  272. // 004-spacing-in-tables.fo,
  273. // but if is not here we generate invalid RTF for word97
  274. if (setCenter) {
  275. writeControlWord("qc");
  276. } else if (setRight) {
  277. writeControlWord("qr");
  278. } else {
  279. RtfElement lastChild = null;
  280. if (getChildren().size() > 0) {
  281. lastChild = (RtfElement) getChildren().get(getChildren().size() - 1);
  282. }
  283. if (lastChild != null
  284. && lastChild instanceof RtfTextrun) {
  285. //Don't write \ql in order to allow for example a right aligned paragraph
  286. //in a not right aligned table-cell to write its \qr.
  287. } else {
  288. writeControlWord("ql");
  289. }
  290. }
  291. if (!containsText()) {
  292. writeControlWord("intbl");
  293. //R.Marra this create useless paragraph
  294. //Seem working into Word97 with the "intbl" only
  295. //writeControlWord("par");
  296. }
  297. writeControlWord("cell");
  298. }
  299. }
  300. //modified by Chris Scott, Westinghouse
  301. private void closeCurrentParagraph() throws IOException {
  302. if (paragraph != null) {
  303. paragraph.close();
  304. }
  305. }
  306. private void closeCurrentList() throws IOException {
  307. if (list != null) {
  308. list.close();
  309. }
  310. }
  311. private void closeCurrentTable() throws IOException {
  312. if (table != null) {
  313. table.close();
  314. }
  315. }
  316. private void closeCurrentExternalGraphic() throws IOException {
  317. if (externalGraphic != null) {
  318. externalGraphic.close();
  319. }
  320. }
  321. private void closeAll()
  322. throws IOException {
  323. closeCurrentTable();
  324. closeCurrentParagraph();
  325. closeCurrentList();
  326. closeCurrentExternalGraphic();
  327. }
  328. /**
  329. * @param mergeStatus vertical cell merging status to set
  330. */
  331. public void setVMerge(int mergeStatus) { this.vMerge = mergeStatus; }
  332. /**
  333. * @return vertical cell merging status
  334. */
  335. public int getVMerge() { return this.vMerge; }
  336. /**
  337. * Set horizontal cell merging status
  338. * @param mergeStatus mergeStatus to set
  339. */
  340. public void setHMerge(int mergeStatus) {
  341. this.hMerge = mergeStatus;
  342. }
  343. /**
  344. * @return horizontal cell merging status
  345. */
  346. public int getHMerge() {
  347. return this.hMerge;
  348. }
  349. /** get cell width */
  350. int getCellWidth() { return this.cellWidth; }
  351. /**
  352. * Overridden so that nested tables cause extra rows to be added after the row
  353. * that contains this cell
  354. * disabled for V0.3 - nested table support is not done yet
  355. * @throws IOException for I/O problems
  356. */
  357. /*
  358. protected void writeRtfContent()
  359. throws IOException {
  360. int extraRowIndex = 0;
  361. RtfTableCell extraCell = null;
  362. for (Iterator it = getChildren().iterator(); it.hasNext();) {
  363. final RtfElement e = (RtfElement)it.next();
  364. if (e instanceof RtfTable) {
  365. // nested table - render its cells in supplementary rows after current row,
  366. // and put the remaining content of this cell in a new cell after nested table
  367. // Line added by Boris Poudérous
  368. parentRow.getExtraRowSet().setParentITableColumnsInfo(
  369. ((RtfTable)this.getParentOfClass(e.getClass())).getITableColumnsInfo());
  370. extraRowIndex = parentRow.getExtraRowSet().addTable((RtfTable)e,
  371. extraRowIndex, widthOffset);
  372. // Boris Poudérous added the passing of the current cell
  373. // attributes to the new cells (in order not to have cell without
  374. // border for example)
  375. extraCell = parentRow.getExtraRowSet().createExtraCell(extraRowIndex,
  376. widthOffset, this.getCellWidth(), attrib);
  377. extraRowIndex++;
  378. } else if (extraCell != null) {
  379. // we are after a nested table, add elements to the extra cell created for them
  380. extraCell.addChild(e);
  381. } else {
  382. // before a nested table, normal rendering
  383. e.writeRtf();
  384. }
  385. }
  386. }*/
  387. /**
  388. * A table cell always contains "useful" content, as it is here to take some
  389. * space in a row.
  390. * Use containsText() to find out if there is really some useful content in the cell.
  391. * TODO: containsText could use the original isEmpty implementation?
  392. * @return false (always)
  393. */
  394. public boolean isEmpty() {
  395. return false;
  396. }
  397. /** true if the "par" control word must be written for given RtfParagraph
  398. * (which is not the case for the last non-empty paragraph of the cell)
  399. */
  400. boolean paragraphNeedsPar(RtfParagraph p) {
  401. // true if there is at least one non-empty paragraph after p in our children
  402. boolean pFound = false;
  403. boolean result = false;
  404. for (Iterator it = getChildren().iterator(); it.hasNext();) {
  405. final Object o = it.next();
  406. if (!pFound) {
  407. // set pFound when p is found in the list
  408. pFound = (o == p);
  409. } else {
  410. if (o instanceof RtfParagraph) {
  411. final RtfParagraph p2 = (RtfParagraph)o;
  412. if (!p2.isEmpty()) {
  413. // found a non-empty paragraph after p
  414. result = true;
  415. break;
  416. }
  417. } else if (o instanceof RtfTable) {
  418. break;
  419. }
  420. }
  421. }
  422. return result;
  423. }
  424. /**
  425. * Returns the current RtfTextrun object.
  426. * Opens a new one if necessary.
  427. * @return The RtfTextrun object
  428. * @throws IOException Thrown when an IO-problem occurs
  429. */
  430. public RtfTextrun getTextrun() throws IOException {
  431. RtfAttributes attrs = new RtfAttributes();
  432. if (!getRow().getTable().isNestedTable()) {
  433. attrs.set("intbl");
  434. }
  435. RtfTextrun textrun = RtfTextrun.getTextrun(this, writer, attrs);
  436. //Suppress the very last \par, because the closing \cell applies the
  437. //paragraph attributes.
  438. textrun.setSuppressLastPar(true);
  439. return textrun;
  440. }
  441. /**
  442. * Get the parent row.
  443. * @return The parent row.
  444. */
  445. public RtfTableRow getRow() {
  446. RtfElement e = this;
  447. while (e.parent != null) {
  448. if (e.parent instanceof RtfTableRow) {
  449. return (RtfTableRow) e.parent;
  450. }
  451. e = e.parent;
  452. }
  453. return null;
  454. }
  455. }