Java使用Graphics2D画图,画圆,矩形,透明度等实现

背景

如上图,需要使用Java生成一个图片, 并以base64编码的形式返回给前端展示。

使用Graphics2D类,来进行画图,其中需要画方框、原型、插入图标、写入文字等,同时需要设置透明度等细节点 

环境:Jdk17,springboot2.7.13

代码如下

有详细的注释

package com.demo;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;

/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author MILLA
 * @version 1.0
 * @since 2024/06/24 9:14
 */
@Slf4j
public class ImageDemo {

    /**
     * 每个div的高度
     */
    private static final int LINE_HEIGHT = 80;

    private static final double COLOR_WIDTH = 0.7;

    /**
     * 处方笺图片宽
     */
    private static final int PIC_WIDTH = 1200;

    /**
     * 顶部与底部留白
     */
    private static final int MARGIN_Y = 52;

    /**
     * 左右留白
     */
    private static final int MARGIN_X = 50;

    /**
     * 生成图片后缀
     */
    private static final String FILE_SUFFIX = ".jpg";

    public static void main(String[] args) throws Exception {
        List<Object> objects = Lists.newArrayList(1, 2, 3, 4, 5, 6);
        String base64 = new ImageDemo().getImage(objects);
        System.out.println(base64);
    }

    /**
     * 初始化
     *
     * @param image    画布
     * @param graphics 画笔
     */
    private void initiation(BufferedImage image, Graphics2D graphics) {
        int width = image.getWidth();
        int height = image.getHeight();
        graphics.setClip(0, 0, width, height);
        // 设置画笔颜色
        graphics.setColor(Color.white);
        // 绘制背景
        graphics.fillRect(0, 0, width, height);
        // 设置抗锯齿
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }

    private String getImage(List<Object> objects) throws IOException {
        //读取图标流
        InputStream stream = this.getClass().getClassLoader().getResourceAsStream("static/icon.png");
        BufferedImage avatar = ImageIO.read(stream);
        // 新建图片
        BufferedImage image = new BufferedImage(PIC_WIDTH, objects.size() * LINE_HEIGHT + MARGIN_Y * 2, BufferedImage.TYPE_INT_BGR);
        // 创建画笔
        Graphics2D graphics = image.createGraphics();
        // 初始化背景色
        initiation(image, graphics);

        // 定义margin
        Margin margin = new Margin(MARGIN_Y, MARGIN_Y, MARGIN_X, MARGIN_X);
        // 初始化坐标
        Point point = new Point(margin.getLeft(), margin.getTop());
        ThreadLocalRandom random = ThreadLocalRandom.current();
        for (int i = 0; i < objects.size(); i++) {
            Color color = new Color(random.nextInt(0, 255), random.nextInt(0, 255), random.nextInt(0, 255));
            drawDiv(point, image, graphics, color, avatar, "颜色名称: " + (i + 1), "P", "颜色编码:" + (i + 1));
        }
        // 销毁画笔,结束绘制
        graphics.dispose();
        byte[] bytes = toByteArray(image);
        //文件生成
        log.info("文件路径", FileUtil.writeBytes(bytes, "test" + FILE_SUFFIX));
        String prefix = "data:image/jpg;base64,";
        return prefix + Base64.encode(bytes);
    }

    private void drawDiv(Point point, BufferedImage image, Graphics2D graphics, Color color, BufferedImage avatar, String name, String type, String code) {
        Font font = new Font("宋体", Font.BOLD, 28);
        int width = image.getWidth() - 2 * point.getX();
        // 设置div的绘制区域
        graphics.setClip(point.getX(), point.getY(), width, LINE_HEIGHT);
//         设置画笔颜色
        graphics.setColor(color);
        int firstWidth = (int) (COLOR_WIDTH * width);
//         绘制背景 一行的前半部分
        graphics.fillRect(point.getX(), point.getY() + 1, firstWidth, LINE_HEIGHT - 2);
        // 设置画笔
        int nameX = point.getX() + 18;
        drawContent(name, graphics, nameX, point.getY(), Color.WHITE, image.getWidth(), point, font);
        int circleX = firstWidth - 15;
        drawCircle(point, graphics, circleX);
        font = new Font("宋体", Font.BOLD, 23);
        drawContent(type.toUpperCase(Locale.ROOT), graphics, circleX + 5, point.getY(), Color.WHITE, image.getWidth(), point, font);

        // 绘制背景 一行的后半部分--外部矩形框
        Color outerColor = new Color(Integer.parseInt("DDDDDD", 16));
        graphics.setColor(outerColor);
        int secondWidth = (int) ((1 - COLOR_WIDTH) * width);
        graphics.fillRect(point.getX() + firstWidth, point.getY(), secondWidth, LINE_HEIGHT);
        // 绘制背景 一行的后半部分---内部矩形框
        Color innerColor = new Color(Integer.parseInt("F4F4F4", 16));
        graphics.setColor(innerColor);
        graphics.fillRect(point.getX() + firstWidth + 1, point.getY() + 1, secondWidth - 2, LINE_HEIGHT - 2);
        //图标
        int avatarHeight = avatar.getHeight() / 2;
        int avatarX = point.getX() + firstWidth + 31;
        graphics.drawImage(avatar, avatarX, point.getY() + 1 + (LINE_HEIGHT - avatarHeight) / 2, avatar.getWidth() / 2, avatarHeight, innerColor, null);
        //图标 --文字
        int codTextX = avatarX + avatar.getWidth() / 3 + 33;
        font = new Font("宋体", Font.PLAIN, 20);
        drawContent(code, graphics, codTextX, point.getY(), Color.BLACK, image.getWidth(), point, font);
        point.setY(point.y + LINE_HEIGHT - 1);
    }

