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.

DataStream.java 19KB

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