From: Matthias Reischenbacher Date: Sat, 27 Jul 2019 13:23:59 +0000 (+0000) Subject: FOP-2875: add support for non-ascii characters in pdf file attachment names, fix... X-Git-Tag: fop-2_4~13 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3ad9b01d08803d6076f7fe0d1d72d6f59f79e6ea;p=xmlgraphics-fop.git FOP-2875: add support for non-ascii characters in pdf file attachment names, fix name collisions of attachments git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1863870 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFAction.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFAction.java index cfc95b3c8..9d41628c4 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFAction.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFAction.java @@ -41,7 +41,7 @@ public abstract class PDFAction extends PDFObject { byte[] enc = getDocument().getEncryption().encrypt(buf, this); return PDFText.toHex(enc, true); } else { - return "(" + text + ")"; + return PDFText.escapeText(text, false); } } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java index 81fd0c38a..aad24cae9 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java @@ -686,21 +686,20 @@ public class PDFFactory { } //Find filespec reference for the embedded file - filename = PDFText.toPDFString(filename, '_'); PDFArray files = embeddedFiles.getNames(); - PDFReference embeddedFileRef = null; + PDFFileSpec fileSpec = null; int i = 0; while (i < files.length()) { - String name = (String)files.get(i); i++; PDFReference ref = (PDFReference)files.get(i); - if (name.equals(filename)) { - embeddedFileRef = ref; + if (ref.getObject() instanceof PDFFileSpec + && ((PDFFileSpec)ref.getObject()).getUnicodeFilename().equals(filename)) { + fileSpec = (PDFFileSpec)ref.getObject(); break; } i++; } - if (embeddedFileRef == null) { + if (fileSpec == null) { throw new IllegalStateException( "No embedded file with name " + filename + " present."); } @@ -716,7 +715,7 @@ public class PDFFactory { //This finally seems to work: StringBuffer scriptBuffer = new StringBuffer(); scriptBuffer.append("this.exportDataObject({cName:\""); - scriptBuffer.append(filename); + scriptBuffer.append(fileSpec.getFilename()); scriptBuffer.append("\", nLaunch:2});"); PDFJavaScriptLaunchAction action = new PDFJavaScriptLaunchAction(scriptBuffer.toString()); diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java index f9f990c95..b5531d035 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java @@ -30,18 +30,37 @@ public class PDFFileSpec extends PDFDictionary { * @param filename the filename represented by this object */ public PDFFileSpec(String filename) { + this(filename, filename); + } - /* generic creation of object */ - super(); + /** + * create a /FileSpec object. + * + * @param filename the filename represented by this object + * @param unicodeFilename the unicode filename represented by this object + */ + public PDFFileSpec(String filename, String unicodeFilename) { put("Type", new PDFName("Filespec")); put("F", filename); - put("UF", filename); // for non-ascii filenames, since PDF 1.7, 3.10.2 + put("UF", unicodeFilename); // for non-ascii filenames, since PDF 1.7, 3.10.2 } - private String getFilename() { + /** + * Gets the filename. + * @return filename + */ + public String getFilename() { return (String)get("F"); } + /** + * Gets the unicode filename + * @return unicode filename + */ + public String getUnicodeFilename() { + return (String)get("UF"); + } + /** * Associates an dictionary with pointers to embedded file streams with this file spec. * @param embeddedFileDict the dictionary with pointers to embedded file streams diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java index 23eb0a63a..b230ad879 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFRoot.java @@ -334,13 +334,12 @@ public class PDFRoot extends PDFDictionary { return dPartRoot; } - public void addAF(PDFFileSpec fileSpec, String filename) { + public void addAF(PDFFileSpec fileSpec) { if (af == null) { af = new PDFArray(); put("AF", af); } af.add(fileSpec); - fileSpec.put("UF", filename); fileSpec.put("AFRelationship", new PDFName("Data")); } } diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index 4ad9ee333..5ba2cae1b 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -73,7 +73,6 @@ import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFPageLabels; import org.apache.fop.pdf.PDFReference; import org.apache.fop.pdf.PDFSetOCGStateAction; -import org.apache.fop.pdf.PDFText; import org.apache.fop.pdf.PDFTransitionAction; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.Version; @@ -677,9 +676,9 @@ class PDFRenderingUtil { } PDFDictionary dict = new PDFDictionary(); dict.put("F", file); - String filename = PDFText.toPDFString(embeddedFile.getFilename(), '_'); - PDFFileSpec fileSpec = new PDFFileSpec(filename); - pdfDoc.getRoot().addAF(fileSpec, filename); + PDFFileSpec fileSpec = new PDFFileSpec(embeddedFile.getFilename(), embeddedFile.getUnicodeFilename()); + String filename = fileSpec.getFilename(); + pdfDoc.getRoot().addAF(fileSpec); fileSpec.setEmbeddedFile(dict); if (embeddedFile.getDesc() != null) { fileSpec.setDescription(embeddedFile.getDesc()); @@ -701,8 +700,7 @@ class PDFRenderingUtil { nameArray = new PDFArray(); embeddedFiles.setNames(nameArray); } - String name = PDFText.toPDFString(filename); - nameArray.add(name); + nameArray.add(filename); nameArray.add(new PDFReference(fileSpec)); } diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java index 96e429d69..dd18308e7 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java @@ -23,6 +23,8 @@ import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; +import org.apache.fop.pdf.PDFText; + /** * This is the pass-through value object for the PDF extension. */ @@ -45,6 +47,9 @@ public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment { /** filename attribute */ private String filename; + /** unicode filename attribute */ + private String unicodeFilename; + /** description attribute (optional) */ private String desc; @@ -66,7 +71,7 @@ public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment { */ public PDFEmbeddedFileAttachment(String filename, String src, String desc) { super(); - this.filename = filename; + this.setFilename(filename); this.src = src; this.desc = desc; } @@ -79,12 +84,26 @@ public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment { return filename; } + /** + * Returns the unicode file name. + * @return the file name + */ + public String getUnicodeFilename() { + return unicodeFilename; + } + /** * Sets the file name. * @param name The file name to set. */ public void setFilename(String name) { - this.filename = name; + if (!PDFText.toPDFString(name).equals(name)) { + // replace with auto generated filename, because non-ascii chars are used. + this.filename = "att" + name.hashCode(); + } else { + this.filename = name; + } + this.unicodeFilename = name; } /** diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java index f1e7423a0..2d7e659e3 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java @@ -58,6 +58,52 @@ public class PDFAttachmentTestCase { Assert.assertTrue(out.toString().contains( "<<\n /Type /Filespec\n /F (filename)\n /UF (filename)\n /AFRelationship /Data")); Assert.assertTrue(out.toString().contains("<<\n/S /JavaScript\n" - + "/JS (this.exportDataObject({cName:\"filename\", nLaunch:2});)\n>>")); + + "/JS (this.exportDataObject\\({cName:\"filename\", nLaunch:2}\\);)\n>>")); + } + + @Test + public void testAddEmbeddedFileGermanUmlaut() throws IFException { + PDFDocumentHandler docHandler = new PDFDocumentHandler(new IFContext(ua)); + docHandler.setFontInfo(new FontInfo()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + docHandler.setResult(new StreamResult(out)); + docHandler.startDocument(); + docHandler.startPage(0, "", "", new Dimension()); + + String germanAe = "\u00E4"; + String unicodeFilename = "t" + germanAe + "st"; + PDFEmbeddedFileAttachment fileAtt = new PDFEmbeddedFileAttachment(unicodeFilename, + "src", "desc"); + docHandler.handleExtensionObject(fileAtt); + docHandler.getDocumentNavigationHandler().renderLink(new Link( + new URIAction("embedded-file:" + unicodeFilename, false), new Rectangle())); + docHandler.endDocument(); + Assert.assertTrue(out.toString().contains( + "<<\n /Type /Filespec\n /F (" + fileAtt.getFilename() + ")\n /UF " + + PDFText.escapeText(fileAtt.getUnicodeFilename()) + "\n /AFRelationship /Data")); + Assert.assertTrue(out.toString().contains("<<\n/S /JavaScript\n" + + "/JS (this.exportDataObject\\({cName:\"" + fileAtt.getFilename() + "\", nLaunch:2}\\);)\n>>")); + } + + @Test + public void testAddEmbeddedFileParenthesis() throws IFException { + PDFDocumentHandler docHandler = new PDFDocumentHandler(new IFContext(ua)); + docHandler.setFontInfo(new FontInfo()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + docHandler.setResult(new StreamResult(out)); + docHandler.startDocument(); + docHandler.startPage(0, "", "", new Dimension()); + + String unicodeFilename = "t(st"; + PDFEmbeddedFileAttachment fileAtt = new PDFEmbeddedFileAttachment(unicodeFilename, + "src", "desc"); + docHandler.handleExtensionObject(fileAtt); + docHandler.getDocumentNavigationHandler().renderLink(new Link( + new URIAction("embedded-file:" + unicodeFilename, false), new Rectangle())); + docHandler.endDocument(); + Assert.assertTrue(out.toString().contains( + "<<\n /Type /Filespec\n /F (t\\(st)\n /UF (t\\(st)\n /AFRelationship /Data")); + Assert.assertTrue(out.toString().contains("<<\n/S /JavaScript\n" + + "/JS (this.exportDataObject\\({cName:\"t\\(st\", nLaunch:2}\\);)\n>>")); } } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java index 61cec5d4a..da7a8a0de 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java @@ -167,4 +167,48 @@ public class PDFFactoryTestCase { assertTrue(bos.toString().contains("/Subtype /Type1\n")); assertTrue(bos.toString().contains("/Subtype /Type1C")); } + + @Test + public void testGetExternalAction() { + + String germanAe = "\u00E4"; + String filename = "test"; + String unicodeFilename = "t" + germanAe + "st.pdf"; + PDFFileSpec fileSpec = new PDFFileSpec(filename, unicodeFilename); + + PDFDocument doc = new PDFDocument(""); + doc.registerObject(fileSpec); + PDFNames names = doc.getRoot().getNames(); + if (names == null) { + //Add Names if not already present + names = doc.getFactory().makeNames(); + doc.getRoot().setNames(names); + } + + PDFEmbeddedFiles embeddedFiles = names.getEmbeddedFiles(); + if (embeddedFiles == null) { + embeddedFiles = new PDFEmbeddedFiles(); + doc.assignObjectNumber(embeddedFiles); + doc.addTrailerObject(embeddedFiles); + names.setEmbeddedFiles(embeddedFiles); + } + + PDFArray nameArray = embeddedFiles.getNames(); + if (nameArray == null) { + nameArray = new PDFArray(); + embeddedFiles.setNames(nameArray); + } + nameArray.add(fileSpec.getFilename()); + nameArray.add(new PDFReference(fileSpec)); + + PDFFactory pdfFactory = new PDFFactory(doc); + String target = "embedded-file:" + unicodeFilename; + PDFJavaScriptLaunchAction pdfAction = (PDFJavaScriptLaunchAction) + pdfFactory.getExternalAction(target, false); + + String expectedString = "<<\n/S /JavaScript\n/JS (this.exportDataObject\\({cName:\"" + + fileSpec.getFilename() + "\", nLaunch:2}\\);)\n>>"; + + assertEquals(expectedString, pdfAction.toPDFString()); + } } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFFileSpecTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFFileSpecTestCase.java new file mode 100644 index 000000000..b21b5c0ca --- /dev/null +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFFileSpecTestCase.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id: PDFFactoryTestCase.java 1823552 2018-02-08 12:26:33Z ssteiner $ */ + +package org.apache.fop.pdf; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class PDFFileSpecTestCase { + + @Test + public void testPDFFileSpec() { + String germanAe = "\u00E4"; + String filename = "test"; + String unicodeFilename = "t" + germanAe + "st"; + PDFFileSpec fileSpec = new PDFFileSpec(filename, unicodeFilename); + assertEquals(fileSpec.getUnicodeFilename(), unicodeFilename); + assertEquals(fileSpec.getFilename(), filename); + } +} diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java index 2f5abd4d9..06b859d12 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java @@ -19,6 +19,7 @@ package org.apache.fop.pdf; +import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachmentTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @@ -42,7 +43,10 @@ import org.junit.runners.Suite.SuiteClasses; PDFReferenceTestCase.class, PDFResourcesTestCase.class, VersionTestCase.class, - VersionControllerTestCase.class + VersionControllerTestCase.class, + PDFRootTestCase.class, + PDFFileSpecTestCase.class, + PDFEmbeddedFileAttachmentTest.class }) public class PDFLibraryTestSuite { } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java index 34c81967f..b4c6a5239 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java @@ -391,6 +391,9 @@ public class PDFLinkTestCase { {"examples.html?foo#bar", quote("/URI (examples.html?foo#bar)")}, {"examples.html", quote("<< /URI (examples.html)")}, {"file:examples.html", quote("<< /Type /Filespec /F (examples.html)")}, + + // parenthesis + {"simple_report_(version2.pdf", quote("<< /URI (simple_report_\\(version2.pdf)")} }); } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFRootTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFRootTestCase.java new file mode 100644 index 000000000..67703c1ff --- /dev/null +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFRootTestCase.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id: PDFFactoryTestCase.java 1823552 2018-02-08 12:26:33Z ssteiner $ */ + +package org.apache.fop.pdf; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class PDFRootTestCase { + + @Test + public void testAddAf() { + String germanAe = "\u00E4"; + String unicodeFilename = "t" + germanAe + "st.pdf"; + PDFFileSpec fileSpec = new PDFFileSpec(unicodeFilename); + + String filename = fileSpec.getFilename(); + + PDFDocument doc = new PDFDocument(""); + doc.getRoot().addAF(fileSpec); + + assertEquals(filename, fileSpec.getFilename()); + assertEquals(unicodeFilename, fileSpec.getUnicodeFilename()); + } +} diff --git a/fop-core/src/test/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachmentTest.java b/fop-core/src/test/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachmentTest.java new file mode 100644 index 000000000..6f6c90259 --- /dev/null +++ b/fop-core/src/test/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachmentTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id: PDFRenderingUtil.java 1761019 2016-09-16 10:43:45Z ssteiner $ */ + +package org.apache.fop.render.pdf.extensions; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class PDFEmbeddedFileAttachmentTest { + + @Test + public void testGetFilename() { + String germanAe = "\u00E4"; + String unicodeFilename = "t" + germanAe + "st"; + String src = "src"; + String desc = "desc"; + PDFEmbeddedFileAttachment fileAtt = new PDFEmbeddedFileAttachment(unicodeFilename, src, desc); + assertEquals(fileAtt.getUnicodeFilename(), unicodeFilename); + assertFalse(fileAtt.getFilename().contains(germanAe)); + assertEquals(fileAtt.getSrc(), src); + assertEquals(fileAtt.getDesc(), desc); + } + +}