    private void drawCircle(Point point, Graphics2D graphics, int circleX) {
        Composite composite = graphics.getComposite();
        //透明度设置
        AlphaComposite instance = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
        graphics.setComposite(instance);
        graphics.setColor(Color.BLACK);
        graphics.fillOval(circleX, point.getY() + (LINE_HEIGHT - 32) / 2, 32, 32);
        //恢复原来的透明度
        graphics.setComposite(composite);
    }

    private void drawContent(String text, Graphics2D cs, int x, int y, Color color, int width, Point point, Font font) {
        //临时将需要裁剪区域置空
        cs.setClip(null);
        //设置文本颜色
        cs.setColor(color);
        //设置文本字体
        cs.setFont(font);
        //文本抗锯齿
        cs.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        cs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        //为画布添加文字,并居中
        FontMetrics fm = cs.getFontMetrics(font);
        int ascent = fm.getAscent();
        int descent = fm.getDescent();
        cs.drawString(text, x + 5, y + (LINE_HEIGHT - (ascent + descent)) / 2 + ascent);

        //恢复之前的裁剪区域
        cs.setClip(point.getX(), point.getY(), width - 2 * point.getX(), LINE_HEIGHT);
    }


    private byte[] toByteArray(BufferedImage image) throws IOException {
        // 输出png图片
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        image.flush();
        ImageIO.write(image, "png", os);
        return os.toByteArray();
    }

    @Data
    @AllArgsConstructor
    public static class Margin {
        /**
         * 上
         */
        private int top;

        /**
         * 底
         */
        private int bottom;

        /**
         * 左
         */
        private int left;

        /**
         * 右
         */
        private int right;
    }

    @Data
    @AllArgsConstructor
    public static class Point {
        private int x;
        private int y;
    }

}

 PS:生成的图片如文头,base64编码如下图

 

 但是在移植到docker容器中部署的时候,报以下错误

2024-06-25 16:26:31.019 [http-nio-10008-exec-7] ERROR com.a.mybatis.common.exception.RestfulExceptionHandler - 异常堆栈:
jakarta.servlet.ServletException: Handler dispatch failed: java.lang.UnsatisfiedLinkError: /opt/java/openjdk/lib/libfontmanager.so: Error loading shared library libfreetype.so.6: No such file or directory (needed by /opt/java/openjdk/lib/libfontmanager.so)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1096)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:705)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at com.github.xiaoymin.knife4j.extend.filter.basic.JakartaServletSecurityBasicAuthFilter.doFilter(JakartaServletSecurityBasicAuthFilter.java:55)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at com.huanyu.common.config.filter.TokenFilter.doFilter(TokenFilter.java:58)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.UnsatisfiedLinkError: /opt/java/openjdk/lib/libfontmanager.so: Error loading shared library libfreetype.so.6: No such file or directory (needed by /opt/java/openjdk/lib/libfontmanager.so)
        at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
        at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(Unknown Source)
        at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(Unknown Source)
        at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(Unknown Source)
        at java.base/jdk.internal.loader.NativeLibraries.findFromPaths(Unknown Source)
        at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(Unknown Source)
        at java.base/java.lang.ClassLoader.loadLibrary(Unknown Source)
        at java.base/java.lang.Runtime.loadLibrary0(Unknown Source)
        at java.base/java.lang.System.loadLibrary(Unknown Source)
        at java.desktop/sun.font.FontManagerNativeLibrary$1.run(Unknown Source)
        at java.base/java.security.AccessController.doPrivileged(Unknown Source)
        at java.desktop/sun.font.FontManagerNativeLibrary.<clinit>(Unknown Source)
        at java.desktop/sun.font.SunFontManager$1.run(Unknown Source)
        at java.desktop/sun.font.SunFontManager$1.run(Unknown Source)
        at java.base/java.security.AccessController.doPrivileged(Unknown Source)
        at java.desktop/sun.font.SunFontManager.initStatic(Unknown Source)
        at java.desktop/sun.font.SunFontManager.<clinit>(Unknown Source)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Unknown Source)
        at java.desktop/sun.font.FontManagerFactory$1.run(Unknown Source)
        at java.base/java.security.AccessController.doPrivileged(Unknown Source)
        at java.desktop/sun.font.FontManagerFactory.getInstance(Unknown Source)
        at java.desktop/java.awt.Font.getFont2D(Unknown Source)
        at java.desktop/java.awt.Font$FontAccessImpl.getFont2D(Unknown Source)
        at java.desktop/sun.font.FontUtilities.getFont2D(Unknown Source)
        at java.desktop/sun.java2d.SunGraphics2D.checkFontInfo(Unknown Source)
        at java.desktop/sun.java2d.SunGraphics2D.getFontInfo(Unknown Source)
        at java.desktop/sun.java2d.pipe.GlyphListPipe.drawString(Unknown Source)
        at java.desktop/sun.java2d.pipe.ValidatePipe.drawString(Unknown Source)
        at java.desktop/sun.java2d.SunGraphics2D.drawString(Unknown Source)

