在上一篇文章之中,我们已经知道怎样创建和操作加载pdf文件了。现在来看一看使用PDFBOX进行图形的和文字的绘制
(友情提示:pdfbox的坐标轴是以左下角为原点-----宽边为X轴,高边为Y轴)
在PDF中绘制图形
通过流对象不仅可以绘制文字信息,也可以绘制一些简单的图形。
例:在PDF中绘制一个实心的长方形。
public static void main(String[] args) throws IOException {
PDDocument docment = new PDDocument();
PDPage pageOne = new PDPage(PDRectangle.A4);
docment.addPage(pageOne);
PDPageContentStream stream = new PDPageContentStream(docment, pageOne);
stream.setNonStrokingColor(0.12f, 0.34f, 0.62f);
//直接绘制一个正方形,参数x,y为在pdf中的坐标,width和height为长方形的长宽
stream.addRect(20f, 30f, 100f, 100f);
//填充,如果不掉用则不会进行绘制,如果想要设置边框的话可以使用fillandStroke
//但是目前好像无法设置边框的宽度
stream.fill();
stream.close();
docment.save(new File("drawPic.pdf"));
}
例:使用PDFBOX绘制曲线
pdfbox也支持绘制曲线,他主要由流对象的curseTo方法来实现。
下图为该方法的实现
第一种方法是直接给出三个点,画出的弧线由(X1,Y1)到(X2,Y2)再到(X3,Y3)
如果三个坐标间可以在一条整点的直线上,则会绘制成直线。
第二种和第三种方法要配合使用,同样也是和第一种的方法同理,只不过把坐标点拆开了。
代码示例:
public static void DrawCurve() throws IOException {
PDDocument doc = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
doc.addPage(page);
PDPageContentStream stream = new PDPageContentStream(doc, page);
stream.saveGraphicsState();
//设置绘制区域的背景色
stream.setNonStrokingColor(0.3f, 0.87f, 0.56f);
//设置起始点
stream.moveTo(0.0f, 0.0f);
//根据坐标点绘制曲线
stream.curveTo(11.23f, 11.12f, 100.0f, 100.0f, 299.0f, 0.0f);
//填充区域
stream.fillAndStroke();
stream.close();
doc.save("curve.pdf");
}
使用PDFBOX的曲线绘制饼图
既然可以使用PDFBOX绘制曲线,那么只要给出的点足够多,则可以绘制圆形,进一步则可以用于各种图标,以下是一个绘制饼图的示例。
这个例子是从网上找的,有时间可以分析一下里面绘制的具体细节。
public static void main(String[] args) throws IOException
{
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage(page);
PDPageContentStream cs = new PDPageContentStream(doc, page);
cs.transform(Matrix.getTranslateInstance(250, 400));
cs.setNonStrokingColor(0.15f,0.54f,0.87f);
drawSlice(cs, 100, 0, 80);
cs.fill();
cs.setNonStrokingColor(0.32f,0.11f,0.55f);
drawSlice(cs, 100, 80, 150);
cs.fill();
cs.setNonStrokingColor(0.05f,0.66f,0.71f);
drawSlice(cs, 100, 150, 215);
cs.fill();
cs.setNonStrokingColor(0.32f,0.77f,0.33f);
drawSlice(cs, 100, 215, 305);
cs.fill();
cs.setNonStrokingColor(0.15f,0.25f,0.35f);
drawSlice(cs, 100, 305, 360);
cs.fill();
cs.close();
doc.save("piechart.pdf");
doc.close();
}
private static void drawSlice(PDPageContentStream cs, float rad, float startDeg, float endDeg) throws IOException
{
cs.moveTo(0, 0);
List<Float> smallArc = createSmallArc(rad, Math.toRadians(startDeg), Math.toRadians(endDeg));
cs.lineTo(smallArc.get(0), smallArc.get(1));
cs.curveTo(smallArc.get(2), smallArc.get(3), smallArc.get(4), smallArc.get(5), smallArc.get(6), smallArc.get(7));
cs.closePath();
}
/**
* From https://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html
*
* Cubic bezier approximation of a circular arc centered at the origin,
* from (radians) a1 to a2, where a2-a1 < pi/2. The arc's radius is r.
*
* Returns a list with 4 points, where x1,y1 and x4,y4 are the arc's end points
* and x2,y2 and x3,y3 are the cubic bezier's control points.
*
* This algorithm is based on the approach described in:
* Aleksas Riškus, "Approximation of a Cubic Bezier Curve by Circular Arcs and Vice Versa,"
* Information Technology and Control, 35(4), 2006 pp. 371-378.
*/
private static List<Float> createSmallArc(double r, double a1, double a2)
{
// Compute all four points for an arc that subtends the same total angle
// but is centered on the X-axis
double a = (a2 - a1)/2;
double x4 = r * Math.cos(a);
double y4 = r * Math.sin(a);
double x1 = x4;
double y1 = -y4;
double q1 = x1*x1 + y1*y1;
double q2 = q1 + x1*x4 + y1*y4;
double k2 = 4/3d * (Math.sqrt(2 * q1 * q2) - q2)/(x1 * y4 - y1 * x4);
double x2 = x1 - k2 * y1;
double y2 = y1 + k2 * x1;
double x3 = x2;
double y3 = -y2;
// Find the arc points' actual locations by computing x1,y1 and x4,y4
// and rotating the control points by a + a1
double ar = a + a1;
double cos_ar = Math.cos(ar);
double sin_ar = Math.sin(ar);
List<Float> list = new ArrayList<Float>();
list.add((float) (r * Math.cos(a1)));
list.add((float) (r * Math.sin(a1)));
list.add((float) (x2 * cos_ar - y2 * sin_ar));
list.add((float) (x2 * sin_ar + y2 * cos_ar));
list.add((float) (x3 * cos_ar - y3 * sin_ar));
list.add((float) (x3 * sin_ar + y3 * cos_ar));
list.add((float) (r * Math.cos(a2)));
list.add((float) (r * Math.sin(a2)));
return list;
}
效果如下。
使用PDFBOX绘制水印
同样可以在页面中设置页面水印具体使用如下
public static void main(String[] args) throws IOException {
PDDocument document = new PDDocument();
PDPage pageOne = new PDPage(PDRectangle.A4);
document.addPage(pageOne);
PDPageContentStream stream = new PDPageContentStream(document, pageOne);
// 加载字体
PDFont font = PDType0Font.load(document, new FileInputStream("src/SimHei.ttf"), true);
//创建拓展显示状态
PDExtendedGraphicsState r = new PDExtendedGraphicsState();
// 设置透明度(阿尔法混合是一种让图形显示透明的技术,设置其透明度参数)
r.setNonStrokingAlphaConstant(0.2f);
r.setAlphaSourceFlag(true);
stream.setGraphicsStateParameters(r);
stream.beginText();
stream.setFont(font, 32);
stream.newLineAtOffset(0, -15);
// 获取PDF页面大小
float pageHeight = pageOne.getMediaBox().getHeight();
float pageWidth = pageOne.getMediaBox().getWidth();
//设置文本显示矩阵,并设置旋转的角度
stream.setTextMatrix(Matrix.getRotateInstance(0.4, pageHeight/3, pageWidth/3));
//写入水印文本
stream.showText("这是你要的水印");
// 结束渲染,关闭流
stream.endText();
stream.restoreGraphicsState();
stream.close();
document.save("addwaterGrafh.pdf");
}
}
显示效果: