Вы действительно нашли ошибку в iText 5 PdfCleanUpProcessor
: Она удаляет все встроенные изображения, которые не подлежат частичной редакции.
Ошибкаподробно
Ошибка находится в методе PdfCleanUpRenderListener
renderImage
:
public void renderImage(ImageRenderInfo renderInfo) {
List<Rectangle> areasToBeCleaned = getImageAreasToBeCleaned(renderInfo);
if (areasToBeCleaned == null) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} else if ( areasToBeCleaned.size() > 0) {
try {
PdfImageObject pdfImage = renderInfo.getImage();
byte[] imageBytes = processImage(pdfImage.getImageAsBytes(), areasToBeCleaned);
if (renderInfo.getRef() == null && pdfImage != null) { // true => inline image
PdfDictionary dict = pdfImage.getDictionary();
PdfObject imageMask = dict.get(PdfName.IMAGEMASK);
Image image = Image.getInstance(imageBytes);
if (imageMask == null) {
imageMask = dict.get(PdfName.IM);
}
if (imageMask != null && imageMask.equals(PdfBoolean.PDFTRUE)) {
image.makeMask();
}
PdfContentByte canvas = getContext().getCanvas();
canvas.addImage(image, 1, 0, 0, 1, 0, 0, true);
} else if (pdfImage != null && imageBytes != pdfImage.getImageAsBytes()) {
chunks.add(new PdfCleanUpContentChunk.Image(true, imageBytes));
}
} catch (UnsupportedPdfException pdfException) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Здесь для встроенных изображений требуется специальная обработка: для не встроенных изображений действительная инструкция для рисования изображенияXObject обрабатывается в другом месте, и renderImage
нужно только проверить, нужно ли редактировать изображение, и предоставить версию отредактированного изображения.Для встроенных изображений, однако, этот метод также должен добавить изображение в поток содержимого результата (если оно не полностью удалено).
Как вы можете видеть, в блоке есть специальная обработка встроенных изображений дляизображения, частично покрытые областями редактирования (areasToBeCleaned.size() > 0
), но не относящиеся к изображениям, не покрытым областями редактирования (areasToBeCleaned != null
и areasToBeCleaned.size() == 0
).
Как это исправить
Вы можете это исправитьдобавив аналогичную специальную обработку в новом предложении else
:
public void renderImage(ImageRenderInfo renderInfo) {
List<Rectangle> areasToBeCleaned = getImageAreasToBeCleaned(renderInfo);
if (areasToBeCleaned == null) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} else if ( areasToBeCleaned.size() > 0) {
try {
PdfImageObject pdfImage = renderInfo.getImage();
byte[] imageBytes = processImage(pdfImage.getImageAsBytes(), areasToBeCleaned);
if (renderInfo.getRef() == null && pdfImage != null) { // true => inline image
PdfDictionary dict = pdfImage.getDictionary();
PdfObject imageMask = dict.get(PdfName.IMAGEMASK);
Image image = Image.getInstance(imageBytes);
if (imageMask == null) {
imageMask = dict.get(PdfName.IM);
}
if (imageMask != null && imageMask.equals(PdfBoolean.PDFTRUE)) {
image.makeMask();
}
PdfContentByte canvas = getContext().getCanvas();
canvas.addImage(image, 1, 0, 0, 1, 0, 0, true);
} else if (pdfImage != null && imageBytes != pdfImage.getImageAsBytes()) {
chunks.add(new PdfCleanUpContentChunk.Image(true, imageBytes));
}
} catch (UnsupportedPdfException pdfException) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else { // add inline images not subject to redaction to the content
try {
PdfImageObject pdfImage = renderInfo.getImage();
if (renderInfo.getRef() == null && pdfImage != null) { // true => inline image
PdfDictionary dict = pdfImage.getDictionary();
PdfObject imageMask = dict.get(PdfName.IMAGEMASK);
Image image = Image.getInstance(pdfImage.getImageAsBytes());
if (imageMask == null) {
imageMask = dict.get(PdfName.IM);
}
if (imageMask != null && imageMask.equals(PdfBoolean.PDFTRUE)) {
image.makeMask();
}
PdfContentByte canvas = getContext().getCanvas();
canvas.addImage(image, 1, 0, 0, 1, 0, 0, true);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Пример
Чтобы проиллюстрировать проблему и эффект исправления, я создал простой PDF-файл с пятью встроенными изображениями:
Document document = new Document(new Rectangle(500, 500));
PdfWriter writer = PdfWriter.getInstance(document, baos);
document.open();
PdfContentByte canvas = writer.getDirectContent();
for (int i = 0; i < 5; i++) {
canvas.addImage(image, 50, 0, 0, 50, i * 100 + 25, i * 100 + 25, true);
}
document.close();
( RedactWithImageIssue вспомогательный метод createPdfWithInlineImages
)
выглядит следующим образом:
Применение такой редакции
PdfReader reader = new PdfReader(pdf);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
List<com.itextpdf.text.pdf.pdfcleanup.PdfCleanUpLocation> locations = new ArrayList<>();
locations.add(new com.itextpdf.text.pdf.pdfcleanup.PdfCleanUpLocation(1, new Rectangle(150, 150, 350, 350), BaseColor.RED));
new PdfCleanUpProcessor(locations, stamper).cleanUp();
stamper.close();
( RedactWithImageIssue test testRedactPdfWithInlineImages
)
бези с патчем соответственно приводит к
AsВы можете видеть, что первоначально оставались только частично отредактированные встроенные изображения, но с встроенными изображениями патча, полностью находящимися за пределами области редактирования, также остаются.