FOP-2882: Allow PDFFormXObject to improve performance
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop-pdf-images/trunk@1866665 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/fop.jar b/lib/fop.jar
index 3242ade..844011d 100644
--- a/lib/fop.jar
+++ b/lib/fop.jar
Binary files differ
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java b/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
index 3612bef..758f1b6 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java
@@ -49,7 +49,7 @@
*/
public abstract class AbstractPDFBoxHandler {
- protected String createStreamForPDF(ImagePDF image, PDFPage targetPage, FOUserAgent userAgent,
+ protected Object createStreamForPDF(ImagePDF image, PDFPage targetPage, FOUserAgent userAgent,
AffineTransform at, FontInfo fontinfo, Rectangle pos,
Map<Integer, PDFArray> pageNumbers,
PDFLogicalStructureHandler handler,
@@ -108,8 +108,7 @@
if (handler != null) {
adapter.setCurrentMCID(handler.getPageParentTree().length());
}
- String stream = adapter.createStreamFromPDFBoxPage(pddoc, page, originalImageUri,
- at, fontinfo, pos);
+ Object stream = adapter.createStreamFromPDFBoxPage(pddoc, page, originalImageUri, at, fontinfo, pos);
if (userAgent.isAccessibilityEnabled() && curentSessionElem != null) {
TaggedPDFConductor conductor = new TaggedPDFConductor(curentSessionElem, handler, page, adapter);
conductor.handleLogicalStructure(pddoc);
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
index 55cf5af..0841c6d 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
@@ -21,6 +21,7 @@
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -69,6 +70,7 @@
import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFormXObject;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFObject;
@@ -341,8 +343,8 @@
* @return the stream
* @throws IOException if an I/O error occurs
*/
- public String createStreamFromPDFBoxPage(PDDocument sourceDoc, PDPage page, String key,
- AffineTransform atdoc, FontInfo fontinfo, Rectangle pos)
+ public Object createStreamFromPDFBoxPage(PDDocument sourceDoc, PDPage page, String key,
+ AffineTransform atdoc, FontInfo fontinfo, Rectangle pos)
throws IOException {
handleAnnotations(sourceDoc, page, atdoc);
if (pageNumbers.containsKey(targetPage.getPageIndex())) {
@@ -359,24 +361,18 @@
fontsBackup = new COSDictionary(fonts);
MergeFontsPDFWriter m = new MergeFontsPDFWriter(fonts, fontinfo, uniqueName, parentFonts, currentMCID);
newStream = m.writeText(pdStream);
-// if (newStream != null) {
-// for (Object f : fonts.keySet().toArray()) {
-// COSDictionary fontdata = (COSDictionary)fonts.getDictionaryObject((COSName)f);
-// if (getUniqueFontName(fontdata) != null) {
-// fonts.removeItem((COSName)f);
-// }
-// }
-// }
}
- if (newStream == null) {
- newStream = (String) clonedVersion.get(key);
+ if (!pdfDoc.isFormXObjectEnabled()) {
if (newStream == null) {
- PDFWriter writer = new PDFWriter(uniqueName, currentMCID);
- newStream = writer.writeText(pdStream);
- clonedVersion.put(key, newStream);
+ newStream = (String) clonedVersion.get(key);
+ if (newStream == null) {
+ PDFWriter writer = new PDFWriter(uniqueName, currentMCID);
+ newStream = writer.writeText(pdStream);
+ clonedVersion.put(key, newStream);
+ }
}
+ pdStream = new PDStream(sourceDoc, new ByteArrayInputStream(newStream.getBytes("ISO-8859-1")));
}
- pdStream = new PDStream(sourceDoc, new ByteArrayInputStream(newStream.getBytes("ISO-8859-1")));
mergeXObj(sourcePageResources, fontinfo, uniqueName);
PDFDictionary pageResources = (PDFDictionary)cloneForNewDocument(sourcePageResources);
@@ -419,6 +415,11 @@
if (pageStream == null) {
pageStream = new PDFStream();
}
+
+ if (pdfDoc.isFormXObjectEnabled()) {
+ return getFormXObject(pageResources, pageStream, key, page);
+ }
+
if (originalPageContents != null) {
transferDict(originalPageContents, pageStream, filter);
}
@@ -464,6 +465,57 @@
return pdStream;
}
+ private PDFFormXObject getFormXObject(PDFDictionary pageResources, PDFStream pageStream, String key, PDPage page)
+ throws IOException {
+ if (pdfDoc.isMergeFontsEnabled()) {
+ throw new RuntimeException("merge-fonts and form-xobject can't both be enabled");
+ }
+ if (!pageResources.hasObjectNumber()) {
+ pdfDoc.registerObject(pageResources);
+ }
+ PDFFormXObject form = pdfDoc.addFormXObject(null, pageStream, pageResources.makeReference(), key);
+ final Set<String> page2Form = new HashSet<String>(Arrays.asList("Group", "LastModified", "Metadata"));
+ transferDict(page.getCOSObject(), pageStream, page2Form, true);
+
+ AffineTransform at = form.getMatrix();
+ PDRectangle mediaBox = page.getMediaBox();
+ PDRectangle cropBox = page.getCropBox();
+ PDRectangle viewBox = cropBox != null ? cropBox : mediaBox;
+
+ //Handle the /Rotation entry on the page dict
+ int rotation = PDFUtil.getNormalizedRotation(page);
+
+ //Transform to FOP's user space
+ at.scale(1 / viewBox.getWidth(), 1 / viewBox.getHeight());
+ at.translate(mediaBox.getLowerLeftX() - viewBox.getLowerLeftX(),
+ mediaBox.getLowerLeftY() - viewBox.getLowerLeftY());
+ switch (rotation) {
+ case 90:
+ at.scale(viewBox.getWidth() / viewBox.getHeight(), viewBox.getHeight() / viewBox.getWidth());
+ at.translate(0, viewBox.getWidth());
+ at.rotate(-Math.PI / 2.0);
+ break;
+ case 180:
+ at.translate(viewBox.getWidth(), viewBox.getHeight());
+ at.rotate(-Math.PI);
+ break;
+ case 270:
+ at.scale(viewBox.getWidth() / viewBox.getHeight(), viewBox.getHeight() / viewBox.getWidth());
+ at.translate(viewBox.getHeight(), 0);
+ at.rotate(-Math.PI * 1.5);
+ break;
+ default:
+ //no additional transformations necessary
+ break;
+ }
+ form.setMatrix(at);
+
+ form.setBBox(new Rectangle2D.Float(
+ viewBox.getLowerLeftX(), viewBox.getLowerLeftY(),
+ viewBox.getUpperRightX(), viewBox.getUpperRightY()));
+ return form;
+ }
+
private COSDictionary getResources(PDPage page) {
PDResources res = page.getResources();
if (res == null) {
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java
index 3e20aa7..88631f7 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java
@@ -30,6 +30,7 @@
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.pdf.PDFXObject;
import org.apache.fop.render.ImageHandler;
import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.pdf.PDFContentGenerator;
@@ -58,6 +59,7 @@
try {
float x = (float)pos.getX() / 1000f;
float y = (float)pos.getY() / 1000f;
+ float w = (float)pos.getWidth() / 1000f;
float h = (float)pos.getHeight() / 1000f;
AffineTransform pageAdjust = new AffineTransform();
@@ -68,17 +70,21 @@
(float)(generator.getState().getTransform().getTranslateY() - h - y));
}
FontInfo fontinfo = (FontInfo)context.getHint("fontinfo");
- String stream = createStreamForPDF(pdfImage, pdfContext.getPage(), pdfContext.getUserAgent(),
+ Object stream = createStreamForPDF(pdfImage, pdfContext.getPage(), pdfContext.getUserAgent(),
pageAdjust, fontinfo, pos, pdfContext.getPageNumbers(),
pdfContext.getPdfLogicalStructureHandler(), pdfContext.getCurrentSessionStructElem());
if (stream == null) {
return;
}
- if (pageAdjust.getScaleX() != 0) {
- pageAdjust.translate(x * (1 / pageAdjust.getScaleX()), -y * (1 / -pageAdjust.getScaleY()));
+ if (stream instanceof String) {
+ if (pageAdjust.getScaleX() != 0) {
+ pageAdjust.translate(x * (1 / pageAdjust.getScaleX()), -y * (1 / -pageAdjust.getScaleY()));
+ }
+ generator.placeImage(pageAdjust, (String) stream);
+ } else {
+ generator.placeImage(x, y, w, h, (PDFXObject) stream);
}
- generator.placeImage(pageAdjust, stream);
pdfImage.close();
} catch (Throwable t) {
throw new RuntimeException(
diff --git a/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
index cb78dd2..5ffd10b 100644
--- a/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
@@ -74,6 +74,7 @@
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFFormXObject;
import org.apache.fop.pdf.PDFGState;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFResources;
@@ -125,10 +126,11 @@
return new PDFPage(new PDFResources(doc), 0, r, r, r, r);
}
- protected static PDFBoxAdapter getPDFBoxAdapter(boolean mergeFonts) {
+ protected static PDFBoxAdapter getPDFBoxAdapter(boolean mergeFonts, boolean formXObject) {
PDFDocument doc = new PDFDocument("");
PDFPage pdfpage = getPDFPage(doc);
doc.setMergeFontsEnabled(mergeFonts);
+ doc.setFormXObjectEnabled(formXObject);
pdfpage.setDocument(doc);
pdfpage.setObjectNumber(1);
return new PDFBoxAdapter(pdfpage, new HashMap(), new HashMap<Integer, PDFArray>());
@@ -221,24 +223,21 @@
PDDocument doc = PDDocument.load(new File(pdf));
PDPage page = doc.getPage(0);
AffineTransform at = new AffineTransform();
- String c = getPDFBoxAdapter(true).createStreamFromPDFBoxPage(doc, page, pdf, at, fi, new Rectangle());
-// PDResources sourcePageResources = page.findResources();
-// COSDictionary fonts = (COSDictionary)sourcePageResources.getCOSDictionary().getDictionaryObject(COSName.FONT);
-// PDFBoxAdapter.PDFWriter w = adapter. new MergeFontsPDFWriter(fonts, fi, "", new ArrayList<COSName>());
-// String c = w.writeText(page.getContents());
+ String c = (String) getPDFBoxAdapter(true, false)
+ .createStreamFromPDFBoxPage(doc, page, pdf, at, fi, new Rectangle());
doc.close();
return c;
}
@Test
public void testTaggedPDFWriter() throws IOException {
- PDFBoxAdapter adapter = getPDFBoxAdapter(false);
+ PDFBoxAdapter adapter = getPDFBoxAdapter(false, false);
adapter.setCurrentMCID(5);
PDDocument doc = PDDocument.load(new File(HELLOTagged));
PDPage page = doc.getPage(0);
AffineTransform at = new AffineTransform();
Rectangle r = new Rectangle(0, 1650, 842000, 595000);
- String stream = adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r);
+ String stream = (String) adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r);
Assert.assertTrue(stream, stream.contains("/P <</MCID 5 >>BDC"));
doc.close();
}
@@ -265,7 +264,7 @@
@Test
public void testAnnot2() throws Exception {
- PDFBoxAdapter adapter = getPDFBoxAdapter(false);
+ PDFBoxAdapter adapter = getPDFBoxAdapter(false, false);
PDDocument doc = PDDocument.load(new File(ANNOT));
PDPage page = doc.getPage(0);
COSArray annots = (COSArray) page.getCOSObject().getDictionaryObject(COSName.ANNOTS);
@@ -290,7 +289,7 @@
PDPage page = doc.getPage(0);
AffineTransform at = new AffineTransform();
Rectangle r = new Rectangle(0, 1650, 842000, 595000);
- String stream = adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r);
+ String stream = (String) adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r);
Assert.assertTrue(stream.contains("/Link <</MCID 5 >>BDC"));
Assert.assertEquals(pageNumbers.size(), 4);
PDFAnnotList annots = (PDFAnnotList) pdfpage.get("Annots");
@@ -518,7 +517,7 @@
pdfpage, objectCachePerFile, new HashMap<Integer, PDFArray>(), pdfCache);
PDDocument doc = PDDocument.load(new File(pdf));
PDPage page = doc.getPage(0);
- String stream = adapter.createStreamFromPDFBoxPage(
+ String stream = (String) adapter.createStreamFromPDFBoxPage(
doc, page, pdf, new AffineTransform(), null, new Rectangle());
doc.close();
return stream;
@@ -566,7 +565,7 @@
PDPage page = doc.getPage(0);
page.setResources(null);
AffineTransform at = new AffineTransform();
- getPDFBoxAdapter(false).createStreamFromPDFBoxPage(doc, page, CFF1, at, new FontInfo(), new Rectangle());
+ getPDFBoxAdapter(false, false).createStreamFromPDFBoxPage(doc, page, CFF1, at, new FontInfo(), new Rectangle());
doc.close();
}
@@ -586,4 +585,33 @@
c.setPageNumbers(new HashMap<Integer, PDFArray>());
new PDFBoxImageHandler().handleImage(c, img, new Rectangle());
}
+
+ @Test
+ public void testMergeFontsAndFormXObject() throws IOException {
+ String msg = "";
+ PDDocument doc = PDDocument.load(new File(IMAGE));
+ PDPage page = doc.getPage(0);
+ AffineTransform at = new AffineTransform();
+ try {
+ getPDFBoxAdapter(true, true)
+ .createStreamFromPDFBoxPage(doc, page, IMAGE, at, new FontInfo(), new Rectangle());
+ } catch (RuntimeException e) {
+ msg = e.getMessage();
+ }
+ doc.close();
+ Assert.assertEquals(msg, "merge-fonts and form-xobject can't both be enabled");
+ }
+
+ @Test
+ public void testFormXObject() throws IOException {
+ PDDocument doc = PDDocument.load(new File(IMAGE));
+ PDPage page = doc.getPage(0);
+ AffineTransform at = new AffineTransform();
+ PDFFormXObject formXObject = (PDFFormXObject) getPDFBoxAdapter(false, true)
+ .createStreamFromPDFBoxPage(doc, page, IMAGE, at, new FontInfo(), new Rectangle());
+ doc.close();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ formXObject.output(bos);
+ Assert.assertTrue(bos.toString("UTF-8").contains("/Type /XObject"));
+ }
}
diff --git a/test/java/org/apache/fop/render/pdf/PDFRotateTestCase.java b/test/java/org/apache/fop/render/pdf/PDFRotateTestCase.java
index 08561bf..fc69e8a 100644
--- a/test/java/org/apache/fop/render/pdf/PDFRotateTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/PDFRotateTestCase.java
@@ -75,13 +75,13 @@
}
private AffineTransform getTransform(int angle) throws IOException {
- PDFBoxAdapter adapter = PDFBoxAdapterTestCase.getPDFBoxAdapter(false);
+ PDFBoxAdapter adapter = PDFBoxAdapterTestCase.getPDFBoxAdapter(false, false);
PDDocument doc = PDDocument.load(new File(PDFBoxAdapterTestCase.ROTATE));
PDPage page = doc.getPage(0);
page.setRotation(angle);
AffineTransform at = new AffineTransform();
Rectangle r = new Rectangle(0, 1650, 842000, 595000);
- String stream = adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r);
+ String stream = (String) adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r);
Assert.assertTrue(stream.contains("/GS0106079 gs"));
Assert.assertTrue(stream.contains("/TT0106079 1 Tf"));
doc.close();