org.apache.fop.afp.util.AFPResourceUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fop Show documentation
Show all versions of fop Show documentation
Apache FOP (Formatting Objects Processor) is the world's first print formatter driven by XSL formatting objects (XSL-FO) and the world's first output independent formatter. It is a Java application that reads a formatting object (FO) tree and renders the resulting pages to a specified output. Output formats currently supported include PDF, PCL, PS, AFP, TIFF, PNG, SVG, XML (area tree representation), Print, AWT and TXT. The primary output target is PDF.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id: AFPResourceUtil.java 1679676 2015-05-16 02:10:42Z adelmelle $ */
package org.apache.fop.afp.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.modca.AbstractAFPObject.Category;
import org.apache.fop.afp.modca.ResourceObject;
import org.apache.fop.afp.parser.MODCAParser;
import org.apache.fop.afp.parser.UnparsedStructuredField;
/**
* TODO better docs
* Utility for AFP resource handling
*
*
* A utility class to read structured fields from a MO:DCA document. Each
* component of a mixed object document is explicitly defined and delimited
* in the data. This is accomplished through the use of MO:DCA data structures,
* called structured fields. Structured fields are used to envelop document
* components and to provide commands and information to applications using
* the data. Structured fields may contain one or more parameters. Each
* parameter provides one value from a set of values defined by the architecture.
*
* MO:DCA structured fields consist of two parts: an introducer that identifies
* the length and type of the structured field, and data that provides the
* structured field's effect. The data is contained in a set of parameters,
* which can consist of other data structures and data elements. The maximum
* length of a structured field is 32767 bytes.
*/
public final class AFPResourceUtil {
private static final byte TYPE_CODE_BEGIN = (byte) (0xA8 & 0xFF);
private static final byte TYPE_CODE_END = (byte) (0xA9 & 0xFF);
private static final byte END_FIELD_ANY_NAME = (byte) (0xFF & 0xFF);
private static final Log LOG = LogFactory.getLog(AFPResourceUtil.class);
private AFPResourceUtil() {
//nop
}
/**
* Get the next structured field as identified by the identifier
* parameter (this must be a valid MO:DCA structured field).
* @param identifier the three byte identifier
* @param inputStream the inputStream
* @throws IOException if an I/O exception occurred
* @return the next structured field or null when there are no more
*/
public static byte[] getNext(byte[] identifier, InputStream inputStream) throws IOException {
MODCAParser parser = new MODCAParser(inputStream);
while (true) {
UnparsedStructuredField field = parser.readNextStructuredField();
if (field == null) {
return null;
}
if (field.getSfClassCode() == identifier[0]
&& field.getSfTypeCode() == identifier[1]
&& field.getSfCategoryCode() == identifier[2]) {
return field.getCompleteFieldAsBytes();
}
}
}
private static String getResourceName(UnparsedStructuredField field)
throws UnsupportedEncodingException {
//The first 8 bytes of the field data represent the resource name
byte[] nameBytes = new byte[8];
byte[] fieldData = field.getData();
if (fieldData.length < 8) {
throw new IllegalArgumentException("Field data does not contain a resource name");
}
System.arraycopy(fieldData, 0, nameBytes, 0, 8);
return new String(nameBytes, AFPConstants.EBCIDIC_ENCODING);
}
/**
* Copy a complete resource file to a given {@link OutputStream}.
* @param in external resource input
* @param out output destination
* @throws IOException if an I/O error occurs
*/
public static void copyResourceFile(final InputStream in, OutputStream out)
throws IOException {
MODCAParser parser = new MODCAParser(in);
while (true) {
UnparsedStructuredField field = parser.readNextStructuredField();
if (field == null) {
break;
}
out.write(MODCAParser.CARRIAGE_CONTROL_CHAR);
field.writeTo(out);
}
}
/**
* Copy a named resource to a given {@link OutputStream}. The MO:DCA fields read from the
* {@link InputStream} are scanned for the resource with the given name.
* @param name name of structured field
* @param in external resource input
* @param out output destination
* @throws IOException if an I/O error occurs
*/
public static void copyNamedResource(String name,
final InputStream in, final OutputStream out) throws IOException {
final MODCAParser parser = new MODCAParser(in);
Collection resourceNames = new java.util.HashSet();
//Find matching "Begin" field
final UnparsedStructuredField fieldBegin;
while (true) {
final UnparsedStructuredField field = parser.readNextStructuredField();
if (field == null) {
throw new IOException("Requested resource '" + name
+ "' not found. Encountered resource names: " + resourceNames);
}
if (field.getSfTypeCode() != TYPE_CODE_BEGIN) { //0xA8=Begin
continue; //Not a "Begin" field
}
final String resourceName = getResourceName(field);
resourceNames.add(resourceName);
if (resourceName.equals(name)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Start of requested structured field found:\n"
+ field);
}
fieldBegin = field;
break; //Name doesn't match
}
}
//Decide whether the resource file has to be wrapped in a resource object
boolean wrapInResource;
if (fieldBegin.getSfCategoryCode() == Category.PAGE_SEGMENT) {
//A naked page segment must be wrapped in a resource object
wrapInResource = true;
} else if (fieldBegin.getSfCategoryCode() == Category.NAME_RESOURCE) {
//A resource object can be copied directly
wrapInResource = false;
} else {
throw new IOException("Cannot handle resource: " + fieldBegin);
}
//Copy structured fields (wrapped or as is)
if (wrapInResource) {
ResourceObject resourceObject = new ResourceObject(name) {
protected void writeContent(OutputStream os) throws IOException {
copyNamedStructuredFields(name, fieldBegin, parser, out);
}
};
resourceObject.setType(ResourceObject.TYPE_PAGE_SEGMENT);
resourceObject.writeToStream(out);
} else {
copyNamedStructuredFields(name, fieldBegin, parser, out);
}
}
private static void copyNamedStructuredFields(final String name, UnparsedStructuredField fieldBegin,
MODCAParser parser, OutputStream out) throws IOException {
UnparsedStructuredField field = fieldBegin;
while (true) {
if (field == null) {
throw new IOException("Ending structured field not found for resource " + name);
}
out.write(MODCAParser.CARRIAGE_CONTROL_CHAR);
field.writeTo(out);
if (isEndOfStructuredField(field, fieldBegin, name)) {
break;
}
field = parser.readNextStructuredField();
}
}
private static boolean isEndOfStructuredField(UnparsedStructuredField field,
UnparsedStructuredField fieldBegin, String name) throws UnsupportedEncodingException {
return fieldMatchesEndTagType(field)
&& fieldMatchesBeginCategoryCode(field, fieldBegin)
&& fieldHasValidName(field, name);
}
private static boolean fieldMatchesEndTagType(UnparsedStructuredField field) {
return field.getSfTypeCode() == TYPE_CODE_END;
}
private static boolean fieldMatchesBeginCategoryCode(UnparsedStructuredField field,
UnparsedStructuredField fieldBegin) {
return fieldBegin.getSfCategoryCode() == field.getSfCategoryCode();
}
/**
* The AFP specification states that it is valid for the end structured field to have:
* - No tag name specified, which will cause it to match any existing tag type match.
* - The name has FFFF as its first two bytes
* - The given name matches the previous structured field name
*/
private static boolean fieldHasValidName(UnparsedStructuredField field, String name)
throws UnsupportedEncodingException {
if (field.getData().length > 0) {
if (field.getData()[0] == field.getData()[1]
&& field.getData()[0] == END_FIELD_ANY_NAME) {
return true;
} else {
return name.equals(getResourceName(field));
}
}
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy