Search code examples
adobescaleitext7

How do I add a watermark in A PDF and use the "Scale Relative to target Page"


I am using an example based on the TransparentWatermark example in

http://developers.itextpdf.com/examples/stamping-content-existing-pdfs/clone-watermark-examples

but I cannot seem to find a method or an option that correlates to the one in the Adobe settings with Add Watermark for "Scale Relative to target page".

Can someone point me to that?


Solution

  • Had a go at creating a variant of the Textwatermark examples that scales the watermark to cover a percentage of the total page area. In essence, it's a linear algebra problem of finding the right scaling transformation and making sure all other translations and rotations are correct

    package Stackoverflow.ScalingWatermark;
    
    import com.itextpdf.io.IOException;
    import com.itextpdf.io.font.FontConstants;
    import com.itextpdf.kernel.color.Color;
    import com.itextpdf.kernel.color.DeviceCmyk;
    import com.itextpdf.kernel.events.Event;
    import com.itextpdf.kernel.events.IEventHandler;
    import com.itextpdf.kernel.events.PdfDocumentEvent;
    import com.itextpdf.kernel.font.PdfFont;
    import com.itextpdf.kernel.font.PdfFontFactory;
    import com.itextpdf.kernel.geom.AffineTransform;
    import com.itextpdf.kernel.geom.Rectangle;
    import com.itextpdf.kernel.pdf.PdfDocument;
    import com.itextpdf.kernel.pdf.PdfPage;
    import com.itextpdf.kernel.pdf.PdfWriter;
    import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
    import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
    import com.itextpdf.layout.Document;
    import com.itextpdf.layout.border.SolidBorder;
    import com.itextpdf.layout.element.AreaBreak;
    import com.itextpdf.layout.element.Div;
    import com.itextpdf.layout.element.Paragraph;
    import com.itextpdf.layout.property.AreaBreakType;
    import com.itextpdf.licensekey.LicenseKey;
    
    import java.io.File;
    
    public class ScalingWatermark {
        public static String DEST = "target/output/Stackoverflow/ScalingWatermark/output.pdf";
        public static String LICENSE = "src/test/resources/skeleton-itextkey.xml";
        public static String[] CONTENT = {"I have seen the face of sorrow", "She looks away in the distance",
                "Across all these bridges", "From whence I came",
                "And those spans, trussed and arched", "Hold up our lives as we go back again",
                "To how we thought then", "To how we thought we thought then",
                "I have seen sorrow's face", "But she is ever turned away",
                "And her words leave me blind", "Her eyes make me mute",
                "I do not understand what she says to me", "I do not know if to obey",
                "Or attempt a flood of tears", "I have seen her face",
                "She does not speak", "She does not weep",
                "She does not know me", "For I am but a stone fitted in place",
                "On the bridge where she walks", "―Lay of the Bridgeburners",
                "Toc the Younger"
        };
    
        public static void main(String[] args) throws IOException, java.io.IOException {
            LicenseKey.loadLicenseFile(LICENSE);
            File file = new File(DEST);
            file.getParentFile().mkdirs();
            new ScalingWatermark().createPdf(DEST);
        }
    
        public void createPdf(String dest) throws IOException, java.io.IOException {
            PdfWriter writer = new PdfWriter(dest);
            PdfDocument pdfDoc = new PdfDocument(writer);
            Document doc = new Document(pdfDoc);
            pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE,
                    new TextWatermark());
            addContent(doc);
            doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
            addContent(doc);
            doc.close();
    
        }
    
        private void addContent(Document doc) {
            Div div = new Div().setBorder(new SolidBorder(1));
            for (String line : CONTENT) {
                Paragraph p = new Paragraph(line).setMarginBottom(0).setMarginTop(0).setMarginLeft(5f);
                div.add(p);
            }
            doc.add(div);
        }
    
    
        protected class TextWatermark implements IEventHandler {
    
            Color lime, blue;
            PdfFont helvetica;
    
            protected TextWatermark() throws java.io.IOException {
                helvetica = PdfFontFactory.createFont(FontConstants.HELVETICA);
                lime = new DeviceCmyk(0.208f, 0, 0.584f, 0);
                blue = new DeviceCmyk(0.445f, 0.0546f, 0, 0.0667f);
            }
    
            @Override
            public void handleEvent(Event event) {
                PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
                PdfDocument pdf = docEvent.getDocument();
                PdfPage page = docEvent.getPage();
                int pageNumber = pdf.getPageNumber(page);
                Rectangle pageSize = page.getPageSize();
    
                //Scale to a percentage of the page area
                float scalingAreaReq = pageNumber % 2 == 0 ? 0.75f : 0.50f;
                //Calculate scaling and x-y positioning
                float scalingFactor = (float) Math.sqrt(scalingAreaReq);
                AffineTransform scaleTf = new AffineTransform();
                //Calculation based on centering the watermark
                float xPos = ((1 - scalingFactor) * pageSize.getWidth()) / 2;
                float yPos = ((1 - scalingFactor) * pageSize.getHeight()) / 2;
                //Add translation to our transformation
                scaleTf.translate(xPos,yPos);
                //Add scaling to our transformation
                scaleTf.scale(scalingFactor, scalingFactor);
    
                //Use values from calculation
                PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdf);
                PdfFormXObject watermarkAsXObject = createWatermarkXObject(pageNumber, pageSize, pdf);
                pdfCanvas.saveState()
                        //Apply scaling
                        .concatMatrix(scaleTf)
                        //Insert watermark
                        .addXObject(watermarkAsXObject, 0, 0)
                        .restoreState();
    
                pdfCanvas.release();
            }
    
    
            private PdfFormXObject createWatermarkXObject(int pageNumber, Rectangle bBox, PdfDocument pdfDoc) {
                PdfFormXObject xObject = new PdfFormXObject(bBox);
                PdfCanvas pdfCanvas = new PdfCanvas(xObject, pdfDoc);
                int fontSize = 40;
                float margin = 100;
    
                //Paint the area that the watermark occupies
                pdfCanvas.saveState()
                        .setFillColor(pageNumber % 2 == 1 ? lime : blue)
                        .rectangle(bBox.getLeft(), bBox.getBottom(),
                                bBox.getWidth(), bBox.getHeight())
                        .fill().restoreState();
    
                //Translation and rotation to center the watermark text
                AffineTransform rotationAndTranslation = AffineTransform.getTranslateInstance(bBox.getWidth()/2- margin,bBox.getHeight()/2);
                AffineTransform rotation = AffineTransform.getRotateInstance(Math.PI/4);
                rotationAndTranslation.concatenate(rotation);
    
                //Create the actual text
                pdfCanvas.saveState()
                        .beginText()
                        .setFontAndSize(helvetica, fontSize)
                        .setFillColor(pageNumber % 2 != 1 ? lime : blue)
                        .setTextMatrix(rotationAndTranslation)
                        .showText("Watermark")
                        .endText()
                        .restoreState();
    
                pdfCanvas.release();
    
                return xObject;
    
            }
        }
    }