Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

AFPResourceManager.java 18KB

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