package jnpf.fileStorage.dms; import jnpf.dmsEntity.preview.model.DmsWatermarkPositionSnapshot; import jnpf.dmsEntity.preview.model.DmsWatermarkSnapshot; import jnpf.exception.DataException; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.rendering.PDFRenderer; import org.junit.Test; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.util.List; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; public class DmsPdfWatermarkServiceTest { private final DmsPdfWatermarkService service = new DmsPdfWatermarkService( new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))); @Test public void stampsSelectedPdfPageAndKeepsOtherPagesUntouched() throws Exception { byte[] source = pdf(2); DmsWatermarkSnapshot mark = watermark(); mark.setRenderPages(List.of(2)); mark.setContent("Page {/CurrPage} of {/TotalPage}"); byte[] result = service.apply(source, List.of(mark)); try (PDDocument original = Loader.loadPDF(source); PDDocument stamped = Loader.loadPDF(result)) { assertEquals(2, stamped.getNumberOfPages()); assertEquals(darkPixels(new PDFRenderer(original).renderImageWithDPI(0, 72)), darkPixels(new PDFRenderer(stamped).renderImageWithDPI(0, 72))); assertTrue(darkPixels(new PDFRenderer(stamped).renderImageWithDPI(1, 72)) > 20); } } @Test public void rejectsOutOfRangePageWithoutReturningSource() throws Exception { DmsWatermarkSnapshot mark = watermark(); mark.setRenderPages(List.of(3)); assertThrows(DataException.class, () -> service.apply(pdf(2), List.of(mark))); } @Test public void usesLandscapeCoordinatesForRotatedA4AndA3Pages() throws Exception { DmsWatermarkSnapshot mark = watermark(); mark.setContent("CHECK"); mark.setMarkWidthMm(new BigDecimal("30")); mark.setMarkHeightMm(new BigDecimal("15")); DmsWatermarkPositionSnapshot a4 = mark.getPositions().get(0); a4.setPaperType("A4"); a4.setLandscapeXMm(new BigDecimal("180")); a4.setLandscapeYMm(new BigDecimal("40")); DmsWatermarkPositionSnapshot a3 = new DmsWatermarkPositionSnapshot(); a3.setPaperType("A3"); a3.setPaperWidthMm(new BigDecimal("297")); a3.setPaperHeightMm(new BigDecimal("420")); a3.setPortraitXMm(new BigDecimal("10")); a3.setPortraitYMm(new BigDecimal("20")); a3.setLandscapeXMm(new BigDecimal("350")); a3.setLandscapeYMm(new BigDecimal("50")); mark.setPositions(List.of(a4, a3)); byte[] source; try (PDDocument document = new PDDocument(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { PDPage first = new PDPage(PDRectangle.A4); first.setRotation(90); document.addPage(first); PDPage second = new PDPage(PDRectangle.A3); second.setRotation(270); document.addPage(second); PDPage third = new PDPage(PDRectangle.A4); third.setRotation(180); document.addPage(third); document.save(output); source = output.toByteArray(); } try (PDDocument stamped = Loader.loadPDF(service.apply(source, List.of(mark)))) { PDFRenderer renderer = new PDFRenderer(stamped); assertWatermarkWithin(renderer.renderImageWithDPI(0, 72), 180, 40, 30, 15); assertWatermarkWithin(renderer.renderImageWithDPI(1, 72), 350, 50, 30, 15); assertWatermarkWithin(renderer.renderImageWithDPI(2, 72), 65, 138, 30, 15); } } private void assertWatermarkWithin(BufferedImage image, int xMm, int yMm, int widthMm, int heightMm) { int minX = (int) Math.floor(xMm * 72D / 25.4D) - 2; int maxX = (int) Math.ceil((xMm + widthMm) * 72D / 25.4D) + 2; int minY = (int) Math.floor(yMm * 72D / 25.4D) - 2; int maxY = (int) Math.ceil((yMm + heightMm) * 72D / 25.4D) + 2; int visible = 0; for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { if ((image.getRGB(x, y) & 0x00ffffff) >= 0x00808080) continue; visible++; assertTrue("Watermark used the wrong paper position", x >= minX && x <= maxX && y >= minY && y <= maxY); } } assertTrue("Expected a visible watermark", visible > 0); } private byte[] pdf(int pages) throws Exception { try (PDDocument document = new PDDocument(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { for (int i = 0; i < pages; i++) document.addPage(new PDPage(PDRectangle.A4)); document.save(output); return output.toByteArray(); } } private DmsWatermarkSnapshot watermark() { DmsWatermarkSnapshot mark = new DmsWatermarkSnapshot(); mark.setWatermarkType("TEXT"); mark.setContent("CONFIDENTIAL"); mark.setFontFamily("SansSerif"); mark.setFontColor("#000000"); mark.setFontSize(18); mark.setOpacity(100); mark.setLayoutMode("POSITION"); mark.setMarkWidthMm(new BigDecimal("80")); mark.setMarkHeightMm(new BigDecimal("20")); DmsWatermarkPositionSnapshot position = new DmsWatermarkPositionSnapshot(); position.setPaperWidthMm(new BigDecimal("210")); position.setPaperHeightMm(new BigDecimal("297")); position.setPortraitXMm(new BigDecimal("65")); position.setPortraitYMm(new BigDecimal("138")); mark.setPositions(List.of(position)); return mark; } private long darkPixels(BufferedImage image) { long count = 0; for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { if ((image.getRGB(x, y) & 0x00ffffff) < 0x00808080) count++; } } return count; } }