Java处理PDF文档【下】( 全新 iText 8.0 画布、条形码、渲染器 )

一:序

  这篇主要围绕 Itext 8.0画布(Canvas) 使用,关于Itext的基本使用可以翻看我上篇文章的讲解;下面的这些章节我主要从画布的常见方法和基本使用的两方面来介绍。
  我写的这篇文章主要是给大家参考的,方便对于有着需要在PDF上绘画各种效果图的人入门,本文关于画布其实还是有许多未讲到的方面,具体的大家可以去官方文档查阅或者拉取Itext官方给出的示例代码,官方示例里面有着各式各样的效果,对有着工作需求的人还是会有着不错的帮助。

\(\color{#f00}{没接触过Itext的推荐先看看Itext基本使用:}\)
  Java处理PDF文档【上】( 全新 iText 8.0 基础入门、元素)
下文主要介绍:画布元素渲染器条形码生成二维码生成

二:画布基本使用

  这里写了一个基本的案例,若想入门则需要详细按照官网文档或者下面我写的一些基本示例;在开发中关于Itext画布还是挺重要的,掌握画布可以为页面添加水印、特殊的表格(如课程表)等各式各样操作。
代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建页面,并获取页面的宽高
    PdfPage pdfPage = pdfDocument.addNewPage(new PageSize(600, 200));
    float width = pdfPage.getPageSize().getWidth();
    float height = pdfPage.getPageSize().getHeight();
    // ============================= 使用画布(PdfCanvas)画格子 =============================
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvasA = new PdfCanvas(pdfPage);
    // 为画布创建坐标系点每个间隔10*10
    PdfExtGState pdfExtGState = new PdfExtGState();
    pdfExtGState.setStrokeOpacity(.3f); // 描边透明度
    for (int x = 0; x < width; x += 10) {
        for (int y = 0; y < height; y += 10) {
            pdfCanvasA.saveState().setStrokeColor(ColorConstants.GRAY)
                    .setExtGState(pdfExtGState)                   // 设置补充信息
                    .setLineDash(1, 1, 0)   // 设置虚线模式
                    .moveTo(x, y).lineTo(x, y + 10)
                    .moveTo(x - 10, y).lineTo(x + 10, y)
                    .stroke().restoreState();
        }
    }
    // ============================= 使用画布(Canvas)画矩形 =============================
    // 创建了一个高级的画布,画布大小为页面四边往内30pt
    Canvas canvas = new Canvas(pdfPage, new Rectangle(30, 30, width - 60, height - 60));
    // 获取当前画布的大小
    Rectangle rootArea = canvas.getRootArea();
    // 添加一个矩形,后面添加到画布
    Div div = new Div().setWidth(rootArea.getWidth()).setHeight(rootArea.getHeight())
            .setBorder(new DashedBorder(ColorConstants.RED, 2))
            .setVerticalAlignment(VerticalAlignment.MIDDLE)  // 居中对齐
            .setBorderRadius(new BorderRadius(10));
    // 添加一个文本到矩形中
    Paragraph paragraph = new Paragraph("Canvas is your favorite")
            .setHeight(60)
            .setBold()      // 加粗
            .setFont(PdfFontFactory.createFont())   // 字体样式
            .setFontSize(30)// 字体大小
            .setBorderRadius(new BorderRadius(20))  // 边框圆角20pt
            .setTextAlignment(TextAlignment.CENTER) // 文本左右居中
            // 关于文本的居中对齐,受字体的影响,上面会多几磅(pt),实在难受可以使用画布
            .setVerticalAlignment(VerticalAlignment.MIDDLE)  // 居中对齐
            .setBackgroundColor(ColorConstants.GRAY, .2f)// 背景色和0.2的透明
            .setFontColor(ColorConstants.RED); // 字体红色
    // 添加到画布中并关闭
    div.add(paragraph);
    canvas.add(div);
    canvas.close();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

三:画布PdfCanvas对象说明

  这一章节我主要从 PdfCanvas 的 PDF 画布对象来着手;这个画布对象它是直接创建在 PDF 页上的,需要什么效果可以直接在上面绘画。对于这个对象有着一定了解后,再去摸索其它的对象;在这章节里面我将带着大小一起在画布内绘制图形、路径描述、虚线模式、图片或文字水印等等
  PdfCanvas类是一个更底层的类,直接用于绘制PDF页面上的内容。它提供了直接操作PDF内容流的方法,如绘制线条、矩形、文本等低级绘图操作。
主要特点:
  1:提供了对PDF内容流的直接访问,使得可以进行细粒度的绘图操作。
  2:适合需要精确控制绘图过程的场景,例如自定义图形、复杂路径等。
  3:使用PdfCanvas可以实现各种基本绘图操作,如移动到某个点、绘制路径、设置颜色、填充等。
总结: PdfCanvas对象偏向底层精确控制绘图操作,适合自定义图形和路径。提供更加细粒度的绘图方法。
\(\color{#00f}{关于PdfCanvas画布的基本方法:}\)

常用的构造函数:
    PdfCanvas(PdfPage page)
        通过PDF页面对象来构建PDF画布
    PdfCanvas(PdfDocument doc, int pageNum)
        通过PDF文档对象和当前文档内的指定页码来构建PDF画布
    PdfCanvas(PdfFormXObject xObj, PdfDocument document)
        通过PDF的表单对象和PDF文档来构建PDF画布
关于一些状态的方法:
    release()
        用于释放画布。在使用完画布后调用此方法可以确保资源被正确释放,避免内存泄漏。
        当完成所有绘图操作并且不再需要对当前画布进行任何修改时,调用此方法。
    stroke()
        用于绘制当前路径。该方法会使用当前的画笔属性(如颜色、线宽等)来描边路径,并清空当前路径。
        一般当你需要绘制线条或轮廓时使用。
    saveState()
        用于保存当前的图形状态。这包括当前的变换矩阵、剪切路径、线宽、线条样式和颜色等。
        一般在绘图过程中临时改变某些属性,并希望在稍后恢复这些属性的初始值时使用。
    restoreState()
        用于恢复之前保存的图形状态。它与saveState()配合使用,
        可以将绘图的各种设置(如颜色、线宽、变换等)恢复到调用saveState()时的状态。
        一般需要在一系列绘图操作中多次切换图形状态时非常有用。
    closePath()
        用于关闭当前路径。会将当前路径的起点和终点连接起来,形成一个封闭的路径。
        绘制多边形或封闭形状时,closePath特别有用,它可以确保路径的起点和终点之间自动连接一条直线。
        绘制一个需要封闭的形状(例如矩形、多边形等)时,可以使用 closePath()方法来封闭路径。
    endPath()
        用于结束当前路径而不绘制它。即该方法会终止路径的定义,但不会对路径进行描边或填充操作。
        比如定义路径作为剪切路径的一部分,但是不想让它在页面上可见时,可以使用endPath()。
关于绘图的一些通用方法:
    setLineWidth(float lineWidth)
        设置线段的宽度(粗细)
    setLineCapStyle(int lineCapStyle)
        设置线段端点的样式,接收一个数值,具体的取值如下:
            静态类获取PdfCanvasConstants.LineCapStyle
            0:【默认】线段在端点处直接截断,形成平直的结束。
            1:线段在端点处形成半圆,圆心位于端点,半径为线宽的一半。
            2:线段在端点处继续延伸一段距离,形成方形的结束,延伸的长度为线宽的一半。
    setLineJoinStyle(int lineJoinStyle)
        设置线段的连接样式,接收一个数值,具体的取值如下:
            静态类获取PdfCanvasConstants.LineJoinStyle
            0:【默认】在两条线段的连接处生成一个尖角,如果角度过小,则会自动转换为2。
            1:在两条线段的连接处生成一个圆角。
            2:在两条线段的连接处生成一个斜角。
    moveTo(double x, double y)
        用于移动绘图点(当前画笔位置)到指定坐标 (x, y) 的方法。
        这个方法通常用于开始新的路径或在当前路径上移动到一个新的起始点。

(一):图形绘画

  在画布中的图形绘画是不可避免的,它可以勾勒出各式各样好看的图形,比如圆形、矩形、直线等等,下面我们就简单说说一些图形的绘制。

使用画笔绘画图形的具体方法如下:
'【直线】:'
    lineTo(double x, double y)
        从当前点(x,y)追加一条直线段
'【矩形】:'
    rectangle(Rectangle rectangle)
    rectangle(double x, double y, double width, double height)
        绘画矩形,其中x,y代表从哪那个坐标开始画矩形,width,height代表矩形的大小;
        Rectangle构造函数内部可传如下参数:
            Rectangle(float width, float height)
            Rectangle(float x, float y, float width, float height)
'【圆角矩形】:'
    roundRectangle(double x, double y, double width, double height, double radius)
        参数和矩形的绘画参数是一样的,只是多个radius圆角参数,值越大,矩形越圆。
'【圆形】:'
    circle(double x, double y, double r)
        画圈的,其中x,y是确定圆的坐标中心点,r代表圆的半径
'【圆弧】:'
    arc(double x1, double y1, double x2, double y2, double startAng, double extent)
    arcContinuous(double x1, double y1, double x2, double y2, double startAng, double extent)
        用来绘制圆弧的方法,若想画半圆或者画指定度数的圆弧就用它咯。具体参数如下:
            x1,y1:椭圆边界矩形的第一个对角点的坐标。
            x2,y2:椭圆边界矩形的第二个对角点的坐标。
            startAng: 弧开始的角度,以度为单位,从椭圆的水平右侧(3点钟方向)开始逆时针测量。
            extent: 弧的扩展角度(需要画的弧大小),以度为单位。正值表示逆时针方向,负值表示顺时针方向。
        arcContinuous方法是将前面的线绘制到圆弧起点的部分椭圆,以防止路径中转。
'【椭圆】:'
    ellipse(double x1, double y1, double x2, double y2):。
        绘制在矩形x1,y1,x2,y2内切的椭圆。
            x1,y1:椭圆边界矩形的第一个对角点的坐标。
            x2,y2:椭圆边界矩形的第二个对角点的坐标。

'【贝塞尔曲线】:'
    curveTo(double x2, double y2, double x3, double y3)
        x2,y2:第一个控制点的x和y坐标。x3,y3:第二个控制点的x和y坐标。
    curveTo(double x1, double y1, double x2, double y2, double x3, double y3)
        x1,y1:起始点的x和y坐标。x2,y2:第一个控制点的x和y坐标。x3,y3:第二个控制点的x和y坐标。
    curveFromTo(double x1, double y1, double x3, double y3)、
        x2,y2:第一个控制点的x和y坐标。x3,y3:第二个控制点的x和y坐标。
    bezierArc(double x1, double y1, double x2, double y2, double startAng, double extent)
        生成贝塞尔曲线集合以绘制圆弧,有需求的可以用一下

1:画直线

代码效果:
image.png

点开查看详情:直线段绘画代码
public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 画直线(普通)
    pdfCanvas.saveState()
            .moveTo(50, 50)   // 将画笔移到指定位置
            .setLineWidth(2)          // 线粗
            .setStrokeColor(new DeviceRgb(255, 0, 0)) // 描边颜色
            .lineTo(100, 100)   // 画笔在画布上绘制线到指定位置
            .stroke().restoreState();
    // 使用画布线条画一个 "L"
    pdfCanvas.saveState()
            .moveTo(120, 100)   // 从这个点开始下笔
            .lineTo(120, 50)    // 画 |
            .lineTo(150, 50)    // 画 ——
            .setStrokeColor(new DeviceRgb(255, 0, 255))
            .stroke().restoreState();
    // 使用画布线条画一个 "▲"(填充已闭合)
    pdfCanvas.saveState()
            .moveTo(200, 50)     // 从这个点开始下笔
            .setLineWidth(5)     // 设置线粗5磅
            .lineTo(225, 100)    // 画 /
            .lineTo(250, 50)     // 画 \
            // .lineTo(200, 50)     // 画 ——
            // 最后的 "——" 可以不用画,让closePath()帮我们关闭路径
            .closePath()            //  关闭路径(结束点连向开始点)
            .setStrokeColor(new DeviceRgb(0, 0, 255)) // 线条颜色
            .setFillColor(new DeviceRgb(255, 0, 0))   // 设置填充色
            .closePathFillStroke()     // 设置路径闭合及轮廓和填充(那些路径属性必须在闭合前设置完)
            .stroke().restoreState();
    // 使用画布线条画一个 "▲"(填充未闭合)
    pdfCanvas.saveState()
            .moveTo(400, 50)     // 从这个点开始下笔
            .setLineWidth(5)     // 设置线粗5磅
            .lineTo(425, 100)    // 画 /
            .lineTo(450, 50)     // 画 \
            .lineTo(400, 50)     // 画 ——
            .setStrokeColor(new DeviceRgb(0, 0, 255))   // 线条颜色
            .setFillColor(new DeviceRgb(255, 0, 0))     // 设置填充色
            .fillStroke()     // 设置路径轮廓及填充(那些路径属性必须在闭合前设置完)
            .stroke().restoreState();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

2:画矩形

代码效果:
image.png

点开查看详情:矩形绘画代码
public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage(new PageSize(600, 150));
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 画一个简单的矩形
    pdfCanvas.saveState()
            .rectangle(new Rectangle(400,10,150,100))
            .setLineWidth(3)
            .setFillColor(new DeviceRgb(255, 255, 0))  // 填充色
            .setStrokeColor(new DeviceRgb(0, 255, 0))  // 矩形轮廓描边颜色
            .fillStroke()   // 填充+构建轮廓
            .stroke().restoreState();
    // 画一组带圆角的矩形
    for (int i = 1; i 〈= 10; i++) {
        // 随机数 0~255
        int random1 = new Random().nextInt(255);
        int random2 = new Random().nextInt(255);
        int random3 = new Random().nextInt(255);
        pdfCanvas.saveState()
                .roundRectangle(35 * i, 10, 30, 30, 5) // 构建圆角矩形
                .setLineWidth(3)    // 线宽
                .setFillColor(new DeviceRgb(random1, random2, random3))    // 填充色
                .setStrokeColor(new DeviceRgb(random1, random2, random3))  // 矩形轮廓描边颜色
                .fillStroke()   // 填充+构建轮廓
                .stroke().restoreState();
    }
    // 为当前页面画个轮廓(不要填充哟)
    float width = pdfPage.getPageSize().getWidth();
    float height = pdfPage.getPageSize().getHeight();
    pdfCanvas.saveState()
            .rectangle(5, 5, width - 10, height - 10)
            .setLineWidth(2)
            .setStrokeColor(new DeviceRgb(255, 0, 0))
            .stroke().restoreState();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}
点开查看详情:Rectangle管理矩形区域类说明
'关于Rectangle管理矩形区域类说明:'
'构造器:'
    Rectangle(float x, float y, float width, float height)
        构造一个矩形对象,指定左下角的x、y坐标,以及矩形的宽度和高度。
'常见方法:'
    getX()、getY()、getWidth()、getHeight()
        返回的正是我们设置的构造函数参数,坐标和宽高。
    getLeft()、getRight()、getTop()、getBottom()
        返回矩形左右边缘的是X坐标、返回矩形上下边缘的是Y坐标
    moveLeft(float distance)、moveRight(float distance)
        将矩形往左或者往右移动指定距离。
    moveDown(float distance)、moveUp(float distance)
        将矩形往下或者往上移动指定距离。
    intersects(Rectangle other)
        检查当前矩形是否与另一个矩形other相交。
    clone()
        克隆当前矩形对象,返回一个新的矩形对象。
'说明:可能大家看getX()和getLeft()返回的值是一样的,其实是有区别的:'
    getX()返回的是左下角x坐标。而getLeft()这个值实际上是矩形的左边界,可以
    理解为getX()方法返回的值加上矩形的宽度,因为左边缘是从左下角 x 坐标开始的。
    大概我们可以简单理解:
        getLeft()就是X坐标
        getRight()就是X坐标+元素宽度
        getTop()就是Y坐标+元素高度
        getBottom()就是Y坐标
        因为页面总体坐标系是在页面的左下角。

3:画椭圆和圆

代码效果:
image.png

点开查看详情:椭圆和圆绘画代码
public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage(new PageSize(600, 150));
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 画2个圆
    pdfCanvas.saveState().circle(75, 75, 50)
            .setFillColor(ColorConstants.YELLOW)
            .fillStroke().stroke().restoreState();
    pdfCanvas.saveState().circle(0, 0, 50)
            .setFillColor(ColorConstants.BLUE)
            .fillStroke().stroke().restoreState();
    // 绘制椭圆
    pdfCanvas
            .ellipse(200, 15, 300, 60)
            .setLineWidth(3)
            .setStrokeColor(new DeviceRgb(0, 0, 255))
            .setFillColor(new DeviceRgb(255, 0, 0))
            .fillStroke()
            .stroke();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

4:画弧线

代码效果:
image.png

点开查看详情:弧线绘画代码
public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage(new PageSize(600, 150));
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 通过弧度画一个圈
    pdfCanvas.saveState()
            .setLineWidth(5)
            // 开始画 0~90 度的弧(红色)
            .arc(0, 0, 200, 100, 0, 90)
            .setStrokeColor(ColorConstants.RED)
            .stroke()   // 截断,代表上面绘画路径结束了
            // 开始画 90~180 度的弧(蓝色)
            .arc(0, 0, 200, 100, 90, 90)
            .setStrokeColor(ColorConstants.BLUE)
            .stroke()
            // 开始画 180~270 度的弧(黄色)
            .arc(0, 0, 200, 100, 180, 90)
            .setStrokeColor(ColorConstants.YELLOW)
            .stroke()
            // 开始画 270~360 度的弧(青色)
            .arc(0, 0, 200, 100, 270, 90)
            .setStrokeColor(ColorConstants.CYAN)
            .stroke().restoreState();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

5:画贝塞尔曲线

代码效果:
image.png

点开查看详情:贝塞尔曲线绘画代码
public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage(new PageSize(600, 210));
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 画几个小框,可以明确知道下面画的曲线的起始和终点位置
    pdfCanvas.saveState()
            // 在300,150的坐标点上画个小框
            .rectangle(new Rectangle(300, 150, 2, 2))
            // 在200, 150的坐标点上画个小框
            .rectangle(new Rectangle(200, 150, 2, 2))
            // 在50, 50的坐标点上画个小框
            .rectangle(new Rectangle(50, 50, 2, 2))
            .stroke().restoreState();
    // 画贝塞尔曲线
    pdfCanvas.saveState()
            // 从坐标(100,100)开始绘画贝塞尔曲线
            .moveTo(100, 100)
            .curveTo(0, 0, 300, 150)
            .stroke()
            // 从坐标(50,50)开始绘画贝塞尔曲线
            .moveTo(50, 50)
            .setLineWidth(3)
            .setStrokeColor(ColorConstants.RED)
            .curveTo(0, 0, 350, 50, 200, 150)
            .stroke()
            // 从坐标(50,50)开始绘画贝塞尔曲线
            .moveTo(50, 50)
            .setLineWidth(2)
            .setStrokeColor(ColorConstants.BLUE)
            .curveFromTo(200, 150, 150, 200)
            .stroke().restoreState();
    // 循环绘画贝塞尔曲线
    for (int i = 10; i 〈 100; i += 10) {
        pdfCanvas.saveState()
                .moveTo(400, 100)
                .curveTo(450, 0, 500, 100 + i)
                .stroke().restoreState();
    }
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(二):路径填充描边

  有些时候我们通过画笔将一个图形画出来,但是如何更将画笔画过的路径以及路径里的填充色改变一下呢?这就是下面需要讲到的。

点开查看详情:关于路径填充及描边的方法说明
'==================================== 路径填充的一些方法 ===================================='
'关于路径填充的3组方式(具体可以试试):'
    路径填充和描边方法:
        fill()
            用于填充并闭合当前路径所围成的区域。它通常用于绘制实心的图形,比如实心的矩形、圆形等。
            当调用fill()方法后,路径所围成的区域会被填充上当前的填充颜色,形成一个实心的图形。
        fillStroke()
            结合了填充和描边两种效果。它首先会填充当前路径所围成的区域,然后再用线条描绘出路径的轮廓。
            这样既能够给图形区域着色,又能够突出图形的轮廓线条。
    路径填充和描边方法:
        eoFill()
            它只是简单地填充路径的内部,而不会绘制路径的轮廓。
            这意味着路径的内部会被填充颜色,但路径本身的边界不会被显示出来。
            这种方法通常用于创建填充的图形,比如填充一个闭合的多边形或区域。
        eoFillStroke()
            它首先填充路径的内部,然后再描边路径的轮廓。
            这意味着路径的内部会被填充颜色,同时路径的边界会被绘制出来。
            这种方法通常用于绘制既有填充又有描边的图形,使得图形看起来更加清晰和完整。
    路径闭合和填充描边方法:
        closePathFillStroke()
            首先会将当前路径闭合,然后填充所形成的区域,并描绘出路径的轮廓线条。
            它的作用类似于fillStroke()方法,但不同之处在于它会自动将路径闭合。
        closePathEoFillStroke()
            将当前路径闭合,但是它使用的是Even-Odd规则来填充所形成的区域,并描绘出路径的轮廓线条。
            EO规则指定如果路径内部从任意方向发射一条直线,那么这条直线要穿过路径的奇数次才被认为在图形内部
'关于路径填充的其它方法:'
    设置路径填充颜色:
        setFillColor(Color color)
        setFillColorGray(float g)
        setFillColorRgb(float r, float g, float b)
        setFillColorCmyk(float c, float m, float y, float k)
            将路径内填充的颜色设置指定的颜色,一般使用setFillColor或setFillColorRgb方式,如下:
                setFillColorRgb(255,0,0):设置红色路径填充
                setFillColor(ColorConstants.RED):设置红色路径填充
                setFillColor(new DeviceRgb(255,0,0)):设置红色路径填充
    路径填充颜色重置:
        resetFillColorRgb()
        resetFillColorCmyk()
        resetFillColorGray()
            3个都是将路径填充的颜色更改为黑色,但后面设置其它颜色会覆盖这些重置方法。
    关闭路径填充内的颜色:
        closePathStroke()
            正常情况下我们设置了路径填充fillStroke(),默认填充的颜色为黑色,除非我们设置了其它颜色,
            但是我们若不想设置路径填充颜色的话就使用这个方法即可。
关于路径填充底纹:
    setFillColorShading(PdfPattern.Shading shading)
        添加或更改当前填充颜色路径的底纹。
'==================================== 路径描边的一些方法 ===================================='
'关于路径描边颜色的其它方法:'
     设置路径描边颜色:
        setStrokeColor(Color color)
        setStrokeColorGray(float g)
        setStrokeColorRgb(float r, float g, float b)
        setStrokeColorCmyk(float c, float m, float y, float k)
            将路径描边的颜色设置指定的颜色,一般使用setStrokeColor或setStrokeColorRgb方式;如下:
                setStrokeColorRgb(255,0,0):设置红色路径描边
                setStrokeColor(ColorConstants.RED):设置红色路径描边
                setStrokeColor(new DeviceRgb(255,0,0)):设置红色路径描边
     路径颜色重置:
        resetStrokeColorRgb()
        resetStrokeColorCmyk()
        resetStrokeColorGray()
            3个都是将路径的颜色更改为黑色,但后面设置其它颜色会覆盖这些重置方法。
    关于路径描边底纹:
        setStrokeColorShading(PdfPattern.Shading shading)
            添加或更改当前描边颜色路径的底纹。
'==================================== 路径填充和描边的通用方法 ===================================='
    setColor(Color color, boolean fill)
    setColor(PdfColorSpace colorSpace, float[] colorValue, boolean fill)
    setColor(PdfColorSpace colorSpace, float[] colorValue, PdfPattern pattern, boolean fill)
        设置绘图颜色的方法。这个方法允许你指定要使用的颜色,并且可以选择是将颜色应用于填充操作还是描边操作
        它是个颜色设置的通用方法,可以设置如图形、文本等元素的填充色和描边色,具体的参数如下:
            color:颜色信息;
            fill:设置当前的颜色是填充还是描边,true=填充、false=描边;
            colorSpace:指定了颜色空间,即颜色值是如何定义的;
            colorValue:指定颜色空间中的颜色成分;
            pattern:指定一个定义颜色的模式;
        其实最常用的记住color和fill参数即可,其它的基本用到了在深入了解一下。

代码效果:
image.png

public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 使用画布线条画一个 "▲"
    // 使用fill()来当路径填充,这种方式没有路径描边,只有路径填充效果
    pdfCanvas.saveState().moveTo(50, 50).setLineWidth(5)
            // 画 /、\、——
            .lineTo(75, 100).lineTo(100, 50).lineTo(50, 50)
            .setStrokeColor(new DeviceRgb(0, 0, 255))   // 路径描边颜色
            .setFillColor(new DeviceRgb(255, 0, 0))     // 设置路径填充色
            .fill()
            .stroke().restoreState();
    // 使用画布线条画一个 "▲"
    // 使用fillStroke()来当路径填充,这种方式有路径描边和路径填充2种效果的
    // 需要注意的是fillStroke()路径填充,它路径是不会自闭和的,比如画三角形时,会有一角没闭合
    pdfCanvas.saveState().moveTo(150, 50).setLineWidth(5)
            // 画 /、\
            .lineTo(175, 100).lineTo(200, 50).lineTo(150, 50)
            .setStrokeColor(new DeviceRgb(0, 0, 255))   // 路径描边颜色
            .setFillColor(new DeviceRgb(255, 0, 0))     // 设置路径填充色
            .resetFillColorRgb() // 路径填充颜色重置,(默认就说黑色,后面再设置颜色会覆盖)
            .fillStroke()
            .stroke().restoreState();
    // 使用画布线条画一个 "▲"
    // 使用closePathFillStroke()会填充路径描边和路径填充2种效果,这方法还有自动闭合路径
    // 闭合的路径也不会存在豁角,即使图形的闭合线没画,他会自动连接开始和接收线的两端,并且自闭和
    pdfCanvas.saveState().moveTo(250, 50).setLineWidth(5)
            // 画 /、\、——
            .lineTo(275, 100).lineTo(300, 50)/*.lineTo(250, 50)*/
            .setStrokeColor(new DeviceRgb(0, 0, 255))   // 路径描边颜色
            .setFillColor(new DeviceRgb(255, 0, 0))     // 设置路径填充色
            .resetStrokeColorRgb() // 路径描边颜色重置,(默认黑色,需要在closePathStroke上面)
            .closePathStroke()  // 关闭路径填充颜色,就没有任何颜色了
            .closePathFillStroke()
            .stroke().restoreState();
    // 使用画布线条画一个 "▲"
    pdfCanvas.saveState()
            .moveTo(350, 50).setLineWidth(5)
            // 画 /、\、——
            .lineTo(375, 100).lineTo(400, 50).lineTo(350, 50)
            .setStrokeColor(new DeviceRgb(0, 0, 255))   // 路径描边颜色
            .setFillColor(new DeviceRgb(255, 255, 0))     // 设置路径填充色
            .closePathFillStroke()
            .stroke() // 打断画下一笔
            .rectangle(new Rectangle(370, 50, 50, 50))
            .setLineWidth(2) // 设置线粗,要不然还是用上面的线粗5
            .setStrokeColor(new DeviceRgb(99, 0, 50))
            .closePathStroke()
            .fill() // 闭合
            .stroke().restoreState();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(三):文本信息显示

  在画布上绘画文本信息可谓是重要的,比如我们常见的页面水印,就是通过这个完成的,下面我详细说说关于在画布上设置文本信息的一些基本操作。

点开查看详情:关于画布上显示文本方法说
'开始和结束文本方法:'
    用于开始和结束常规文本操作。
        beginText()
            开始定义文本区域,并准备在该区域中进行文本绘制操作。在调用beginText()方法后,可以使用其它方法
            来设置文本的字体、内容、位置、字号、颜色等属性;注意beginText()方法必须与endText()方法成对出
            现,以确保正确定义和结束文本区域。
        endText()
            结束文本绘制操作,完成文本定义区域。注意beginText()方法必须与endText()方法成对出现,以确保
            正确定义和结束文本区域。
    补充:用于创建和结束可变文本块,允许在同一个页面区域内绘制具有不同样式的多个文本段落。
        beginVariableText()、endVariableText()
'文本显示系列方法:'
    showText(String text)
    showText(PdfArray textArray)
        在画布上显示文本字符串,设置多个showText只会在一行显示;多行显示参考下面。
        它接收两种类型的参数,一种是一个完整的字符串,另外一种是文本的数组类型;如下:
            // 中文显示字体
            PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
            // 构建数组
            PdfArray textArray = new PdfArray();
            textArray.add(new PdfString(font.convertToBytes("蚂蚁")));
            textArray.add(new PdfString(font.convertToBytes(" - ")));
            textArray.add(new PdfString(font.convertToBytes("小哥")));
        另外两种不常用:
            showText(GlyphLine text)
            showText(GlyphLine text, Iterator〈GlyphLine.GlyphLinePart〉 iterator)
    newlineShowText(String text)
    newlineShowText(float wordSpacing, float charSpacing, String text)
        移动到下一行,并显示文本字符串,一般用于插入多行文本时使用,具体参数说明如下:
            text:需要显示的文本
            charSpacing:字符间距
            wordSpacing:单词间距;(注意:不可以在中文字体下设置间距。)
'调整文本样式系列方法:'
    setFontAndSize(PdfFont font, float size)
        设置字体样式和字体大小。
    moveText(double x, double y)
        通过坐标(左下角0,0)来移动文本信息到画布指定位置
    setHorizontalScaling(float scale)
        设置文本水平缩放参数,按照给定的参数设置文本左右宽度是否被拉长或者挤压,高度不会变。
    setTextRise(float textRise)
        用于设置文本的上升位置。文本上升指的是文本基线相对于当前文本行的垂直偏移量,
    setTextRenderingMode(int textRenderingMode)
        设置文本的呈现方式,可以设置文本是否有描边、填充等效果,常见值设置如下:
            0:文本只填充,这时可以通过setColor设置文本颜色
            1:文本只描边,这时可以通过setStrokeColor设置文本的描边信息,但没有填充
            2:文本既有描边也有填充
        具体参考我上篇文章的第五章里的第一小节中:设置文本的渲染模式。
    setWordSpacing(float wordSpacing)
        设置单词与单词之间的间距;注:是单词间距哟,且不可以在中文字体下设置间距。
    setCharacterSpacing(float charSpacing)
        设置文本字符与字符之间的间距。
    setLeading(float leading)
        设置文本行与文本行之间的垂直距离,通过调整行间距,可以控制文本块中各行之间的距离。
    newlineText()
        移动到下一行的开头
    moveTextWithLeading(float x, float y)
        在PDF页面上移动当前文本插入点的位置,并在移动后的位置添加文本。
        将当前文本插入点的位置相对于当前的行高进行移动。
        参数x和y分别表示在当前行基础上水平和垂直方向上的偏移量。

1:基本文本显示

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 画一个矩形,给下面的文本使用
    pdfCanvas.saveState()
            .rectangle(new Rectangle(20, 30, 500, 100))
            .setLineWidth(5)                    // 设置线宽
            .setStrokeColor(ColorConstants.RED) // 描边颜色
            .setFillColor(ColorConstants.GRAY)  // 填充颜色
            .setLineJoinStyle(1)                // 设置矩形连接点为圆角
            .fillStroke()                       // 设置路径填充
            .stroke().restoreState();
    pdfCanvas.beginText()   // 开启文本
            .moveText(30, 64)           // 设置文本在画布的指定位置
            .setFontAndSize(font, 36)    // 设置文本的样式和大小
            // 设置文本的呈现方式,这里我设置的是既有描边也有填充
            .setTextRenderingMode(PdfCanvasConstants.TextRenderingMode.FILL_STROKE)
            .setStrokeColor(ColorConstants.GREEN)   // 描边绿色
            .setFillColor(ColorConstants.RED)       // 填充红色
            .showText("您好,蚂蚁小哥!Hello World!")
            .endText();     // 关闭文本
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

2:多行文本显示

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    pdfCanvas.beginText()
            .moveText(30, 120)            // 设置文本在画布的指定位置
            .setFontAndSize(font, 16)     // 设置文本的样式和大小
            .setCharacterSpacing(5)            // 设置字符间的间距
            .setColor(new DeviceRgb(0, 0, 255), true) // 设置填充色
            .setLeading(20) // 设置垂直文本的间距(多行文本一定要设置)
            .showText("披着西装穿草鞋——土洋结合")        // 第一行文本显示
            .newlineText()                             // 使文本换行
            .showText("鸡披袍子狗戴帽——衣冠禽兽")        // 第二行文本显示
            .newlineShowText("三个小鬼丢了两——丢魂落魄") // 第三行文本显示
            .newlineShowText("坟地里睡个酒鬼——醉生梦死") // 第四行文本显示
            .endText();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

3:其它基本方法

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 为画布创建坐标系点每个间隔10*10
    PdfExtGState pdfExtGState = new PdfExtGState();
    pdfExtGState.setStrokeOpacity(.3f); // 描边透明的
    for (int x = 0; x < 700; x += 10) {
        for (int y = 0; y < 400; y += 10) {
            pdfCanvas.saveState().setStrokeColor(ColorConstants.GRAY)
                    .setExtGState(pdfExtGState)                   // 设置补充信息
                    .setLineDash(1, 1, 0)   // 设置虚线模式
                    .moveTo(x, y).lineTo(x, y + 10)
                    .moveTo(x - 10, y).lineTo(x + 10, y)
                    .stroke().restoreState();
        }
    }
    // ===== 未发生改变的文本信息 =====
    pdfCanvas.beginText().moveText(30, 40)
            .setFontAndSize(font, 20)
            .showText("您好,蚂蚁小哥;Hello World!")
            .endText();
    // ===== 文本的偏移(可以画如楼梯状文本段落) =====
    pdfCanvas.beginText().moveText(380, 20)
            .setFontAndSize(font, 12)
            .showText("我是蚂蚁小哥A!")
            .moveTextWithLeading(10, 10)
            .newlineShowText("我是蚂蚁小哥B!")
            .moveTextWithLeading(10, 10)
            .newlineShowText("我是蚂蚁小哥C!")
            .endText();
    // ===== 关于文本的缩放模式 =====
    pdfCanvas.beginText()
            .moveText(30, 60)
            .setFontAndSize(font, 20)
            .setHorizontalScaling(80)   // 设置缩放80,水平增大或缩小,高度不变
            .showText("缩放,蚂蚁小哥;Hello World!")
            .endText();
    // ===== 关于文本的垂直偏移 =====
    pdfCanvas
            .beginText()
            .moveText(30, 40)
            .setFontAndSize(font, 20)
            .setFillColor(ColorConstants.RED)
            // 文本原本是和原文在一个位置,但是设置了Rise下移了20磅,正数上升
            .setTextRise(-20)
            .setHorizontalScaling(100)
            .showText("您好,蚂蚁小哥;Hello World!")
            .endText();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(四):矩阵变换操作

  学过前端的CSS3的2D变换transform的话,基本都可以看懂一些,除了角度的计算麻烦,其它如平移、缩放都是挺简单的,下面我在Itext中以画布的方式来介绍PDF生成中如何使用元素的变换。先看变换方法:

变换矩阵系列方法:
'【关于文本的变换(文本变换)】:'
    setTextMatrix(float x, float y)
    setTextMatrix(float a, float b, float c, float d, float x, float y)
    setTextMatrix(AffineTransform transform)
    通过设置对应的参数来使文本信息缩放、旋转、位移等操作,下面为参数说明:
        a:用于水平缩放(scaleX)。当值为正时,保持水平方向不变;当值为负时,会水平翻转文本。
        b:用于垂直倾斜(shearY)。当值为正或负时文本会在Y轴方向上倾斜。
        c:用于水平倾斜(shearX)。当值为正或负时文本会在X轴方向上倾斜。
        d:用于垂直缩放(scaleY)。当值为正时,保持垂直方向不变;当值为负时,会垂直翻转文本。
        x:定义X方向的平移(translationX)。
        y:定义Y方向的平移(translationY)。
        transform:这个具体可以参考CSS3的2D变换transform属性,通过对象可以展现出来。
'【关于通用的变换(文本、图形变换)】:'
    concatMatrix(PdfArray array)
    concatMatrix(double a, double b, double c, double d, double e, double f)
    concatMatrix(AffineTransform transform)
    通过设置对应的参数来使图形或者文本信息实现缩放、旋转、位移等操作,下面为参数说明:
        a,d:这些参数控制缩放和旋转。
        b,c:这些参数控制旋转和倾斜。
        e,f:这些参数控制平移,就是坐标系x,y。
    基本示例:
        将文本或图形平移到x=100,y=200的位置:
            concatMatrix(1, 0, 0, 1, 100, 200);
        将文本或图形缩放它自身的2倍大小:
            concatMatrix(2, 0, 0, 2, 0, 0);
        将文本或图形旋转45度角:
            double angle = Math.toRadians(45);
            double cos = Math.cos(angle);
            double sin = Math.sin(angle);
            concatMatrix(cos, sin, -sin, cos, 0, 0);

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 为画布创建坐标系点每个间隔10*10
    for (int x = 0; x < 700; x += 10) {
        for (int y = 0; y < 400; y += 10) {
            pdfCanvas.saveState().setStrokeColor(ColorConstants.GRAY)
                    .rectangle(new Rectangle(x, y, 1, 1))
                    .stroke().restoreState();
        }
    }
    // 绘画一个普通的矩形(就是图上面的黑色矩形)
    pdfCanvas.saveState()
            .setLineWidth(1)
            .rectangle(new Rectangle(10, 10, 50, 50))
            .stroke().restoreState();
    // 矩形缩放2倍
    // 可以看出不光矩形的宽和高都缩放了2倍(变成100*100),并且矩形的位移10*10也被放大成20*20
    pdfCanvas.saveState()
            .setLineWidth(1)
            .setStrokeColor(ColorConstants.BLUE)
            .concatMatrix(2, 0, 0, 2, 0, 0)
            .rectangle(new Rectangle(10, 10, 50, 50))
            .stroke().restoreState();
    // 旋转5度角
    double angle = Math.toRadians(5);
    double cos = Math.cos(angle);
    double sin = Math.sin(angle);
    pdfCanvas.saveState()
            .setLineWidth(1)
            .setStrokeColor(ColorConstants.GREEN)
            .concatMatrix(cos, sin, -sin, cos, 0, 0)
            .rectangle(new Rectangle(10, 10, 50, 50))
            .stroke().restoreState();
    // 绘画一段普通文本
    pdfCanvas.beginText().moveText(200, 50)
            .setFontAndSize(font, 18)
            .showText("蚂蚁小哥")
            .endText();
    // 对文本进行缩放2倍,.5f则是缩放0.5倍,1是不缩放
    pdfCanvas.beginText().moveText(200, 50)
            .setColor(ColorConstants.GREEN, true)
            .setTextMatrix(2, 0, 0, 2, 200, 50)
            .setFontAndSize(font, 18)
            .showText("蚂蚁小哥")
            .endText();
    // 对文本进行水平和垂直倾斜;具体需要计算度数(0.364f为45度角)
    pdfCanvas.beginText().moveText(200, 50)
            .setColor(ColorConstants.RED, true)
            .setTextMatrix(1, 0.364f, 0.364f, 1, 200, 50)
            .setFontAndSize(font, 18)
            .showText("蚂蚁小哥")
            .endText();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(五):线条虚线模式

  在画布上使用线条虚线模式了解一下即可,其实也简单,只需调用一个方法即可;但是里面有个注意事项,就说虚线左下角的起始点位置,其实也不用太刻意,它主要针对需要精细的绘画使用的。

'设置线条的虚线模式:'
    setLineDash(float phase)
    setLineDash(float[] array, float phase)
    setLineDash(float unitsOn, float phase)
    setLineDash(float unitsOn, float unitsOff, float phase)
    通过不同的参数组合来实现各种虚线效果参数说明:
        phase:虚线模式的起始点。在虚线模式开始时跳过的单位数。
        array:一个包含线段和间隔长度的数组。例如{3, 2}表示线段长度为3,间隔长度为2。
        unitsOn:每个线段的长度(线段长和线段间隔都一样,若设置了unitsOff则以指定的间隔展示)。
        unitsOff:每个间隔的长度。
    关于phase参数的细说:
        假设unitsOn=3,unitsOff=4具体虚线的排列是如下情况(-代表线段,▢代表空格):
            ---▢▢▢▢---▢▢▢▢---▢▢▢▢
        假设unitsOn=3,unitsOff=4,phase=2具体虚线的排列是如下情况(-代表线段,▢代表空格):
            -▢▢▢▢---▢▢▢▢---▢▢▢▢
         假设unitsOn=3,unitsOff=4,phase=7具体虚线的排列是如下情况(-代表线段,▢代表空格): 
            ---▢▢▢▢---▢▢▢▢(前面已经间隔了7个,正好就是一组---▢▢▢▢)

代码效果:
image.png

public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 标准的画一个矩形
    pdfCanvas.saveState()
            .setFillColor(ColorConstants.CYAN)      // 填充颜色
            .setStrokeColor(ColorConstants.RED)     // 路径描边颜色
            .setLineWidth(10)                       // 路径描边宽度
            .rectangle(new Rectangle(50, 30, 400, 50))
            .fillStroke()   // 填充路径
            .stroke().restoreState();
    // 使用虚线模式画一个矩形
    pdfCanvas.saveState()
            .setFillColor(ColorConstants.CYAN)
            .setStrokeColor(ColorConstants.RED)
            .setLineWidth(10)
            // 设置虚线模式; 线段长20,间隙长5,起始点5
            .setLineDash(new float[]{20, 10}, 10)
            //.setLineDash(20, 10, 10) // 上面的等同这个写法
            .rectangle(new Rectangle(50, 100, 400, 50))
            .fillStroke()   // 填充路径
            .stroke().restoreState();
    // 使用虚线模式画一个矩形
    pdfCanvas.saveState()
            .setFillColor(ColorConstants.CYAN)      // 填充颜色
            .setStrokeColor(ColorConstants.RED)     // 路径描边颜色
            .setLineWidth(10)                       // 路径描边宽度
            .setLineDash(new float[]{20, 10}, 0) // 线段长20,间隙长10,起始点0
            .rectangle(new Rectangle(50, 200, 400, 50))
            .fillStroke()   // 填充路径
            .stroke().restoreState();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(六):设置扩展信息

  有一些额外的信息我们可以通过这个方法来实现,比如我们常知的PDF水印就可以通过这个来设置透明度。

设置元素的扩展信息:
    setExtGState(PdfExtGState extGState)
    setExtGState(PdfDictionary extGState)
        这个方法用来设置元素的扩展信息,例如:
            PdfDictionary字典对象可以包含自定义的图形状态属性
            PdfExtGState对象用来设置图形状态(主要用来设置填充或者描边的透明的了)
        设置透明的示例如下:
            PdfExtGState pdfExtGState = new PdfExtGState();
            pdfExtGState.setFillOpacity(.5f);   // 填充透明的
            pdfExtGState.setStrokeOpacity(.5f); // 描边透明的
            pdfCanvas.setExtGState(pdfExtGState);
关于PdfExtGState对象的常用方法:
    设置线条相关方法:
        setLineWidth(float lineWidth):设置线条的宽度
        setLineJoinStyle(int lineJoinStyle):设置线条的连接风格(可选值:1、2、3)。
        setLineCapStyle(int lineCapStyle):设置线条的端点风格(可选值:1、2、3)
    设置透明度相关方法:
        setFillOpacity(float fillOpacity):设置填充的透明度。
        setStrokeOpacity(float strokeOpacity):设置描边的透明度

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 创建画笔的扩展信息,并添加到画布中
    PdfExtGState pdfExtGState = new PdfExtGState();
    pdfExtGState.setLineWidth(1)        // 设置线粗
            .setStrokeOpacity(.3f)  // 设置描边透明度
            .setFillOpacity(.3f)    // 设置填充透明度
            .setLineJoinStyle(PdfCanvasConstants.LineCapStyle.ROUND);
    // 设置画布的一些信息
    pdfCanvas.setExtGState(pdfExtGState);
    pdfCanvas.setFillColor(new DeviceRgb(255, 0, 0));  // 填充色
    pdfCanvas.setStrokeColor(new DeviceRgb(0, 0, 255));// 矩形轮廓描边颜色
    // 创建文本和矩形
    pdfCanvas.beginText().moveText(20, 40)
            .setFontAndSize(font, 60)
            .setTextRenderingMode(PdfCanvasConstants.TextRenderingMode.FILL_STROKE)
            .showText("您好,蚂蚁小哥!")
            .endText();
    // 创建矩形
    pdfCanvas.saveState()
            .rectangle(300, 10, 150, 100)
            .setLineWidth(5)
            .fillStroke()   // 填充+构建轮廓
            .stroke().restoreState();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(七):创建路径裁切

clip()
    用于将当前路径设置为剪切区域。
    只有在这个剪切区域内的内容会被保留或绘制,超出该区域的部分会被裁剪掉。
    当你需要在指定区域内进行绘制,并希望超出该区域的部分不可见时,可以使用clip()方法来设置剪切路径。
eoClip()
    用于将当前路径设置为剪切区域,但使用 Even-Odd 规则来确定剪切区域内外的内容。
    在Even-Odd规则下,根据路径交叉的奇偶性来决定剪切区域。
    当你需要复杂的剪切路径,或者需要与之前设置的路径进行特定的逻辑剪切时,可以使用eoClip()方法。

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 为画布创建坐标系点每个间隔10*10
    PdfExtGState pdfExtGState = new PdfExtGState();
    pdfExtGState.setStrokeOpacity(.3f); // 描边透明的
    for (int x = 0; x < pdfPage.getPageSize().getWidth(); x += 10) {
        for (int y = 0; y < pdfPage.getPageSize().getHeight(); y += 10) {
            pdfCanvas.saveState().setStrokeColor(ColorConstants.GRAY)
                    .setExtGState(pdfExtGState)                   // 设置补充信息
                    .setLineDash(1, 1, 0)   // 设置虚线模式
                    .moveTo(x, y).lineTo(x, y + 10)
                    .moveTo(x - 10, y).lineTo(x + 10, y)
                    .stroke().restoreState();
        }
    }
    // 绘画裁剪区域,只有裁剪区域内的内容才可以展现
    pdfCanvas.circle(110, 110, 60).eoClip().endPath();
    // 创建四个圆,分别构成一个“田”字型排列
    pdfCanvas.saveState()
            .setFillColor(new DeviceRgb(85, 107, 47))  // 设置路径填充色
            .setStrokeColor(new DeviceRgb(85, 107, 47))// 设置描边填充色
            .circle(60, 60, 50)
            .circle(160, 60, 50)
            .circle(160, 160, 50)
            .circle(60, 160, 50)
            .setLineWidth(2)
            .fillStroke()   // 填充+构建轮廓
            .stroke().restoreState();
    // 构建文字“招财进宝”
    pdfCanvas.beginText()
            .setFontAndSize(font, 20)
            .setFillColor(new DeviceRgb(255, 215, 0))
            // 将 招 移动到指定位置
            .moveText(75, 130).showText("招")
            // 将 财 移动到指定位置
            .moveTextWithLeading(50, 0) .newlineShowText("财")
            // 将 进 移动到指定位置
            .moveTextWithLeading(-50, -25).newlineShowText("进")
            // 将 宝 移动到指定位置
            .moveTextWithLeading(50, 0).newlineShowText("宝")
            .endText();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

(八):画布添加图片

  有些东西我们是不需要通过画布进行绘画的,而是通过图片的方式添加到画布中,为画布添加一些色彩。

'将图片添加到画布:'
    addImageAt(ImageData image, float x, float y, boolean asInline)
        将图像添加到指定的坐标x,y上;(一般不使用这种方式)
    addImageFittedIntoRectangle(ImageData image, Rectangle rect, boolean asInline)
        将图形添加到指定位置,位置由Rectangle对象指定;(推荐使用此方式)
        如:new Rectangle(0,0,200,300):将图片放到0,0的坐标上(左下角),并设置图片大小为200*300
    addImageWithTransformationMatrix、addImageWithTransformationMatrix两个方法一般不用,带变换的。

代码效果:
image.png

public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建两张新页面
    pdfDocument.addNewPage();
    pdfDocument.addNewPage(new PageSize(600,200));
    // 加载本地图片资源
    ImageData image1 = ImageDataFactory.create("E:\\JPEGImages\\000619.jpg");
    // 获取文档第二页的页面大小
    Rectangle pageSize = pdfDocument.getPage(2).getPageSize();
    float width = pageSize.getWidth();
    float height = pageSize.getHeight();
    // 图片宽高
    float wImg = 60;
    float hImg = 40;
    // 为文档第二页构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfDocument, 2);
    // 图片位于左下角
    Rectangle leftBottom = new Rectangle(0, 0, wImg, hImg);
    // 图片位于左上角
    Rectangle leftTop = new Rectangle(0, height - hImg, wImg, hImg);
    // 图片位于右上角
    Rectangle rightTop = new Rectangle(width - wImg, height - hImg, wImg, hImg);
    // 图片位于右下角
    Rectangle rightBottom = new Rectangle(width - wImg, 0, wImg, hImg);
    // 图片位于中间
    Rectangle center = new Rectangle(width/2-(wImg/2), height/2-(hImg/2), wImg, hImg);
    // 添加图片到画布
    pdfCanvas.addImageFittedIntoRectangle(image1, leftBottom, true);
    pdfCanvas.addImageFittedIntoRectangle(image1, leftTop, true);
    pdfCanvas.addImageFittedIntoRectangle(image1, rightTop, true);
    pdfCanvas.addImageFittedIntoRectangle(image1, rightBottom, true);
    pdfCanvas.addImageFittedIntoRectangle(image1, center, true);
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

四:画布Canvas对象说明

  Canvas 类是一个更高级别的抽象,提供了更方便的方法来创建和布局内容。它是在Document对象上进行操作的,通常用于添加段落、表格、图像等较高层次的内容。
主要特点:
  1:提供了更高层次的抽象,便于快速创建和布局内容。
  2:适合处理文本、表格、图像等更结构化的内容。
  3:Canvas继承了RootElement类,可以用来管理页面上的元素,支持排版和样式设置。
总结: Canvas更高级,它主要进行排版和布局,快速添加和布局结构化内容,如文本、表格、图片等。
关于本小节的内容还得参考上篇Itext操作的第九章文档块级元素的操作,

Canvas基本构造器:
    Canvas(PdfPage page, Rectangle rootArea)
    Canvas(PdfCanvas pdfCanvas, Rectangle rootArea)
    Canvas(PdfCanvas pdfCanvas, Rectangle rootArea, boolean immediateFlush)
    Canvas(PdfFormXObject formXObject, PdfDocument pdfDocument)
    参数说明:
        page:目标PDF页面对象。
        rootArea:定义当前画布绘制区域的矩形块。
        pdfCanvas:目标PDF画布对象。
        immediateFlush:是否立即刷新内容到PDF文档的标志。
        formXObject:目标表单的XObject对象。
        pdfDocument:关联的PDF文档对象。
Canvas基本方法:
    close():
        用于关闭文档或画布,并释放相关的资源。一旦调用了,就不能再向文档或画布添加新内容。
    flush():
        用于强制所有已注册的呈现器(包括子元素呈现器)将其内容刷新到内容流。
        在某些情况下,可能需要立即将画布上的内容刷新到PDF内容流中,而不是等到自动刷新或关闭时才执行。
    isCanvasOfPage()
        画布是否正是页面的直接内容。只有当这个实例是由Canvas(PdfPage,Rectangle)构造函数重载
        创建的时,这才是已知的,否则都将返回false。
    getPage():
        返回与此Canvas对象相关联的PdfPage对象。
    getPdfCanvas():
        返回与此Canvas对象相关联的PdfCanvas对象。
    getPdfDocument():
        返回与此Canvas对象相关联的PdfDocument对象。
    getRootArea():
        返回Rectangle对象,该对象表示此Canvas对象的根区域(即画布的绘图区域)。

\(\color{#00f}{一个简单的效果:}\)

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建一个新页
    PdfPage pdfPage = pdfDocument.addNewPage();
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // ============== 创建一个画布A在画布内构建一个1个段落,并且文字居中 ==============
    // 画布A的大小是200pt*100pt;并且当前画布位于页面的(20,20)的位置
    Canvas canvasA = new Canvas(pdfCanvas, new Rectangle(20, 20, 200, 100));
    //创建一个段落
    Paragraph paragraph = new Paragraph("Hello World!")
            .setWidth(160)
            .setHeight(50)
            .setBorderRadius(new BorderRadius(30))
            .setBackgroundColor(new DeviceRgb(255, 0, 0), .3f)
            .setFontColor(new DeviceRgb(0, 0, 255))
            .setVerticalAlignment(VerticalAlignment.MIDDLE) // 垂直居中
            .setHorizontalAlignment(HorizontalAlignment.CENTER) // 水平居中
            .setFontSize(16);
    // 创建一个DIV对象,并且覆盖画布A所有位置,在把段落设置进去
    Div divA = new Div();
    divA.add(paragraph)
            .setWidth(200)
            .setHeight(100)
            .setBorderRadius(new BorderRadius(20))
            .setTextAlignment(TextAlignment.CENTER) // 文本居中
            .setVerticalAlignment(VerticalAlignment.MIDDLE) // 垂直居中
            .setBackgroundColor(ColorConstants.GREEN, .3f);
    // 添加到画布中,并强制刷新和关闭
    canvasA.add(divA);
    canvasA.flush();
    canvasA.close();
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}

五:画布案例总结

  说了这么多方法的使用,总得把知道的方法拿出来使用一下,下面我将从几个基础的示例中总结上面所学到的方法。下面会有一些方法的补充:

(一):补充知识点

'关于showTextAligned方式说明:'
    此方法只能在Canvas和Document对象内使用,使其简便语法,一个方法可以完成很多操作。
    showTextAligned(Paragraph p, float x, float y, int pageNumber,
        TextAlignment textAlign, VerticalAlignment vertAlign, float radAngle)
    showTextAligned(Paragraph p, float x, float y, TextAlignment textAlign)
    showTextAligned(Paragraph p, float x, float y, TextAlignment textAlign,
        VerticalAlignment vertAlign)
    showTextAligned(String text, float x, float y, TextAlignment textAlign)
    showTextAligned(String text, float x, float y, TextAlignment textAlign, float angle)
    showTextAligned(String text, float x, float y, TextAlignment textAlign,
        VerticalAlignment vertAlign, float angle)
    参数说明:
        p:设置段落元素对象。
        text:设置文本字符串信息。
        x,y:文本起始点的X和Y坐标。
        pageNumber:要在其上绘制文本的页面编号。
        textAlign:水平对齐方式。
        vertAlign:垂直对齐方式。
        radAngle:文本旋转角度,以弧度表示。

'关于PdfXObject抽象类说明:'
    PdfXObject是一个抽象类,代表了PDF文档中的可重用对象,如表单、图像等。
    PdfXObject及其子类用于表示PDF 文档中的可嵌入对象,它们允许将同一对象多
    次插入到同一文档或不同文档中,从而实现PDF文档的重用和优化。
    具体的子类如下:
        PdfImageXObject:
            作用:PdfImageXObject用于表示一个单独的图像对象,可以是 JPG、PNG、GIF 等格式的图像。
            特点:它通常用于插入静态的图像到 PDF 页面中,比如公司标志、背景图等。
            使用场景:通常用于在 PDF 页面中插入静态的图像内容。
        PdfFormXObject:
            作用:PdfFormXObject用于表示一个独立的页面内容对象,可以包含文本、图形、图像等内容。
            特点:它通常用于创建可重复使用的页面模板,比如页眉、页脚、公司标志等,以便在多个页面中重复使用。
            使用场景:通常用于定义一些通用的页面元素,然后在多个页面中引用这些元素,从而实现页面内容的共享和复用。
    基本的构造器说明:
        PdfFormXObject(Rectangle bBox):创建一个指定大小的PDF的form对象。
        PdfImageXObject(ImageData image):传入一张图片数据,图片就是对象大小。

(二):图片水印

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    // 为图片添加水印并加入到文档
    Image image = new Image(ImageDataFactory.create("E:\\images\\a\\aaaa.jpg"));
    image.setWidth(550).setHeight(300);
    Image watermarkedImage = getWatermarkedImage(pdfDocument, image, "@蚂蚁小哥", 70);
    document.add(watermarkedImage);
    document.close();
}
/***
 * 为图片添加水印
 * @param pdfDocument PDF文档信息
 * @param img 图片信息
 * @param msg 水印信息
 * @param interval 水印间隔
 * @return 添加水印后的图片
 * @throws IOException 这里的异常是无法获取字体异常
 */
public static Image getWatermarkedImage(PdfDocument pdfDocument, Image img,
                                        String msg, int interval) throws IOException {
    // 获取图片的宽高信息,先获取是否更改过图片宽高;77代表宽,27代表高
    float width = img.getImageScaledWidth();
    float height = img.getImageScaledHeight();
    if (img.getProperty(27) != null) {
        height = ((UnitValue)img.getProperty(27)).getValue();
    }
    if (img.getProperty(77) != null) {
        width = ((UnitValue)img.getProperty(77)).getValue();
    }
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 创建PDF表单对象包装器
    PdfFormXObject pdfFormXObject = new PdfFormXObject(new Rectangle(width, height));
    // 创建一个画布并将图片添加到PdfFormXObject对象上
    new Canvas(pdfFormXObject, pdfDocument).add(img).close();
    // =============== 开始绘制水印 ===============
    for (int x = 0; x < width; x += interval) {
        for (int y = 0; y < height; y += interval) {
            new Canvas(pdfFormXObject, pdfDocument)
                    .setFontColor(ColorConstants.WHITE, .3f) // 颜色和透明度
                    .setFont(font)      // 文字样式
                    .setFontSize(18)    // 字体大小(具体可以改)
                    .showTextAligned(msg, x, y, TextAlignment.CENTER,
                            VerticalAlignment.MIDDLE, 19.5f);
        }
    }
    // 转换成图片资源返回
    return new Image(pdfFormXObject);
}

(三):页面水印

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    // 创建2张页面
    pdfDocument.addNewPage();
    pdfDocument.addNewPage();
    // 循环为页设置水印
    for (int i = 0; i < pdfDocument.getNumberOfPages(); i++) {
        PdfPage page = pdfDocument.getPage(i + 1);
        setPageWatermark(page, "@蚂蚁小哥", 140);
    }
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    document.close();
}
/***
 * 为PDF页绘画水印
 * @param pdfPage PDF页对象
 * @param msg 水印信息
 * @param interval 水印间隔
 * @throws IOException 这里的异常是无法获取字体异常
 */
public static void setPageWatermark(PdfPage pdfPage, String msg, int interval) throws IOException {
    // 通过PDF页来构建画布
    PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    float width = pdfPage.getPageSize().getWidth();
    float height = pdfPage.getPageSize().getHeight();
    // 开始绘画水印
    for (int x = 0; x < pdfPage.getPageSize().getWidth(); x += interval) {
        for (int y = 0; y < pdfPage.getPageSize().getHeight(); y += interval) {
            Canvas canvas = new Canvas(pdfCanvas, new Rectangle(0, 0, width, height))
                    .setFontColor(ColorConstants.GRAY, .1f) // 颜色和透明度
                    .setFont(font)      // 文字样式
                    .setFontSize(20)    // 字体大小(具体可以改)
                    .showTextAligned(msg, x, y, TextAlignment.CENTER,
                            VerticalAlignment.MIDDLE, 19.5f);
            canvas.close();
        }
    }
    // 释放画布。使用完画布后,请使用此方法。
    pdfCanvas.release();
}

六:元素渲染器(IRenderer)

  IRenderer(渲染器)是一个接口,它定义了用于呈现PDF文档内容的方法和属性。IRenderer的子类实现了具体的呈现逻辑,例如将文本、图像、表格等元素渲染到PDF页面上。它的作用是将文档元素转换为可视化的内容并在PDF页面上显示出来。它负责处理文档的布局、样式、位置等属性,以确保文档在呈现时能够正确显示并符合设计要求。需要IRenderer的原因是为了实现PDF文档的定制化呈现。通过使用IRenderer的子类,开发人员可以自定义文档的呈现方式,实现特定的排版样式和布局效果。这对于需要根据特定需求生成定制化PDF文档的应用程序非常重要。
  关于 IRenderer 是个接口,正常会使用它各种的子类接口实现。正常来说,所有的元素都有其对应的自身渲染器,常见元素渲染器如下:
  DivRendererParagraphRendererTableRendererCellRendererImageRendererLineRendererTextRendererCanvasRenderer

'Ⅰ:DivRenderer:用于渲染Div元素,即iText中的通用容器元素。'
    支持将多种类型的内容(如文本、表格、图像等)嵌套在一个容器内,并负责容器内各元素的布局管理。
    一般用于创建自定义的文本块或页面区块,实现复杂的布局结构。
'Ⅱ:ParagraphRenderer:用于渲染段落元素。'
    管理段落的文本对齐、行间距、缩进、首行缩进等属性。
    一般在PDF中创建格式化的段落文本,通过设置渲染器可以控制段落的外观和布局。
'Ⅲ:TableRenderer:用于渲染表格元素。'
    负责管理表格的列宽、行高、边框样式、单元格内容对齐等。
    一般生成包含复杂数据的表格,通过设置表格渲染器可以实现自定义的表格布局和样式。
'Ⅳ:CellRenderer:用于渲染表格单元格元素。'
    负责单元格的布局和渲染,包括文本对齐、边框样式、背景色等。
    一般通过设置不同的单元格渲染器,可以实现自定义的表格样式和布局,处理单元格中的文本和图像内容。
'Ⅴ:ImageRenderer:用于渲染图像元素。'
    支持不同类型的图像格式,负责调整图像的大小、位置和旋转。
    一般可以将图像插入到PDF中,并根据需要调整其显示大小和位置,还可以处理图像的透明度和裁剪。
'Ⅵ:LineRenderer:用于渲染线条元素。'
    绘制直线或斜线,支持设置线条的粗细、颜色和样式。
    一般用于插入分隔线或自定义的标记线,使文档内容更加清晰和可读。 
'Ⅶ:TextRenderer:用于渲染文本元素。'
    处理文本的字体、大小、颜色、样式等属性。
    一般在PDF中插入文本段落或标注,并通过设置文本渲染器来调整文本的外观和排版效果。
'Ⅷ:CanvasRenderer:是用于渲染画布元素的类。'
    主要用于在 PDF 页面上绘制自定义的内容,如路径、图形或复杂的矢量图。
    一般可以创建自定义的绘图逻辑,绘制线条、形状或自定义图标。

(一):渲染器常用方法

  这里我只说用到频率最高的,其它的大家可以去查看元素渲染器以及它的子类实现,对于正常开发来说这两个就够了,主要是对元素做定制化,说白了就是在元素里用画布画个样式上去。

'关于渲染器的通用方法:'
    void draw(DrawContext drawContext)
        用于绘制具体的文档元素或内容。这个方法非常重要,因为它允许你将已经布局好的元素绘制到PDF页面上。
        参数说明:
            drawContext:此对象内包含了绘制所需的所有上下文信息,如当前页面、绘制位置、画笔等。
        主要:通常在完成布局后,根据需要调用draw方法来绘制元素。确保绘制顺序正确,避免重叠或覆盖问题。
    Rectangle getOccupiedAreaBBox()
        用于获取渲染器所占据的边界框(Bounding Box);
        返回一个Rectangle对象,表示渲染器在页面上实际占据的区域。
        这包括其绘制内容的所有部分,不仅仅是内容本身,还有可能的边距、填充和边框。
'其它一些方法(不一定每个元素都有,看是否继承指定类)'
    void drawBorder(DrawContext drawContext)
        和draw差不多,只不过这个是专门负责边框的绘画
    void drawBackground(DrawContext drawContext)
        和draw差不多,只不过这个是专门负责背景的绘画
'注:创建完的渲染器需要通过xx.setNextRenderer(IRenderer renderer)设置上去哟'

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档和文档的创建
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    Document document = new Document(pdfDocument);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 创建段落并为段落定义Style样式
    Style style = new Style()
            .setWidth(300)
            .setHeight(120)
            .setBorder(new SolidBorder(2))
            .setBorderRadius(new BorderRadius(10))
            .setMargin(0);   // 干掉它自带的margin样式,默认段落上下是有4pt的外边距
    Paragraph paragraph = new Paragraph("Hello World!").addStyle(style);
    // 创建段落渲染器并重写对应方法(最好使用对应元素的渲染器,如Div则使用DivRenderer)
    ParagraphRenderer pRenderer = new ParagraphRenderer(paragraph) {
        /***
         * 重新方法,这个方法老牛逼了,这个方法可以对这个元素进行绘画和定制。
         * @param drawContext 绘制上下文对象
         */
        @Override
        public void draw(DrawContext drawContext) {
            // 获取当前元素的位置信息和大小信息
            Rectangle rect = getOccupiedAreaBBox();
            // 获取画布PdfCanvas画布,这种画布常用于图形绘画
            PdfCanvas pdfCanvas = drawContext.getCanvas();
            // 创建画笔的扩展信息,并添加到画布中(为中间长方形设置补充样式)
            PdfExtGState pdfExtGState = new PdfExtGState();
            pdfExtGState.setLineWidth(1)        // 设置线粗
                    .setFillOpacity(.5f)    // 设置填充透明度
                    .setLineJoinStyle(PdfCanvasConstants.LineCapStyle.ROUND);
            // 对这个元素绘画一个双对角线“X”
            pdfCanvas.saveState()
                    .setLineWidth(2)                    // 设置线宽
                    .setStrokeColor(ColorConstants.RED) // 设置描边颜色
                    // 绘画元素内的“X”;-3和+3是微调画笔,因为它是有个圆角
                    .moveTo(rect.getLeft() + 3, rect.getTop() - 3)
                    .lineTo(rect.getRight() - 3, rect.getBottom() + 3)
                    .moveTo(rect.getRight() - 3, rect.getTop() - 3)
                    .lineTo(rect.getLeft() + 3, rect.getBottom() + 3)
                    .fillStroke()
                    .stroke()   // 打断上一次画笔,开始下一个画笔
                    // 绘制中间的长方形
                    .moveTo(rect.getX(), rect.getY())
                    // 计算中间长方形坐标点和大小
                    .rectangle(
                            new Rectangle(rect.getX() + rect.getWidth() / 4,
                                    rect.getY() + rect.getHeight() / 4,
                                    rect.getWidth() / 2,
                                    rect.getHeight() / 2)
                    )
                    .setFillColor(ColorConstants.BLACK)   // 设置填充色
                    .setExtGState(pdfExtGState)
                    .fillStroke()
                    .stroke().restoreState();
            // 通过高级画布设置文字信息
            // 设置pdfCanvas和当前高级画布的范围Rectangle
            Canvas canvas = new Canvas(pdfCanvas, rect);
            canvas.setFont(font)
                    .setBold()
                    .setFontColor(ColorConstants.RED)
                    .setFontSize(30);
            // 为画布设置文字
            canvas.showTextAligned("无法查看",
                    rect.getX() + rect.getWidth() / 2,
                    rect.getY() + rect.getHeight() / 2 + 5,
                    TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
            // 关闭高级画布
            canvas.close();
            // 一定要留着,要不然如上的段落一点样式都没了,留着它,让它绘画段落特有的样式
            // 比如我们通过方法设置的边框、文字等等,不加就代表这个元素就执行我们的绘图样式。
            super.draw(drawContext);
        }
    };
    // 将渲染器设置到段落中
    paragraph.setNextRenderer(pRenderer);
    // 添加元素并关闭文档
    document.add(paragraph);
    document.close();
}

(二):渲染器属性设置方法

  在渲染器中可以通过使用 IPropertyContainer 接口的实现类方法来实现样式属性设置,它类似Map的通用接口,定义了通过Property类型的枚举键存储和检索对象的方法。基本上所有的元素对象或渲染器对象都实现了此接口;也就是说元素的样式不只是通过指定的样式设置方法了,也可以通过键值的方式设置,这种方式用处还是挺大的,但是对于没接触过的人写起来可能有点麻烦。
  在了解这个接口前我们得先了解一下 Property 常量类,它内部定义了大量的常量值和继承关系,每种值代表指定的样式。

点开查看详情:关于元素的属性设置
'关于渲染器的属性方法:'
    setProperty(int property, Object value)
        不借助指定的样式方法就可以为对象设置属性值,在property里每种样式都是一种序号,需要什么直接设置。
    getProperty(int property)
    getProperty(int property, T1 defaultValue)
        获取设置的指定属性值,获取不到则返回Null。
            property:属性的Key,通过Property获取。
            defaultValue:默认值,若获取不到则使用此值。
    deleteProperty(int property)
        从这个渲染器中删除属性,或者如果在其模型元素上指定了属性,则删除模型元素的属性。
    hasProperty(int property)
        检查此实体是否具有指定的属性。true代表存在此属性
'补充带Own的属性:'
    getOwnProperty(int property)
    hasOwnProperty(int property)
    deleteOwnProperty(int property)
        这方法本质上和上面的几种不带Own的方法一样
'说明:关于带Own和不带Own的方法区别:'
    带Own:
        获取:话它只能获取通过setProperty方式设置的元素属性,无法获取元素本身(模型元素)设置的元素样式。
        删除:只能删除通过setProperty方式设置的元素属性。
        校验:只能校验通过setProperty方式设置的元素属性是否存在。
    不带Own:
        通过属性设置的和元素本身(模型元素)设置的元素样式都可以获取、删除、查询、校验...
'关于Property静态类的基本常量说明以及设置对应样式:'
    xx.setProperty(Property.BORDER,new SolidBorder(2))                      // 设置边框2pt
    xx.setProperty(Property.HEIGHT,new UnitValue(UnitValue.POINT,200))      // 设置高200pt
    xx.setProperty(Property.WIDTH,new UnitValue(UnitValue.POINT,100))       // 设置宽100pt
    xx.setProperty(Property.BACKGROUND,new Background(ColorConstants.RED))  // 设置背景红色
    键就通过Property获取,值的话,以前通过方法如何设置的就怎么设置,类型不匹配则会报错。

代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    // 初始化文档并关闭写出
    Document document = new Document(pdfDocument);
    // 中文显示字体
    PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
    // 创建段落(这里我就不设置大量样式,样式我就在渲染器里面设置了)
    Paragraph paragraph = new Paragraph("蚂蚁小哥");
    paragraph.setFontSize(30);  // 这里我就设置一个元素样式
    // 注意:Paragraph对象也是间接继承了IPropertyContainer,所以可以通过Paragraph直接调用,如:
    paragraph.setProperty(Property.BORDER, new SolidBorder(2));
    // 创建段落渲染器
    ParagraphRenderer pRenderer = new ParagraphRenderer(paragraph);
    // 设置属性:宽、高、边框、边框圆角、背景色、文本对齐方式、水平居中、垂直居中、中文字体
    pRenderer.setProperty(Property.WIDTH, new UnitValue(UnitValue.POINT, 200));
    pRenderer.setProperty(Property.HEIGHT, new UnitValue(UnitValue.POINT, 100));
    //pRenderer.setProperty(Property.BORDER, new SolidBorder(2));
    pRenderer.setProperty(Property.BORDER_RADIUS, new BorderRadius(10));
    pRenderer.setProperty(Property.BACKGROUND, new Background(ColorConstants.RED, .5f));
    pRenderer.setProperty(Property.TEXT_ALIGNMENT, TextAlignment.CENTER);
    pRenderer.setProperty(Property.HORIZONTAL_ALIGNMENT,HorizontalAlignment.CENTER);
    pRenderer.setProperty(Property.VERTICAL_ALIGNMENT,VerticalAlignment.MIDDLE);
    pRenderer.setProperty(Property.FONT, font);
    // 获取元素属性
    Object property1 = pRenderer.getProperty(Property.WIDTH);
    Object property2 = pRenderer.getProperty(Property.FONT_SIZE);
    // 删除元素中的字体大小属性
    pRenderer.deleteProperty(Property.FONT_SIZE);
    // 校验指定属性是否存在
    boolean b = pRenderer.hasProperty(Property.FONT_SIZE);
    // 将渲染器设置到段落中
    paragraph.setNextRenderer(pRenderer);
    // 添加元素并关闭文档
    document.add(paragraph);
    document.close();
}

六:使用Itext生成条形码

  本章介绍EAN-13条形码的生成,EAN(European Article Number、欧洲物品编码的缩写),其中共计13位代码的EAN-13是比较通用的一般终端产品的条形码协议和标准,主要应用于超市和其它零售业,因此这种是我们比较常见的,随便拿起身边的一个从超市买来的商品都可以从包装上看得到。
  中华人民共和国可用的国家代码有690-699,其中696-699尚未使用。生活中最常见的国家代码为690-693,其中以690、691开头时......;我废话真多,直接切入重点生成条形码EAN-13吧。
  生成条形码前有两个类我们必须熟知,分别是:Barcode1DBarcodeEAN;前者是抽象类,后者是继承这个抽象类的关键类,下面我简单介绍这个类的基本方法:

'===================== 关于Barcode1D抽象类说明(条形码的最终抽象类)====================='
'关于Barcode1D抽象类的重要方法说明:'
    createFormXObject(PdfDocument document)
    createFormXObject(Color barColor, Color textColor, PdfDocument document)
        用于生成条形码并将其作为PdfFormXObject的一个方法。
        PdfFormXObject是PDF中的一种对象,它允许你在多个地方重用相同的图形内容,比如画布。
        参数说明:
            barColor:条形码的颜色。
            textColor:条形码文本的颜色。
            document:目标PdfDocument实例。
'关于Barcode1D抽象类的常用方法说明:'
    setCode(String code)
        设置要生成的条形号码。
    setSize(float size)、setFont(PdfFont font)
        设置条形号码大小和文本字体。
    fitWidth(float width)、setBarHeight(float barHeight)
        设置条形码的宽度和高度。
    setGuardBars(boolean guardBars)
        设置EAN条形码是否显示护栏线(就是条形码那3根长线)
        true显示(比其它条形码线底部高出一点),false不显示(和其它条形码线一样长)
    setInkSpreading(float inkSpreading)
        这个方法用于设置条形码生成时的墨水扩散效果。一般0即可。
        墨水扩散是指在打印条形码时,墨水可能会在条和空之间扩展,导致条形码线条变得模糊或粗细不均匀。
    setBaseline(float baseline)
        设置条形码文本相对于条形码的基线位置。负数将文本向上移动,正数将文本向下移动。
        这个方法允许你调整条形码下方的文本(如果有)的基线位置,以便更好地控制条形码文本的显示效果。
    setTextAlignment(int textAlignment)
        设置文本对齐方式,具体常量值从Barcode1D获取:
        ALIGN_LEFT(左对齐)、ALIGN_RIGHT(右对齐)、ALIGN_RIGHT(中心对齐)
    setX(float x)
        设置最小条形宽度,说白了就是设置线条的宽度。
    setCodeType(int codeType)
        设置代码类型;如:BarcodeEAN.EAN13代表EAN-13的条形码(默认就是EAN13)
        注意:需要创建什么类型的条形码则需要设置指定类型的代码。
        如BarcodeEAN对象创建的条形码对象可以设置如下几种类型:
            BarcodeEAN.EAN13、BarcodeEAN.EAN8、BarcodeEAN.UPCA、
            BarcodeEAN.UPCE、BarcodeEAN.SUPP2、BarcodeEAN.SUPP5
'关于Barcode1D抽象类的常用获取方法说明:'
    上面设置的属性基本上通过get都可以获取到,没有宽度的获取
'============================== 关于BarcodeEAN实现类说明 =============================='
'关于BarcodeEAN构造方法:'
    BarcodeEAN(PdfDocument document)
    BarcodeEAN(PdfDocument document, PdfFont font)
        构建我们熟知的EAN类型的条形码,如常用的EAN-13
'关于BarcodeEAN常用方法:'
    createAwtImage(Color foreground, Color background)
        用于创建一个并返回java.awt.Image对象,它包含条形码图像,支持前景色和背景色。
        参数说明:
            foreground:条形码的前景色。
            background:条形码的背景色。
    placeBarcode(PdfCanvas canvas, Color barColor, Color textColor)
        方法用于将条形码绘制到PDF的画布上。返回的是当前条形码的边界的区域Rectangle
        参数说明:
            canvas:目标 PdfCanvas 实例。
            barColor:条形码的颜色。
            textColor:条形码文本的颜色。
    getBarcodeSize()
        获取条形码和文本(如果有)将占用的最大区域。返回区域对象Rectangle

(一):基本案例EAN码

  下面的案例生成了一个简单的EAN-13条形码,并对他进行了简单布局,但是有的人说我需要其它类型的条形码怎么办呢?关于BarcodeEAN类可以提供如下类型的条形码:EAN-13EAN-8UPC-AUPC-ESUPP-2SUPP-5;我们只需要调用xx.setCodeType(BarcodeEAN.xxx)来设置指定类型即可。
代码效果:
image.png

public static void main(String[] args) throws IOException {
        // 创建并初始化一个PDF文档和文档的创建
        PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
        PdfPage pdfPage = pdfDocument.addNewPage();
        Document document = new Document(pdfDocument);
        // 创建第一个EAN13的条形码
        BarcodeEAN b1EAN = new BarcodeEAN(pdfDocument);
        b1EAN.setCode("6921168504022");       // 设置条形码的代码
        b1EAN.fitWidth(200);                  // 设置条形码宽
        b1EAN.setBarHeight(60);               // 设置条形码高
        b1EAN.setSize(12);                    // 设置条形码文本大小(根据宽高设置)
        b1EAN.setBaseline(b1EAN.getSize());   // 设置文本基于条形码基线位置
        b1EAN.setCodeType(BarcodeEAN.EAN13);  // 设置条形码为EAN13(其实默认EAN13)
        // ======= 将条形码转换为Image对象 ======
        // 创建出PdfFormXObject对象并直接加入到文档中;
        // 对了PdfFormXObject对象可以交给PdfCanvas画布哟,处理一翻在转回图片
        PdfFormXObject formXObject =
                b1EAN.createFormXObject(ColorConstants.BLUE, ColorConstants.BLACK, pdfDocument);
        Image imageA = new Image(formXObject);
        // 别忘咯,Image继承了AbstractElement,正常的一些样式它也有。
        imageA.setBackgroundColor(new DeviceRgb(0, 0, 0), .1f)
                //.setBorder(new SolidBorder(2))
                .setBorderRadius(new BorderRadius(3))
                .setPadding(3);
        document.add(imageA);
        // ======= 将条形码布局到页面的任意地方 ======
        Image imageB = new Image(b1EAN.createFormXObject(pdfDocument));
        imageB.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 创建一个盒子,将条形码放里面
        Div div = new Div();
        div.setBorder(new DottedBorder(new DeviceRgb(195, 98, 21), 3))
                .setBorderRadius(new BorderRadius(5))
                .add(imageB)
                // 使用绝对定位
                .setFixedPosition(
                        imageB.getImageWidth() + 50,
                        pdfPage.getPageSize().getTop() - 120,
                        imageB.getImageWidth())
                .setTextAlignment(TextAlignment.CENTER)
                .setVerticalAlignment(VerticalAlignment.MIDDLE)
                .setBackgroundColor(new DeviceRgb(187, 255, 255))
                .setWidth(imageB.getImageWidth() + 30f)
                .setHeight(imageB.getImageHeight() + 20f);
        document.add(div);
        // 关闭文档
        document.close();
    }

(二):补充案例Code码

  有条件的可以了解一下Code 128Code 39条形码;它是一种高密度、高可靠性的线性条形码符号,广泛应用于商业和工业领域。它能够编码全ASCII字符集的数据,包括数字、字母、符号等,因此非常适合于需要编码多种字符的应用场景。常见于零售、物流、制造业、库存管理等各种需要追踪和识别物品的场合。它的广泛应用使得许多条形码扫描设备都能够读取和解码它。
  具体的可参考指定类: Barcode128Barcode39
代码效果:
image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档和文档的创建
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("E://基本示例.pdf"));
    Document document = new Document(pdfDocument);
    // 创建一个Code 128类型的高密度条形码
    Barcode128 code128 = new Barcode128(pdfDocument);
    code128.setCode("12345678*abcABC*");                // 设置条形码的数据
    code128.setCodeSet(Barcode128.Barcode128CodeSet.B); // 设置Code128的密度类型
    code128.setBaseline(code128.getSize() + 10);          // 设置文本基于条形码基线位置
    code128.setTextAlignment(Barcode128.ALIGN_RIGHT);   // 设置数值的堆区方式,默认居中
    code128.fitWidth(220);      // 设置条形码宽度
    code128.setBarHeight(60);   // 设置条形码高度
    code128.setSize(16);        // 设置条形码文字大小
    // 转换为formXObject对象和Image对象
    PdfFormXObject formXObject = code128
            .createFormXObject(ColorConstants.BLUE, ColorConstants.RED, pdfDocument);
    Image image = new Image(formXObject);
    // 添加条形码
    document.add(image);
    // 关闭文档
    document.close();
}
'============================================'
'关于Barcode39补充方法说明:'
    setCodeSet(Barcode128CodeSet codeSet)
        设置要使用的代码集;可选值从Barcode128.Barcode128CodeSet枚举获取如下:
            A:标准数字和大写字母,控制符,特殊字符;
            B:标准数字和大写字母,小写字母,特殊字符;
            C:只能是双位数值,[00]-[99]的数字对集合,共100个
            AUTO:自动识别A、B、C三种模式

