/* * 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; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.Iterator; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.afp.modca.ResourceGroup; import org.apache.fop.afp.modca.StreamedResourceGroup; /** * Manages the streaming of the AFP output */ public class AFPStreamer implements Streamable { /** Static logging instance */ private static final Log log = LogFactory.getLog(AFPStreamer.class); private static final String AFPDATASTREAM_TEMP_FILE_PREFIX = "AFPDataStream_"; private static final int BUFFER_SIZE = 4096; // 4k writing buffer private static final String DEFAULT_EXTERNAL_RESOURCE_FILENAME = "resources.afp"; private final Factory factory; /** A mapping of external resource destinations to resource groups */ private final Map/**/pathResourceGroupMap = new java.util.HashMap/**/(); private StreamedResourceGroup printFileResourceGroup; /** Sets the default resource group file path */ private String defaultResourceGroupFilePath = DEFAULT_EXTERNAL_RESOURCE_FILENAME; private File tempFile; /** temporary document outputstream */ private OutputStream documentOutputStream; /** the final outputstream */ private OutputStream outputStream; private RandomAccessFile documentFile; private DataStream dataStream; /** * Main constructor * * @param factory a factory */ public AFPStreamer(Factory factory) { this.factory = factory; } /** * Creates a new DataStream * * @param paintingState the AFP painting state * @return a new {@link DataStream} * @throws IOException thrown if an I/O exception of some sort has occurred */ public DataStream createDataStream(AFPPaintingState paintingState) throws IOException { this.tempFile = File.createTempFile(AFPDATASTREAM_TEMP_FILE_PREFIX, null); this.documentFile = new RandomAccessFile(tempFile, "rw"); this.documentOutputStream = new BufferedOutputStream( new FileOutputStream(documentFile.getFD())); this.dataStream = factory.createDataStream(paintingState, documentOutputStream); return dataStream; } /** * Sets the default resource group file path * * @param filePath the default resource group file path */ public void setDefaultResourceGroupFilePath(String filePath) { this.defaultResourceGroupFilePath = filePath; } /** * Returns the resource group for a given resource info * * @param level a resource level * @return a resource group for the given resource info */ public ResourceGroup getResourceGroup(AFPResourceLevel level) { ResourceGroup resourceGroup = null; if (level.isInline()) { // no resource group for inline level return null; } if (level.isExternal()) { String filePath = level.getExternalFilePath(); if (filePath == null) { log.warn("No file path provided for external resource, using default."); filePath = defaultResourceGroupFilePath; } resourceGroup = (ResourceGroup)pathResourceGroupMap.get(filePath); if (resourceGroup == null) { OutputStream os = null; try { os = new BufferedOutputStream(new FileOutputStream(filePath)); } catch (FileNotFoundException fnfe) { log.error("Failed to create/open external resource group file '" + filePath + "'"); } finally { if (os != null) { resourceGroup = factory.createStreamedResourceGroup(os); pathResourceGroupMap.put(filePath, resourceGroup); } } } } else if (level.isPrintFile()) { if (printFileResourceGroup == null) { // use final outputstream for print-file resource group printFileResourceGroup = factory.createStreamedResourceGroup(outputStream); } resourceGroup = printFileResourceGroup; } else { // resource group in afp document datastream resourceGroup = dataStream.getResourceGroup(level); } return resourceGroup; } /** * Closes off the AFP stream writing the document stream * * @throws IOException if an an I/O exception of some sort has occurred */ // write out any external resource groups public void close() throws IOException { Iterator it = pathResourceGroupMap.entrySet().iterator(); while (it.hasNext()) { StreamedResourceGroup resourceGroup = (StreamedResourceGroup)it.next(); resourceGroup.close(); } // close any open print-file resource group if (printFileResourceGroup != null) { printFileResourceGroup.close(); } // write out document writeToStream(outputStream); outputStream.close(); // delete temporary file tempFile.delete(); } /** * Sets the final outputstream * * @param outputStream an outputstream */ public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; } /** {@inheritDoc} */ public void writeToStream(OutputStream os) throws IOException { // long start = System.currentTimeMillis(); int len = (int)documentFile.length(); int numChunks = len / BUFFER_SIZE; int remainingChunkSize = len % BUFFER_SIZE; byte[] buffer; documentFile.seek(0); if (numChunks > 0) { buffer = new byte[BUFFER_SIZE]; for (int i = 0; i < numChunks; i++) { documentFile.read(buffer, 0, BUFFER_SIZE); os.write(buffer, 0, BUFFER_SIZE); } } else { buffer = new byte[remainingChunkSize]; } if (remainingChunkSize > 0) { documentFile.read(buffer, 0, remainingChunkSize); os.write(buffer, 0, remainingChunkSize); } os.flush(); // long end = System.currentTimeMillis(); // log.debug("writing time " + (end - start) + "ms"); } }