I know how Itext works and PDFBox does not work the same. In Itext the
result is that only the right fields are flattened. It seems there is a bug.
In PdfBox every field is flattened, even though I delivered a list of
fields, that were meant to be flattened.
It seems the PDF is destroyed by flattening.
I have attached the code.
On 25.05.21 19:27, Tilman Hausherr wrote:
> Here's a PDF flattened by itext
> https://github.com/itext/i7js-examples/blob/develop/cmpfiles/sandbox/acroforms/cmp_checkbox_flatten.pdf
>
> and they do something similar to what we do, i.e. remove the fields
> and converting it to form XObjects.
> Tilman
>
> Am 25.05.2021 um 19:03 schrieb [email protected]:
>> Am Dienstag, dem 25.05.2021 um 18:33 +0200 schrieb Ranjeet Kuruvilla:
>>> Hallo.
>>> It is clear, that flattening does have a bug. Compare flattening of
>>> PDFBox with IText and you realize, that PDFBox destroys all fields,
>>> once
>>> I call
>>>
>>> acroform.flatten(fields, true) or acroform.flatten(fields, false)
>>>
>>>
>> the purpose of flatten is that the flattened fields are removed and
>> become part of the regular page content.
>>
>> If you'd like to keep the field but would like it to be protected you
>> have to set the field to read only. Keep in mind that one could use a
>> lib and remove the flag and change the content afterwards.
>>
>> Now, if there is really a bug I need to have a clear description how to
>> reproduce it together with sample content. If you're able to provide
>> that I'm happy to take a look.
>>
>> BR
>> Maruan
>>
>>> .
>>>
>>> How can I request someone to fix flattening and make it work like in
>>> IText. That is that the right fields are flattened while all the other
>>> fields remain untouched!
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: [email protected]
>>> For additional commands, e-mail: [email protected]
>>>
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [email protected]
>> For additional commands, e-mail: [email protected]
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
package de.ejb.sf;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.log4j.Logger;
import org.apache.pdfbox.Loader;
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.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDCheckBox;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDRadioButton;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.XMPSchema;
import org.apache.xmpbox.xml.XmpSerializer;
import de.ejb.sf.PDFMetadataInfos;
/**
* Manipulator
*
* @author F-Sebastian.Felsl
* @version $Revision:$<br>
* $Date:$<br>
* $Author:$
*/
public class Manipulator
{
/** Die CM_VERSION. */
public static final String CM_VERSION = "$Revision:$ $HeadURL:$";
@SuppressWarnings("unused")
private static final Logger LOGGER = Logger.getLogger(Manipulator.class);
private PDDocument document;
/**
* F�gt dem �bergebenen PDF den generierten HashCode aus den AusgabeInfos hinzu
*
* @param mitRisikoFragen mitRisikoFragen
* @param erstellungAntragAlsZeitpunkt erstellungAntragAlsZeitpunkt
* @param hashCode hashCode
* @return Neues PDF mit HashCode
*/
public byte[] modifyDocument(boolean mitRisikoFragen, Calendar erstellungAntragAlsZeitpunkt, String hashCode)
{
try
{
final boolean wasEncrypted = document.isEncrypted(); // In some cases the document is not
// supposed to be encrypted.
// document.setAllSecurityToBeRemoved(true);
alterMetaData(erstellungAntragAlsZeitpunkt, hashCode);
prepareSpecialFields(mitRisikoFragen);
if (wasEncrypted)
{
FieldManipulator.secureDocument(document);
}
byte[] pdfData = FieldManipulator.docToByte(document);
// checkPdf(pdfData);
return pdfData;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
private void attachCheckBoxAppearanceStream(PDAnnotationWidget widget)
{
PDAppearanceDictionary apD = widget.getAppearance();
if (apD == null)
{
apD = new PDAppearanceDictionary();
widget.setAppearance(apD);
}
try
{
PDAppearanceCharacteristicsDictionary acD = widget.getAppearanceCharacteristics();
if (acD == null)
{
acD = new PDAppearanceCharacteristicsDictionary(widget.getCOSObject());
widget.setAppearanceCharacteristics(acD);
}
PDBorderStyleDictionary bD = widget.getBorderStyle();
if (bD == null)
{
bD = new PDBorderStyleDictionary(widget.getCOSObject());
widget.setBorderStyle(bD);
}
acD.setNormalCaption(FieldManipulator.NORMAL_CAPTION);
acD.setAlternateCaption(FieldManipulator.NORMAL_CAPTION);
acD.setRolloverCaption(FieldManipulator.NORMAL_CAPTION);
final float lineWidth = getLineWidth(widget);
final PDRectangle rect = widget.getRectangle();
bD.setWidth(lineWidth);
bD.setStyle(PDBorderStyleDictionary.STYLE_SOLID);
PDAppearanceStream aS = new PDAppearanceStream(document);
aS.setBBox(new PDRectangle(rect.getWidth(), rect.getHeight()));
PDResources res = new PDResources();
res.add(FieldManipulator.CHECKBOX_FONT);
aS.setResources(res);
aS.setFormType(1);
PDPageContentStream pcAPCS = new PDPageContentStream(document, aS);
pcAPCS.setLineWidth(lineWidth); // border style (dash) ignored
pcAPCS.addRect(0, 0, rect.getWidth(), rect.getHeight());
pcAPCS.beginText();
pcAPCS.setFont(FieldManipulator.CHECKBOX_FONT, rect.getHeight());
pcAPCS.setStrokingColor(FieldManipulator.MARK_COLOR);
pcAPCS.setNonStrokingColor(FieldManipulator.MARK_COLOR);
pcAPCS.newLineAtOffset(lineWidth * 2, lineWidth * 2);
pcAPCS.showText(FieldManipulator.checkBoxGlyph);
pcAPCS.endText();
pcAPCS.close();
final COSDictionary normalAppearanceDict =
(COSDictionary) (apD.getNormalAppearance()).getCOSObject();
final COSDictionary downAppearanceDict =
(COSDictionary) (apD.getDownAppearance()).getCOSObject();
final COSDictionary rollAppearanceDict =
(COSDictionary) (apD.getRolloverAppearance()).getCOSObject();
normalAppearanceDict.keySet()
.stream()
.forEach(x -> normalAppearanceDict.setItem(x, aS));
rollAppearanceDict.keySet()
.stream()
.forEach(x -> rollAppearanceDict.setItem(x, aS));
downAppearanceDict.keySet()
.stream()
.forEach(x -> downAppearanceDict.setItem(x, aS));
}
catch (IOException exception)
{
}
}
private float getLineWidth(PDAnnotationWidget widget)
{
PDBorderStyleDictionary bs = widget.getBorderStyle();
return (bs != null) ? bs.getWidth() : 1;
}
private void changeColor(COSDictionary dict, String style)
{
dict.setString(COSName.DA, style);
dict.setString(COSName.DS, style);
dict.setNeedToBeUpdated(true);
}
private void markCheckBox(PDField field)
{
List<PDAnnotationWidget> widgets = field.getWidgets();
if (widgets == null)
{
return;
}
LOGGER.debug("Checkbox " + field.getFullyQualifiedName() + " has " + widgets.size() + ".");
for (PDAnnotationWidget widget : widgets)
{
changeColor(widget.getCOSObject(), FieldManipulator.checkboxStyle);
attachCheckBoxAppearanceStream(widget);
}
}
private void markTextfield(PDField field)
{
List<PDAnnotationWidget> widgets = field.getWidgets();
if (widgets == null)
{
return;
}
LOGGER.debug("Textfeld " + field.getFullyQualifiedName() + " has " + widgets.size() + ".");
for (PDAnnotationWidget widget : widgets)
{
changeColor(widget.getCOSObject(), FieldManipulator.textfieldStyle);
}
}
private void alterMetaData(Calendar erstellungAntragAlsZeitpunkt, String hashCode)
{
final PDDocumentInformation documentInfo = document.getDocumentInformation();
if(hashCode != null)
{
documentInfo.setCustomMetadataValue(PDFMetadataInfos.HASH, hashCode);
}
if(erstellungAntragAlsZeitpunkt != null)
{
documentInfo.setCreationDate(erstellungAntragAlsZeitpunkt);
}
try
{
PDDocumentCatalog catalog = document.getDocumentCatalog();
XMPMetadata xmpMetadata = XMPMetadata.createXMPMetadata();
xmpMetadata.addSchema(new XMPSchema(xmpMetadata));
PDMetadata metadata = catalog.getMetadata();
if (metadata == null)
{
metadata = new PDMetadata(document);
}
XmpSerializer serializer = new XmpSerializer();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
serializer.serialize(xmpMetadata, buffer, false);
metadata.importXMPMetadata(buffer.toByteArray());
catalog.setMetadata(metadata);
}
catch (Exception exception)
{
}
}
private void prepareSpecialFields(boolean mitRisikoFragen)
{
try
{
if (document == null)
{
return;
}
final PDDocumentCatalog catalog = document.getDocumentCatalog();
if (catalog == null)
{
return;
}
PDAcroForm acroForm = catalog.getAcroForm();
if (acroForm == null || acroForm.getFields() == null || acroForm.getFields()
.isEmpty())
{
return;
}
PDResources res = acroForm.getDefaultResources();
if (res == null)
{
res = new PDResources();
}
res.put(FieldManipulator.TEXTFIELD_FONTTYPE, FieldManipulator.TEXTFIELD_FONT);
res.put(FieldManipulator.CHECKBOX_FONTTYPE, FieldManipulator.CHECKBOX_FONT);
acroForm.setDefaultResources(res);
markESignFields(mitRisikoFragen);
final List<PDField> acroFieldsNonEdit = new ArrayList<>();
final List<PDField> acroFieldsEdit = new ArrayList<>();
for (int i = acroForm.getFields()
.size() - 1; i >= 0; i--)
{
PDField field = acroForm.getFields()
.get(i);
if (!((field instanceof PDTextField) || (field instanceof PDCheckBox)
|| (field instanceof PDRadioButton)))
{
LOGGER.debug("Feld " + field.getFullyQualifiedName()
+ " is not relevant and will not be altered.");
continue;
}
if (field.isReadOnly())
{
LOGGER.debug(
"Feld " + field.getFullyQualifiedName() + " is readonly and will be flattened.");
if(field.getValueAsString().isEmpty())
{
acroForm.getFields().remove(field);
}
else
{
acroFieldsNonEdit.add(0, field);
}
}
else
{
acroFieldsEdit.add(0, field);
}
}
// acroForm.flatten(acroFieldsNonEdit, true); // refreshAppearance = false will make the document
for(PDField field: acroFieldsEdit)
{
// unusable in eDocBox!
if (field instanceof PDTextField)
{
LOGGER.debug("Textfield " + field.getFullyQualifiedName() + " is not readonly.");
markTextfield(field);
}
else if (field instanceof PDCheckBox)
{
LOGGER.debug("Checkbox " + field.getFullyQualifiedName() + " is not readonly.");
markCheckBox(field);
}
else if (field instanceof PDRadioButton)
{
LOGGER.debug("Checkbox " + field.getFullyQualifiedName() + " is not readonly.");
markCheckBox(field);
}
}
// acroForm.flatten();
acroForm.refreshAppearances(acroFieldsEdit);
acroForm.setNeedAppearances(true);
}
catch (Exception exception)
{
exception.printStackTrace();
}
}
/**
* @param doc doc
* @return PDDocument
* @throws Exception Exception
*/
public PDDocument transformToPdf(byte[] doc) throws Exception
{
@SuppressWarnings("unused")
PDDocument reader = Loader.loadPDF(doc, FieldManipulator.PDF_PASSWORD);
// reader.setVersion(1.8f);// PDF
// einlesen
// um es
// auf
// Integrität
// zu
// prüfen
return reader;
}
/**
*
*/
public void close()
{
FieldManipulator.close(document);
}
/**
* @param source source
* @throws SpecialException SpecialException
*/
public void consumeDocument(byte[] source) throws SpecialException
{
try
{
document = transformToPdf(source);
}
catch (final Exception exception)
{
throw new SpecialException(ANTRAG_PDF_NICHT_LESBAR);
}
}
/**
* Liefert documentFromBase64BinaryString.
*
* @return liefert documentFromBase64BinaryString.
*/
public PDDocument getPDDocument()
{
return document;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]