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如下图所示:

Java使用Graphics2D绘制SVG和PNG的方法

进阶

现在我们试着设计一张名片,其中包含logo,姓名和联系方式等信息,居中显示。在写入字符串的时候,如果字符串要居中,都需要根据字体和字符串去获取字符串宽度,然后计算x,y点的位置。

文字的原点坐标不同于一般图形在右上角,如果需要将文字准确的绘制到指定位置,需要了解文字的原点坐标位置。如下图所示,文本框的原点x坐标位于文本框最左侧,y坐标则位于基线上。基线以上为asent,基线以下为decent。为什么会有基线这个概念,可能会让人产生疑惑,实际上是有原因的。实际上基线相同的文字,即使字体大小不一样,绘制出来的结果是文本底部对齐的。 因为文字底部还有一部分留白,因此文字底部对齐不是基于底部坐标对齐,而是进行基线对齐。

Java使用Graphics2D绘制SVG和PNG的方法

Java使用Graphics2D绘制SVG和PNG的方法

实现代码如下:

@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);
       }
   }
}

绘制图片效果如下:

Java使用Graphics2D绘制SVG和PNG的方法

我们可以看到图片和文本信息锯齿状比较明显,可以通过增加图片质量参数来改善质量。

/**
* 设置图片质量(抗锯齿等)
* @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);
}

左边未设置图片质量,右边设置了图片质量,对比效果如下所示:

Java使用Graphics2D绘制SVG和PNG的方法

来源:https://juejin.cn/post/7239484016948740155

标签:Java,Graphics2D,SVG,PNG
0
投稿

猜你喜欢

  • Java多线程的临界资源问题解决方案

    2021-12-29 07:44:35
  • java实现电话本系统

    2021-06-05 20:39:39
  • Android通过代码控制ListView上下滚动的方法

    2022-06-29 03:07:57
  • 基于Java信号量解决死锁过程解析

    2023-05-13 22:23:02
  • Java使用Thread和Runnable的线程实现方法比较

    2021-11-17 07:52:54
  • C#采用OpenXml实现给word文档添加文字

    2022-06-13 09:48:46
  • Android应用中Back键的监听及处理实例

    2022-09-26 02:00:47
  • java多线程累加计数的实现方法

    2021-07-19 12:10:36
  • SpringBoot JS-SDK自定义微信分享的实现

    2023-11-10 05:08:28
  • 详解Spring Data Jpa 模糊查询的正确用法

    2022-03-28 15:07:50
  • 解决JTable排序问题的方法详解

    2023-02-07 08:53:42
  • Android画板开发之添加背景和保存画板内容为图片

    2022-05-30 04:01:37
  • 初学者Android studio安装图文详解

    2022-08-06 07:22:12
  • Java中包装类介绍与其注意事项

    2023-03-20 18:26:36
  • Java IO流和文件操作实现过程解析

    2022-03-10 02:08:13
  • IntelliJ IDEA本地代码提交到github网站不显示与本地不同步问题的解决办法

    2022-09-21 10:50:44
  • Java中List Set和Map之间的区别_动力节点Java学院整理

    2022-12-11 04:57:38
  • 解决Weblogic部署war找不到spring配置文件的问题

    2022-12-29 07:03:08
  • Java 使用getClass().getResourceAsStream()方法获取资源

    2023-07-23 08:59:56
  • JAVA学习之一步步搭建spring框架

    2023-02-24 06:39:44
  • asp之家 软件编程 m.aspxhome.com