/* * 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$ */ package org.apache.fop.afp.modca; import java.io.IOException; import java.io.OutputStream; import java.util.Collection; import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.Streamable; import org.apache.fop.afp.util.BinaryUtils; /** * This is the base class for all data stream objects. Page objects are * responsible for building and generating the binary datastream in an * AFP format. */ public abstract class AbstractAFPObject implements Streamable { /** Static logging instance */ protected static final Log LOG = LogFactory.getLog(AbstractAFPObject.class); /** the structured field class id */ protected static final byte SF_CLASS = (byte)0xD3; private static final byte[] SF_HEADER = new byte[] { 0x5A, // Structured field identifier 0x00, // Length byte 1 0x10, // Length byte 2 SF_CLASS, // Structured field id byte 1 (byte) 0x00, // Structured field id byte 2 (byte) 0x00, // Structured field id byte 3 0x00, // Flags 0x00, // Reserved 0x00, // Reserved }; /** Length of bytes of a Structured Field Header */ protected static final int SF_HEADER_LENGTH = SF_HEADER.length; /** * Copies the template structured field data array to the given byte array * * @param data the structured field data byte array * @param type the type code * @param category the category code */ protected void copySF(byte[] data, byte type, byte category) { copySF(data, SF_CLASS, type, category); } /** * Copies the template structured field data array to the given byte array * * @param data the structured field data byte array * @param clazz the class code * @param type the type code * @param category the category code */ protected static void copySF(byte[] data, byte clazz, byte type, byte category) { System.arraycopy(SF_HEADER, 0, data, 0, SF_HEADER_LENGTH); data[3] = clazz; data[4] = type; data[5] = category; } /** * Writes a collection of Streamable to the AFP Datastream. * * @param objects a list of AFPObjects * @param <S> Streamable view of an AFPObject * @param os The stream to write to * @throws java.io.IOException an I/O exception of some sort has occurred. */ protected <S extends Streamable> void writeObjects(Collection<S> objects, OutputStream os) throws IOException { if (objects != null) { Iterator<S> it = objects.iterator(); while (it.hasNext()) { Streamable s = it.next(); s.writeToStream(os); it.remove(); // once written, immediately remove the object } } } /** * Writes data chunks to a given outputstream * * @param data the data byte array * @param dataHeader the header data * @param lengthOffset offset of length field in data chunk * @param maxChunkLength the maximum chunk length * @param os the outputstream to write to * @throws IOException thrown if an I/O exception of some sort has occurred. */ protected static void writeChunksToStream(byte[] data, byte[] dataHeader, int lengthOffset, int maxChunkLength, OutputStream os) throws IOException { int dataLength = data.length; int numFullChunks = dataLength / maxChunkLength; int lastChunkLength = dataLength % maxChunkLength; int headerLen = dataHeader.length - lengthOffset; // length field is just before data so do not include in data length if (headerLen == 2) { headerLen = 0; } byte[] len; int off = 0; if (numFullChunks > 0) { // write out full data chunks len = BinaryUtils.convert(headerLen + maxChunkLength, 2); dataHeader[lengthOffset] = len[0]; // Length byte 1 dataHeader[lengthOffset + 1] = len[1]; // Length byte 2 for (int i = 0; i < numFullChunks; i++, off += maxChunkLength) { os.write(dataHeader); os.write(data, off, maxChunkLength); } } if (lastChunkLength > 0) { // write last data chunk len = BinaryUtils.convert(headerLen + lastChunkLength, 2); dataHeader[lengthOffset] = len[0]; // Length byte 1 dataHeader[lengthOffset + 1] = len[1]; // Length byte 2 os.write(dataHeader); os.write(data, off, lastChunkLength); } } /** * Truncates the string as necessary * * @param str a character string * @param maxLength the maximum length allowed for the string * @return a possibly truncated string */ protected static String truncate(String str, int maxLength) { if (str.length() > maxLength) { str = str.substring(0, maxLength); LOG.warn("truncated character string '" + str + "', longer than " + maxLength + " chars"); } return str; } /** structured field type codes */ public interface Type { /** Attribute */ byte ATTRIBUTE = (byte)0xA0; /** Copy Count */ byte COPY_COUNT = (byte)0xA2; /** Descriptor */ byte DESCRIPTOR = (byte)0xA6; /** Control */ byte CONTROL = (byte)0xA7; /** Begin */ byte BEGIN = (byte)0xA8; /** End */ byte END = (byte)0xA9; /** Map */ byte MAP = (byte)0xAB; /** Position */ byte POSITION = (byte)0xAC; /** Process */ byte PROCESS = (byte)0xAD; /** Include */ byte INCLUDE = (byte)0xAF; /** Table */ byte TABLE = (byte)0xB0; /** Migration */ byte MIGRATION = (byte)0xB1; /** Variable */ byte VARIABLE = (byte)0xB2; /** Link */ byte LINK = (byte)0xB4; /** Data */ byte DATA = (byte)0xEE; } /** structured field category codes */ public interface Category { /** Page Segment */ byte PAGE_SEGMENT = (byte)0x5F; /** Object Area */ byte OBJECT_AREA = (byte)0x6B; /** Color Attribute Table */ byte COLOR_ATTRIBUTE_TABLE = (byte)0x77; /** IM Image */ byte IM_IMAGE = (byte)0x7B; /** Medium */ byte MEDIUM = (byte)0x88; /** Coded Font */ byte CODED_FONT = (byte)0x8A; /** Process Element */ byte PROCESS_ELEMENT = (byte)0x90; /** Object Container */ byte OBJECT_CONTAINER = (byte)0x92; /** Presentation Text */ byte PRESENTATION_TEXT = (byte)0x9B; /** Index */ byte INDEX = (byte)0xA7; /** Document */ byte DOCUMENT = (byte)0xA8; /** Page Group */ byte PAGE_GROUP = (byte)0xAD; /** Page */ byte PAGE = (byte)0xAF; /** Graphics */ byte GRAPHICS = (byte)0xBB; /** Data Resource */ byte DATA_RESOURCE = (byte)0xC3; /** Document Environment Group (DEG) */ byte DOCUMENT_ENVIRONMENT_GROUP = (byte)0xC4; /** Resource Group */ byte RESOURCE_GROUP = (byte)0xC6; /** Object Environment Group (OEG) */ byte OBJECT_ENVIRONMENT_GROUP = (byte)0xC7; /** Active Environment Group (AEG) */ byte ACTIVE_ENVIRONMENT_GROUP = (byte)0xC9; /** Medium Map */ byte MEDIUM_MAP = (byte)0xCC; /** Form Map */ byte FORM_MAP = (byte)0xCD; /** Name Resource */ byte NAME_RESOURCE = (byte)0xCE; /** Page Overlay */ byte PAGE_OVERLAY = (byte)0xD8; /** Resource Environment Group (REG) */ byte RESOURCE_ENVIROMENT_GROUP = (byte)0xD9; /** Overlay */ byte OVERLAY = (byte)0xDF; /** Data Suppression */ byte DATA_SUPRESSION = (byte)0xEA; /** Bar Code */ byte BARCODE = (byte)0xEB; /** No Operation */ byte NO_OPERATION = (byte)0xEE; /** Image */ byte IMAGE = (byte)0xFB; } }