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 19KB

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