# ItextPdf Layout 7.2.3 添加水印

ItextPdf Layout 7.2.3 添加水印

背景介绍

  • 使用 Itextpdf Layout 7.2.3 ,需要在Pdf上添加水印
  • Itext 5 添加水印已经在之前的文章中有讲过,可以看这片文章:Itext5 常用 API
  • 使用 PdfCanvas 相关方法

ItextPdf Layout

  • ItextPdf LayoutiTextPdf 的一个特定版本或子模块,用于处理 PDF 布局相关的功能。

  • ItextPdf Layout 提供了一系列用于创建和控制 PDF 布局的功能,如添加文本、图像、表格等元素到 PDF 页面。

  • 它允许您指定每个元素的位置、大小和样式,并可以应用字体类型、颜色、对齐方式和间距等格式选项。

水印相关属性

属性
透明度(0.0f -1.0f)
水印字体类型
水印字体大小 (12px 单位:像素)
水印字体颜色 (#d9d9d9)
水印多行文本行间距 (10 单位:毫米)
水印角度(-30 单位:度)
水印横向间距(50 单位:毫米 )纵向间距(50 单位:毫米)

maven 依赖

  • 添加中文支持、和 Itext layout 相关依赖
 <!-- pdf 中文支持 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>
<!--itext-->
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>core-renderer</artifactId>
    <version>R8</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>layout</artifactId>
    <version>7.2.3</version>
</dependency>

代码示例

水印效果图

  • ItextPdf以左下角为原点(0,0) 开始计算坐标
    在这里插入图片描述

完整代码

  • ItextPdf 7.2.3 添加水印示例
  • 可以设置水印是否只打印一个,还是填充完整整个打印页
  • 设置水印的角度
  • 设置多行水印上下的行距、居左、居右、居中显示
  • 设置水印字体、颜色、大小
  • 水印横向间距、纵向间距
 /**
 * 打印页面设置水印
 */
@Test
public void addWaterMark() throws IOException {

    PdfWriter writer = new PdfWriter(new File(UUID.randomUUID()+FILENAME));
    PdfDocument pdfDoc = new PdfDocument(writer);
    Document document = new Document(pdfDoc, PageSize.A4);
    IBlockElement paragraph = new Paragraph("Hello, world!");
    document.add(paragraph);
    String waterMarkContent = "测试水印\n你好";
    int numberOfPages = pdfDoc.getNumberOfPages();

    // 角度
    float angle = 0;
    // 横向间距
    float xSpacing = 100;
    // 纵向间距
    float ySpacing = 100;
    // 透明度
    float opacity = 0.5f;
    // 多行水印文本行距
    float lineSpacing = 0;
    // 是否重重
    Boolean repeat = true;
    // 多行水印文本对齐方式
    String textAlign = "LEFT";
    // 水印大小
    float watermarkFontSize = 12;

    PdfFont watermarkFont = PdfFontFactory.createFont("src/main/resources/fonts/STSong.ttf", PdfEncodings.IDENTITY_H);

    float x = xSpacing;
    float y = ySpacing;

    // 将水印内容按换行符分割成多行
    String[] lines = waterMarkContent.split("\n");
    // 文本长度数组
    float[] textLength = new float[lines.length];
    // 最长文本的索引
    int maxLengthIndex = 0;
    for (int i = 0; i < lines.length; i++) {
        textLength[i] = watermarkFont.getWidth(lines[i], watermarkFontSize);
        if (i > 0 && textLength[i] > textLength[i - 1]) {
            maxLengthIndex = i;
        }
    }

    String tempStr = Arrays.stream(lines).max(Comparator.comparing(String::length)).get();
    for (int i = 1; i <= numberOfPages; i++) {
        PdfPage page = pdfDoc.getPage(i);
        Rectangle pageSize = page.getPageSize();
        float pageWidth = pageSize.getWidth();

        float pageHeight = pageSize.getHeight();
        float watermarkWidth = watermarkFont.getWidth(tempStr, watermarkFontSize);
        float watermarkHeight = getFontHeight(watermarkFont, watermarkFontSize);
        PdfPage firstPage = pdfDoc.getFirstPage();
        PdfCanvas canvas1 = new PdfCanvas(firstPage)
                .setFillColor(ColorConstants.RED)
                .setColor(ColorConstants.RED, true)
                .setExtGState(new PdfExtGState().setFillOpacity(opacity));

        float tempX = (x + watermarkWidth) / 2;
        int rowNum = 1;
        while (y + watermarkHeight < pageHeight) {
            // 偶数行错行展示
            if (rowNum % 2 == 0) {
                x = x - tempX;
            }
            while (x + watermarkWidth < pageWidth) {

                float tempY = y;
                for (int j = 0; j < lines.length; j++) {

                    float textAlignX = 0;
                    // 实现居中居左居右的效果
                    if (j != maxLengthIndex) {
                        float maxLength = textLength[maxLengthIndex];
                        float currentLength = textLength[j];
                        if ("RIGHT".equals(textAlign)) {
                            textAlignX = maxLength - currentLength;
                        } else if ("CENTER".equals(textAlign)) {
                            textAlignX = (maxLength - currentLength) / 2;
                        }
                    }
                    x = x + textAlignX;

                    String line = lines[j];
                    canvas1.saveState()
                            .concatMatrix(new AffineTransform());
                    canvas1.concatMatrix(
                            StrictMath.cos(Math.toRadians(angle)), StrictMath.sin(Math.toRadians(angle)),
                            -StrictMath.sin(Math.toRadians(angle)), StrictMath.cos(Math.toRadians(angle)),
                            x, tempY
                    );
                    canvas1.beginText()
                            .setFontAndSize(watermarkFont, watermarkFontSize)
                            .showText(line)
                            .endText();
                    canvas1.restoreState();
                    // 多行文本行间距
                    tempY -= watermarkHeight + lineSpacing;
                }
                x += watermarkWidth + xSpacing;

                // 水印重复
                if (!Boolean.TRUE.equals(repeat)) {
                    break;
                }
            }
            // 水印重复
            if (!Boolean.TRUE.equals(repeat)) {
                break;
            }
            x = xSpacing;
            y += watermarkHeight * lines.length + ySpacing;
            rowNum++;
        }
    }
    pdfDoc.close();

}

颜色转换

  • #D9D9D9截取16进制的字符串、获取 16进制的数
/**
 * 获取颜色的16进制的值
 *
 * @param color #D9D9D9
 * @return int
 */
public static int colorValue(String color) {
    String tempColor = color;
    String[] split = tempColor.split("#");
    if (split.length > 1) {
        tempColor = split[split.length - 1];
    } else {
        tempColor = split[0];
    }
    return Integer.valueOf(tempColor, 16);
}

获取字体高度

  • 注意使用与ItextPdf 7.2.3
/**
 * 获取字体高度
 *
 * @param font     字体
 * @param fontSize 字体大小
 * @return float
 */
public float getFontHeight(PdfFont font, float fontSize) {
    FontMetrics fontMetrics = font.getFontProgram().getFontMetrics();
    // 获取字体上升高度
    float ascent = fontMetrics.getTypoAscender() * fontSize / fontMetrics.getUnitsPerEm();
    // 获取字体下降高度
    float descent = fontMetrics.getTypoDescender() * fontSize / fontMetrics.getUnitsPerEm();
    // 计算行距
    float leading = fontMetrics.getTypoAscender() - fontMetrics.getTypoDescender() + fontMetrics.getLineGap();
    // 将行距和字体大小转换为实际单位
    leading *= fontSize / fontMetrics.getUnitsPerEm();
    float lineHeight = ascent - descent + leading;
    // 输出字体行高
    System.out.println("line height: " + lineHeight);
    return lineHeight;
}

方法说明

saveState()

  • saveState 方法是 iText 中的一个方法,用于保存当前的绘图状态。它会将当前的绘图状态(如颜色、字体、线宽等)保存到一个堆栈中,以便稍后可以恢复到该状态。

  • 通过使用 saveStaterestoreState 方法,你可以在需要的时候保存和恢复绘图状态,以确保绘制的内容符合预期。

concatMatrix()

  • concatMatrix 方法是 iText PDF 中用于将一个变换矩阵与当前变换矩阵进行连接的方法。通过这个方法,你可以对 PDF 文档中的内容应用各种变换,如平移、旋转、缩放和倾斜。

PdfCanvas

  • PdfCanvas 类用于在 PDF 文档上绘制图形元素和内容。它提供了各种方法来绘制线条、形状、文本、图像,并应用变换。
绘制方法
  • moveTo(x, y) :将当前绘图位置移动到指定的坐标。
  • lineTo(x, y) :从当前位置绘制一条线到指定的坐标。
  • rectangle(x, y, width, height) :在指定位置和大小绘制矩形。
  • circle(x, y, radius) :在指定位置绘制给定半径的圆形。
  • arc(x, y, radius, startAngle, endAngle) :在指定位置、半径和角度绘制弧形。
  • text(x, y, text) :在指定位置绘制文本。
路径构造方法
  • newPath() :开始一个新路径。
  • closePath() :关闭当前子路径。
  • stroke() :使用当前描边颜色和线宽描边路径。
  • fill() :使用当前填充颜色填充路径。
图形状态方法
  • setLineWidth(width) :设置后续绘制操作的线宽。
  • setStrokeColor(color) :设置后续绘制操作的描边颜色。
  • setFillColor(color) :设置后续绘制操作的填充颜色。
  • concatMatrix(a, b, c, d, e, f) :将一个变换矩阵连接到当前变换矩阵上。
图像方法
  • addImage(image, x, y, width, height) :在指定位置和大小添加图像到画布上。
    颜色填充路径。
图形状态方法
  • setLineWidth(width) :设置后续绘制操作的线宽。
  • setStrokeColor(color) :设置后续绘制操作的描边颜色。
  • setFillColor(color) :设置后续绘制操作的填充颜色。
  • concatMatrix(a, b, c, d, e, f) :将一个变换矩阵连接到当前变换矩阵上。
图像方法
  • addImage(image, x, y, width, height) :在指定位置和大小添加图像到画布上。

源码地址

<think>好的,用户想要确定一个PDF文件是否是由iTextPdf生成的。首先,我需要回忆一下iText在生成PDF时是否会留下特定的标识或元数据。根据之前接触过的知识,很多软件在生成文件时会在元数据中添加自己的信息,比如创建工具的名称、版本等。iText可能也会这样做。 接下来,应该检查PDF的文档属性中的元数据部分。通常,PDF的元数据可以通过查看“文档属性”或使用工具如Adobe Acrobat来查看。如果PDF是用iText生成的,可能在“Producer”或“Creator”字段中包含“iText”相关的信息。例如,iText 5.x版本可能在Producer字段中显示类似“iText® 5.5.13 ©2000-2015 iText Group NV”的内容,而更高版本如7.x可能会有不同的标识。 另外,用户可能需要通过编程方式来检测。这时候可以考虑使用iText或其他PDF库来读取元数据。比如使用iText的PdfReader获取元数据信息,然后检查其中是否包含“iText”关键字。例如,通过PdfReader的getInfo()方法或直接访问Catalog中的元数据。需要注意的是,有些情况下元数据可能被修改或清除,这时候就需要其他方法。 还有一种情况是,即使用户在生成PDF时没有显式设置Producer信息,iText可能仍然会自动添加。因此,检查Producer字段是一个有效的方法。不过,如果PDF被其他工具处理过,或者生成时特意去除了这些信息,可能会导致检测失败。这时候可能需要更深入的分析,比如检查PDF中的特定对象结构或字体使用情况,但这种方法比较复杂,可能需要更专业的工具或知识。 参考用户提供的引用内容,特别是引用[3]和[5],里面提到了使用iText生成PDF时的依赖和代码示例,但并没有直接提到元数据的部分。不过根据一般的经验,iText确实会在元数据中添加标识。因此,建议用户首先检查PDF的元数据,如果存在iText相关的Producer信息,就可以确定是使用iText生成的。如果元数据被修改,可能需要其他线索,比如特定的字体、结构或遗留的对象标识。 总结一下,步骤应该是:1. 使用PDF阅读器查看元数据中的Producer字段;2. 若存在iText相关字样,则确认;3. 若元数据被修改,考虑编程提取信息或分析PDF内部结构。</think>要检测PDF文件是否由iTextPdf生成,可以通过以下步骤实现: --- ### 1. **检查PDF元数据** iText生成的PDF通常会在元数据中标记`Producer`或`Creator`字段为`iText®`及其版本信息。例如: - iText 5.x版本:`Producer: iText® 5.5.13 ©2000-2015 iText Group NV` - iText 7.x版本:`Producer: iText® Core 7.2.3 ©2000-2022 iText Group NV` **操作方法**: 使用PDF阅读器(如Adobe Acrobat)查看文件属性,或在代码中通过iText的`PdfReader`获取元数据: ```java PdfReader reader = new PdfReader("input.pdf"); String producer = reader.getInfo().get("Producer"); if (producer != null && producer.contains("iText")) { System.out.println("PDF由iText生成"); } ``` --- ### 2. **分析PDF内部结构** 如果元数据被修改或删除,可通过检查PDF对象的特定标识: - iText生成的PDF可能在`Catalog`或`Info`字典中包含`iText`关键字[^3]。 - 检查字体名称、XObject等资源是否包含iText的默认命名规则(如`F1`、`F2`等)。 --- ### 3. **编程检测示例** 通过iText库直接读取PDF并判断: ```java try (PdfReader reader = new PdfReader("input.pdf")) { PdfDictionary catalog = reader.getCatalog(); PdfString creator = catalog.getAsString(PdfName.CREATOR); if (creator != null && creator.toString().contains("iText")) { System.out.println("检测到iText生成标识"); } } catch (IOException e) { e.printStackTrace(); } ``` --- ### 注意事项 - **元数据可能被篡改**:部分工具会清除或修改Producer字段,需结合其他特征分析[^4]。 - **版本差异**:iText 5.x与7.x的元数据标识不同,需注意版本兼容性[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值