原因分析:

 Graphics2D类在执行文本写入的时候,需要使用字体插件,因为当前的运行环境中没有对应的 Error loading shared library libfreetype.so.6插件,因此就会报上述的错误。

经排查,博主使用的docker镜像是精简版本的,将一些不常用的功能代码都去除了,因此会出现这样那样的问题,最终使用完全的jre17,解决了该问题,备查!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/754408.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NAS—网络附加存储

关键词&#xff1a;私有化存储、Nas、云盘、群晖、Tailscale、 前言 身处于互联网时代的我们&#xff0c;几乎每时每刻都在与计算机打交道&#xff0c;而软件则作为我们和计算机之间沟通的桥梁&#xff0c;因此可以认为软件的作用是&#xff1a;将计算机能力进行包装&#xf…

期货散户应该如何有效的管理仓位呢?

期货散户应该如何有效的管理仓位呢?首先,他们要意识到自己所做的决定是很重要的。其次,还要注意自己的风险承受能力。最后,要做好风险管理,以便避免出现任何问题。 1&#xff1a;散户如何管理仓位 散户在进行期货交易时&#xff0c;要想有效地管理仓位&#xff0c;需要遵循一…

Windwos +vs 2022 编译openssl 1.0.2 库

一 前言 先说 结论&#xff0c;编译64位报错&#xff0c;查了一圈没找到解决方案&#xff0c;最后换了32位的。 使用qt访问web接口&#xff0c;因为是https&#xff0c;没有openssl库会报错 QNetworkReply* reply qobject_cast<QNetworkReply*>(sender());if (reply){…

vivo 互联网自研代码评审 VCR 落地实践

作者&#xff1a;vivo 互联网效能平台团队- Chi Wei 本文介绍了vivo工程效能团队基于 Gitlab、Gerrit等开源工具搭建的VCR平台&#xff0c;代码评审idea插件开发及开发过程中遇到的挑战、困难&#xff0c;并分享了相应的应对策略和优化方案。 代码评审是软件质量保证一种活动&…

5年成为10亿级大单品,海天如何策划黄豆酱的极致产品力?

在当今的调味品市场中&#xff0c;酱料作为一个重要的细分品类&#xff0c;正迅速成为消费者日常饮食中不可或缺的一部分。随着人们对烹饪品质和口味多样化的追求不断提升&#xff0c;酱料市场的前景愈发广阔。而在这片广阔的市场中&#xff0c;海天味业&#xff0c;作为中国调…

ElasticSearch-Windows系统ElasticSearch(ES)的下载及安装

前言 下载ElasticSearch 可以进入ElasticSearch官方下载地址&#xff0c;选择与电脑系统相对应的版本&#xff1b;博主已经上传资源&#xff0c;或者点此直接免费下载&#xff0c;本次演示版本为8.14.1。 注意&#xff1a; Elasticsearch 5 需要 Java 8 以上版本&#xff1b;…

Excel 宏录制与VBA编程 ——VBA编程技巧篇一 (Union方法、Resize方法、Cells方法、UseSelect方法、With用法)

Uniom方法 使用Union方法可以将多个非连续区域连接起来成为一个区域&#xff0c;从而可以实现对多个非连续区域一起进行操作。 Resize方法 使用Range对象的Resize属性调整指定区域的大小&#xff0c;并返回调整大小后的单元格区域。 Cells方法 Cells属性返回一个Range对象。 Us…