七:使用Itext生成二维码

  二维码就没啥好说的,能生成一个二维码就行了,下面我就说说创建二维码的对象和常用的方法;具体的大家可以去探索。
  注:如果有需要将二维码导出图片格式如png或jpg等,Java生成二维码及条形码工具 ,这篇文章详细介绍了二维码生成的细节;但是要在PDF中生成二维码则参考我下面的示例即可:

'关于二维码实现类BarcodeQRCode基本说明:'
'构造方法:'
    BarcodeQRCode()
    BarcodeQRCode(String content)
    BarcodeQRCode(String code, Map<EncodeHintType,Object> hints)
    参数说明:
        content、cede:二维码内承载的内容
        hints:额外的二维码设置信息(字符集、容错率、版本)
'常用方法:'
    setCode(String code)
        设置二维码的承载内容。
    setHints(Map<EncodeHintType,Object> hints)
        设置二维码的额外信息。
    regenerate()
        在生成二维码后更改了,则可重新生成二维码。
    createFormXObject(Color foreground, PdfDocument document)
    createFormXObject(Color foreground, float moduleSize, PdfDocument document)
        将二维码转换为PdfFormXObject对象类型格式并返回
        参数说明:
            foreground:二维码的前景色
            moduleSize:二维码像素点大小
            document:PDF文档对象
    placeBarcode(PdfCanvas canvas, Color foreground)
    placeBarcode(PdfCanvas canvas, Color foreground, float moduleSide)
        在一个PDF文档的特定位置上绘制二维码,并通过指定模块大小来控制二维码的尺寸,并返回Rectangle。
    createAwtImage(Color foreground, Color background)
        将二维码转换为图片Image类型格式并返回,注意它返回的是java.awt.Image类型的图片哟
        参数说明:
            foreground:二维码的前景色
            background:二维码的背景色
