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

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