123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /*
- * 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.BufferedInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.util.Map;
-
- import org.apache.commons.io.IOUtils;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.afp.AFPResourceLevel.ResourceType;
- import org.apache.fop.afp.fonts.AFPFont;
- import org.apache.fop.afp.fonts.CharacterSet;
- import org.apache.fop.afp.modca.AbstractNamedAFPObject;
- import org.apache.fop.afp.modca.AbstractPageObject;
- import org.apache.fop.afp.modca.IncludeObject;
- import org.apache.fop.afp.modca.IncludedResourceObject;
- import org.apache.fop.afp.modca.PageSegment;
- import org.apache.fop.afp.modca.Registry;
- import org.apache.fop.afp.modca.ResourceGroup;
- import org.apache.fop.afp.modca.ResourceObject;
- import org.apache.fop.afp.util.AFPResourceAccessor;
- import org.apache.fop.afp.util.AFPResourceUtil;
- import org.apache.fop.apps.io.InternalResourceResolver;
-
- /**
- * Manages the creation and storage of document resources
- */
- public class AFPResourceManager {
-
- /** logging instance */
- private static Log log = LogFactory.getLog(AFPResourceManager.class);
-
- /** The AFP datastream (document tree) */
- private DataStream dataStream;
-
- /** Resource creation factory */
- private final Factory factory;
-
- private final AFPStreamer streamer;
-
- private final AFPDataObjectFactory dataObjectFactory;
-
- /** Maintain a reference count of instream objects for referencing purposes */
- private int instreamObjectCount;
-
- /** Mapping of resourceInfo to AbstractCachedObject */
- private final Map<AFPResourceInfo, AbstractCachedObject> includeObjectCache
- = new java.util.HashMap<AFPResourceInfo, AbstractCachedObject>();
- private AFPResourceLevelDefaults resourceLevelDefaults = new AFPResourceLevelDefaults();
-
- /**
- * Main constructor
- */
- public AFPResourceManager(InternalResourceResolver resourceResolver) {
- this.factory = new Factory();
- this.streamer = new AFPStreamer(factory, resourceResolver);
- this.dataObjectFactory = new AFPDataObjectFactory(factory);
- }
-
- /**
- * Sets the outputstream
- *
- * @param paintingState the AFP painting state
- * @param outputStream the outputstream
- * @return a new AFP DataStream
- * @throws IOException thrown if an I/O exception of some sort has occurred
- */
- public DataStream createDataStream(AFPPaintingState paintingState, OutputStream outputStream)
- throws IOException {
- this.dataStream = streamer.createDataStream(paintingState);
- streamer.setOutputStream(outputStream);
- return this.dataStream;
- }
-
- /**
- * Returns the AFP DataStream
- *
- * @return the AFP DataStream
- */
- public DataStream getDataStream() {
- return this.dataStream;
- }
-
- /**
- * Tells the streamer to write
- *
- * @throws IOException thrown if an I/O exception of some sort has occurred.
- */
- public void writeToStream() throws IOException {
- streamer.close();
- }
-
- /**
- * Sets the default resource group URI.
- *
- * @param uri the default resource group URI
- */
-
- public void setDefaultResourceGroupUri(URI uri) {
- streamer.setDefaultResourceGroupUri(uri);
- }
-
- /**
- * Tries to create an include of a data object that has been previously added to the
- * AFP data stream. If no such object was available, the method returns false which serves
- * as a signal that the object has to be created.
- * @param dataObjectInfo the data object info
- * @return true if the inclusion succeeded, false if the object was not available
- * @throws IOException thrown if an I/O exception of some sort has occurred.
- */
- public boolean tryIncludeObject(AFPDataObjectInfo dataObjectInfo) throws IOException {
- AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
- updateResourceInfoUri(resourceInfo);
- return includeCachedObject(resourceInfo, dataObjectInfo.getObjectAreaInfo());
- }
-
- /**
- * Creates a new data object in the AFP datastream
- *
- * @param dataObjectInfo the data object info
- *
- * @throws IOException thrown if an I/O exception of some sort has occurred.
- */
- public void createObject(AFPDataObjectInfo dataObjectInfo) throws IOException {
- if (tryIncludeObject(dataObjectInfo)) {
- //Object has already been produced and is available by inclusion, so return early.
- return;
- }
-
- AbstractNamedAFPObject namedObj = null;
- AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
-
- boolean useInclude = true;
- Registry.ObjectType objectType = null;
-
- // new resource so create
- if (dataObjectInfo instanceof AFPImageObjectInfo) {
- AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)dataObjectInfo;
- namedObj = dataObjectFactory.createImage(imageObjectInfo);
- } else if (dataObjectInfo instanceof AFPGraphicsObjectInfo) {
- AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)dataObjectInfo;
- namedObj = dataObjectFactory.createGraphic(graphicsObjectInfo);
- } else {
- // natively embedded data object
- namedObj = dataObjectFactory.createObjectContainer(dataObjectInfo);
- objectType = dataObjectInfo.getObjectType();
- useInclude = objectType != null && objectType.isIncludable();
- }
-
- AFPResourceLevel resourceLevel = resourceInfo.getLevel();
- ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
-
- useInclude &= resourceGroup != null;
- if (useInclude) {
- final boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
-
- // if it is to reside within a resource group at print-file or external level
- if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
- if (usePageSegment) {
- String pageSegmentName = "S10" + namedObj.getName().substring(3);
- namedObj.setName(pageSegmentName);
- PageSegment seg = new PageSegment(pageSegmentName);
- seg.addObject(namedObj);
- namedObj = seg;
- }
-
- // wrap newly created data object in a resource object
- namedObj = dataObjectFactory.createResource(namedObj, resourceInfo, objectType);
- }
-
- // add data object into its resource group destination
- resourceGroup.addObject(namedObj);
- includeObject(namedObj, dataObjectInfo);
- } else {
- // not to be included so inline data object directly into the current page
- dataStream.getCurrentPage().addObject(namedObj);
- }
- }
-
- private abstract class AbstractCachedObject {
- protected String objectName;
- protected AFPDataObjectInfo dataObjectInfo;
-
- public AbstractCachedObject(String objectName, AFPDataObjectInfo dataObjectInfo) {
- this.objectName = objectName;
- this.dataObjectInfo = dataObjectInfo;
-
-
- }
- protected abstract void includeObject();
- }
-
- private class CachedPageSegment extends AbstractCachedObject {
-
- public CachedPageSegment(String objectName, AFPDataObjectInfo dataObjectInfo) {
- super(objectName, dataObjectInfo);
- }
-
- protected void includeObject() {
- includePageSegment(dataObjectInfo, objectName);
- }
-
- }
-
- private class CachedObject extends AbstractCachedObject {
-
- public CachedObject(String objectName, AFPDataObjectInfo dataObjectInfo) {
- super(objectName, dataObjectInfo);
- }
-
- protected void includeObject() {
- AFPResourceManager.this.includeObject(dataObjectInfo, objectName);
- }
-
- }
-
-
- private void includeObject(AbstractNamedAFPObject namedObj, AFPDataObjectInfo dataObjectInfo) {
-
- // create the include object
- String objectName = namedObj.getName();
-
- AbstractCachedObject cachedObject;
-
- if (dataObjectInfo.isCreatePageSegment()) {
- cachedObject = new CachedPageSegment(objectName, dataObjectInfo);
- } else {
- cachedObject = new CachedObject(objectName, dataObjectInfo);
- }
-
- cachedObject.includeObject();
-
- includeObjectCache.put(dataObjectInfo.getResourceInfo(), cachedObject);
-
- //The data field of dataObjectInfo is not further required
- // therefore we are safe to null the reference, saving memory
- dataObjectInfo.setData(null);
-
- }
-
- /**
- * TODO
- * @param resourceInfo
- * @return
- */
- public boolean isObjectCached(AFPResourceInfo resourceInfo) {
- return includeObjectCache.containsKey(resourceInfo);
- }
-
- /**
- * TODO
- * @param resourceInfo
- * @param areaInfo
- * @return
- */
- public boolean includeCachedObject(AFPResourceInfo resourceInfo, AFPObjectAreaInfo areaInfo) {
-
- String objectName;
-
- AbstractCachedObject cachedObject = includeObjectCache.get(resourceInfo);
-
- if (cachedObject != null) {
- if (areaInfo != null) {
- cachedObject.dataObjectInfo.setObjectAreaInfo(areaInfo);
- }
- cachedObject.includeObject();
-
- return true;
- } else {
- return false;
- }
- }
-
-
- private void updateResourceInfoUri(AFPResourceInfo resourceInfo) {
- String uri = resourceInfo.getUri();
- if (uri == null) {
- uri = "/";
- }
- // if this is an instream data object adjust the uri to ensure that its unique
- if (uri.endsWith("/")) {
- uri += "#" + (++instreamObjectCount);
- resourceInfo.setUri(uri);
- }
- }
-
- private void includeObject(AFPDataObjectInfo dataObjectInfo,
- String objectName) {
- IncludeObject includeObject = dataObjectFactory.createInclude(objectName, dataObjectInfo);
- dataStream.getCurrentPage().addObject(includeObject);
- }
-
- /**
- * Handles font embedding. If a font is embeddable and has not already been embedded it will be.
- * @param afpFont the AFP font to be checked for embedding
- * @param charSet the associated character set
- * @throws IOException if there's a problem while embedding the external resources
- */
- public void embedFont(AFPFont afpFont, CharacterSet charSet)
- throws IOException {
- if (afpFont.isEmbeddable()) {
- //Embed fonts (char sets and code pages)
- if (charSet.getResourceAccessor() != null) {
- AFPResourceAccessor accessor = charSet.getResourceAccessor();
- createIncludedResource(
- charSet.getName(), accessor,
- ResourceObject.TYPE_FONT_CHARACTER_SET);
- createIncludedResource(
- charSet.getCodePage(), accessor,
- ResourceObject.TYPE_CODE_PAGE);
- }
- }
- }
-
- private void includePageSegment(AFPDataObjectInfo dataObjectInfo,
- String pageSegmentName) {
- int x = dataObjectInfo.getObjectAreaInfo().getX();
- int y = dataObjectInfo.getObjectAreaInfo().getY();
- AbstractPageObject currentPage = dataStream.getCurrentPage();
- boolean createHardPageSegments = true;
- currentPage.createIncludePageSegment(pageSegmentName, x, y, createHardPageSegments);
- }
-
- /**
- * Creates an included resource object by loading the contained object from a file.
- * @param resourceName the name of the resource
- * @param accessor resource accessor to access the resource with
- * @param resourceObjectType the resource object type ({@link ResourceObject}.*)
- * @throws IOException if an I/O error occurs while loading the resource
- */
- public void createIncludedResource(String resourceName, AFPResourceAccessor accessor,
- byte resourceObjectType) throws IOException {
- URI uri;
- try {
- uri = new URI(resourceName.trim());
- } catch (URISyntaxException e) {
- throw new IOException("Could not create URI from resource name: " + resourceName
- + " (" + e.getMessage() + ")");
- }
-
- createIncludedResource(resourceName, uri, accessor, resourceObjectType);
- }
-
- /**
- * Creates an included resource object by loading the contained object from a file.
- * @param resourceName the name of the resource
- * @param uri the URI for the resource
- * @param accessor resource accessor to access the resource with
- * @param resourceObjectType the resource object type ({@link ResourceObject}.*)
- * @throws IOException if an I/O error occurs while loading the resource
- */
- public void createIncludedResource(String resourceName, URI uri, AFPResourceAccessor accessor,
- byte resourceObjectType) throws IOException {
- AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE);
-
- AFPResourceInfo resourceInfo = new AFPResourceInfo();
- resourceInfo.setLevel(resourceLevel);
- resourceInfo.setName(resourceName);
- resourceInfo.setUri(uri.toASCIIString());
-
- AbstractCachedObject cachedObject = includeObjectCache.get(resourceInfo);
- if (cachedObject == null) {
- if (log.isDebugEnabled()) {
- log.debug("Adding included resource: " + resourceName);
- }
- IncludedResourceObject resourceContent = new IncludedResourceObject(
- resourceName, accessor, uri);
-
- ResourceObject resourceObject = factory.createResource(resourceName);
- resourceObject.setDataObject(resourceContent);
- resourceObject.setType(resourceObjectType);
-
- ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
- resourceGroup.addObject(resourceObject);
-
- //TODO what is the data object?
- cachedObject = new CachedObject(resourceName, null);
-
- // record mapping of resource info to data object resource name
- includeObjectCache.put(resourceInfo, cachedObject);
- } else {
- //skip, already created
- }
- }
-
- /**
- * Creates an included resource extracting the named resource from an external source.
- * @param resourceName the name of the resource
- * @param uri the URI for the resource
- * @param accessor resource accessor to access the resource with
- * @throws IOException if an I/O error occurs while loading the resource
- */
- public void createIncludedResourceFromExternal(final String resourceName,
- final URI uri, final AFPResourceAccessor accessor) throws IOException {
-
- AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE);
-
- AFPResourceInfo resourceInfo = new AFPResourceInfo();
- resourceInfo.setLevel(resourceLevel);
- resourceInfo.setName(resourceName);
- resourceInfo.setUri(uri.toASCIIString());
-
- AbstractCachedObject cachedObject = includeObjectCache.get(resourceInfo);
- if (cachedObject == null) {
- ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
-
- //resourceObject delegates write commands to copyNamedResource()
- //The included resource may already be wrapped in a resource object
- AbstractNamedAFPObject resourceObject = new AbstractNamedAFPObject(null) {
-
- @Override
- protected void writeContent(OutputStream os) throws IOException {
- InputStream inputStream = null;
- try {
- inputStream = accessor.createInputStream(uri);
- BufferedInputStream bin = new BufferedInputStream(inputStream);
- AFPResourceUtil.copyNamedResource(resourceName, bin, os);
- } finally {
- IOUtils.closeQuietly(inputStream);
- }
- }
-
- //bypass super.writeStart
- @Override
- protected void writeStart(OutputStream os) throws IOException { }
- //bypass super.writeEnd
- @Override
- protected void writeEnd(OutputStream os) throws IOException { }
- };
- resourceGroup.addObject(resourceObject);
- cachedObject = new CachedObject(resourceName, null);
- includeObjectCache.put(resourceInfo, cachedObject);
- }
- }
-
-
- /**
- * Sets resource level defaults. The existing defaults over merged with the ones passed in
- * as parameter.
- * @param defaults the new defaults
- */
- public void setResourceLevelDefaults(AFPResourceLevelDefaults defaults) {
- this.resourceLevelDefaults.mergeFrom(defaults);
- }
-
- /**
- * Returns the resource level defaults in use with this resource manager.
- * @return the resource level defaults
- */
- public AFPResourceLevelDefaults getResourceLevelDefaults() {
- return this.resourceLevelDefaults;
- }
-
- }
|