基于Java的大学生租房系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架&#xff0c;MVC模式 工具&#xff1a;Vscode&#xff0c;MySQL&#xff0c;B/S架构…

计算机网络之OSI七层体系结构

目录 1.物理层 1.1物理层组成 1.2物理层功能 1.3物理层服务 1.4物理层标准 1.5物理层接口 2.数据链路层 2.1基于物理层的问题 2.2数据链路层功能 2.3数据链路层服务 2.4数据链路层协议 3.网络层 3.1基于DL层的问题 3.2网络层功能 3.3网络层服务 3.4网络层协议 …

根据肥胖类型选择减调方向收获窈窕身材

我们生活中胖子很多&#xff0c;从胖到瘦的人也不少&#xff0c;但瘦了后对自己身材满意的人却是不多的&#xff0c;很多人瘦了也只是减掉了身上的赘肉而已&#xff0c;大体的身形却是没有变化的&#xff0c;因此&#xff0c;并不感到满意。因为他们本身的形体是固定的&#xf…

拼多多面试总结

文章目录 一面自我介绍提问算法反问结果 二面提问算法反问结果 主管面主管面试准备算法题其他个人提问准备 提问数据库普通索引和覆盖索引的区别索引是什么&#xff1f;索引怎么加快数据库查询的&#xff1f;索引具体怎么实现的&#xff1f;以B树为例&#xff0c;节点放了什么&…

java生成excel,uniapp微信小程序接收excel并打开

java引包&#xff0c;引的是apache.poi <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency> 写一个测试类&#xff0c;把excel输出到指定路径 public s…

leetcode494. 目标和

1.思想方法 2.代码 class Solution { public int findTargetSumWays(int[] nums, int target) {int sum 0;for(int num : nums)sum num;if(sum < Math.abs(target) || (targetsum)%2 ! 0)return 0;int x (targetsum) / 2,n nums.length;//基于滚动数组的方法int[] dp…

JavaWeb-day28_HTML

今日内容 零、 复习昨日 一、HTML 零、 复习昨日 一、Web开发 前端三大件 HTML ,页面展现CSS , 样式JS (JavaScript) , 动起来 二、HTML 2.1 HTML概念 ​ 网页&#xff0c;是网站中的一个页面&#xff0c;通常是网页是构成网站的基本元素&#xff0c;是承载各种网站应用的平台…

MySQL高级-SQL优化- count 优化 - 尽量使用count(*)

文章目录 1、count 优化2、count的几种用法3、count(*)4、count(id)5、count(profession)6、count(null)7、 count(1) 1、count 优化 MyISAM引擎把一个表的总行数存在了磁盘上&#xff0c;因此执行count&#xff08;*&#xff09;的时候会直接返回这个数&#xff0c;效率很高&a…

阿里云nginx更新证书后依旧显示旧证书

尝试的解决办法 重启nginx服务删除服务器上的旧证书清除浏览器缓存检查是否使用CDN服务 最后的解决办法 云服务器开启了WAF服务&#xff0c;在WAF服务中配置证书

MySQL学习(5):SQL语句之数据查询语言:DQL

1.DQL语法 select 字段列表 from 表名列表 #DQL是可以进行多表查询的 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序字段列表 limit 分页参数 2.基本查询&#xff08;select&#xff09; 2.1查询多字段 select 字段1,字段2,字段3,......fro…

重要通知:据最新TEMU要求所有欧区车灯都需要能效标签(eu energy lable)

重要通知&#xff1a; 据最新TEMU要求&#xff0c;所有“欧区车灯”都需要能效标签&#xff08;eu energy lable&#xff09;&#xff0c;目前已下架欧区站点&#xff0c;上传成功后可恢复。 灯具类欧盟EU ENERGY LABEL 近日有不少欧洲站卖家收到TEMU平台商品要求卖家们发布的…

SHELL脚本学习(十二)sed进阶

一、多行命令 概述 sed 编辑器的基础命令都是对一行文本进行操作。如果要处理的数据分布在多行中&#xff0c;sed基础命令是没办法处理的。 幸运的是&#xff0c;sed编辑器的设计人员已经考虑了这个问题的解决方案。sed编辑器提供了3个处理多行文本的特殊命令。 命令描述N加…

大数据学习之分布式数据采集系统Flume学习

分布式数据采集系统Flume学习 一、Flume架构 1.1 Hadoop业务开发流程 1.2 Flume概述 flume是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。 支持在日志系统中定制各类数据发送方&#xff0c;用于收集数据; 同时&#xff0c;Flume提供对数据进行简单处理&…