记一次java开发生成图片经历

8 篇文章 0 订阅
3 篇文章 0 订阅

本人由于工作需要开发将文字转为图片的功能,所以其中也遇到坑,也找了很多的解决方法,但是不是每个方法对你都是有用,因为每个环境都不一样,下面将讲述这些经历。

从设计的时候对于文字大小是随着图片的大小自适应居中,所以在计算文字大小和居中的时候遇到了一些问题。

首先自适应居中,这个时候对于文字的基准线也就是x、y的位置没有了解清楚,认为就是在文字的中间,所以计算位置展示在图片上的时候会存现很大的偏移,经过很多次的测试,都无法找到准确的位置算法,所以最后查就找这种计算方式:

        // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);

        //产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
        Graphics2D g = image.createGraphics();
        // 设置背景颜色
        g.setColor(Color.black);
        // 背景色填充,这里设置为全部
        g.fillRect(0, 0, width, height);

        //字体大小
        Font font = new Font(fontType, Font.PLAIN, fontSize);
        g.setFont(font);
        //字体颜色
        g.setColor(fontColor);

        FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);

        int startWidth = metrics.stringWidth(text);
        int startHeight = metrics.getHeight();
        //左边位置
        int left = (width - startWidth) / 2;
        // 顶边位置+上升距离(原本字体基线位置对准画布的y坐标导致字体偏上ascent距离,加上ascent后下移刚好顶边吻合)
        int top = (height - startHeight) / 2 + metrics.getAscent();
        g.drawString(text, left, top);

上面的方法解决了单行文字的位置,但是这里需要设置多行文字,单行位移为0,两行的话就需要位移-0.5、0.5,三行就是-1、0、1这样以此类推,所以会计算位置就是行数为奇数中间为0,偶数的时候两边对称,所以偏移时候的算法:当前行数 -(行数/2.0 + 0.5)注意这里都是 double 类型的算法,所以改造后:

        // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);

        //产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
        Graphics2D g = image.createGraphics();
        // 设置背景颜色
        g.setColor(Color.black);
        // 背景色填充,这里设置为全部
        g.fillRect(0, 0, width, height);

        double median = content.size()/2.0 + 0.5;
        for (int i = 0; i < content.size(); i++) {
            TextMessage textMessage = content.get(i);
            //字体大小
            Font font = new Font(textMessage.getFont(), Font.PLAIN, fontSize);
            g.setFont(font);
            //字体颜色
            g.setColor(getColor(textMessage.getFontColor()));

            FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);

            String text = textMessage.getText();
            int strWidth = metrics.stringWidth(text);
            int strHeight = metrics.getHeight();
            //左边位置
            int left = (width - strWidth) / 2;
            // 顶边位置+上升距离(原本字体基线位置对准画布的y坐标导致字体偏上ascent距离,加上ascent后下移刚好顶边吻合)
            int top = (height - strHeight) / 2 + metrics.getAscent();
            top = (int) (top + (Double.parseDouble(String.valueOf(i + 1)) - median)* fontSize);
            g.drawString(text, left, top);
        }

上面字体居中的问题解决后,下面解决字体大小的问题,也遇到了一些问题,由于自适应大小,所以是根据画布大小来做的,会出现字体在不同大小的画布上,字体超出画布或者字体随着画布的变化,距离两边的边框越远,以至于后来的多行文字也是这样,当时查了很多的资料,各种字体的长宽比、字体大小和像素的比,但是都没有找到好的方法,最后查到一个说是5:4,这个使用测试还是可以的,同时由于字体中汉字、英文和数字它们占的位置也是不一样的,所以就按照字节来计算位置,两个字节算是一个位置,所以算法就是先找到行数最长的,然后再按照字体的长宽比为5:4(这里按照1.2来进行计算的),获取到字体的值:

        // TextMessage 中有 text 文字属性
        int length = 0;
        for (TextMessage message : content) {
            int size = message.getText().getBytes().length/2;
            if (length < size) {
                length = size;
            }
        }
        int fontSize = Math.min((int)(width / length * 1.2), height/content.size());

总算是将上面的基础的功能开发完成了,idea 基本测试也正常,下面提交代码,进行打包发布到测试环境,本以为会一切顺利,可是意外情况还是出现了,真是防不胜防啊,测试生成的图片汉字为口,数字和英文正常,这里补充下测试环境使用的是docker,在centos的环境中,网上查询了下,原来是系统中缺少字体库,找了很多种方法,比如:向系统中字体、jvm中家字体,但是我们这边开发人员无法操作系统环境,我这边进行了很多种尝试,当然这时候的尝试其实还有坑没有发现后面会讲到,比如直接将我本地盘中的字体放到项目的resource下建的font文件夹里,然后项目启动自动同步到系统字体的文件夹、也测试同步到jvm字体文件夹,但都是失败告终。

通过上面的尝试,已经感到很无力的时候,这个时候想到是不是可以 java 加载字体,这里在测试读取字体库创建字体,就去网上查找资料,别说还真有,这时候我就测试读取字体文件创建字体,但是发现都会报错,这时候一查报错的原因是因为字体库文件损坏读取异常,这时候就查询为什么字体库会异常,在stackoverfloew 找到这方面的问题,原来是maven搞的鬼,项目编译过后编译文件里的字体库会比原来的字体库大,这里讲下我们的项目以springboot作为框架,maven 作为项目的jar引用管理和打包编译,所以这里就将添加了如下的maven配置:

        <resources>
            <resource>
                <directory>${project.basedir}/src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/jasperreports_extension.properties</include>
                    <include>**/tecsofti-fonts.xml</include>
                    <include>**/*.ttf</include>
                    <include>**/*.TTF</include>
                    <include>**/*.ttc</include>
                    <include>**/*.fon</include>
                    <include>**/*.yml</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.0.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <nonFilteredFileExtensions>
                        <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                        <nonFilteredFileExtension>TTF</nonFilteredFileExtension>
                        <nonFilteredFileExtension>ttc</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>
        </plugins>

这里解决了这个编译打包的问题,这时候进行测试,基本上没有问题,打包发布到测试环境也都可以了,可能上面的设计还是没有很严谨,但是对于基本的还是可以的。

后面附赠一个计算颜色的代码,这里是颜色的编码转为java颜色对象(没有前面的#):

    /**
     * 颜色编码转颜色对象
     *
     * @param rgb 颜色编码
     * @return 转换后的对象
     */
    private static Color getColor(String rgb) {
        if (rgb == null || rgb.length() != 6) {
            return Color.red;
        }
        int red = Integer.parseInt(rgb.substring(0, 2), 16);
        int green = Integer.parseInt(rgb.substring(2, 4), 16);
        int blue = Integer.parseInt(rgb.substring(4), 16);
        int alpha = (int) (255 * 1.0);
        int colour = (red << 16) | (green << 8) | blue;
        colour = colour | (alpha << 24);
        return new Color(colour, true);
    }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值