in pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java [1483:1596]
public void write(PDDocument doc, SignatureInterface signInterface) throws IOException
{
pdDocument = doc;
COSDocument cosDoc = pdDocument.getDocument();
COSDictionary trailer = cosDoc.getTrailer();
if (incrementalUpdate)
{
trailer.toIncrement().exclude(trailer).forEach(base -> {
objectsToWrite.add(base);
if (base instanceof COSObject)
{
actualsAdded.add(((COSObject) base).getObject());
}
else
{
actualsAdded.add(base);
}
});
}
signatureInterface = signInterface;
number = pdDocument.getDocument().getHighestXRefObjectNumber();
if (incrementalUpdate)
{
prepareIncrement();
}
long idTime = pdDocument.getDocumentId() == null ? System.currentTimeMillis()
: pdDocument.getDocumentId();
// if the document says we should remove encryption, then we shouldn't encrypt
if (doc.isAllSecurityToBeRemoved())
{
willEncrypt = false;
// also need to get rid of the "Encrypt" in the trailer so readers
// don't try to decrypt a document which is not encrypted
trailer.removeItem(COSName.ENCRYPT);
}
else
{
if (pdDocument.getEncryption() != null)
{
if (!incrementalUpdate)
{
SecurityHandler<? extends ProtectionPolicy> securityHandler =
pdDocument.getEncryption().getSecurityHandler();
if (!securityHandler.hasProtectionPolicy())
{
throw new IllegalStateException("PDF contains an encryption dictionary, please remove it with "
+ "setAllSecurityToBeRemoved() or set a protection policy with protect()");
}
securityHandler.prepareDocumentForEncryption(pdDocument);
}
willEncrypt = true;
}
else
{
willEncrypt = false;
}
}
COSArray idArray;
boolean missingID = true;
COSBase base = trailer.getDictionaryObject(COSName.ID);
if (base instanceof COSArray)
{
idArray = (COSArray) base;
if (idArray.size() == 2)
{
missingID = false;
}
}
else
{
idArray = new COSArray();
}
if( missingID || incrementalUpdate)
{
MessageDigest sha256;
try
{
sha256 = MessageDigest.getInstance("SHA-256");
}
catch (NoSuchAlgorithmException e)
{
// should never happen
throw new RuntimeException(e);
}
// algorithm says to use time/path/size/values in doc to generate the id.
// we don't have path or size, so do the best we can
sha256.update(Long.toString(idTime).getBytes(StandardCharsets.ISO_8859_1));
COSDictionary info = trailer.getCOSDictionary(COSName.INFO);
if( info != null )
{
for (COSBase cosBase : info.getValues())
{
sha256.update(cosBase.toString().getBytes(StandardCharsets.ISO_8859_1));
}
}
// reuse origin documentID if available as first value
COSString firstID = missingID ? new COSString(sha256.digest()) : (COSString) idArray.get(0);
// it's ok to use the same ID for the second part if the ID is created for the first time
COSString secondID = missingID ? firstID : new COSString(sha256.digest());
idArray = new COSArray();
idArray.add( firstID );
idArray.add( secondID );
trailer.setItem(COSName.ID, idArray);
}
cosDoc.accept(this);
if (!incrementalUpdate)
{
cosDoc.setHighestXRefObjectNumber(number);
}
}