Как добавить прозрачный текст водяного знака на каждую страницу, используя iText7 pdfHTML

#java #itext #itext7

#java #itext #itext7

Вопрос:

В настоящее время я пытаюсь добавить водяной знак, который отображается на заднем плане каждой страницы моего pdf, используя iText7 pdfHTML, но я не могу найти решение. Например, я хотел бы, чтобы текст «Конфиденциально» отображался на заднем плане каждой страницы. Я попытался добавить его с помощью css следующим образом

 @page {
    size: Letter;
    margin: .5in .5in .5in .5in;

    @left-middle {
        content: "Confidential";
        /* z-index: 100; */
        font-size: 80pt;
        font-weight: bold;
        opacity: .2;
        text-align: center;
        text-transform: uppercase;
        transform: translateX(350px) rotate(-54.7deg);
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow: auto;
        z-index: 0;
    }
}
  

Это почти решает мою проблему, но текст непрозрачен и скрывает текст за ним. Он также не вращается, но это не является обязательным требованием.

Приветствуются решения, использующие некоторую комбинацию Java, CSS или Html.

Вот пример моего Java-кода:

         FileInputStream htmlStream = null;
        FileOutputStream pdfStream = null;

        try {
            ConverterProperties converterProperties = new ConverterProperties().setBaseUri(path);
            converterProperties.setMediaDeviceDescription(new MediaDeviceDescription(MediaType.PRINT));
            htmlStream = new FileInputStream(inputPath);
            pdfStream = new FileOutputStream(outputPath);
            HtmlConverter.convertToPdf(htmlStream, pdfStream, converterProperties);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (htmlStream != null) {
                htmlStream.close();
            }
            if (pdfStream != null) {
                pdfStream.close();
            }
        }
  

Редактировать

пример html для воспроизведения:

 <!DOCTYPE html>
<html>
<link id="watermark_link" href="watermark.css" rel="stylesheet" type="text/css" />
</head>

<body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
</body>

</html>
  

Редактировать 2

После опробования ответа от @BenIngle вот мой Java-код, чтобы заставить его работать с pdfHTML:

     private static void generatePDFFromHTML(String inputPath, String outputPath, String baseUrl) throws IOException {
        FileInputStream htmlStream = null;
        FileOutputStream pdfStream = null;

        try {
            ConverterProperties converterProperties = new ConverterProperties().setBaseUri(baseUrl);
            converterProperties.setMediaDeviceDescription(new MediaDeviceDescription(MediaType.PRINT));
            htmlStream = new FileInputStream(inputPath);
            pdfStream = new FileOutputStream(outputPath);
            PdfWriter writer = new PdfWriter(pdfStream);
            PdfDocument pdfDocument = new PdfDocument(writer);
            Watermark watermark = new Watermark("Confidential");
            pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE,watermark);
            HtmlConverter.convertToPdf(htmlStream, pdfDocument, converterProperties);
            pdfDocument.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (htmlStream != null) {
                htmlStream.close();
            }
            if (pdfStream != null) {
                pdfStream.close();
            }
        }

    }

    protected static class Watermark implements IEventHandler {

        String watermarkText;

        public Watermark(String watermarkText) {
            this.watermarkText = watermarkText;
        }

        @Override
        public void handleEvent(Event event) {
            //Retrieve document and
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdf = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            Rectangle pageSize = page.getPageSize();
            PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdf);
            Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
            PdfExtGState gstate = new PdfExtGState();
            gstate.setFillOpacity(.2f);
            pdfCanvas.setExtGState(gstate);

            double rotationDeg = -54.7d;
            double rotationRad = Math.toRadians(rotationDeg);
            Paragraph watermarkParagraph = new Paragraph(watermarkText)
                    .setFontSize(80f)
                    .setTextAlignment(TextAlignment.CENTER)
                    .setVerticalAlignment(VerticalAlignment.MIDDLE)
                    .setRotationAngle(rotationRad)
                    .setFixedPosition(100, page.getPageSize().getHeight(), page.getPageSize().getWidth());
            canvas.add(watermarkParagraph);
            canvas.close();

        }

    }
  

Я надеюсь, что это поможет всем, кто еще пытается начать работу с iText pdfHTML!

Комментарии:

1. Я пытался создать html-файл с вашим css, но либо не удалось, либо «Конфиденциально» не отображается в html. Не могли бы вы прикрепить весь HTML-файл, который пытаетесь обработать?