'关于一些获取的方法:'
    getBarcodeSize()
    getBarcodeSize(float moduleSize)
        对于生成的二维码,您可能需要获取其尺寸,以便在PDF文档中进行布局和定位。
        没参数的方法返回二维码的默认尺寸。有参数的方法允许您指定像素大小,然后返回二维码的尺寸。
    getCode()
        获取当前二维码内的数据字符串信息。
    getHints()
        就是获取二维码的额外配置。
'关于Map<EncodeHintType,Object> hints参数详细说明:'
    EncodeHintType.CHARACTER_SET:
        代表字符集,可以设置字符串类型;一般中文的话需要设置UTF-8
    EncodeHintType.ERROR_CORRECTION:
        二维码容错率,当二维码图片被遮挡一部分后仍可以扫描出来,可选值如下:
            ErrorCorrectionLevel.L:低,最大 7% 的错误能够被纠正
            ErrorCorrectionLevel.M:中,最大 15% 的错误能够被纠正
            ErrorCorrectionLevel.Q:中上,最大 25% 的错误能够被纠正
            ErrorCorrectionLevel.H:高,最大 30% 的错误能够被纠正
    EncodeHintType.MIN_VERSION_NR:
        指定要使用的最低版本级别,可以设置数值类型
        比如:版本1的QR码只能容纳最多25个字符,而版本40的QR码则可以容纳最多7089个字符。

