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

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