2. Я не очень знаком с html2pdf, но вы могли бы вернуться к страницам и добавить блок текста на каждую страницу после завершения процесса html2pdf. Это было бы решением Java.

3. @VladzimirAsipchuk Я отредактировал свой первоначальный пост с помощью некоторого базового html, чтобы воспроизвести эту проблему. Я не могу предоставить полный html-документ, потому что он на самом деле содержит конфиденциальную информацию, но этого html в сочетании с CSS, который я опубликовал ранее, достаточно для воспроизведения. Кроме того, вы просто открываете html в браузере? Я не верю, что предоставленный мной CSS будет работать в большинстве браузеров, поэтому вам нужно будет отправить его через iText, чтобы просмотреть результат. Результирующий pdf содержит одну страницу с надписью «Конфиденциально» посередине, за исключением того, что она на 100% непрозрачна и скрывает все, что за ней.

4. @BenIngle Спасибо за предложение. Я рассмотрю возможность добавления водяного знака на каждую страницу с помощью Java-кода.

Ответ №1:

Вот решение для добавления текста «на заднем плане каждой страницы». Это добавит текст за существующим содержимым, чтобы он не закрывал его. Обратите внимание, что это не добавляет прозрачности. Прозрачность должна быть добавлена с состоянием внешней графики.

 try (PdfDocument doc = new PdfDocument(new PdfReader(in.toFile()), new PdfWriter(out.toFile()))) {
    PdfFont helvetica = PdfFontFactory.createFont();
    for (int pageNum = 1; pageNum <= doc.getNumberOfPages(); pageNum  ) {
        PdfPage page = doc.getPage(pageNum);
        // important - add a new content stream in the beginning, to render behind existing text
        PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), doc);

        // option 1 - manual placement
        canvas.saveState();
        canvas.beginText();
        canvas.setFillColor(ColorConstants.GRAY);
        canvas.setFontAndSize(helvetica, 80f);
        canvas.moveText(0f, page.getPageSize().getHeight() - 80f);
        canvas.showText("Confidential1");
        canvas.endText();
        canvas.restoreState();

        // option 2 - let iText place it
        try (Canvas canvas1 = new Canvas(canvas, doc, page.getPageSize())) {
            Paragraph watermark = new Paragraph("Confidential2")
                    .setFontColor(ColorConstants.GRAY)
                    .setFont(helvetica)
                    .setFontSize(80f)
                    .setHorizontalAlignment(HorizontalAlignment.LEFT)
                    .setVerticalAlignment(VerticalAlignment.BOTTOM)
                    .setFixedPosition(0f, page.getPageSize().getHeight() - 100f, page.getPageSize().getWidth());
            canvas1.add(watermark);
        }

        // option 3 - set opacity and place on top of existing content, plus rotation
        PdfExtGState gstate = new PdfExtGState();
        gstate.setFillOpacity(.2f);
        canvas = new PdfCanvas(page);
        canvas.saveState();
        canvas.setExtGState(gstate);
        try (Canvas canvas2 = new Canvas(canvas, doc, page.getPageSize())) {
            double rotationDeg = -54.7d;
            double rotationRad = Math.toRadians(rotationDeg);
            Paragraph watermark = new Paragraph("Confidential3")
                    .setFont(helvetica)
                    .setFontSize(80f)
                    .setTextAlignment(TextAlignment.CENTER)
                    .setVerticalAlignment(VerticalAlignment.MIDDLE)
                    .setRotationAngle(rotationRad)
                    .setFixedPosition(100, page.getPageSize().getHeight(), page.getPageSize().getWidth());
            canvas2.add(watermark);
        }
        canvas.restoreState();
    }
}
  

Редактировать

Добавлена третья опция, которая применяет прозрачность и поворот.

Комментарии:

1. Спасибо за ответ! Сейчас я занят, но я обновлю, когда у меня будет возможность попробовать это. Надеюсь, сегодня или завтра.

2. Еще раз спасибо! В итоге я изменил третий вариант для работы с тем, что у меня уже было для pdfHTML. Я обновил сообщение своим окончательным кодом для дальнейшего использования на случай, если кто-нибудь вернется к этому. Я немного поиграл с чем-то похожим на окончательное решение перед публикацией, но я думаю, что ключевой частью, которой мне не хватало, был PdfExtGState для добавления прозрачности к тексту. Я очень ценю помощь!