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.

AFPDataStream.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. * Copyright 2006 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.render.afp.modca;
  18. import java.io.IOException;
  19. import java.io.OutputStream;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.apache.fop.render.afp.AFPFontColor;
  23. import org.apache.fop.render.afp.fonts.AFPFont;
  24. import org.apache.fop.render.afp.tools.StringUtils;
  25. /**
  26. * A data stream is a continuous ordered stream of data elements and objects
  27. * conforming to a given format. Application programs can generate data streams
  28. * destined for a presentation service, archive library, presentation device or
  29. * another application program. The strategic presentation data stream
  30. * architectures used is Mixed Object Document Content Architecture (MO:DCA�).
  31. *
  32. * The MO:DCA architecture defines the data stream used by applications to
  33. * describe documents and object envelopes for interchange with other
  34. * applications and application services. Documents defined in the MO:DCA format
  35. * may be archived in a database, then later retrieved, viewed, annotated and
  36. * printed in local or distributed systems environments. Presentation fidelity
  37. * is accommodated by including resource objects in the documents that reference
  38. * them.
  39. *
  40. */
  41. public class AFPDataStream {
  42. /**
  43. * Static logging instance
  44. */
  45. protected static final Log log = LogFactory.getLog("org.apache.fop.render.afp.modca");
  46. /**
  47. * Boolean completion indicator
  48. */
  49. private boolean _complete = false;
  50. /**
  51. * The application producing the AFP document
  52. */
  53. private String _producer = null;
  54. /**
  55. * The AFP document object
  56. */
  57. private Document _document = null;
  58. /**
  59. * The current page group object
  60. */
  61. private PageGroup _currentPageGroup = null;
  62. /**
  63. * The current page object
  64. */
  65. private PageObject _currentPageObject = null;
  66. /**
  67. * The current overlay object
  68. */
  69. private Overlay _currentOverlay = null;
  70. /**
  71. * The current page
  72. */
  73. private AbstractPageObject _currentPage = null;
  74. /**
  75. * The page count
  76. */
  77. private int _pageCount = 0;
  78. /**
  79. * The page group count
  80. */
  81. private int _pageGroupCount = 0;
  82. /**
  83. * The overlay count
  84. */
  85. private int _ovlCount = 0;
  86. /**
  87. * The portrait rotation
  88. */
  89. private int _portraitRotation = 0;
  90. /**
  91. * The landscape rotation
  92. */
  93. private int _landscapeRotation = 270;
  94. /**
  95. * The x offset
  96. */
  97. private int _xOffset = 0;
  98. /**
  99. * The y offset
  100. */
  101. private int _yOffset = 0;
  102. /**
  103. * The rotation
  104. */
  105. private int _rotation;
  106. /**
  107. * The outputstream for the data stream
  108. */
  109. private OutputStream _outputStream = null;
  110. /**
  111. * Default constructor for the AFPDataStream.
  112. */
  113. public AFPDataStream() {
  114. }
  115. /**
  116. * The document is started by invoking this method which creates an instance
  117. * of the AFP Document object.
  118. */
  119. public void startDocument(OutputStream outputStream) {
  120. if (_document != null) {
  121. String msg = "Invalid state - document already started.";
  122. log.warn("startDocument():: " + msg);
  123. throw new IllegalStateException(msg);
  124. }
  125. _document = new Document();
  126. _outputStream = outputStream;
  127. }
  128. /**
  129. * The document is ended by invoking this method which creates an instance
  130. * of the AFP Document object and registers the start with a validation map
  131. * which ensures that methods are not invoked out of the correct sequence.
  132. */
  133. public void endDocument()
  134. throws IOException {
  135. if (_complete) {
  136. String msg = "Invalid state - document already ended.";
  137. log.warn("endDocument():: " + msg);
  138. throw new IllegalStateException(msg);
  139. }
  140. if (_currentPageObject != null) {
  141. // End the current page if necessary
  142. endPage();
  143. }
  144. if (_currentPageGroup != null) {
  145. // End the current page group if necessary
  146. endPageGroup();
  147. }
  148. _document.endDocument();
  149. _document.writeDataStream(_outputStream);
  150. _outputStream.flush();
  151. _complete = true;
  152. _document = null;
  153. _outputStream = null;
  154. }
  155. /**
  156. * Start a new page. When processing has finished on the current page, the
  157. * {@link #endPage()}method must be invoked to mark the page ending.
  158. *
  159. * @param pageWidth
  160. * the width of the page
  161. * @param pageHeight
  162. * the height of the page
  163. * @param pageRotation
  164. * the rotation of the page
  165. */
  166. public void startPage(int pageWidth, int pageHeight, int pageRotation) {
  167. String pageName = "PGN"
  168. + StringUtils.lpad(String.valueOf(_pageCount++), '0', 5);
  169. _currentPageObject = new PageObject(pageName, pageWidth, pageHeight, pageRotation);
  170. _currentPage = _currentPageObject;
  171. _currentOverlay = null;
  172. setOffsets(0, 0, 0);
  173. }
  174. /**
  175. * Start a new overlay. When processing has finished on the current overlay, the
  176. * {@link #endOverlay()}method must be invoked to mark the overlay ending.
  177. *
  178. * @param overlayX
  179. * the x position of the overlay on the page
  180. * @param overlayY
  181. * the y position of the overlay on the page
  182. * @param overlayWidth
  183. * the width of the overlay
  184. * @param overlayHeight
  185. * the height of the overlay
  186. * @param overlayRotation
  187. * the rotation of the overlay
  188. */
  189. public void startOverlay(int overlayX, int overlayY, int overlayWidth, int overlayHeight, int overlayRotation) {
  190. String overlayName = "OVL"
  191. + StringUtils.lpad(String.valueOf(_ovlCount++), '0', 5);
  192. _currentOverlay = new Overlay(overlayName, overlayWidth, overlayHeight, overlayRotation);
  193. _currentPageObject.addOverlay(_currentOverlay);
  194. _currentPageObject.createIncludePageOverlay(overlayName, overlayX, overlayY, 0);
  195. _currentPage = _currentOverlay;
  196. setOffsets(0, 0, 0);
  197. }
  198. /**
  199. * Helper method to mark the end of the current overlay.
  200. */
  201. public void endOverlay() {
  202. _currentOverlay.endPage();
  203. _currentOverlay = null;
  204. _currentPage = _currentPageObject;
  205. }
  206. /**
  207. * Helper method to save the current page.
  208. */
  209. public PageObject savePage() {
  210. PageObject pageObject = _currentPageObject;
  211. if (_currentPageGroup != null) {
  212. _currentPageGroup.addPage(_currentPageObject);
  213. } else {
  214. _document.addPage(_currentPageObject);
  215. }
  216. _currentPageObject = null;
  217. _currentPage = null;
  218. return pageObject;
  219. }
  220. /**
  221. * Helper method to restore the current page.
  222. */
  223. public void restorePage(PageObject pageObject) {
  224. _currentPageObject = pageObject;
  225. _currentPage = pageObject;
  226. }
  227. /**
  228. * Helper method to mark the end of the current page.
  229. */
  230. public void endPage()
  231. throws IOException {
  232. _currentPageObject.endPage();
  233. if (_currentPageGroup != null) {
  234. _currentPageGroup.addPage(_currentPageObject);
  235. } else {
  236. _document.addPage(_currentPageObject);
  237. _document.writeDataStream(_outputStream);
  238. }
  239. _currentPageObject = null;
  240. _currentPage = null;
  241. }
  242. /**
  243. * Sets the offsets to be used for element positioning
  244. *
  245. * @param xOffset
  246. * the offset in the x direction
  247. * @param yOffset
  248. * the offset in the y direction
  249. * @param rotation
  250. * the rotation
  251. */
  252. public void setOffsets(int xOffset, int yOffset, int rotation) {
  253. _xOffset = xOffset;
  254. _yOffset = yOffset;
  255. _rotation = rotation;
  256. }
  257. /**
  258. * Helper method to create a map coded font object on the current page, this
  259. * method delegates the construction of the map coded font object to the
  260. * active environment group on the current page.
  261. *
  262. * @param fontReference
  263. * the font number used as the resource identifier
  264. * @param font
  265. * the font
  266. * @param size
  267. * the point size of the font
  268. */
  269. public void createFont(byte fontReference, AFPFont font, int size) {
  270. _currentPage.createFont(fontReference, font, size);
  271. }
  272. /**
  273. * Helper method to create text on the current page, this method delegates
  274. * to the current presentation text object in order to construct the text.
  275. *
  276. * @param fontNumber
  277. * the font number used as the resource identifier
  278. * @param x
  279. * the x coordinate of the text
  280. * @param y
  281. * the y coordinate of the text
  282. * @param col
  283. * the text color
  284. * @param vsci
  285. * The variable space character increment.
  286. * @param ica
  287. * The inter character adjustment.
  288. * @param data
  289. * the text data to create
  290. */
  291. public void createText(int fontNumber, int x, int y, AFPFontColor col, int vsci, int ica, byte[] data) {
  292. _currentPage.createText(fontNumber, x + _xOffset, y + _yOffset, _rotation, col, vsci, ica, data);
  293. }
  294. /**
  295. * Returns an ImageObject used to create an image in the datastream.
  296. *
  297. * @param x
  298. * the x position of the image
  299. * @param y
  300. * the y position of the image
  301. * @param w
  302. * the width of the image
  303. * @param h
  304. * the height of the image
  305. */
  306. public ImageObject getImageObject(int x, int y, int w, int h) {
  307. int xOrigin;
  308. int yOrigin;
  309. int width;
  310. int height;
  311. switch (_rotation) {
  312. case 90:
  313. xOrigin = _currentPage.getWidth() - y - _yOffset;
  314. yOrigin = x + _xOffset;
  315. width = h;
  316. height = w;
  317. break;
  318. case 180:
  319. xOrigin = _currentPage.getWidth() - x - _xOffset;
  320. yOrigin = _currentPage.getHeight() - y - _yOffset;
  321. width = w;
  322. height = h;
  323. break;
  324. case 270:
  325. xOrigin = y + _yOffset;
  326. yOrigin = _currentPage.getHeight() - x - _xOffset;
  327. width = h;
  328. height = w;
  329. break;
  330. default:
  331. xOrigin = x + _xOffset;
  332. yOrigin = y + _yOffset;
  333. width = w;
  334. height = h;
  335. break;
  336. }
  337. ImageObject io = _currentPage.getImageObject();
  338. io.setImageViewport(xOrigin, yOrigin, width, height, _rotation);
  339. return io;
  340. }
  341. /**
  342. * Method to create a line on the current page.
  343. *
  344. * @param x1
  345. * the first x coordinate of the line
  346. * @param y1
  347. * the first y coordinate of the line
  348. * @param x2
  349. * the second x coordinate of the line
  350. * @param y2
  351. * the second y coordinate of the line
  352. * @param thickness
  353. * the thickness of the line
  354. * @param col
  355. * The text color.
  356. */
  357. public void createLine(int x1, int y1, int x2, int y2, int thickness, AFPFontColor col) {
  358. _currentPage.createLine(x1 + _xOffset, y1 + _yOffset, x2 + _xOffset, y2 + _yOffset, thickness, _rotation, col);
  359. }
  360. /**
  361. * Sets the application producing the AFP.
  362. *
  363. * @param producer
  364. * the application producing the AFP datastream
  365. */
  366. public void setProducer(String producer) {
  367. _producer = producer;
  368. }
  369. /**
  370. * This method will create shading on the page using the specified
  371. * coordinates (the shading contrast is controlled via the red, green, blue
  372. * parameters, by converting this to grey scale).
  373. *
  374. * @param x
  375. * the x coordinate of the shading
  376. * @param y
  377. * the y coordinate of the shading
  378. * @param w
  379. * the width of the shaded area
  380. * @param h
  381. * the height of the shaded area
  382. * @param red
  383. * the red value
  384. * @param green
  385. * the green value
  386. * @param blue
  387. * the blue value
  388. */
  389. public void createShading(int x, int y, int w, int h, int red, int green,
  390. int blue) {
  391. _currentPage.createShading(x + _xOffset, y + _xOffset, w, h, red, green, blue);
  392. }
  393. /**
  394. * Helper method which allows creation of the MPO object, via the AEG. And
  395. * the IPO via the Page. (See actual object for descriptions.)
  396. *
  397. * @param name
  398. * the name of the static overlay
  399. */
  400. public void createIncludePageOverlay(String name) {
  401. _currentPageObject.createIncludePageOverlay(name, 0, 0, _rotation);
  402. ActiveEnvironmentGroup aeg = _currentPageObject.getActiveEnvironmentGroup();
  403. aeg.createOverlay(name);
  404. }
  405. /**
  406. * Helper method which allows creation of the IMM object.
  407. *
  408. * @param name
  409. * the name of the medium map
  410. */
  411. public void createInvokeMediumMap(String name) {
  412. if (_currentPageGroup == null) {
  413. startPageGroup();
  414. }
  415. _currentPageGroup.createInvokeMediumMap(name);
  416. }
  417. /**
  418. * Creates an IncludePageSegment on the current page.
  419. *
  420. * @param name
  421. * the name of the include page segment
  422. * @param x
  423. * the x coordinate for the overlay
  424. * @param y
  425. * the y coordinate for the overlay
  426. */
  427. public void createIncludePageSegment(String name, int x, int y) {
  428. int xOrigin;
  429. int yOrigin;
  430. switch (_rotation) {
  431. case 90:
  432. xOrigin = _currentPage.getWidth() - y - _yOffset;
  433. yOrigin = x + _xOffset;
  434. break;
  435. case 180:
  436. xOrigin = _currentPage.getWidth() - x - _xOffset;
  437. yOrigin = _currentPage.getHeight() - y - _yOffset;
  438. break;
  439. case 270:
  440. xOrigin = y + _yOffset;
  441. yOrigin = _currentPage.getHeight() - x - _xOffset;
  442. break;
  443. default:
  444. xOrigin = x + _xOffset;
  445. yOrigin = y + _yOffset;
  446. break;
  447. }
  448. _currentPage.createIncludePageSegment(name, xOrigin, yOrigin);
  449. }
  450. /**
  451. * Creates a TagLogicalElement on the current page.
  452. *
  453. * @param attributes
  454. * the array of key value pairs.
  455. */
  456. public void createPageTagLogicalElement(TagLogicalElementBean[] attributes) {
  457. for (int i = 0; i < attributes.length; i++) {
  458. String name = (String) attributes[i].getKey();
  459. String value = (String) attributes[i].getValue();
  460. _currentPage.createTagLogicalElement(name, value);
  461. }
  462. }
  463. /**
  464. * Creates a TagLogicalElement on the current page group.
  465. *
  466. * @param attributes
  467. * the array of key value pairs.
  468. */
  469. public void createPageGroupTagLogicalElement(
  470. TagLogicalElementBean[] attributes) {
  471. for (int i = 0; i < attributes.length; i++) {
  472. String name = (String) attributes[i].getKey();
  473. String value = (String) attributes[i].getValue();
  474. _currentPageGroup.createTagLogicalElement(name, value);
  475. }
  476. }
  477. /**
  478. * Creates a TagLogicalElement on the current page or page group
  479. *
  480. * @param name
  481. * The tag name
  482. * @param value
  483. * The tag value
  484. */
  485. public void createTagLogicalElement(String name, String value) {
  486. if (_currentPageGroup != null) {
  487. _currentPageGroup.createTagLogicalElement(name, value);
  488. } else {
  489. _currentPage.createTagLogicalElement(name, value);
  490. }
  491. }
  492. /**
  493. * Start a new page group. When processing has finished on the current page
  494. * group the {@link #endPageGroup()}method must be invoked to mark the page
  495. * group ending.
  496. *
  497. * @param name
  498. * the name of the page group
  499. */
  500. public void startPageGroup() {
  501. String pageGroupName = "PGP"
  502. + StringUtils.lpad(String.valueOf(_pageCount++), '0', 5);
  503. _currentPageGroup = new PageGroup(pageGroupName);
  504. }
  505. /**
  506. * Helper method to mark the end of the page group.
  507. */
  508. public void endPageGroup()
  509. throws IOException {
  510. _currentPageGroup.endPageGroup();
  511. _document.addPageGroup(_currentPageGroup);
  512. _document.writeDataStream(_outputStream);
  513. _currentPageGroup = null;
  514. }
  515. /**
  516. * Sets the rotation to be used for portrait pages, valid values are 0
  517. * (default), 90, 180, 270.
  518. *
  519. * @param rotation
  520. * The rotation in degrees.
  521. */
  522. public void setPortraitRotation(int rotation) {
  523. if (rotation == 0 || rotation == 90 || rotation == 180
  524. || rotation == 270) {
  525. _portraitRotation = rotation;
  526. } else {
  527. throw new IllegalArgumentException(
  528. "The portrait rotation must be one of the values 0, 90, 180, 270");
  529. }
  530. }
  531. /**
  532. * Sets the rotation to be used for landscape pages, valid values are 0, 90,
  533. * 180, 270 (default).
  534. *
  535. * @param rotation
  536. * The rotation in degrees.
  537. */
  538. public void setLandscapeRotation(int rotation) {
  539. if (rotation == 0 || rotation == 90 || rotation == 180
  540. || rotation == 270) {
  541. _landscapeRotation = rotation;
  542. } else {
  543. throw new IllegalArgumentException(
  544. "The landscape rotation must be one of the values 0, 90, 180, 270");
  545. }
  546. }
  547. }