/*
* 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.
*/
package org.apache.pdfbox.pdmodel.interactive.form;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.rendering.TestPDFToImage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Test for the PDButton class.
*
*/
public class PDAcroFormTest
{
private PDDocument document;
private PDAcroForm acroForm;
private static final File OUT_DIR = new File("target/test-output");
private static final File IN_DIR = new File("src/test/resources/org/apache/pdfbox/pdmodel/interactive/form");
@Before
public void setUp()
{
document = new PDDocument();
acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
}
@Test
public void testFieldsEntry()
{
// the /Fields entry has been created with the AcroForm
// as this is a required entry
assertNotNull(acroForm.getFields());
assertEquals(acroForm.getFields().size(),0);
// there shouldn't be an exception if there is no such field
assertNull(acroForm.getField("foo"));
// remove the required entry which is the case for some
// PDFs (see PDFBOX-2965)
acroForm.getCOSObject().removeItem(COSName.FIELDS);
// ensure there is always an empty collection returned
assertNotNull(acroForm.getFields());
assertEquals(acroForm.getFields().size(),0);
// there shouldn't be an exception if there is no such field
assertNull(acroForm.getField("foo"));
}
@Test
public void testAcroFormProperties()
{
assertTrue(acroForm.getDefaultAppearance().isEmpty());
acroForm.setDefaultAppearance("/Helv 0 Tf 0 g");
assertEquals(acroForm.getDefaultAppearance(),"/Helv 0 Tf 0 g");
}
@Test
public void testFlatten() throws IOException
{
PDDocument testPdf = PDDocument.load(new File(IN_DIR, "AlignmentTests.pdf"));
testPdf.getDocumentCatalog().getAcroForm().flatten();
assertTrue(testPdf.getDocumentCatalog().getAcroForm().getFields().isEmpty());
File file = new File(OUT_DIR, "AlignmentTests-flattened.pdf");
testPdf.save(file);
// compare rendering
TestPDFToImage testPDFToImage = new TestPDFToImage(TestPDFToImage.class.getName());
if (!testPDFToImage.doTestFile(file, IN_DIR.getAbsolutePath(), OUT_DIR.getAbsolutePath()))
{
// don't fail, rendering is different on different systems, result must be viewed manually
System.out.println("Rendering of " + file + " failed or is not identical to expected rendering in " + IN_DIR + " directory");
}
}
/*
* Same as above but remove the page reference from the widget annotation
* before doing the flatten() to ensure that the widgets page reference is properly looked up
* (PDFBOX-3301)
*/
@Test
public void testFlattenWidgetNoRef() throws IOException
{
PDDocument testPdf = PDDocument.load(new File(IN_DIR, "AlignmentTests.pdf"));
PDAcroForm acroForm = testPdf.getDocumentCatalog().getAcroForm();
for (PDField field : acroForm.getFieldTree()) {
for (PDAnnotationWidget widget : field.getWidgets()) {
widget.getCOSObject().removeItem(COSName.P);
}
}
testPdf.getDocumentCatalog().getAcroForm().flatten();
assertTrue(testPdf.getDocumentCatalog().getAcroForm().getFields().isEmpty());
File file = new File(OUT_DIR, "AlignmentTests-flattened-noRef.pdf");
testPdf.save(file);
// compare rendering
TestPDFToImage testPDFToImage = new TestPDFToImage(TestPDFToImage.class.getName());
if (!testPDFToImage.doTestFile(file, IN_DIR.getAbsolutePath(), OUT_DIR.getAbsolutePath()))
{
// don't fail, rendering is different on different systems, result must be viewed manually
System.out.println("Rendering of " + file + " failed or is not identical to expected rendering in " + IN_DIR + " directory");
}
}
/*
* Test that we do not modify an AcroForm with missing resource information
* when loading the document only.
* (PDFBOX-3752)
*/
@Test
public void testDontAddMissingInformationOnDocumentLoad()
{
try
{
byte[] pdfBytes = createAcroFormWithMissingResourceInformation();
PDDocument pdfDocument = PDDocument.load(pdfBytes);
// do a low level access to the AcroForm to avoid the generation of missing entries
PDDocumentCatalog documentCatalog = pdfDocument.getDocumentCatalog();
COSDictionary catalogDictionary = documentCatalog.getCOSObject();
COSDictionary acroFormDictionary = (COSDictionary) catalogDictionary.getDictionaryObject(COSName.ACRO_FORM);
// ensure that the missing information has not been generated
assertNull(acroFormDictionary.getDictionaryObject(COSName.DA));
assertNull(acroFormDictionary.getDictionaryObject(COSName.RESOURCES));
pdfDocument.close();
}
catch (IOException e)
{
System.err.println("Couldn't create test document, test skipped");
return;
}
}
/*
* Test that we add missing ressouce information to an AcroForm
* when accessing the AcroForm on the PD level
* (PDFBOX-3752)
*/
@Test
public void testAddMissingInformationOnAcroFormAccess()
{
try
{
byte[] pdfBytes = createAcroFormWithMissingResourceInformation();
PDDocument pdfDocument = PDDocument.load(pdfBytes);
PDDocumentCatalog documentCatalog = pdfDocument.getDocumentCatalog();
// this call shall trigger the generation of missing information
PDAcroForm theAcroForm = documentCatalog.getAcroForm();
// ensure that the missing information has been generated
// DA entry
assertEquals("/Helv 0 Tf 0 g ", theAcroForm.getDefaultAppearance());
assertNotNull(theAcroForm.getDefaultResources());
// DR entry
PDResources acroFormResources = theAcroForm.getDefaultResources();
assertNotNull(acroFormResources.getFont(COSName.getPDFName("Helv")));
assertEquals("Helvetica", acroFormResources.getFont(COSName.getPDFName("Helv")).getName());
assertNotNull(acroFormResources.getFont(COSName.getPDFName("ZaDb")));
assertEquals("ZapfDingbats", acroFormResources.getFont(COSName.getPDFName("ZaDb")).getName());
pdfDocument.close();
}
catch (IOException e)
{
System.err.println("Couldn't create test document, test skipped");
return;
}
}
@After
public void tearDown() throws IOException
{
document.close();
}
private byte[] createAcroFormWithMissingResourceInformation() throws IOException
{
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDAcroForm newAcroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(newAcroForm);
PDTextField textBox = new PDTextField(newAcroForm);
textBox.setPartialName("SampleField");
newAcroForm.getFields().add(textBox);
PDAnnotationWidget widget = textBox.getWidgets().get(0);
PDRectangle rect = new PDRectangle(50, 750, 200, 20);
widget.setRectangle(rect);
widget.setPage(page);
page.getAnnotations().add(widget);
// acroForm.setNeedAppearances(true);
// acroForm.getField("SampleField").getCOSObject().setString(COSName.V, "content");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos); // this is a working PDF
document.close();
return baos.toByteArray();
}
}