image.png

public static void main(String[] args) throws IOException {
    // 创建并初始化一个PDF文档和文档的创建
    PdfDocument pdfDocument = new PdfDocument(new PdfWriter("D://基本示例.pdf"));
    Document document = new Document(pdfDocument);
    
    // 设置生成二维码的额外信息(重要)
    Map<EncodeHintType, Object> hints = new HashMap<>();
    // 设置字符集信息、纠错等级、版本
    hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
    //hints.put(EncodeHintType.MIN_VERSION_NR,1);
    // 构建二维码对象,并设置二维码的额外配置
    BarcodeQRCode qrCode = new BarcodeQRCode("蚂蚁小哥欢迎您", hints);
    // qrCode.setHints(hints); // 设置二维码的额外配置(这里在构造器上设置了)
    
    // ======================== 从二维码对象中构建一张图片对象 ========================
    // 生成PdfFormXObject对象并转换程Image
    PdfFormXObject pdfFormXObject = qrCode.createFormXObject(ColorConstants.RED, pdfDocument);
    Image imageA = new Image(pdfFormXObject);
    // 设置图片的一些样式
    imageA.setBorder(new SolidBorder(new DeviceRgb(102, 205, 170), 2))
            .setBorderRadius(new BorderRadius(5))
            .setWidth(200)
            .setHeight(200);
    // ======================== 将图片添加到画布并添加二维码logo ========================
    // 重新获取一个二维码图片资源,并设置宽高
    Image imageB = new Image(qrCode.createFormXObject(ColorConstants.RED, pdfDocument));
    imageB.setWidth(200).setHeight(200);
    // 加载一张二维码的中logo图片
    ImageData logo = ImageDataFactory.create("D:\\123.jpeg");
    // 为二维码中间添加logo
    Image image = addLogoToQRCode(imageB, logo, 30f, pdfDocument);
    // 为图片外面套个盒子并定位
    Div div = new Div()
            .setWidth(200).setHeight(200).add(image)
            .setBorderRadius(new BorderRadius(5))
            .setBorder(new SolidBorder(new DeviceRgb(102, 205, 170),2))
            .setFixedPosition(300,605,200);
            
    // 添加二维码并关闭文档
    document.add(imageA);
    document.add(div);
    document.close();
}
/***
 * 二维码图片中添加Logo图片
 * @param qrCode 二维码图片对象
 * @param logo Logo图片
 * @param logoSize Logo图片大小
 * @param pdfDocument PDF文档
 * @return 图片
 */
