Skip to content

Commit 401897a

Browse files
FOP-3293: Support for caching of pdf image object streams
1 parent 0e03314 commit 401897a

17 files changed

+520
-15
lines changed

fop-core/src/main/java/org/apache/fop/apps/FOUserAgent.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import org.apache.fop.render.intermediate.IFDocumentHandler;
7070
import org.apache.fop.util.ColorSpaceCache;
7171
import org.apache.fop.util.ContentHandlerFactoryRegistry;
72+
import org.apache.fop.util.ImageObjectCache;
7273

7374
/**
7475
* This is the user agent for FOP.
@@ -827,6 +828,11 @@ public ImageHandlerRegistry getImageHandlerRegistry() {
827828
return factory.getImageHandlerRegistry();
828829
}
829830

831+
/** @return the image object cache */
832+
public ImageObjectCache getImageObjectCache() {
833+
return factory.getImageObjectCache();
834+
}
835+
830836
/** @return the color space cache */
831837
public ColorSpaceCache getColorSpaceCache() {
832838
return factory.getColorSpaceCache();

fop-core/src/main/java/org/apache/fop/apps/FopConfParser.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@ private void configure(final URI baseURI, final ResourceResolver resourceResolve
209209
}
210210
}
211211

212+
if (cfg.getChild("image-cache", false) != null) {
213+
try {
214+
fopFactoryBuilder.setImageCache(cfg.getChild("image-cache").getValueAsBoolean());
215+
} catch (ConfigurationException e) {
216+
LogUtil.handleException(LOG, e, false);
217+
}
218+
}
219+
212220
// base definitions for relative path resolution
213221
if (cfg.getChild("base", false) != null) {
214222
try {

fop-core/src/main/java/org/apache/fop/apps/FopFactory.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import org.apache.fop.render.XMLHandlerRegistry;
5757
import org.apache.fop.util.ColorSpaceCache;
5858
import org.apache.fop.util.ContentHandlerFactoryRegistry;
59+
import org.apache.fop.util.ImageObjectCache;
60+
import org.apache.fop.util.ImageObjectCacheImpl;
5961

6062
/**
6163
* Factory class which instantiates new Fop and FOUserAgent instances. This
@@ -86,6 +88,7 @@ public final class FopFactory implements ImageContext {
8688

8789
private final ColorSpaceCache colorSpaceCache;
8890

91+
private final ImageObjectCache imageObjectCache;
8992
private final FopFactoryConfig config;
9093

9194
private final InternalResourceResolver resolver;
@@ -103,6 +106,11 @@ private FopFactory(FopFactoryConfig config) {
103106
config.getResourceResolver());
104107
this.elementMappingRegistry = new ElementMappingRegistry(this);
105108
this.colorSpaceCache = new ColorSpaceCache(resolver);
109+
if (config.isImageCacheEnabled()) {
110+
this.imageObjectCache = new ImageObjectCacheImpl();
111+
} else {
112+
this.imageObjectCache = new ImageObjectCache();
113+
}
106114
this.rendererFactory = new RendererFactory(config.preferRenderer());
107115
this.xmlHandlers = new XMLHandlerRegistry();
108116
this.imageHandlers = new ImageHandlerRegistry();
@@ -495,6 +503,14 @@ FallbackResolver getFallbackResolver() {
495503
return config.getFallbackResolver();
496504
}
497505

506+
/**
507+
* Returns the ImageObjectCache for this instance.
508+
* @return the image object cache
509+
*/
510+
public ImageObjectCache getImageObjectCache() {
511+
return this.imageObjectCache;
512+
}
513+
498514
/**
499515
* Returns the color space cache for this instance.
500516
* <p>

fop-core/src/main/java/org/apache/fop/apps/FopFactoryBuilder.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ public FopFactoryBuilder setKeepEmptyTags(boolean b) {
172172
return this;
173173
}
174174

175+
public FopFactoryBuilder setImageCache(boolean b) {
176+
fopFactoryConfigBuilder.setImageCache(b);
177+
return this;
178+
}
179+
175180
/**
176181
* Sets the {@link LayoutManagerMaker} so that users can configure how FOP creates
177182
* {@link org.apache.fop.layoutmgr.LayoutManager}s.
@@ -392,6 +397,8 @@ public static class FopFactoryConfigImpl implements FopFactoryConfig {
392397

393398
private boolean keepEmptyTags = true;
394399

400+
private boolean imageCache = true;
401+
395402
private LayoutManagerMaker layoutManagerMaker;
396403

397404
private URI baseURI;
@@ -468,6 +475,10 @@ public boolean isKeepEmptyTags() {
468475
return keepEmptyTags;
469476
}
470477

478+
public boolean isImageCacheEnabled() {
479+
return imageCache;
480+
}
481+
471482
/** {@inheritDoc} */
472483
public LayoutManagerMaker getLayoutManagerMakerOverride() {
473484
return layoutManagerMaker;
@@ -601,6 +612,8 @@ private interface FopFactoryConfigBuilder {
601612

602613
void setKeepEmptyTags(boolean b);
603614

615+
void setImageCache(boolean b);
616+
604617
void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker);
605618

606619
void setBaseURI(URI baseURI);
@@ -669,6 +682,10 @@ public void setKeepEmptyTags(boolean b) {
669682
throwIllegalStateException();
670683
}
671684

685+
public void setImageCache(boolean b) {
686+
throwIllegalStateException();
687+
}
688+
672689
public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
673690
throwIllegalStateException();
674691

@@ -784,6 +801,10 @@ public void setKeepEmptyTags(boolean b) {
784801
config.keepEmptyTags = b;
785802
}
786803

804+
public void setImageCache(boolean b) {
805+
config.imageCache = b;
806+
}
807+
787808
public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
788809
config.layoutManagerMaker = lmMaker;
789810
}

fop-core/src/main/java/org/apache/fop/apps/FopFactoryConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ public interface FopFactoryConfig {
7777

7878
boolean isKeepEmptyTags();
7979

80+
boolean isImageCacheEnabled();
81+
8082
/**
8183
* Returns the overriding LayoutManagerMaker instance, if any.
8284
* @return the overriding LayoutManagerMaker or null

fop-core/src/main/java/org/apache/fop/pdf/AlphaRasterImage.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public class AlphaRasterImage implements PDFImage {
4141
private Raster alpha;
4242
private String key;
4343

44+
private ImageObjectStream imageObjectStream;
45+
4446
/**
4547
* Create a alpha channel image.
4648
* Creates a new bitmap image with the given data.
@@ -248,6 +250,13 @@ public PDFFilter getPDFFilter() {
248250
}
249251

250252
/** {@inheritDoc} */
253+
public void outputImageData(OutputStream out) throws IOException {
254+
if (imageObjectStream == null) {
255+
imageObjectStream = new ImageObjectStream(this);
256+
}
257+
imageObjectStream.outputImageData(out);
258+
}
259+
251260
public boolean multipleFiltersAllowed() {
252261
return true;
253262
}

fop-core/src/main/java/org/apache/fop/pdf/BitmapImage.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public class BitmapImage implements PDFImage {
4040
private PDFFilter pdfFilter;
4141
private boolean multipleFiltersAllowed = true;
4242

43+
private ImageObjectStream imageObjectStream;
44+
4345
/**
4446
* Create a bitmap image.
4547
* Creates a new bitmap image with the given data.
@@ -217,6 +219,14 @@ public void setPDFFilter(PDFFilter pdfFilter) {
217219
this.pdfFilter = pdfFilter;
218220
}
219221

222+
/** {@inheritDoc} */
223+
public void outputImageData(OutputStream out) throws IOException {
224+
if (imageObjectStream == null) {
225+
imageObjectStream = new ImageObjectStream(this);
226+
}
227+
imageObjectStream.outputImageData(out);
228+
}
229+
220230
/** {@inheritDoc} */
221231
public boolean multipleFiltersAllowed() {
222232
return multipleFiltersAllowed;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
18+
package org.apache.fop.pdf;
19+
20+
import java.io.ByteArrayOutputStream;
21+
import java.io.IOException;
22+
import java.io.OutputStream;
23+
24+
public class ImageObjectStream {
25+
26+
private PDFImage pdfImage;
27+
private byte[] pdfStreamBytes;
28+
29+
public ImageObjectStream(PDFImage img) {
30+
pdfImage = img;
31+
}
32+
33+
public void outputImageData(OutputStream out) throws IOException {
34+
if (pdfStreamBytes == null) {
35+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
36+
pdfImage.outputContents(baos);
37+
pdfStreamBytes = baos.toByteArray();
38+
}
39+
out.write(pdfStreamBytes);
40+
}
41+
}

fop-core/src/main/java/org/apache/fop/pdf/PDFImage.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import java.io.IOException;
2323
import java.io.OutputStream;
2424

25+
import org.apache.fop.util.ImageObject;
26+
2527
/**
2628
* Interface for a PDF image.
2729
* This is used for inserting an image into PDF.
2830
*/
29-
public interface PDFImage {
31+
public interface PDFImage extends ImageObject {
3032

3133
/**
3234
* Key to look up XObject.
@@ -158,6 +160,13 @@ public interface PDFImage {
158160
*/
159161
String getFilterHint();
160162

163+
/**
164+
* Writes a possibly previously cached stream of bytes that represent the image.
165+
* @param out OutputStream to write to
166+
* @throws IOException if issues happen when writing the stream
167+
*/
168+
void outputImageData(OutputStream out) throws IOException;
169+
161170
/**
162171
* Indicates whether multiple image filters are allowed; this is implemented because Adobe
163172
* Reader does not like multiple FlateDecode filters applied to an image even though that

fop-core/src/main/java/org/apache/fop/pdf/PDFImageXObject.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.io.ByteArrayOutputStream;
2424
import java.io.IOException;
2525
import java.io.OutputStream;
26+
import java.lang.ref.SoftReference;
2627
import java.util.Set;
2728
import java.util.UUID;
2829

@@ -42,6 +43,7 @@
4243
public class PDFImageXObject extends PDFXObject {
4344

4445
private PDFImage pdfimage;
46+
private SoftReference<PDFImage> softReferencePdfimage;
4547

4648
/**
4749
* create an XObject with the given number and name and load the
@@ -54,6 +56,7 @@ public PDFImageXObject(int xnumber, PDFImage img) {
5456
super();
5557
put("Name", new PDFName("Im" + xnumber));
5658
pdfimage = img;
59+
softReferencePdfimage = new SoftReference<>(img);
5760
}
5861

5962
/**
@@ -67,7 +70,7 @@ public PDFImageXObject(int xnumber, PDFImage img) {
6770
public int output(OutputStream stream) throws IOException {
6871
if (getDocument().getProfile().isPDFVTActive()) {
6972
ByteArrayOutputStream baos = new ByteArrayOutputStream();
70-
pdfimage.outputContents(baos);
73+
pdfimage.outputImageData(baos);
7174
put("GTS_XID", "uuid:" + UUID.nameUUIDFromBytes(baos.toByteArray()));
7275
}
7376
int length = super.output(stream);
@@ -150,7 +153,7 @@ private void populateDictionaryFromImage() {
150153

151154
/** {@inheritDoc} */
152155
protected void outputRawStreamData(OutputStream out) throws IOException {
153-
pdfimage.outputContents(out);
156+
pdfimage.outputImageData(out);
154157
}
155158

156159
/** {@inheritDoc} */
@@ -174,6 +177,14 @@ protected String getDefaultFilterName() {
174177
return pdfimage.getFilterHint();
175178
}
176179

180+
/**
181+
* Returns the associated PDFImage.
182+
* @return the PDFImage associated with this XObject
183+
*/
184+
public PDFImage getPDFImage() {
185+
return softReferencePdfimage.get();
186+
}
187+
177188
/** {@inheritDoc} */
178189
protected boolean multipleFiltersAllowed() {
179190
return pdfimage.multipleFiltersAllowed();

0 commit comments

Comments
 (0)