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 23KB

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