public static Image addLogoToQRCode(Image qrCode, ImageData logo,
                                    float logoSize, PdfDocument pdfDocument) {
    // 获取图片的宽高信息,先获取是否更改过图片宽高;77代表宽,27代表高
    float width = qrCode.getImageScaledWidth();
    float height = qrCode.getImageScaledHeight();
    if (qrCode.getProperty(27) != null) {
        height = ((UnitValue) qrCode.getProperty(27)).getValue();
    }
    if (qrCode.getProperty(77) != null) {
        width = ((UnitValue) qrCode.getProperty(77)).getValue();
    }
    // 创建一个PdfFormXObject对象
    PdfFormXObject pdfFormXObject = new PdfFormXObject(new Rectangle(width, height));
    // 创建一个Canvas高级画布并将图片添加到PdfFormXObject对象上
    new Canvas(pdfFormXObject, pdfDocument).add(qrCode).close();
    // 创建画布PdfCanvas
    PdfCanvas pdfCanvas = new PdfCanvas(pdfFormXObject, pdfDocument);
    // 将logo图片添加到画布上的中间位置
    // 计算位置,width和height代表当前画布的宽高,
    Rectangle center = new Rectangle(width / 2 - (logoSize / 2),
            height / 2 - (logoSize / 2), logoSize, logoSize);
    pdfCanvas.addImageFittedIntoRectangle(logo, center, false);
    // 关闭画布
    pdfCanvas.release();
    return new Image(pdfFormXObject);
}
posted @ 2024-06-24 10:44  蚂蚁小哥  阅读(532)  评论(0编辑  收藏  举报