From 5a64f9cda402dff9864b3eaf1af143114278d57c Mon Sep 17 00:00:00 2001 From: Michael Olayemi Date: Fri, 23 Dec 2022 21:05:04 +0100 Subject: [PATCH] BAEL-5943 Using Watermarks with iText in Java (#13178) * BAEL-5943 Using Watermarks with iText in Java * BAEL-5943 Update * BAEL-5943 Update * BAEL-5943 Update --- libraries-files/output/aliceandpaul.pdf | Bin 0 -> 2601 bytes .../output/alicepaulwithoutwatermark.pdf | 43 ++++++ libraries-files/output/aliceupdated.pdf | Bin 0 -> 2693 bytes libraries-files/pom.xml | 12 ++ .../java/com/baeldung/iTextPDF/StoryTime.java | 126 ++++++++++++++++++ .../AddWaterMarkToExistingPdfUnitTest.java | 39 ++++++ .../GenerateNewPdfWithWatermarkUnitTest.java | 37 +++++ 7 files changed, 257 insertions(+) create mode 100644 libraries-files/output/aliceandpaul.pdf create mode 100644 libraries-files/output/alicepaulwithoutwatermark.pdf create mode 100644 libraries-files/output/aliceupdated.pdf create mode 100644 libraries-files/src/main/java/com/baeldung/iTextPDF/StoryTime.java create mode 100644 libraries-files/src/test/java/com/baeldung/iTextPDFTest/AddWaterMarkToExistingPdfUnitTest.java create mode 100644 libraries-files/src/test/java/com/baeldung/iTextPDFTest/GenerateNewPdfWithWatermarkUnitTest.java diff --git a/libraries-files/output/aliceandpaul.pdf b/libraries-files/output/aliceandpaul.pdf new file mode 100644 index 0000000000000000000000000000000000000000..de71693db564b6e4de817e54e93329902d211bd2 GIT binary patch literal 2601 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 literal 0 HcmV?d00001 diff --git a/libraries-files/output/alicepaulwithoutwatermark.pdf b/libraries-files/output/alicepaulwithoutwatermark.pdf new file mode 100644 index 0000000000..f291340bc1 --- /dev/null +++ b/libraries-files/output/alicepaulwithoutwatermark.pdf @@ -0,0 +1,43 @@ +%PDF-1.7 +% +5 0 obj +<>stream +x[o7+) l-ND@Y-+. 40[&g9sfx<.՚bsO:бY{0fzUk.379rQ#,=2^N:Vtx}IZNvq;Ovj} +]t:c+uƽJ̒:gǖd]̰8I4/l :7yTU "$|v4Dg=/_ֲDh٘TנB@.;QW#mvv|E+j*D]+yLme:v>)(hWuD=pI*qy,U>*4^{)h6:\9kIjIE@W@"+XZ>Zg(m!>qmsbZzZ @9[wյ<{iⱣxUe#jΡ1\W\èϿhCq~wUtyߎ0\x$8bI\pN|RE>>>/TrimBox[0 0 595 842]/Type/Page>> +endobj +1 0 obj +<> +endobj +3 0 obj +<> +endobj +6 0 obj +<> +endobj +7 0 obj +<> +endobj +2 0 obj +<> +endobj +xref +0 8 +0000000000 65535 f +0000001238 00000 n +0000001611 00000 n +0000001283 00000 n +0000001096 00000 n +0000000015 00000 n +0000001433 00000 n +0000001523 00000 n +trailer +<<216b46f23ae9d6b57462e9f47fac0cc5>]/Info 3 0 R/Root 1 0 R/Size 8>> +%iText-Core-7.2.4 +startxref +1662 +%%EOF diff --git a/libraries-files/output/aliceupdated.pdf b/libraries-files/output/aliceupdated.pdf new file mode 100644 index 0000000000000000000000000000000000000000..13e1cbd91f72e2e31d93f8b526883d3082093d19 GIT binary patch literal 2693 zcmb_eX;c(f7B(z$(g8F^hbS2>Vu&oEtEyM3fj+DaEd&r62%FMNH8j%Q&{Z^z;0ziT zj~p05MfL$?h?p5!WY+{D0Tqb^0a3<8MFbQi$Do*js8iJvQG0&m%uM~L^XlI3-FMgT zm8HKs&km-tDVF1R28JjI00Bx-EXC0gww?TkH4s;oZFg_b zcWANFaFe_9Uu%B|wX>zRPxkaqjy^GN&;KW*Yc*sL{&Doz2qyjItj3Z0cD3 zBFyj1(@M_w4WVaKpbqJtpndB{F77_2mqWcyFKcE$8beeCwL7+E6fPdVoq6TfC%IYE zN1f*Lt7v99<~z%LIl=g>W^pe+%vXJJDdAu94}a=$$RF@K-!s)S|GZW`6I)W-w0H2` zB1$Aa%n{$PP3-uh_p9yw`DvW!w!ZTPlF0sg`z?oChxorTz_^Ddx%HgW>^tlKQj%PA z=0UaKY*0p_$%h}Ms%P~}3K|(z-?&GYI)RnNows&fZLHm_XXqYtz3{GJw8GUnuVJ2A z=CGqFq_=;oTs@*bTC`~@wPx@S9=5SZb8OrrZJRD9>-)&7(K?R0m23TFp4CLQ{^-S+ zyR~yiLK5AbM$FkZ!v$Ud-iVzQzhXC-b?!Xro55n(~eJ4+GOJm$2CEX!#|>1EBf!7 znd--0t}H2qBOZ*mhm4I!bf2gg=Y_(E0K-$`QKQVJ%{XQ>8Kt| zh?p!oX?XrwI@%?z2wdl!ZJMXx2OC6W-2dLXU@$JewQd?lKOdmWqbko0V?Ujp1gLo( z$`s2B#Y?tRjB^{SHr2UpGYX%wXasp?qETM`uSppf`qr9zum_s<=k(P)Dh-JY-cnYi zR{0KpiK!Q&1mBIc-oC25J-4tF+j;5sGr_gh!92gs>7I38$Hp0Xf1YHQRZ-^Ab+uiy zziJ$509zdJw7rL9-#WFA&D4y<6$dyyEl7LZjNMymerhg#QaN@kXX)+YvK@ZixL~eY zV&UWc_T(C$U02e6=@i#)J5?UVpLpKg%^Mod&oyEIiWB_H^1f%9&;k-drQ@%k(8 zhgR&bL63)S@J=$Ou#nCR`}l4Aiu+?5N4ic8;Yh!8mhWgEeHQUM-BD+nH1y8i5exRW zCI>g~S%7ZHOa5Hvy*`oA9^#eK@bhQe4;$!R?J+oT`B>Oz)Yhb zS}#U*gtfSI#xHzRGXMOo-mgZby2N4s`-sE-72bGf!e-)1637yGfvX#hAN8B`o0x2f zYNBSQH5BEQ@3TI{Vg`9#&WK^@$xPGhb1YUP8S(edY_u&bwob23C@LwL-WYCG7<#{` z&?db)US_|v)SleYwwR&eEXHI?652CYHM;?;dLy>0F3&|jz`HUKmd0|ua&GEpT)F@k~1py*YBtQgAppwaTd=MnZ zql6>TD3?n{M{+lL*z!FH;4SuSW=&BGpOsM=-N=c#^Rawads0Kd;xG7aAz^2lu3?RbF#nayhP@^i0 zupMCy)Y2dbvLmenS~TFPQYOX&zQGQFTq%)BWvB#5#AJ%-7wZ4gnDt7C+7vFw=qkie zGTopT8mC5anOF#VD2V%!I1UVvDO?no?B#c0q6jCmOC}E1zO|KFF-(^7x7;ZciB5n) zZ+k& zEQrYd8!|eBNY3jr2ttT4d0hr0Y#kX5V(G|Wm`zmv^}P@T6NK@)47R81?4`jBoj7PP zTgN|g^j7TQDxoY6Rgr&oe0Lz+k(f#mgC(WWg(xCniI{8#i-san23sl=Lt-(L>nNd1 z5HSKX*@y&|O4(wmh%OdM38O+bB7{VAZY0Q8NR@z=g1`W!l4xGCYr#O-9+W5rQDaN( eblQ=lY^NP%3Wf_+xVBm}8iV-jwDj=fQT`1GU;7XM literal 0 HcmV?d00001 diff --git a/libraries-files/pom.xml b/libraries-files/pom.xml index efb102712c..b36dc150a8 100644 --- a/libraries-files/pom.xml +++ b/libraries-files/pom.xml @@ -37,6 +37,18 @@ jackson-databind ${jackson.version} + + com.itextpdf + itext7-core + 7.2.4 + pom + + + org.assertj + assertj-core + 3.23.1 + test + diff --git a/libraries-files/src/main/java/com/baeldung/iTextPDF/StoryTime.java b/libraries-files/src/main/java/com/baeldung/iTextPDF/StoryTime.java new file mode 100644 index 0000000000..78977ea160 --- /dev/null +++ b/libraries-files/src/main/java/com/baeldung/iTextPDF/StoryTime.java @@ -0,0 +1,126 @@ +package com.baeldung.iTextPDF; + +import static com.itextpdf.layout.properties.TextAlignment.CENTER; +import static com.itextpdf.layout.properties.VerticalAlignment.TOP; +import static java.lang.Math.PI; + +import java.io.File; +import java.io.IOException; + +import com.itextpdf.io.font.constants.StandardFonts; +import com.itextpdf.kernel.font.PdfFont; +import com.itextpdf.kernel.font.PdfFontFactory; +import com.itextpdf.kernel.geom.PageSize; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfPage; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.pdf.canvas.PdfCanvas; +import com.itextpdf.kernel.pdf.extgstate.PdfExtGState; +import com.itextpdf.layout.Document; +import com.itextpdf.layout.element.Paragraph; +import com.itextpdf.layout.element.Text; + +public class StoryTime { + + public static final String OUTPUT_FILE = "output/aliceupdated.pdf"; + + public String alice = " Alice was a curious young girl who one day stumbled upon a strange, talking rabbit." + " The rabbit, in a hurry, invited Alice to follow him down a rabbit hole." + + "Alice, not one to turn down an adventure, eagerly followed the rabbit and found herself tumbling down a long, dark tunnel.As she fell, Alice passed through a small door and into a fantastical world filled with talking animals and peculiar characters." + + "She met the Cheshire Cat, who seemed to appear and disappear at will, and the Mad Hatter, who invited her to a tea party." + + "Alice also encountered the tyrannical Queen of Hearts, who was quick to anger and ordered the beheading of anyone who crossed her." + + "Despite the dangers, Alice remained determined to find her way home and eventually stumbled upon the key to the door that would take her back to the real world." + + " As Alice stepped through the door and back into reality, she couldn't help but wonder if it had all been a dream. But the memories of her adventures in Wonderland stayed with her forever, and she often found herself longing to return to that strange and magical place."; + + public String paul = " Paulinho is a Brazilian professional footballer who currently plays as a midfielder for Guangzhou Evergrande Taobao in the Chinese Super League." + + "He has also played for several top clubs around the world, including Barcelona, Tottenham Hotspur, and Guangzhou Evergrande. Paulinho has represented Brazil at the international level and has won several accolades throughout his career, including the Copa America and the Chinese Super League title." + + "He is known for his strong work ethic and powerful offensive play."; + + public static void main(String[] args) throws IOException { + File file = new File(OUTPUT_FILE); + file.getParentFile() + .mkdirs(); + + // new StoryTime().createPdf(); + new StoryTime().addWatermarkToExistingPdf(); + + } + + public void createPdf() throws IOException { + StoryTime storyTime = new StoryTime(); + String waterMark = "CONFIDENTIAL"; + PdfWriter writer = new PdfWriter(storyTime.OUTPUT_FILE); + PdfDocument pdf = new PdfDocument(writer); + + try (Document document = new Document(pdf)) { + + document.add(new Paragraph(storyTime.alice).setFont(PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN))); + document.add(new Paragraph(storyTime.paul)); + + Paragraph paragraph = storyTime.createWatermarkParagraph(waterMark); + for (int i = 1; i <= document.getPdfDocument() + .getNumberOfPages(); i++) { + + storyTime.addWatermarkToPage(document, i, paragraph, 0f); + } + } + } + + public void addWatermarkToExistingPdf() throws IOException { + StoryTime storyTime = new StoryTime(); + String outputPdf = "output/aliceupdated.pdf"; + String watermark = "CONFIDENTIAL"; + + try (PdfDocument pdfDocument = new PdfDocument(new PdfReader("output/alicepaulwithoutwatermark.pdf"), new PdfWriter(outputPdf))) { + Document document = new Document(pdfDocument); + + Paragraph paragraph = storyTime.createWatermarkParagraph(watermark); + PdfExtGState transparentGraphicState = new PdfExtGState().setFillOpacity(0.5f); + for (int i = 1; i <= document.getPdfDocument() + .getNumberOfPages(); i++) { + storyTime.addWatermarkToExistingPage(document, i, paragraph, transparentGraphicState, 0f); + } + } + } + + public Paragraph createWatermarkParagraph(String watermark) throws IOException { + PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA); + Text text = new Text(watermark); + text.setFont(font); + text.setFontSize(56); + text.setOpacity(0.5f); + + return new Paragraph(text); + } + + public void addWatermarkToPage(Document document, int pageIndex, Paragraph paragraph, float verticalOffset) { + PdfPage pdfPage = document.getPdfDocument() + .getPage(pageIndex); + PageSize pageSize = (PageSize) pdfPage.getPageSizeWithRotation(); + + float x = (pageSize.getLeft() + pageSize.getRight()) / 2; + float y = (pageSize.getTop() + pageSize.getBottom()) / 2; + float xOffset = 100f / 2; + float rotationInRadians = (float) (PI / 180 * 45f); + document.showTextAligned(paragraph, x - xOffset, y + verticalOffset, pageIndex, CENTER, TOP, rotationInRadians); + } + + public void addWatermarkToExistingPage(Document document, int pageIndex, Paragraph paragraph, PdfExtGState graphicState, float verticalOffset) { + PdfDocument pdfDoc = document.getPdfDocument(); + PdfPage pdfPage = pdfDoc.getPage(pageIndex); + PageSize pageSize = (PageSize) pdfPage.getPageSizeWithRotation(); + + float x = (pageSize.getLeft() + pageSize.getRight()) / 2; + float y = (pageSize.getTop() + pageSize.getBottom()) / 2; + PdfCanvas over = new PdfCanvas(pdfDoc.getPage(pageIndex)); + over.saveState(); + over.setExtGState(graphicState); + float xOffset = 100f / 2; + float rotationInRadians = (float) (PI / 180 * 45f); + document.showTextAligned(paragraph, x - xOffset, y + verticalOffset, pageIndex, CENTER, TOP, rotationInRadians); + document.flush(); + over.restoreState(); + over.release(); + } + +} diff --git a/libraries-files/src/test/java/com/baeldung/iTextPDFTest/AddWaterMarkToExistingPdfUnitTest.java b/libraries-files/src/test/java/com/baeldung/iTextPDFTest/AddWaterMarkToExistingPdfUnitTest.java new file mode 100644 index 0000000000..4e45fe4281 --- /dev/null +++ b/libraries-files/src/test/java/com/baeldung/iTextPDFTest/AddWaterMarkToExistingPdfUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.iTextPDFTest; + +import static com.itextpdf.kernel.pdf.canvas.parser.PdfTextExtractor.getTextFromPage; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.baeldung.iTextPDF.StoryTime; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.pdf.canvas.parser.listener.LocationTextExtractionStrategy; +import com.itextpdf.kernel.pdf.extgstate.PdfExtGState; +import com.itextpdf.layout.Document; +import com.itextpdf.layout.element.Paragraph; + +class AddWaterMarkToExistingPdfUnitTest { + + @Test + public void givenAnExistingPDF_whenManipulatedPDFWithIText_thenGeneratePDFwithWatermarks() throws IOException { + StoryTime storyTime = new StoryTime(); + String outputPdf = "output/aliceupdated.pdf"; + String watermark = "CONFIDENTIAL"; + + LocationTextExtractionStrategy extStrategy = new LocationTextExtractionStrategy(); + try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(outputPdf))) { + for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) { + String textFromPage = getTextFromPage(pdfDocument.getPage(i), extStrategy); + assertThat(textFromPage).contains(watermark); + } + } + + } + +} diff --git a/libraries-files/src/test/java/com/baeldung/iTextPDFTest/GenerateNewPdfWithWatermarkUnitTest.java b/libraries-files/src/test/java/com/baeldung/iTextPDFTest/GenerateNewPdfWithWatermarkUnitTest.java new file mode 100644 index 0000000000..0d383d2f38 --- /dev/null +++ b/libraries-files/src/test/java/com/baeldung/iTextPDFTest/GenerateNewPdfWithWatermarkUnitTest.java @@ -0,0 +1,37 @@ +package com.baeldung.iTextPDFTest; + +import static com.itextpdf.kernel.pdf.canvas.parser.PdfTextExtractor.getTextFromPage; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import com.baeldung.iTextPDF.StoryTime; +import com.itextpdf.io.font.constants.StandardFonts; +import com.itextpdf.kernel.font.PdfFontFactory; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.pdf.canvas.parser.listener.LocationTextExtractionStrategy; +import com.itextpdf.layout.Document; +import com.itextpdf.layout.element.Paragraph; + +class GenerateNewPdfWithWatermarkUnitTest { + + @Test + public void givenNewTexts_whenGeneratingNewPDFWithIText_thenGeneratePDFwithWatermarks() throws IOException { + StoryTime storyTime = new StoryTime(); + String waterMark = "CONFIDENTIAL"; + + LocationTextExtractionStrategy extStrategy = new LocationTextExtractionStrategy(); + try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(storyTime.OUTPUT_FILE))) { + for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) { + String textFromPage = getTextFromPage(pdfDocument.getPage(i), extStrategy); + assertThat(textFromPage).contains(waterMark); + } + } + + } + +}