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.

AFPResourceUtil.java 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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.util;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.OutputStream;
  22. import java.io.UnsupportedEncodingException;
  23. import java.util.Collection;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.apache.fop.afp.AFPConstants;
  27. import org.apache.fop.afp.modca.AbstractAFPObject.Category;
  28. import org.apache.fop.afp.modca.ResourceObject;
  29. import org.apache.fop.afp.parser.MODCAParser;
  30. import org.apache.fop.afp.parser.UnparsedStructuredField;
  31. /**
  32. * TODO better docs
  33. * Utility for AFP resource handling
  34. *
  35. *
  36. * A utility class to read structured fields from a MO:DCA document. Each
  37. * component of a mixed object document is explicitly defined and delimited
  38. * in the data. This is accomplished through the use of MO:DCA data structures,
  39. * called structured fields. Structured fields are used to envelop document
  40. * components and to provide commands and information to applications using
  41. * the data. Structured fields may contain one or more parameters. Each
  42. * parameter provides one value from a set of values defined by the architecture.
  43. * <p/>
  44. * MO:DCA structured fields consist of two parts: an introducer that identifies
  45. * the length and type of the structured field, and data that provides the
  46. * structured field's effect. The data is contained in a set of parameters,
  47. * which can consist of other data structures and data elements. The maximum
  48. * length of a structured field is 32767 bytes.
  49. * <p/>
  50. */
  51. public final class AFPResourceUtil {
  52. private static final byte TYPE_CODE_BEGIN = (byte)(0xA8 & 0xFF);
  53. private static final byte TYPE_CODE_END = (byte)(0xA9 & 0xFF);
  54. private static final Log LOG = LogFactory.getLog(AFPResourceUtil.class);
  55. private AFPResourceUtil() {
  56. //nop
  57. }
  58. /**
  59. * Get the next structured field as identified by the identifier
  60. * parameter (this must be a valid MO:DCA structured field).
  61. * @param identifier the three byte identifier
  62. * @param inputStream the inputStream
  63. * @throws IOException if an I/O exception occurred
  64. * @return the next structured field or null when there are no more
  65. */
  66. public static byte[] getNext(byte[] identifier, InputStream inputStream) throws IOException {
  67. MODCAParser parser = new MODCAParser(inputStream);
  68. while (true) {
  69. UnparsedStructuredField field = parser.readNextStructuredField();
  70. if (field == null) {
  71. return null;
  72. }
  73. if (field.getSfClassCode() == identifier[0]
  74. && field.getSfTypeCode() == identifier[1]
  75. && field.getSfCategoryCode() == identifier[2]) {
  76. return field.getCompleteFieldAsBytes();
  77. }
  78. }
  79. }
  80. private static String getResourceName(UnparsedStructuredField field)
  81. throws UnsupportedEncodingException {
  82. //The first 8 bytes of the field data represent the resource name
  83. byte[] nameBytes = new byte[8];
  84. System.arraycopy(field.getData(), 0, nameBytes, 0, 8);
  85. String asciiName;
  86. asciiName = new String(nameBytes, AFPConstants.EBCIDIC_ENCODING);
  87. return asciiName;
  88. }
  89. /**
  90. * Copy a complete resource file to a given {@link OutputStream}.
  91. * @param in external resource input
  92. * @param out output destination
  93. * @throws IOException if an I/O error occurs
  94. */
  95. public static void copyResourceFile(final InputStream in, OutputStream out)
  96. throws IOException {
  97. MODCAParser parser = new MODCAParser(in);
  98. while (true) {
  99. UnparsedStructuredField field = parser.readNextStructuredField();
  100. if (field == null) {
  101. break;
  102. }
  103. out.write(MODCAParser.CARRIAGE_CONTROL_CHAR);
  104. field.writeTo(out);
  105. }
  106. }
  107. /**
  108. * Copy a named resource to a given {@link OutputStream}. The MO:DCA fields read from the
  109. * {@link InputStream} are scanned for the resource with the given name.
  110. * @param name name of structured field
  111. * @param in external resource input
  112. * @param out output destination
  113. * @throws IOException if an I/O error occurs
  114. */
  115. public static void copyNamedResource(String name,
  116. final InputStream in, final OutputStream out) throws IOException {
  117. final MODCAParser parser = new MODCAParser(in);
  118. Collection resourceNames = new java.util.HashSet();
  119. //Find matching "Begin" field
  120. final UnparsedStructuredField fieldBegin;
  121. while (true) {
  122. UnparsedStructuredField field = parser.readNextStructuredField();
  123. if (field == null) {
  124. throw new IOException("Requested resource '" + name
  125. + "' not found. Encountered resource names: " + resourceNames);
  126. }
  127. if (field.getSfTypeCode() != TYPE_CODE_BEGIN) { //0xA8=Begin
  128. continue; //Not a "Begin" field
  129. }
  130. String resourceName = getResourceName(field);
  131. resourceNames.add(resourceName);
  132. if (resourceName.equals(name)) {
  133. if (LOG.isDebugEnabled()) {
  134. LOG.debug("Start of requested structured field found:\n"
  135. + field);
  136. }
  137. fieldBegin = field;
  138. break; //Name doesn't match
  139. }
  140. }
  141. //Decide whether the resource file has to be wrapped in a resource object
  142. boolean wrapInResource;
  143. if (fieldBegin.getSfCategoryCode() == Category.PAGE_SEGMENT) {
  144. //A naked page segment must be wrapped in a resource object
  145. wrapInResource = true;
  146. } else if (fieldBegin.getSfCategoryCode() == Category.NAME_RESOURCE) {
  147. //A resource object can be copied directly
  148. wrapInResource = false;
  149. } else {
  150. throw new IOException("Cannot handle resource: " + fieldBegin);
  151. }
  152. //Copy structured fields (wrapped or as is)
  153. if (wrapInResource) {
  154. ResourceObject resourceObject = new ResourceObject(name) {
  155. protected void writeContent(OutputStream os) throws IOException {
  156. copyStructuredFields(name, fieldBegin, parser, out);
  157. }
  158. };
  159. resourceObject.setType(ResourceObject.TYPE_PAGE_SEGMENT);
  160. resourceObject.writeToStream(out);
  161. } else {
  162. copyStructuredFields(name, fieldBegin, parser, out);
  163. }
  164. }
  165. private static void copyStructuredFields(String name, UnparsedStructuredField fieldBegin,
  166. MODCAParser parser, OutputStream out) throws IOException {
  167. boolean inRequestedResource;
  168. //The "Begin" field first
  169. out.write(MODCAParser.CARRIAGE_CONTROL_CHAR);
  170. fieldBegin.writeTo(out);
  171. UnparsedStructuredField field;
  172. //Then the rest of the fields until the corresponding "End" field
  173. inRequestedResource = true;
  174. do {
  175. field = parser.readNextStructuredField();
  176. if (field == null) {
  177. break; //Unexpected EOF
  178. }
  179. if (field.getSfTypeCode() == TYPE_CODE_END) {
  180. String resourceName = getResourceName(field);
  181. if (resourceName.equals(name)) {
  182. inRequestedResource = false; //Signal end of loop
  183. }
  184. }
  185. out.write(MODCAParser.CARRIAGE_CONTROL_CHAR);
  186. field.writeTo(out);
  187. } while (inRequestedResource);
  188. if (inRequestedResource) {
  189. throw new IOException("Ending structured field not found for resource " + name);
  190. }
  191. }
  192. }