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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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 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. RtfString r = new RtfString(this, writer, s);
  241. rtfSpaceManager.popRtfSpaceSplitter();
  242. }
  243. /**
  244. * Inserts a footnote.
  245. *
  246. * @return inserted footnote
  247. * @throws IOException for I/O problems
  248. */
  249. public RtfFootnote addFootnote() throws IOException {
  250. return new RtfFootnote(this, writer);
  251. }
  252. /**
  253. * Inserts paragraph break before all close group marks.
  254. *
  255. * @throws IOException for I/O problems
  256. */
  257. public void addParagraphBreak() throws IOException {
  258. // get copy of children list
  259. List children = getChildren();
  260. Stack tmp = new Stack();
  261. // delete all previous CloseGroupMark
  262. int deletedCloseGroupCount = 0;
  263. ListIterator lit = children.listIterator(children.size());
  264. while (lit.hasPrevious()
  265. && (lit.previous() instanceof RtfCloseGroupMark)) {
  266. tmp.push(new Integer(((RtfCloseGroupMark)lit.next()).getBreakType()));
  267. lit.remove();
  268. deletedCloseGroupCount++;
  269. }
  270. if (children.size() != 0) {
  271. // add paragraph break and restore all deleted close group marks
  272. setChildren(children);
  273. new RtfParagraphBreak(this, writer);
  274. for (int i = 0; i < deletedCloseGroupCount; i++) {
  275. addCloseGroupMark(((Integer)tmp.pop()).intValue());
  276. }
  277. }
  278. }
  279. /**
  280. * Inserts a leader.
  281. * @param attrs Attributes for the leader
  282. * @throws IOException for I/O problems
  283. */
  284. public void addLeader(RtfAttributes attrs) throws IOException {
  285. new RtfLeader(this, writer, attrs);
  286. }
  287. /**
  288. * Inserts a page number.
  289. * @param attr Attributes for the page number to insert.
  290. * @throws IOException for I/O problems
  291. */
  292. public void addPageNumber(RtfAttributes attr) throws IOException {
  293. RtfPageNumber r = new RtfPageNumber(this, writer, attr);
  294. }
  295. /**
  296. * Inserts a hyperlink.
  297. * @param attr Attributes for the hyperlink to insert.
  298. * @return inserted hyperlink
  299. * @throws IOException for I/O problems
  300. */
  301. public RtfHyperLink addHyperlink(RtfAttributes attr) throws IOException {
  302. return new RtfHyperLink(this, writer, attr);
  303. }
  304. /**
  305. * Inserts a bookmark.
  306. * @param id Id for the inserted bookmark
  307. * @throws IOException for I/O problems
  308. */
  309. public void addBookmark(String id) throws IOException {
  310. if (id != "") {
  311. // if id is not empty, add boormark
  312. new RtfBookmark(this, writer, id);
  313. }
  314. }
  315. /**
  316. * Inserts an image.
  317. * @return inserted image
  318. * @throws IOException for I/O problems
  319. */
  320. public RtfExternalGraphic newImage() throws IOException {
  321. return new RtfExternalGraphic(this, writer);
  322. }
  323. /**
  324. * Adds a new RtfTextrun to the given container if necessary, and returns it.
  325. * @param container RtfContainer, which is the parent of the returned RtfTextrun
  326. * @param writer Writer of the given RtfContainer
  327. * @param attrs RtfAttributes which are to write at the beginning of the RtfTextrun
  328. * @return new or existing RtfTextrun object.
  329. * @throws IOException for I/O problems
  330. */
  331. public static RtfTextrun getTextrun(RtfContainer container, Writer writer, RtfAttributes attrs)
  332. throws IOException {
  333. List list = container.getChildren();
  334. if (list.size() == 0) {
  335. //add a new RtfTextrun
  336. RtfTextrun textrun = new RtfTextrun(container, writer, attrs);
  337. list.add(textrun);
  338. return textrun;
  339. }
  340. Object obj = list.get(list.size() - 1);
  341. if (obj instanceof RtfTextrun) {
  342. //if the last child is a RtfTextrun, return it
  343. return (RtfTextrun) obj;
  344. }
  345. //add a new RtfTextrun as the last child
  346. RtfTextrun textrun = new RtfTextrun(container, writer, attrs);
  347. list.add(textrun);
  348. return textrun;
  349. }
  350. /**
  351. * specify, if the last paragraph control word (\par) should be suppressed.
  352. * @param bSuppress true, if the last \par should be suppressed
  353. */
  354. public void setSuppressLastPar(boolean bSuppress) {
  355. bSuppressLastPar = bSuppress;
  356. }
  357. /**
  358. * write RTF code of all our children
  359. * @throws IOException for I/O problems
  360. */
  361. protected void writeRtfContent() throws IOException {
  362. /**
  363. *TODO: The textrun's children are iterated twice:
  364. * 1. To determine the last RtfParagraphBreak
  365. * 2. To write the children
  366. * Maybe this can be done more efficient.
  367. */
  368. boolean bHasTableCellParent
  369. = this.getParentOfClass(RtfTableCell.class) != null;
  370. RtfAttributes attrBlockLevel = new RtfAttributes();
  371. //determine, if this RtfTextrun is the last child of its parent
  372. boolean bLast = false;
  373. for (Iterator it = parent.getChildren().iterator(); it.hasNext();) {
  374. if (it.next() == this) {
  375. bLast = !it.hasNext();
  376. break;
  377. }
  378. }
  379. //get last RtfParagraphBreak, which is not followed by any visible child
  380. RtfParagraphBreak lastParagraphBreak = null;
  381. if (bLast) {
  382. for (Iterator it = getChildren().iterator(); it.hasNext();) {
  383. final RtfElement e = (RtfElement)it.next();
  384. if (e instanceof RtfParagraphBreak) {
  385. lastParagraphBreak = (RtfParagraphBreak)e;
  386. } else {
  387. if (!(e instanceof RtfOpenGroupMark)
  388. && !(e instanceof RtfCloseGroupMark)
  389. && e.isEmpty()) {
  390. lastParagraphBreak = null;
  391. }
  392. }
  393. }
  394. }
  395. //may contain for example \intbl
  396. writeAttributes(attrib, null);
  397. if (rtfListItem != null) {
  398. rtfListItem.getRtfListStyle().writeParagraphPrefix(this);
  399. }
  400. //write all children
  401. boolean bPrevPar = false;
  402. boolean bFirst = true;
  403. for (Iterator it = getChildren().iterator(); it.hasNext();) {
  404. final RtfElement e = (RtfElement)it.next();
  405. final boolean bRtfParagraphBreak = (e instanceof RtfParagraphBreak);
  406. if (bHasTableCellParent) {
  407. attrBlockLevel.set(e.getRtfAttributes());
  408. }
  409. /**
  410. * -Write RtfParagraphBreak only, if the previous visible child
  411. * was't also a RtfParagraphBreak.
  412. * -Write RtfParagraphBreak only, if it is not the first visible
  413. * child.
  414. * -If the RtfTextrun is the last child of its parent, write a
  415. * RtfParagraphBreak only, if it is not the last child.
  416. */
  417. boolean bHide = false;
  418. bHide = bRtfParagraphBreak;
  419. bHide = bHide
  420. && (bPrevPar
  421. || bFirst
  422. || (bSuppressLastPar && bLast && lastParagraphBreak != null
  423. && e == lastParagraphBreak));
  424. if (!bHide) {
  425. newLine();
  426. e.writeRtf();
  427. if (rtfListItem != null && e instanceof RtfParagraphBreak) {
  428. rtfListItem.getRtfListStyle().writeParagraphPrefix(this);
  429. }
  430. }
  431. if (e instanceof RtfParagraphBreak) {
  432. bPrevPar = true;
  433. } else if (e instanceof RtfCloseGroupMark) {
  434. //do nothing
  435. } else if (e instanceof RtfOpenGroupMark) {
  436. //do nothing
  437. } else {
  438. bPrevPar = bPrevPar && e.isEmpty();
  439. bFirst = bFirst && e.isEmpty();
  440. }
  441. } //for (Iterator it = ...)
  442. //
  443. if (bHasTableCellParent) {
  444. writeAttributes(attrBlockLevel, null);
  445. }
  446. }
  447. /**
  448. * Set the parent list-item of the textrun.
  449. *
  450. * @param listItem parent list-item of the textrun
  451. */
  452. public void setRtfListItem(RtfListItem listItem) {
  453. rtfListItem = listItem;
  454. }
  455. /**
  456. * Gets the parent list-item of the textrun.
  457. *
  458. * @return parent list-item of the textrun
  459. */
  460. public RtfListItem getRtfListItem() {
  461. return rtfListItem;
  462. }
  463. }