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.

AFPResourceManager.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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;
  19. import java.io.BufferedInputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;
  23. import java.net.URI;
  24. import java.net.URISyntaxException;
  25. import java.util.Map;
  26. import org.apache.commons.io.IOUtils;
  27. import org.apache.commons.logging.Log;
  28. import org.apache.commons.logging.LogFactory;
  29. import org.apache.fop.afp.AFPResourceLevel.ResourceType;
  30. import org.apache.fop.afp.fonts.AFPFont;
  31. import org.apache.fop.afp.fonts.CharacterSet;
  32. import org.apache.fop.afp.modca.AbstractNamedAFPObject;
  33. import org.apache.fop.afp.modca.AbstractPageObject;
  34. import org.apache.fop.afp.modca.IncludeObject;
  35. import org.apache.fop.afp.modca.IncludedResourceObject;
  36. import org.apache.fop.afp.modca.PageSegment;
  37. import org.apache.fop.afp.modca.Registry;
  38. import org.apache.fop.afp.modca.ResourceGroup;
  39. import org.apache.fop.afp.modca.ResourceObject;
  40. import org.apache.fop.afp.util.AFPResourceAccessor;
  41. import org.apache.fop.afp.util.AFPResourceUtil;
  42. import org.apache.fop.apps.io.InternalResourceResolver;
  43. /**
  44. * Manages the creation and storage of document resources
  45. */
  46. public class AFPResourceManager {
  47. /** logging instance */
  48. private static Log log = LogFactory.getLog(AFPResourceManager.class);
  49. /** The AFP datastream (document tree) */
  50. private DataStream dataStream;
  51. /** Resource creation factory */
  52. private final Factory factory;
  53. private final AFPStreamer streamer;
  54. private final AFPDataObjectFactory dataObjectFactory;
  55. /** Maintain a reference count of instream objects for referencing purposes */
  56. private int instreamObjectCount = 0;
  57. /** a mapping of resourceInfo --> include name */
  58. private final Map<AFPResourceInfo, String> includeNameMap
  59. = new java.util.HashMap<AFPResourceInfo, String>();
  60. /** a mapping of resourceInfo --> page segment name */
  61. private Map<AFPResourceInfo, String> pageSegmentMap
  62. = new java.util.HashMap<AFPResourceInfo, String>();
  63. private AFPResourceLevelDefaults resourceLevelDefaults = new AFPResourceLevelDefaults();
  64. /**
  65. * Main constructor
  66. */
  67. public AFPResourceManager(InternalResourceResolver resourceResolver) {
  68. this.factory = new Factory();
  69. this.streamer = new AFPStreamer(factory, resourceResolver);
  70. this.dataObjectFactory = new AFPDataObjectFactory(factory);
  71. }
  72. /**
  73. * Sets the outputstream
  74. *
  75. * @param paintingState the AFP painting state
  76. * @param outputStream the outputstream
  77. * @return a new AFP DataStream
  78. * @throws IOException thrown if an I/O exception of some sort has occurred
  79. */
  80. public DataStream createDataStream(AFPPaintingState paintingState, OutputStream outputStream)
  81. throws IOException {
  82. this.dataStream = streamer.createDataStream(paintingState);
  83. streamer.setOutputStream(outputStream);
  84. return this.dataStream;
  85. }
  86. /**
  87. * Returns the AFP DataStream
  88. *
  89. * @return the AFP DataStream
  90. */
  91. public DataStream getDataStream() {
  92. return this.dataStream;
  93. }
  94. /**
  95. * Tells the streamer to write
  96. *
  97. * @throws IOException thrown if an I/O exception of some sort has occurred.
  98. */
  99. public void writeToStream() throws IOException {
  100. streamer.close();
  101. }
  102. /**
  103. * Sets the default resource group file path
  104. *
  105. * @param filePath the default resource group file path
  106. */
  107. public void setDefaultResourceGroupUri(URI uri) {
  108. streamer.setDefaultResourceGroupUri(uri);
  109. }
  110. /**
  111. * Tries to create an include of a data object that has been previously added to the
  112. * AFP data stream. If no such object was available, the method returns false which serves
  113. * as a signal that the object has to be created.
  114. * @param dataObjectInfo the data object info
  115. * @return true if the inclusion succeeded, false if the object was not available
  116. * @throws IOException thrown if an I/O exception of some sort has occurred.
  117. */
  118. public boolean tryIncludeObject(AFPDataObjectInfo dataObjectInfo) throws IOException {
  119. AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
  120. updateResourceInfoUri(resourceInfo);
  121. String objectName = includeNameMap.get(resourceInfo);
  122. if (objectName != null) {
  123. // an existing data resource so reference it by adding an include to the current page
  124. includeObject(dataObjectInfo, objectName);
  125. return true;
  126. }
  127. objectName = pageSegmentMap.get(resourceInfo);
  128. if (objectName != null) {
  129. // an existing data resource so reference it by adding an include to the current page
  130. includePageSegment(dataObjectInfo, objectName);
  131. return true;
  132. }
  133. return false;
  134. }
  135. /**
  136. * Creates a new data object in the AFP datastream
  137. *
  138. * @param dataObjectInfo the data object info
  139. *
  140. * @throws IOException thrown if an I/O exception of some sort has occurred.
  141. */
  142. public void createObject(AFPDataObjectInfo dataObjectInfo) throws IOException {
  143. if (tryIncludeObject(dataObjectInfo)) {
  144. //Object has already been produced and is available by inclusion, so return early.
  145. return;
  146. }
  147. AbstractNamedAFPObject namedObj = null;
  148. AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
  149. boolean useInclude = true;
  150. Registry.ObjectType objectType = null;
  151. // new resource so create
  152. if (dataObjectInfo instanceof AFPImageObjectInfo) {
  153. AFPImageObjectInfo imageObjectInfo = (AFPImageObjectInfo)dataObjectInfo;
  154. namedObj = dataObjectFactory.createImage(imageObjectInfo);
  155. } else if (dataObjectInfo instanceof AFPGraphicsObjectInfo) {
  156. AFPGraphicsObjectInfo graphicsObjectInfo = (AFPGraphicsObjectInfo)dataObjectInfo;
  157. namedObj = dataObjectFactory.createGraphic(graphicsObjectInfo);
  158. } else {
  159. // natively embedded data object
  160. namedObj = dataObjectFactory.createObjectContainer(dataObjectInfo);
  161. objectType = dataObjectInfo.getObjectType();
  162. useInclude = objectType != null && objectType.isIncludable();
  163. }
  164. AFPResourceLevel resourceLevel = resourceInfo.getLevel();
  165. ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
  166. useInclude &= resourceGroup != null;
  167. if (useInclude) {
  168. boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
  169. // if it is to reside within a resource group at print-file or external level
  170. if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
  171. if (usePageSegment) {
  172. String pageSegmentName = "S10" + namedObj.getName().substring(3);
  173. namedObj.setName(pageSegmentName);
  174. PageSegment seg = new PageSegment(pageSegmentName);
  175. seg.addObject(namedObj);
  176. namedObj = seg;
  177. }
  178. // wrap newly created data object in a resource object
  179. namedObj = dataObjectFactory.createResource(namedObj, resourceInfo, objectType);
  180. }
  181. // add data object into its resource group destination
  182. resourceGroup.addObject(namedObj);
  183. // create the include object
  184. String objectName = namedObj.getName();
  185. if (usePageSegment) {
  186. includePageSegment(dataObjectInfo, objectName);
  187. pageSegmentMap.put(resourceInfo, objectName);
  188. } else {
  189. includeObject(dataObjectInfo, objectName);
  190. // record mapping of resource info to data object resource name
  191. includeNameMap.put(resourceInfo, objectName);
  192. }
  193. } else {
  194. // not to be included so inline data object directly into the current page
  195. dataStream.getCurrentPage().addObject(namedObj);
  196. }
  197. }
  198. private void updateResourceInfoUri(AFPResourceInfo resourceInfo) {
  199. String uri = resourceInfo.getUri();
  200. if (uri == null) {
  201. uri = "/";
  202. }
  203. // if this is an instream data object adjust the uri to ensure that its unique
  204. if (uri.endsWith("/")) {
  205. uri += "#" + (++instreamObjectCount);
  206. resourceInfo.setUri(uri);
  207. }
  208. }
  209. private void includeObject(AFPDataObjectInfo dataObjectInfo,
  210. String objectName) {
  211. IncludeObject includeObject = dataObjectFactory.createInclude(objectName, dataObjectInfo);
  212. dataStream.getCurrentPage().addObject(includeObject);
  213. }
  214. /**
  215. * Handles font embedding. If a font is embeddable and has not already been embedded it will be.
  216. * @param afpFont the AFP font to be checked for embedding
  217. * @param charSet the associated character set
  218. * @throws IOException if there's a problem while embedding the external resources
  219. */
  220. public void embedFont(AFPFont afpFont, CharacterSet charSet)
  221. throws IOException {
  222. if (afpFont.isEmbeddable()) {
  223. //Embed fonts (char sets and code pages)
  224. if (charSet.getResourceAccessor() != null) {
  225. AFPResourceAccessor accessor = charSet.getResourceAccessor();
  226. createIncludedResource(
  227. charSet.getName(), accessor,
  228. ResourceObject.TYPE_FONT_CHARACTER_SET);
  229. createIncludedResource(
  230. charSet.getCodePage(), accessor,
  231. ResourceObject.TYPE_CODE_PAGE);
  232. }
  233. }
  234. }
  235. private void includePageSegment(AFPDataObjectInfo dataObjectInfo,
  236. String pageSegmentName) {
  237. int x = dataObjectInfo.getObjectAreaInfo().getX();
  238. int y = dataObjectInfo.getObjectAreaInfo().getY();
  239. AbstractPageObject currentPage = dataStream.getCurrentPage();
  240. boolean createHardPageSegments = true;
  241. currentPage.createIncludePageSegment(pageSegmentName, x, y, createHardPageSegments);
  242. }
  243. /**
  244. * Creates an included resource object by loading the contained object from a file.
  245. * @param resourceName the name of the resource
  246. * @param accessor resource accessor to access the resource with
  247. * @param resourceObjectType the resource object type ({@link ResourceObject}.*)
  248. * @throws IOException if an I/O error occurs while loading the resource
  249. */
  250. public void createIncludedResource(String resourceName, AFPResourceAccessor accessor,
  251. byte resourceObjectType) throws IOException {
  252. URI uri;
  253. try {
  254. uri = new URI(resourceName.trim());
  255. } catch (URISyntaxException e) {
  256. throw new IOException("Could not create URI from resource name: " + resourceName
  257. + " (" + e.getMessage() + ")");
  258. }
  259. createIncludedResource(resourceName, uri, accessor, resourceObjectType);
  260. }
  261. /**
  262. * Creates an included resource object by loading the contained object from a file.
  263. * @param resourceName the name of the resource
  264. * @param uri the URI for the resource
  265. * @param accessor resource accessor to access the resource with
  266. * @param resourceObjectType the resource object type ({@link ResourceObject}.*)
  267. * @throws IOException if an I/O error occurs while loading the resource
  268. */
  269. public void createIncludedResource(String resourceName, URI uri, AFPResourceAccessor accessor,
  270. byte resourceObjectType) throws IOException {
  271. AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE);
  272. AFPResourceInfo resourceInfo = new AFPResourceInfo();
  273. resourceInfo.setLevel(resourceLevel);
  274. resourceInfo.setName(resourceName);
  275. resourceInfo.setUri(uri.toASCIIString());
  276. String objectName = includeNameMap.get(resourceInfo);
  277. if (objectName == null) {
  278. if (log.isDebugEnabled()) {
  279. log.debug("Adding included resource: " + resourceName);
  280. }
  281. IncludedResourceObject resourceContent = new IncludedResourceObject(
  282. resourceName, accessor, uri);
  283. ResourceObject resourceObject = factory.createResource(resourceName);
  284. resourceObject.setDataObject(resourceContent);
  285. resourceObject.setType(resourceObjectType);
  286. ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
  287. resourceGroup.addObject(resourceObject);
  288. // record mapping of resource info to data object resource name
  289. includeNameMap.put(resourceInfo, resourceName);
  290. } else {
  291. //skip, already created
  292. }
  293. }
  294. /**
  295. * Creates an included resource extracting the named resource from an external source.
  296. * @param resourceName the name of the resource
  297. * @param uri the URI for the resource
  298. * @param accessor resource accessor to access the resource with
  299. * @throws IOException if an I/O error occurs while loading the resource
  300. */
  301. public void createIncludedResourceFromExternal(final String resourceName,
  302. final URI uri, final AFPResourceAccessor accessor) throws IOException {
  303. AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE);
  304. AFPResourceInfo resourceInfo = new AFPResourceInfo();
  305. resourceInfo.setLevel(resourceLevel);
  306. resourceInfo.setName(resourceName);
  307. resourceInfo.setUri(uri.toASCIIString());
  308. String resource = includeNameMap.get(resourceInfo);
  309. if (resource == null) {
  310. ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
  311. //resourceObject delegates write commands to copyNamedResource()
  312. //The included resource may already be wrapped in a resource object
  313. AbstractNamedAFPObject resourceObject = new AbstractNamedAFPObject(null) {
  314. @Override
  315. protected void writeContent(OutputStream os) throws IOException {
  316. InputStream inputStream = null;
  317. try {
  318. inputStream = accessor.createInputStream(uri);
  319. BufferedInputStream bin = new BufferedInputStream(inputStream);
  320. AFPResourceUtil.copyNamedResource(resourceName, bin, os);
  321. } finally {
  322. IOUtils.closeQuietly(inputStream);
  323. }
  324. }
  325. //bypass super.writeStart
  326. @Override
  327. protected void writeStart(OutputStream os) throws IOException { }
  328. //bypass super.writeEnd
  329. @Override
  330. protected void writeEnd(OutputStream os) throws IOException { }
  331. };
  332. resourceGroup.addObject(resourceObject);
  333. includeNameMap.put(resourceInfo, resourceName);
  334. }
  335. }
  336. /**
  337. * Sets resource level defaults. The existing defaults over merged with the ones passed in
  338. * as parameter.
  339. * @param defaults the new defaults
  340. */
  341. public void setResourceLevelDefaults(AFPResourceLevelDefaults defaults) {
  342. this.resourceLevelDefaults.mergeFrom(defaults);
  343. }
  344. /**
  345. * Returns the resource level defaults in use with this resource manager.
  346. * @return the resource level defaults
  347. */
  348. public AFPResourceLevelDefaults getResourceLevelDefaults() {
  349. return this.resourceLevelDefaults;
  350. }
  351. }