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.

RtfTextrun.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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. // Java
  20. import java.io.IOException;
  21. import java.io.Writer;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.ListIterator;
  25. import java.util.Stack;
  26. import org.apache.commons.logging.Log;
  27. import org.apache.commons.logging.LogFactory;
  28. /**
  29. * Class which contains a linear text run. It has methods to add attributes,
  30. * text, paragraph breaks....
  31. * @author Peter Herweg, pherweg@web.de
  32. */
  33. public class RtfTextrun extends RtfContainer {
  34. /** Constant for no page break */
  35. public static final int BREAK_NONE = 0;
  36. /** Constant for a normal page break */
  37. public static final int BREAK_PAGE = 1;
  38. /** Constant for a column break */
  39. public static final int BREAK_COLUMN = 2;
  40. /** Constant for a even page break */
  41. public static final int BREAK_EVEN_PAGE = 3;
  42. /** Constant for a odd page break */
  43. public static final int BREAK_ODD_PAGE = 4;
  44. private boolean bSuppressLastPar = false;
  45. private RtfListItem rtfListItem;
  46. /**
  47. * logging instance
  48. */
  49. protected static final Log log = LogFactory.getLog(RtfTextrun.class);
  50. /** Manager for handling space-* property. */
  51. private RtfSpaceManager rtfSpaceManager = new RtfSpaceManager();
  52. /** Class which represents the opening of a RTF group mark.*/
  53. private class RtfOpenGroupMark extends RtfElement {
  54. RtfOpenGroupMark(RtfContainer parent, Writer w, RtfAttributes attr)
  55. throws IOException {
  56. super(parent, w, attr);
  57. }
  58. /**
  59. * @return true if this element would generate no "useful" RTF content
  60. */
  61. public boolean isEmpty() {
  62. return false;
  63. }
  64. /**
  65. * write RTF code of all our children
  66. * @throws IOException for I/O problems
  67. */
  68. protected void writeRtfContent() throws IOException {
  69. writeGroupMark(true);
  70. writeAttributes(getRtfAttributes(), null);
  71. }
  72. }
  73. /** Class which represents the closing of a RTF group mark.*/
  74. private class RtfCloseGroupMark extends RtfElement {
  75. private int breakType = BREAK_NONE;
  76. RtfCloseGroupMark(RtfContainer parent, Writer w, int breakType)
  77. throws IOException {
  78. super(parent, w);
  79. this.breakType = breakType;
  80. }
  81. /**
  82. * @return true if this element would generate no "useful" RTF content
  83. */
  84. public boolean isEmpty() {
  85. return false;
  86. }
  87. /**
  88. * Returns the break type.
  89. * @return the break type (BREAK_* constants)
  90. */
  91. public int getBreakType() {
  92. return breakType;
  93. }
  94. /**
  95. * Write RTF code of all our children.
  96. * @throws IOException for I/O problems
  97. */
  98. protected void writeRtfContent() throws IOException {
  99. writeGroupMark(false);
  100. boolean bHasTableCellParent = this.getParentOfClass(RtfTableCell.class) != null;
  101. //Unknown behavior when a table starts a new section,
  102. //Word may crash
  103. if (breakType != BREAK_NONE) {
  104. if (!bHasTableCellParent) {
  105. writeControlWord("sect");
  106. /* The following modifiers don't seem to appear in the right place */
  107. switch (breakType) {
  108. case BREAK_EVEN_PAGE:
  109. writeControlWord("sbkeven");
  110. break;
  111. case BREAK_ODD_PAGE:
  112. writeControlWord("sbkodd");
  113. break;
  114. case BREAK_COLUMN:
  115. writeControlWord("sbkcol");
  116. break;
  117. default:
  118. writeControlWord("sbkpage");
  119. }
  120. } else {
  121. log.warn("Cannot create break-after for a paragraph inside a table.");
  122. }
  123. }
  124. }
  125. }
  126. /** Class which represents a paragraph break.*/
  127. private class RtfParagraphBreak extends RtfElement {
  128. RtfParagraphBreak(RtfContainer parent, Writer w)
  129. throws IOException {
  130. super(parent, w);
  131. }
  132. /**
  133. * @return true if this element would generate no "useful" RTF content
  134. */
  135. public boolean isEmpty() {
  136. return false;
  137. }
  138. /**
  139. * write RTF code of all our children
  140. * @throws IOException for I/O problems
  141. */
  142. protected void writeRtfContent() throws IOException {
  143. writeControlWord("par");
  144. }
  145. }
  146. /** Create an RTF container as a child of given container */
  147. RtfTextrun(RtfContainer parent, Writer w, RtfAttributes attrs) throws IOException {
  148. super(parent, w, attrs);
  149. }
  150. /**
  151. * Adds instance of <code>OpenGroupMark</code> as a child with attributes.
  152. *
  153. * @param attrs attributes to add
  154. * @throws IOException for I/O problems
  155. */
  156. private void addOpenGroupMark(RtfAttributes attrs) throws IOException {
  157. RtfOpenGroupMark r = new RtfOpenGroupMark(this, writer, attrs);
  158. }
  159. /**
  160. * Adds instance of <code>CloseGroupMark</code> as a child.
  161. *
  162. * @throws IOException for I/O problems
  163. */
  164. private void addCloseGroupMark(int breakType) throws IOException {
  165. RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer, breakType);
  166. }
  167. /**
  168. * Adds instance of <code>CloseGroupMark</code> as a child, but without a break option.
  169. * Inline attributes do not need that for example
  170. *
  171. * @throws IOException for I/O problems
  172. */
  173. private void addCloseGroupMark() throws IOException {
  174. RtfCloseGroupMark r = new RtfCloseGroupMark(this, writer, BREAK_NONE);
  175. }
  176. /**
  177. * Pushes block attributes, notifies all opened blocks about pushing block
  178. * attributes, adds <code>OpenGroupMark</code> as a child.
  179. *
  180. * @param attrs the block attributes to push
  181. * @throws IOException for I/O problems
  182. */
  183. public void pushBlockAttributes(RtfAttributes attrs) throws IOException {
  184. rtfSpaceManager.stopUpdatingSpaceBefore();
  185. RtfSpaceSplitter splitter = rtfSpaceManager.pushRtfSpaceSplitter(attrs);
  186. addOpenGroupMark(splitter.getCommonAttributes());
  187. }
  188. /**
  189. * Pops block attributes, notifies all opened blocks about pushing block
  190. * attributes, adds <code>CloseGroupMark</code> as a child.
  191. * @param breakType the break type
  192. * @throws IOException for I/O problems
  193. */
  194. public void popBlockAttributes(int breakType) throws IOException {
  195. rtfSpaceManager.popRtfSpaceSplitter();
  196. rtfSpaceManager.stopUpdatingSpaceBefore();
  197. addCloseGroupMark(breakType);
  198. }
  199. /**
  200. * Pushes inline attributes.
  201. *
  202. * @param attrs the inline attributes to push
  203. * @throws IOException for I/O problems
  204. */
  205. public void pushInlineAttributes(RtfAttributes attrs) throws IOException {
  206. rtfSpaceManager.pushInlineAttributes(attrs);
  207. addOpenGroupMark(attrs);
  208. }
  209. /**
  210. * Inserts a page number citation.
  211. * @param refId the identifier being referenced
  212. * @throws IOException for I/O problems
  213. */
  214. public void addPageNumberCitation(String refId) throws IOException {
  215. RtfPageNumberCitation r = new RtfPageNumberCitation(this, writer, refId);
  216. }
  217. /**
  218. * Pop inline attributes.
  219. *
  220. * @throws IOException for I/O problems
  221. */
  222. public void popInlineAttributes() throws IOException {
  223. rtfSpaceManager.popInlineAttributes();
  224. addCloseGroupMark();
  225. }
  226. /**
  227. * Add string to children list.
  228. *
  229. * @param s string to add
  230. * @throws IOException for I/O problems
  231. */
  232. public void addString(String s) throws IOException {
  233. if (s.equals("")) {
  234. return;
  235. }
  236. RtfAttributes attrs = rtfSpaceManager.getLastInlineAttribute();
  237. //add RtfSpaceSplitter to inherit accumulated space
  238. rtfSpaceManager.pushRtfSpaceSplitter(attrs);
  239. rtfSpaceManager.setCandidate(attrs);
  240. rtfSpaceManager.popRtfSpaceSplitter();
  241. }
  242. /**
  243. * Inserts a footnote.
  244. *
  245. * @return inserted footnote
  246. * @throws IOException for I/O problems
  247. */
  248. public RtfFootnote addFootnote() throws IOException {
  249. return new RtfFootnote(this, writer);
  250. }
  251. /**
  252. * Inserts paragraph break before all close group marks.
  253. *
  254. * @throws IOException for I/O problems
  255. */
  256. public void addParagraphBreak() throws IOException {
  257. // get copy of children list
  258. List children = getChildren();
  259. Stack tmp = new Stack();
  260. // delete all previous CloseGroupMark
  261. int deletedCloseGroupCount = 0;
  262. ListIterator lit = children.listIterator(children.size());
  263. while (lit.hasPrevious()
  264. && (lit.previous() instanceof RtfCloseGroupMark)) {
  265. tmp.push(Integer.valueOf(((RtfCloseGroupMark)lit.next()).getBreakType()));
  266. lit.remove();
  267. deletedCloseGroupCount++;
  268. }
  269. if (children.size() != 0) {
  270. // add paragraph break and restore all deleted close group marks
  271. setChildren(children);
  272. new RtfParagraphBreak(this, writer);
  273. for (int i = 0; i < deletedCloseGroupCount; i++) {
  274. addCloseGroupMark(((Integer)tmp.pop()).intValue());
  275. }
  276. }
  277. }
  278. /**
  279. * Inserts a leader.
  280. * @param attrs Attributes for the leader
  281. * @throws IOException for I/O problems
  282. */
  283. public void addLeader(RtfAttributes attrs) throws IOException {
  284. new RtfLeader(this, writer, attrs);
  285. }
  286. /**
  287. * Inserts a page number.
  288. * @param attr Attributes for the page number to insert.
  289. * @throws IOException for I/O problems
  290. */
  291. public void addPageNumber(RtfAttributes attr) throws IOException {
  292. RtfPageNumber r = new RtfPageNumber(this, writer, attr);
  293. }
  294. /**
  295. * Inserts a hyperlink.
  296. * @param attr Attributes for the hyperlink to insert.
  297. * @return inserted hyperlink
  298. * @throws IOException for I/O problems
  299. */
  300. public RtfHyperLink addHyperlink(RtfAttributes attr) throws IOException {
  301. return new RtfHyperLink(this, writer, attr);
  302. }
  303. /**
  304. * Inserts a bookmark.
  305. * @param id Id for the inserted bookmark
  306. * @throws IOException for I/O problems
  307. */
  308. public void addBookmark(String id) throws IOException {
  309. if (id != "") {
  310. // if id is not empty, add boormark
  311. new RtfBookmark(this, writer, id);
  312. }
  313. }
  314. /**
  315. * Inserts an image.
  316. * @return inserted image
  317. * @throws IOException for I/O problems
  318. */
  319. public RtfExternalGraphic newImage() throws IOException {
  320. return new RtfExternalGraphic(this, writer);
  321. }
  322. /**
  323. * Adds a new RtfTextrun to the given container if necessary, and returns it.
  324. * @param container RtfContainer, which is the parent of the returned RtfTextrun
  325. * @param writer Writer of the given RtfContainer
  326. * @param attrs RtfAttributes which are to write at the beginning of the RtfTextrun
  327. * @return new or existing RtfTextrun object.
  328. * @throws IOException for I/O problems
  329. */
  330. public static RtfTextrun getTextrun(RtfContainer container, Writer writer, RtfAttributes attrs)
  331. throws IOException {
  332. List list = container.getChildren();
  333. if (list.size() == 0) {
  334. //add a new RtfTextrun
  335. RtfTextrun textrun = new RtfTextrun(container, writer, attrs);
  336. list.add(textrun);
  337. return textrun;
  338. }
  339. Object obj = list.get(list.size() - 1);
  340. if (obj instanceof RtfTextrun) {
  341. //if the last child is a RtfTextrun, return it
  342. return (RtfTextrun) obj;
  343. }
  344. //add a new RtfTextrun as the last child
  345. RtfTextrun textrun = new RtfTextrun(container, writer, attrs);
  346. list.add(textrun);
  347. return textrun;
  348. }
  349. /**
  350. * specify, if the last paragraph control word (\par) should be suppressed.
  351. * @param bSuppress true, if the last \par should be suppressed
  352. */
  353. public void setSuppressLastPar(boolean bSuppress) {
  354. bSuppressLastPar = bSuppress;
  355. }
  356. /**
  357. * write RTF code of all our children
  358. * @throws IOException for I/O problems
  359. */
  360. protected void writeRtfContent() throws IOException {
  361. /**
  362. *TODO: The textrun's children are iterated twice:
  363. * 1. To determine the last RtfParagraphBreak
  364. * 2. To write the children
  365. * Maybe this can be done more efficient.
  366. */
  367. boolean bHasTableCellParent
  368. = this.getParentOfClass(RtfTableCell.class) != null;
  369. RtfAttributes attrBlockLevel = new RtfAttributes();
  370. //determine, if this RtfTextrun is the last child of its parent
  371. boolean bLast = false;
  372. for (Iterator it = parent.getChildren().iterator(); it.hasNext();) {
  373. if (it.next() == this) {
  374. bLast = !it.hasNext();
  375. break;
  376. }
  377. }
  378. //get last RtfParagraphBreak, which is not followed by any visible child
  379. RtfParagraphBreak lastParagraphBreak = null;
  380. if (bLast) {
  381. for (Iterator it = getChildren().iterator(); it.hasNext();) {
  382. final RtfElement e = (RtfElement)it.next();
  383. if (e instanceof RtfParagraphBreak) {
  384. lastParagraphBreak = (RtfParagraphBreak)e;
  385. } else {
  386. if (!(e instanceof RtfOpenGroupMark)
  387. && !(e instanceof RtfCloseGroupMark)
  388. && e.isEmpty()) {
  389. lastParagraphBreak = null;
  390. }
  391. }
  392. }
  393. }
  394. //may contain for example \intbl
  395. writeAttributes(attrib, null);
  396. if (rtfListItem != null) {
  397. rtfListItem.getRtfListStyle().writeParagraphPrefix(this);
  398. }
  399. //write all children
  400. boolean bPrevPar = false;
  401. boolean bFirst = true;
  402. for (Iterator it = getChildren().iterator(); it.hasNext();) {
  403. final RtfElement e = (RtfElement)it.next();
  404. final boolean bRtfParagraphBreak = (e instanceof RtfParagraphBreak);
  405. if (bHasTableCellParent) {
  406. attrBlockLevel.set(e.getRtfAttributes());
  407. }
  408. /**
  409. * -Write RtfParagraphBreak only, if the previous visible child
  410. * was't also a RtfParagraphBreak.
  411. * -Write RtfParagraphBreak only, if it is not the first visible
  412. * child.
  413. * -If the RtfTextrun is the last child of its parent, write a
  414. * RtfParagraphBreak only, if it is not the last child.
  415. */
  416. boolean bHide = false;
  417. bHide = bRtfParagraphBreak;
  418. bHide = bHide
  419. && (bPrevPar
  420. || bFirst
  421. || (bSuppressLastPar && bLast && lastParagraphBreak != null
  422. && e == lastParagraphBreak));
  423. if (!bHide) {
  424. newLine();
  425. e.writeRtf();
  426. if (rtfListItem != null && e instanceof RtfParagraphBreak) {
  427. rtfListItem.getRtfListStyle().writeParagraphPrefix(this);
  428. }
  429. }
  430. if (e instanceof RtfParagraphBreak) {
  431. bPrevPar = true;
  432. } else if (e instanceof RtfCloseGroupMark) {
  433. //do nothing
  434. } else if (e instanceof RtfOpenGroupMark) {
  435. //do nothing
  436. } else {
  437. bPrevPar = bPrevPar && e.isEmpty();
  438. bFirst = bFirst && e.isEmpty();
  439. }
  440. } //for (Iterator it = ...)
  441. //
  442. if (bHasTableCellParent) {
  443. writeAttributes(attrBlockLevel, null);
  444. }
  445. }
  446. /**
  447. * Set the parent list-item of the textrun.
  448. *
  449. * @param listItem parent list-item of the textrun
  450. */
  451. public void setRtfListItem(RtfListItem listItem) {
  452. rtfListItem = listItem;
  453. }
  454. /**
  455. * Gets the parent list-item of the textrun.
  456. *
  457. * @return parent list-item of the textrun
  458. */
  459. public RtfListItem getRtfListItem() {
  460. return rtfListItem;
  461. }
  462. }