Java使用Graphics2D绘制SVG和PNG的方法
作者:iiopsd 发布时间:2021-11-13 01:03:51
前言
emmm… 有个需求,需要根据信息生成svg,因为考虑到样式一致性的问题最终决定有服务端来生成svg。
Java提供了许多图形处理类和方法,如Graphics2D、AffineTransform、Stroke等,这些都可以用于绘制SVG图像。
Graphics2D介绍
Graphics2D是Java中用于绘制2D图形的类,它是Graphics类的子类,提供了更多的图形绘制方法和属性。在Java中,可以使用Graphics2D来创建各种类型的2D图形,例如直线、矩形、圆形、弧形等。
Graphics2D类中一些常用的方法包括:
drawLine(int x1, int y1, int x2, int y2):绘制一条直线。
drawRect(int x, int y, int width, int height):绘制一个矩形。
fillRect(int x, int y, int width, int height):填充一个矩形。
drawOval(int x, int y, int width, int height):绘制一个椭圆或圆形。
fillOval(int x, int y, int width, int height):填充一个椭圆或圆形。
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle):绘制一个弧形。
fillArc(int x, int y, int width, int height, int startAngle, int arcAngle):填充一个弧形。
drawString(String str, int x, int y):绘制一个字符串。
setPaint(Paint paint):设置绘制颜色。
setStroke(Stroke s):设置线条样式(粗细、虚线等)。
rotate(double theta):旋转坐标系。
translate(double tx, double ty):平移坐标系。
入门使用
maven引入依赖:
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreesvg</artifactId>
<version>3.4</version>
</dependency>
使用Graphics2D类需要先获取Graphics2D对象,可以通过将Graphics对象向下转型为Graphics2D对象或通过创建BufferedImage对象来获取。例如:
@SneakyThrows
@Test
public void t2() {
int width = 700;
int height = 500;
SVGGraphics2D g2 = new SVGGraphics2D(width, height);
// 设置字体和颜色
Font font = new Font("SFProText", Font.BOLD, 96);
g2.setColor(Color.BLACK);
g2.setFont(font);
// 画一条直线,起点坐标为(100,100),终点坐标为(10,20)
g2.drawLine(100, 100, 10, 20);
// 绘制一个矩形,左上角坐标为(15,10),宽为10,高为20
g2.drawRect(15, 10, 10, 20);
// 绘制一个圆形,圆心坐标为(30,25),半径为55
g2.drawOval(30, 25, 55, 55);
// 绘制一个圆弧,圆心坐标为(90,25),半径为50,起始角度为0,终止角度为90度
g2.drawArc(90, 25, 50, 50, 0, 90);
// 填充一个矩形,左上角坐标为(150,100),宽为10,高为20
g2.fillRect(150, 100, 10, 20);
// 保存svg到本地文件
InputStream inputStream = new ByteArrayInputStream(g2.getSVGDocument().getBytes());
try (FileOutputStream outputStream = new FileOutputStream(new File("C:\Users\Administrator\Downloads\1test.svg"))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
生成的svg如下图所示:
进阶
现在我们试着设计一张名片,其中包含logo,姓名和联系方式等信息,居中显示。在写入字符串的时候,如果字符串要居中,都需要根据字体和字符串去获取字符串宽度,然后计算x,y点的位置。
文字的原点坐标不同于一般图形在右上角,如果需要将文字准确的绘制到指定位置,需要了解文字的原点坐标位置。如下图所示,文本框的原点x坐标位于文本框最左侧,y坐标则位于基线上。基线以上为asent,基线以下为decent。为什么会有基线这个概念,可能会让人产生疑惑,实际上是有原因的。实际上基线相同的文字,即使字体大小不一样,绘制出来的结果是文本底部对齐的。 因为文字底部还有一部分留白,因此文字底部对齐不是基于底部坐标对齐,而是进行基线对齐。
实现代码如下:
@SneakyThrows
@Test
public void t2() {
int width = 400;
int height = 600;
SVGGraphics2D g2 = new SVGGraphics2D(width, height);
g2.setColor(new Color(168, 252, 229));
// 填充一个矩形,左上角坐标为(150,100),宽为10,高为20
g2.fillRect(0, 0, width, height);
Font font = new Font("SFProText", Font.BOLD, 40);
g2.setColor(Color.BLACK);
g2.setFont(font);
String companyLogo = "https://xxx.jpg";
URL companyLogoUrl = new URL(companyLogo);
BufferedImage image = ImageIO.read(companyLogoUrl);
g2.drawImage(image, 100, 64, 200, 120, null);
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
String name = "张三三三三";
// 计算位置
int startX = (width - metrics.stringWidth(name)) / 2;
int startY = 300 + metrics.getAscent();
g2.drawString(name, startX, startY);
String phone = "55555555555";
startX = (width - metrics.stringWidth(phone)) / 2;
startY = 400 + metrics.getAscent();
g2.drawString(phone, startX, startY);
g2.dispose();
InputStream inputStream = new ByteArrayInputStream(g2.getSVGDocument().getBytes());
try (FileOutputStream outputStream = new FileOutputStream(new File("C:\Users\Administrator\Downloads\1test.svg"))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
SVG转JPG
pom.xml增加依赖:
<!--svg2png-->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.7</version>
</dependency>
转换代码:
// svg2jpg
ImageTranscoder transcoder = new JPEGTranscoder();
TranscoderInput input = new TranscoderInput(inputStream);
OutputStream out = Files.newOutputStream(new File("C:\Users\Administrator\Downloads\557.jpg").toPath());
TranscoderOutput output = new TranscoderOutput(out);
//转换
transcoder.transcode(input, output);
绘制PNG
可以直接选择绘制PNG图片,节省转换过程中的各种问题,方法都是一样的,只是操作的对象有点不同,
通过BufferedImage去创建绘图对象:
int width = 400;
int height = 600;
BufferedImage tarImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = tarImage.createGraphics();
完整实现代码:
@SneakyThrows
@Test
public void t3() {
int width = 400;
int height = 600;
BufferedImage tarImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
val g2 = tarImage.createGraphics();
g2.setColor(new Color(168, 252, 229));
// 填充一个矩形,左上角坐标为(150,100),宽为10,高为20
g2.fillRect(0, 0, width, height);
Font font = new Font("SFProText", Font.BOLD, 40);
g2.setColor(Color.BLACK);
g2.setFont(font);
String companyLogo = "https://xxx.jpg";
URL companyLogoUrl = new URL(companyLogo);
BufferedImage image = ImageIO.read(companyLogoUrl);
g2.drawImage(image, 100, 64, 200, 120, null);
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
String name = "张三三三三";
int startX = (width - metrics.stringWidth(name)) / 2;
int startY = 300 + metrics.getAscent();
g2.drawString(name, startX, startY);
String phone = "55555555555";
startX = (width - metrics.stringWidth(phone)) / 2;
startY = 400 + metrics.getAscent();
g2.drawString(phone, startX, startY);
// 绘制图片
g2.dispose();
// tarImage转inputStream
String IMAGE_TYPE = "png";
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(tarImage, IMAGE_TYPE, os);
InputStream inputStream = new ByteArrayInputStream(os.toByteArray());
try (FileOutputStream outputStream = new FileOutputStream(new File("C:\Users\Administrator\Downloads\2test.png"))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
绘制图片效果如下:
我们可以看到图片和文本信息锯齿状比较明显,可以通过增加图片质量参数来改善质量。
/**
* 设置图片质量(抗锯齿等)
* @param
*/
public void setImagQuality(Graphics2D g2) {
RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
qualityHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
qualityHints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
qualityHints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2.setRenderingHints(qualityHints);
}
左边未设置图片质量,右边设置了图片质量,对比效果如下所示:
来源:https://juejin.cn/post/7239484016948740155


猜你喜欢
- 本文主要介绍了c# 使用线程对串口serialPort进行收发数据,分享给大家,具体如下:一共写了四种方法,窗体界面都是一样的:方法一:us
- Mybatis防止sql注入原理SQL 注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL 语句 * 入到执行的实体字段中(例如,为
- 今天可是遇到一个很简单的需求,但是却让我蛋疼了半天。滑动屏幕控制物体旋转,但是旋转的角度要在-60到60之间。乍一听这简直是小儿科啊。判断一
- 封面图下个季度的目标是把前端监控相关的内容梳理出来,梳理出来之后可能会在公司内部做个分享~Flutter应用程序既括代码也包括一些其他的资产
- 拒绝策略介绍线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施。当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异
- 可以用如下方法: 修改AudioYusuStreamOut.cpp,添加方法: void AudioYusuStreamOut::swS2M
- 本文实例为大家分享了PopupWindow+RecyclerView实现上下滑动框功能的具体代码,供大家参考,具体内容如下1.新建一个适配器
- 一.组合widget实现1.android和flutter自定义控件对比Android中,一般会继承View或已经存在的某个控件,然后覆盖d
- 目录基本用法基于接口的 * 基于类的 * 异步函数拦截Autofac 集成基于接口的 * 基于类的 * 异步函数拦截Castle 是 200
- 本文通过实例详细阐述了C++关于智能指针的概念及用法,有助于读者加深对智能指针的理解。详情如下:一、简介由于 C++ 语言没有自动内存回收机
- 一、概述针对八种基本数据类型定义相应的引用类型—包装类(封装类)。二、作用有了类的特点,就可以调用类中的方法,Java才
- 本文实例为大家分享了android拖拽框,裁剪出图片的具体代码,供大家参考,具体内容如下import android.graphics.Bi
- 这篇实例中有四个类,分别为CacheItem 缓存实体类CachePool 缓存池Student 学生实
- 如图,刚开始报错获取不到bean因为配置文件1、原因一: *.properties等没有值,还是用${变量的}。获取不到,于是把所有值复制到
- 前言在上一篇文章中《Notification自定义界面》中我们实现了自定义的界面,那么我们该怎么为自定义的界面添加点击事件呢?像酷狗在通知栏
- Java Tess4J实现图像识别最近需要用Java做一个图像识别的东西,查了一些资料,在此写一个基于Tess4J的教程,方便其他人参考和使
- 平时开发中经常遇到的很小的问题,这里记录一下。一般在AndroidManifest.xml中添加了android:windowSoftInp
- @RequestBody的作用@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的),所以只能发送
- 记一下学习单例模式的笔记:单例就是要保证该类仅有一个实例。实现完全封闭的单例(外部不能new)其实就要两点要求:全局访问:需要一个该类型的全
- 本文实例为大家分享了android自定义滚动上下回弹scollView的具体代码,供大家参考,具体内容如下这是一个自定义view,在xml布