Author: ssteiner Date: Thu Sep 11 15:05:23 2014 New Revision: 1624313 URL: http://svn.apache.org/r1624313 Log: FOP-2411: PDF to PDF Link not working
Modified: xmlgraphics/fop-pdf-images/trunk/lib/fop.jar xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java Modified: xmlgraphics/fop-pdf-images/trunk/lib/fop.jar URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/lib/fop.jar?rev=1624313&r1=1624312&r2=1624313&view=diff ============================================================================== Binary files - no diff available. Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java?rev=1624313&r1=1624312&r2=1624313&view=diff ============================================================================== --- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java (original) +++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/AbstractPDFBoxHandler.java Thu Sep 11 15:05:23 2014 @@ -36,6 +36,7 @@ import org.apache.xmlgraphics.image.load import org.apache.fop.apps.FOUserAgent; import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.pdf.PDFArray; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFResources; @@ -66,8 +67,8 @@ public abstract class AbstractPDFBoxHand = Collections.synchronizedMap(new WeakHashMap<Object, Cache<String, Map<Object, Object>>>()); protected String createStreamForPDF(ImagePDF image, PDFPage targetPage, FOUserAgent userAgent, - AffineTransform at, FontInfo fontinfo, Rectangle pos) throws IOException { - + AffineTransform at, FontInfo fontinfo, Rectangle pos, Map<Integer, PDFArray> pageNumbers) + throws IOException { EventBroadcaster eventBroadcaster = null; if (userAgent != null) { eventBroadcaster = userAgent.getEventBroadcaster(); @@ -115,9 +116,8 @@ public abstract class AbstractPDFBoxHand targetPage.put("Resources", res); } - PDFBoxAdapter adapter = new PDFBoxAdapter(targetPage, objectCache); - String stream = adapter.createStreamFromPDFBoxPage(pddoc, page, originalImageUri, - eventBroadcaster, at, fontinfo, pos); + PDFBoxAdapter adapter = new PDFBoxAdapter(targetPage, objectCache, pageNumbers); + String stream = adapter.createStreamFromPDFBoxPage(pddoc, page, originalImageUri, at, fontinfo, pos); return stream; } Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java?rev=1624313&r1=1624312&r2=1624313&view=diff ============================================================================== --- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java (original) +++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java Thu Sep 11 15:05:23 2014 @@ -25,11 +25,13 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.Serializable; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -39,6 +41,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; @@ -92,7 +95,6 @@ import org.apache.pdfbox.pdmodel.interac import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.util.operator.PDFOperator; -import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fonts.CIDFontType; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.EmbeddingMode; @@ -127,7 +129,7 @@ public class PDFBoxAdapter { /** logging instance */ protected static final Log log = LogFactory.getLog(PDFBoxAdapter.class); - private static final Set FILTER_FILTER = new java.util.HashSet( + private static final Set FILTER_FILTER = new HashSet( Arrays.asList(new String[] {"Filter", "DecodeParms"})); private static final Pattern SUBSET_PATTERN = Pattern.compile("[A-Z][A-Z][A-Z][A-Z][A-Z][A-Z]\\+.+"); @@ -139,16 +141,19 @@ public class PDFBoxAdapter { private final Map<COSDictionary, PDSimpleFont> fontMap = new HashMap<COSDictionary, PDSimpleFont>(); private Map<COSName, String> newXObj = new HashMap<COSName, String>(); private Collection<String> parentFonts; + private Map<Integer, PDFArray> pageNumbers; /** * Creates a new PDFBoxAdapter. * @param targetPage The target FOP PDF page object * @param objectCache the object cache for reusing objects shared by multiple pages. + * @param pageNumbers references to page object numbers */ - public PDFBoxAdapter(PDFPage targetPage, Map objectCache) { + public PDFBoxAdapter(PDFPage targetPage, Map objectCache, Map<Integer, PDFArray> pageNumbers) { this.targetPage = targetPage; this.pdfDoc = this.targetPage.getDocument(); this.clonedVersion = objectCache; + this.pageNumbers = pageNumbers; } private Object cloneForNewDocument(Object base) throws IOException { @@ -1370,7 +1375,6 @@ public class PDFBoxAdapter { * @param sourceDoc the source PDF the given page to be copied belongs to * @param page the page to transform into a stream * @param key value to use as key for the stream - * @param eventBroadcaster events * @param atdoc adjustment for stream * @param fontinfo fonts * @param pos rectangle @@ -1378,11 +1382,14 @@ public class PDFBoxAdapter { * @throws IOException if an I/O error occurs */ public String createStreamFromPDFBoxPage(PDDocument sourceDoc, PDPage page, String key, - EventBroadcaster eventBroadcaster, AffineTransform atdoc, FontInfo fontinfo, Rectangle pos) + AffineTransform atdoc, FontInfo fontinfo, Rectangle pos) throws IOException { - handleAcroForm(sourceDoc, page, eventBroadcaster, atdoc); + handleAnnotations(sourceDoc, page, atdoc); + if (pageNumbers.containsKey(targetPage.getPageIndex())) { + pageNumbers.get(targetPage.getPageIndex()).set(0, targetPage.makeReference()); + } PDResources sourcePageResources = page.findResources(); - PDFDictionary pageResources = null; + PDFDictionary pageResources; PDStream pdStream = page.getContents(); if (pdStream == null) { return ""; @@ -1500,7 +1507,7 @@ public class PDFBoxAdapter { //no additional transformations necessary break; } - StringBuffer boxStr = new StringBuffer(); + StringBuilder boxStr = new StringBuilder(); boxStr.append(0).append(' ').append(0).append(' '); boxStr.append(PDFNumber.doubleOut(mediaBox.getWidth())).append(' '); boxStr.append(PDFNumber.doubleOut(mediaBox.getHeight())).append(" re W n\n"); @@ -1643,8 +1650,7 @@ public class PDFBoxAdapter { */ } - private void handleAcroForm(PDDocument sourceDoc, PDPage page, - EventBroadcaster eventBroadcaster, AffineTransform at) throws IOException { + private void handleAnnotations(PDDocument sourceDoc, PDPage page, AffineTransform at) throws IOException { PDDocumentCatalog srcCatalog = sourceDoc.getDocumentCatalog(); PDAcroForm srcAcroForm = srcCatalog.getAcroForm(); List pageAnnotations = page.getAnnotations(); @@ -1652,67 +1658,38 @@ public class PDFBoxAdapter { return; } - PDRectangle mediaBox = page.findMediaBox(); - PDRectangle cropBox = page.findCropBox(); - PDRectangle viewBox = cropBox != null ? cropBox : mediaBox; - - for (Object obj : pageAnnotations) { - PDAnnotation annot = (PDAnnotation)obj; - PDRectangle rect = annot.getRectangle(); - rect.move((float)(at.getTranslateX() - viewBox.getLowerLeftX()), - (float)at.getTranslateY() - viewBox.getLowerLeftY()); - } + moveAnnotations(page, pageAnnotations, at); //Pseudo-cache the target page in place of the original source page. //This essentially replaces the original page reference with the target page. COSObject cosPage = null; if (page.getCOSObject() instanceof COSObject) { - cosPage = (COSObject)page.getCOSObject(); + cosPage = (COSObject) page.getCOSObject(); + cacheClonedObject(cosPage, this.targetPage); } else { PDPageNode pageNode = page.getParent(); - - COSArray kids = (COSArray)pageNode.getDictionary().getDictionaryObject(COSName.KIDS); - Iterator iter = kids.iterator(); - while (iter.hasNext()) { + COSArray kids = (COSArray) pageNode.getDictionary().getDictionaryObject(COSName.KIDS); + for (int i = 0; i < kids.size(); i++) { //Hopefully safe to cast, as kids need to be indirect objects - COSObject kid = (COSObject)iter.next(); + COSObject kid = (COSObject) kids.get(i); + if (!pageNumbers.containsKey(i)) { + PDFArray a = new PDFArray(); + a.add(null); + pdfDoc.assignObjectNumber(a); + pdfDoc.addTrailerObject(a); + pageNumbers.put(i, a); + } + cacheClonedObject(kid, pageNumbers.get(i)); if (kid.getObject() == page.getCOSObject()) { cosPage = kid; - break; } } if (cosPage == null) { throw new IOException("Illegal PDF. Page not part of parent page node."); } } - cacheClonedObject(cosPage, this.targetPage); - COSArray annots = (COSArray) page.getCOSDictionary().getDictionaryObject(COSName.ANNOTS); - Set<COSObject> fields = Collections.emptySet(); - if (annots != null) { - fields = new HashSet(); - Iterator iter = annots.iterator(); - while (iter.hasNext()) { - COSObject annot = (COSObject) iter.next(); - COSObject fieldObject = annot; - COSDictionary field = (COSDictionary) fieldObject.getObject(); - if ("Widget".equals(field.getNameAsString(COSName.SUBTYPE))) { - COSObject parent; - while ((parent = (COSObject) field.getItem(COSName.PARENT)) != null) { - fieldObject = parent; - field = (COSDictionary) fieldObject.getObject(); - } - fields.add(fieldObject); - Collection<COSName> exclude = new ArrayList<COSName>(); - exclude.add(COSName.P); - if (((COSDictionary)annot.getObject()).getItem(COSName.getPDFName("StructParent")) != null) { - exclude.add(COSName.PARENT); - } - PDFObject clonedAnnot = (PDFObject) cloneForNewDocument(annot, annot, exclude); - targetPage.addAnnotation(clonedAnnot); - } - } - } + Set<COSObject> fields = copyAnnotations(page); boolean formAlreadyCopied = getCachedClone(srcAcroForm) != null; PDFRoot catalog = this.pdfDoc.getRoot(); @@ -1746,4 +1723,89 @@ public class PDFBoxAdapter { clonedFields.add(clone); } } + + private void updateAnnotationLink(PDFDictionary clonedAnnot) { + PDFDictionary a = (PDFDictionary) clonedAnnot.get("A"); + if (a != null) { + PDFArray oldarray = (PDFArray) a.get("D"); + if (oldarray != null) { + PDFArray newarray = (PDFArray) oldarray.get(0); + if (newarray != null) { + for (int i = 1; i < oldarray.length(); i++) { + newarray.add(oldarray.get(i)); + } + a.put("D", oldarray.get(0)); + } + } + } + } + + private Set<COSObject> copyAnnotations(PDPage page) throws IOException { + COSArray annots = (COSArray) page.getCOSDictionary().getDictionaryObject(COSName.ANNOTS); + Set<COSObject> fields = Collections.emptySet(); + if (annots != null) { + fields = new TreeSet<COSObject>(new CompareFields()); + for (Object annot1 : annots) { + Collection<COSName> exclude = new ArrayList<COSName>(); + exclude.add(COSName.P); + if (annot1 instanceof COSObject) { + COSObject annot = (COSObject) annot1; + COSObject fieldObject = annot; + COSDictionary field = (COSDictionary) fieldObject.getObject(); + COSObject parent; + while ((parent = (COSObject) field.getItem(COSName.PARENT)) != null) { + fieldObject = parent; + field = (COSDictionary) fieldObject.getObject(); + } + fields.add(fieldObject); + + if (((COSDictionary) annot.getObject()).getItem(COSName.getPDFName("StructParent")) != null) { + exclude.add(COSName.PARENT); + } + } + + PDFObject clonedAnnot = (PDFObject) cloneForNewDocument(annot1, annot1, exclude); + if (clonedAnnot instanceof PDFDictionary) { + clonedAnnot.setParent(targetPage); + updateAnnotationLink((PDFDictionary) clonedAnnot); + } + targetPage.addAnnotation(clonedAnnot); + } + } + return fields; + } + + private void moveAnnotations(PDPage page, List pageAnnotations, AffineTransform at) { + PDRectangle mediaBox = page.findMediaBox(); + PDRectangle cropBox = page.findCropBox(); + PDRectangle viewBox = cropBox != null ? cropBox : mediaBox; + for (Object obj : pageAnnotations) { + PDAnnotation annot = (PDAnnotation)obj; + PDRectangle rect = annot.getRectangle(); + float translateX = (float) (at.getTranslateX() - viewBox.getLowerLeftX()); + float translateY = (float) (at.getTranslateY() - viewBox.getLowerLeftY()); + if (rect != null) { + rect.move(translateX, translateY); + annot.setRectangle(rect); + } + COSArray vertices = (COSArray) annot.getDictionary().getDictionaryObject("Vertices"); + if (vertices != null) { + Iterator iter = vertices.iterator(); + while (iter.hasNext()) { + COSFloat x = (COSFloat) iter.next(); + COSFloat y = (COSFloat) iter.next(); + x.setValue(x.floatValue() + translateX); + y.setValue(y.floatValue() + translateY); + } + } + } + } + + static class CompareFields implements Comparator<COSObject>, Serializable { + private static final long serialVersionUID = -6081505461660440801L; + + public int compare(COSObject o1, COSObject o2) { + return o1.getObjectNumber().intValue() - o2.getObjectNumber().intValue(); + } + } } Modified: xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java?rev=1624313&r1=1624312&r2=1624313&view=diff ============================================================================== --- xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java (original) +++ xmlgraphics/fop-pdf-images/trunk/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxImageHandler.java Thu Sep 11 15:05:23 2014 @@ -70,7 +70,7 @@ public class PDFBoxImageHandler extends } FontInfo fontinfo = (FontInfo)context.getHint("fontinfo"); String stream = createStreamForPDF(pdfImage, pdfContext.getPage(), pdfContext.getUserAgent(), - pageAdjust, fontinfo, pos); + pageAdjust, fontinfo, pos, pdfContext.getPageNumbers()); if (stream == null) { return; } Modified: xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java URL: http://svn.apache.org/viewvc/xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java?rev=1624313&r1=1624312&r2=1624313&view=diff ============================================================================== --- xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java (original) +++ xmlgraphics/fop-pdf-images/trunk/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java Thu Sep 11 15:05:23 2014 @@ -25,13 +25,17 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.imageio.ImageIO; +import org.apache.fop.pdf.PDFAnnotList; +import org.apache.fop.pdf.PDFArray; import org.apache.fop.pdf.PDFGState; import org.apache.fop.render.pdf.pdfbox.ImagePDF; import org.apache.fop.render.pdf.pdfbox.PDFBoxImageHandler; @@ -85,12 +89,14 @@ public class PDFBoxAdapterTestCase { private static final String Type1Subset4 = "test/resources/t1subset4.pdf"; private static final String ROTATE = "test/resources/rotate.pdf"; private static final String SHADING = "test/resources/shading.pdf"; + private static final String LINK = "test/resources/link.pdf"; private PDFBoxAdapter getPDFBoxAdapter() { PDFDocument doc = new PDFDocument(""); doc.setMergeFontsEnabled(true); pdfpage.setDocument(doc); - return new PDFBoxAdapter(pdfpage, new HashMap()); + pdfpage.setObjectNumber(1); + return new PDFBoxAdapter(pdfpage, new HashMap(), new HashMap<Integer, PDFArray>()); } @Test @@ -152,7 +158,7 @@ public class PDFBoxAdapterTestCase { PDDocument doc = PDDocument.load(pdf); PDPage page = (PDPage) doc.getDocumentCatalog().getAllPages().get(0); AffineTransform at = new AffineTransform(); - String c = getPDFBoxAdapter().createStreamFromPDFBoxPage(doc, page, pdf, null, at, fi, new Rectangle()); + String c = getPDFBoxAdapter().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>()); @@ -277,12 +283,12 @@ public class PDFBoxAdapterTestCase { @Test public void testStream() throws Exception { pdfpage.setDocument(new PDFDocument("")); - PDFBoxAdapter adapter = new PDFBoxAdapter(pdfpage, new HashMap()); + PDFBoxAdapter adapter = new PDFBoxAdapter(pdfpage, new HashMap(), new HashMap<Integer, PDFArray>()); PDDocument doc = PDDocument.load(ROTATE); PDPage page = (PDPage) doc.getDocumentCatalog().getAllPages().get(0); AffineTransform at = new AffineTransform(); Rectangle r = new Rectangle(0, 1650, 842000, 595000); - String stream = adapter.createStreamFromPDFBoxPage(doc, page, "key", null, at, null, r); + String stream = adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r); Assert.assertEquals(at, new AffineTransform(-0.0, 1.0000000554888686, 1.0000000554888686, 0.0, 0.0, -2.0742416381835938E-5)); Assert.assertTrue(stream.contains("/GS0106079 gs")); @@ -291,6 +297,26 @@ public class PDFBoxAdapterTestCase { } @Test + public void testLink() throws Exception { + pdfpage.setObjectNumber(1); + PDFDocument pdfdoc = new PDFDocument(""); + pdfpage.setDocument(pdfdoc); + Map<Integer, PDFArray> pageNumbers = new HashMap<Integer, PDFArray>(); + PDFBoxAdapter adapter = new PDFBoxAdapter(pdfpage, new HashMap(), pageNumbers); + PDDocument doc = PDDocument.load(LINK); + PDPage page = (PDPage) doc.getDocumentCatalog().getAllPages().get(0); + AffineTransform at = new AffineTransform(); + Rectangle r = new Rectangle(0, 1650, 842000, 595000); + String stream = adapter.createStreamFromPDFBoxPage(doc, page, "key", at, null, r); + Assert.assertTrue(stream.contains("/Link <</MCID 5 >>BDC")); + Assert.assertTrue(pageNumbers.size() == 4); + PDFAnnotList annots = (PDFAnnotList) pdfpage.get("Annots"); + Assert.assertEquals(annots.toPDFString(), "[\n9 0 R\n12 0 R\n]"); +// pdfdoc.output(System.out); + doc.close(); + } + + @Test public void testPreloaderPDF() throws Exception { ImageSource imageSource = new ImageSource(ImageIO.createImageInputStream(new File(ROTATE)), "", true); ImageInfo imageInfo = new PreloaderPDF().preloadImage("", imageSource, new DefaultImageContext()); @@ -310,6 +336,7 @@ public class PDFBoxAdapterTestCase { pdfpage.addGState(g); PDFContentGenerator con = new PDFContentGenerator(pdfdoc, null, null); PDFRenderingContext c = new PDFRenderingContext(null, con, pdfpage, null); + c.setPageNumbers(new HashMap<Integer, PDFArray>()); new PDFBoxImageHandler().handleImage(c, img, new Rectangle()); PDFResources res = c.getPage().getPDFResources(); OutputStream bos = new ByteArrayOutputStream(); --------------------------------------------------------------------- To unsubscribe, e-mail: fop-commits-unsubscr...@xmlgraphics.apache.org For additional commands, e-mail: fop-commits-h...@xmlgraphics.apache.org