使用 image-combiner 开源项目实现对海报图片的生成

1:gitee 项目地址

image-combiner: ImageCombiner是一个专门用于Java服务端图片合成的工具,没有很复杂的功能,简单实用,从实际业务场景出发,提供简单的接口,几行代码即可实现图片拼合(当然用于合成水印也可以),素材上支持图片、文本、矩形三种,支持定位、缩放、旋转、圆角、透明度、颜色、字体、字号、删除线、居中绘制、文本自动换行等特性,足够覆盖图片合成的日常需求。icon-default.png?t=N7T8https://gitee.com/dromara/image-combiner#242-%E5%A6%82%E4%BD%95%E6%96%87%E5%AD%97%E7%AB%96%E6%8E%92

2:ImageCombiner怎么使用

        ImageCombiner使用起来相当简单,主要的类只用一个,new一个ImageCombiner对象,指定背景图片和输出格式,然后加入各种素材元素,设置元素的位置、大小和效果(如圆角、颜色、透明度等),调用combine()方法即可。combine()方法直接返回BufferedImage对象,也可以调用getCombinedImageStream()获得流,方便上传oss等后续操作,或者调用save()方法保存到本地,调试的时候比较方便。

3:使用安装

<dependency>
    <groupId>com.freewayso</groupId>
    <artifactId>image-combiner</artifactId>
    <version>2.6.8</version>
</dependency>

4:使用示例

public void simpleDemo() throws Exception {

    //合成器(指定背景图和输出格式,整个图片的宽高和相关计算依赖于背景图,所以背景图的大小是个基准)
    ImageCombiner combiner = new ImageCombiner("http://xxx.com/image/bg.jpg", OutputFormat.JPG);

    //加图片元素
    combiner.addImageElement("http://xxx.com/image/product.png", 0, 300);

    //加文本元素
    combiner.addTextElement("周末大放送", 60, 100, 960);

    //执行图片合并
    combiner.combine();

    //可以获取流(并上传oss等)
    InputStream is = combiner.getCombinedImageStream();

    //也可以保存到本地
    //combiner.save("d://image.jpg");


5:完整示例

public void demo() throws Exception {

    //图片元素可以是Url(支持file协议),也可以是BufferImage对象
    String bgImageUrl = "http://xxx.com/image/bg.jpg";                  //背景图(url)
    String qrCodeUrl = "file:///d:/qrCode.png";                         //二维码(file协议)
    String productImageUrl = "https://xxx.com/image/product.jpg";       //商品图
    BufferedImage waterMark = ImageIO.read(new URL("https://xxx.com/image/waterMark.jpg")); //水印图
    BufferedImage avatar = ImageIO.read(new File("d:/avatar.jpg"));     //头像
    String title = "# 最爱的家居";                                       //标题文本
    String content = "苏格拉底说:“如果没有那个桌子,可能就没有那个水壶”";  //内容文本

    //创建合成器(指定背景图和输出格式,整个图片的宽高和相关计算依赖于背景图,所以背景图的大小是个基准)
    ImageCombiner combiner = new ImageCombiner(bgImageUrl, 1500, 0, ZoomMode.Height, OutputFormat.JPG);  //v1.1.4之后可以指定背景图新宽高了(不指定则默认用图片原宽高)
    
    //针对背景和整图的设置
    combiner.setBackgroundBlur(30);     //设置背景高斯模糊(毛玻璃效果)
    combiner.setCanvasRoundCorner(100); //设置整图圆角(输出格式必须为PNG)
    combiner.setQuality(.8f);           //设置图片保存质量(0.0~1.0,Java9以下仅jpg格式有效)
    
    //标题(默认字体为阿里普惠、黑色,也可以自己指定Font对象)
    combiner.addTextElement(title, 0, 150, 1400)
            .setCenter(true)        //居中绘制(会忽略x坐标,改为自动计算)
            .setAlpha(.8f)          //透明度(0.0~1.0)
            .setRotate(45)          //旋转(0~360)
            .setColor(Color.Red)    //颜色
            .setDirection(Direction.RightLeft) //绘制方向(从右到左,用于需要右对齐场景)
            .setAutoFitWidth(200);  //自适应最大宽度(超出则自动缩小字体)
        
    //副标题(v2.6.3版本开始支持加载项目内字体文件,可以不用在服务器安装,性能略低)
    combiner.addTextElement("年度狂欢", "/font/yahei.ttf", 0, 150, 1450)
        
    //内容(设置文本自动换行,需要指定最大宽度(超出则换行)、最大行数(超出则丢弃)、行高)
    combiner.addTextElement(content, "微软雅黑", Font.BOLD, 40, 150, 1480)
            .setSpace(.5f)                      //字间距
            .setStrikeThrough(true)             //删除线
            .setAutoBreakLine(837, 2, 60);      //自动换行(还有一个LineAlign参数可以指定对齐方式)

    //商品图(设置坐标、宽高和缩放模式,若按宽度缩放,则高度按比例自动计算)
    combiner.addImageElement(productImageUrl, 0, 160, 837, 0, ZoomMode.Width)
            .setCenter(true)        //居中绘制(会忽略x坐标,改为自动计算)
            .setRoundCorner(46)     //设置圆角

    //头像(圆角设置一定的大小,可以把头像变成圆的)
    combiner.addImageElement(avatar, 200, 1200)
            .setRoundCorner(200);   //圆角

    //水印(设置透明度,0.0~1.0)
    combiner.addImageElement(waterMark, 630, 1200)
            .setAlpha(.8f)          //透明度(0.0~1.0)
            .setRotate(45)          //旋转(0~360)
            .setBlur(20)            //高斯模糊(1~100)
            .setRepeat(true, 100, 50);    //平铺绘制(可设置水平、垂直间距)

    //加入圆角矩形元素(版本>=1.2.0),作为二维码的底衬
    combiner.addRectangleElement(138, 1707, 300, 300)
            .setColor(Color.WHITE)
            .setRoundCorner(50)     //该值大于等于宽高时,就是圆形,如设为300
            .setAlpha(.8f)
            .setGradient(Color.yellow,Color.blue,GradientDirection.LeftRight);  //颜色渐变
            .setBorderSize(5);      //设置border大小就是空心,不设置就是实心

    //二维码(强制按指定宽度、高度缩放)
    combiner.addImageElement(qrCodeUrl, 138, 1707, 186, 186, ZoomMode.WidthHeight);

    //价格(元素对象也可以直接new,然后手动加入待绘制列表)
    TextElement textPrice = new TextElement("¥1290", 60, 230, 1300);
    textPrice.setColor(Color.red);          //红色
    textPrice.setStrikeThrough(true);       //删除线
    combiner.addElement(textPrice);         //加入待绘制集合

    //执行图片合并
    combiner.combine();

    //可以获取流(并上传oss等)
    InputStream is = combiner.getCombinedImageStream();

    //也可以保存到本地
    //combiner.save("d://image.jpg");
}

6:个人项目案例

1: 第一张效果

package com.example.juc.posterTemplate;

import com.freewayso.image.combiner.ImageCombiner;
import com.freewayso.image.combiner.enums.LineAlign;
import com.freewayso.image.combiner.enums.OutputFormat;
import com.freewayso.image.combiner.enums.ZoomMode;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("PosterOne")
public class PosterOne {

    @GetMapping
    public void PosterOne(HttpServletResponse response) throws Exception {
        // 记录开始时间
        long startTime = System.nanoTime(); // 获取开始时间

        ClassLoader loader = Thread.currentThread().getContextClassLoader();

//        BufferedImage backgroundImage = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/67868d805e20240426145820.png")));
        BufferedImage backgroundImage = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/293231.jpg")));

        System.out.println("宽度"+ backgroundImage.getWidth());
        System.out.println("高度"+ backgroundImage.getHeight());
        // 创建 Graphics2D 对象
        Graphics2D g2d = backgroundImage.createGraphics();

        Color color = new Color(0, 88, 38);
        // 设置透明度
        int alpha = 180; // 透明度(取值范围:0 - 255)
        AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) alpha / 255);
        g2d.setComposite(alphaComposite);

        // 设置背景色
        g2d.setColor(color);

        // 填充整个图片区域
        g2d.fillRect(0, 0, backgroundImage.getWidth(), backgroundImage.getHeight());



        //合成器(指定背景图和输出格式,整个图片的宽高和相关计算依赖于背景图,所以背景图的大小是个基准)
        ImageCombiner combiner = new ImageCombiner(backgroundImage, 1372, 1978, ZoomMode.WidthHeight, OutputFormat.JPG);  //v1.1.4之后可以指定背景图新宽高了(不指定则默认用图片原宽高)

        //加图片元素
        BufferedImage backgroundImageTwo = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/cb18e0953720240426150230.png")));

        // 读取二维码
        BufferedImage erweima = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/d4eaffbdb920240426150125.png")));

        //读取头像
        BufferedImage touxiang = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/44415e306a4802dd43c161e872a1fc56.png")));

        combiner.addImageElement(backgroundImageTwo, 50, 50, 230, 60, ZoomMode.WidthHeight);  //中大logo

        combiner.addImageElement(erweima, 50, 1280, 230, 230, ZoomMode.WidthHeight);  //二维码(强制按指定宽度、高度缩放)

        //头像(圆角设置一定的大小,可以把头像变成圆的)
        combiner.addImageElement(touxiang, 800, 800, 400, 400, ZoomMode.WidthHeight).setRoundCorner(400);   //圆角



        //加文本元素
        combiner.addTextElement("物理与天文学院学术报告 第259期", "宋体", Font.BOLD, 50, 50, 180)
                .setColor(Color.white)  ;
        //加文本元素
        combiner.addTextElement("MIMO天线及其空口测试技术", "", Font.BOLD, 120, 50, 280)
                .setColor(Color.white)
                .setSpace(0.1f)
                .setAutoBreakLine(1300, 5, 120);      //自动换行(还有一个LineAlign参数可以指定对齐方式)

        combiner.addTextElement("2024", "Source Han Sans CN", Font.BOLD, 50, 0, 830)
                .setColor(Color.white)
                .setRotate(90);  // 旋转

        combiner.addTextElement("10.18", "Source Han Sans CN", Font.BOLD, 120, 125, 810)
                .setColor(Color.white);

        combiner.addTextElement("14:30 ~ 15:30", "Source Han Sans CN", Font.BOLD, 60, 50, 950)
                .setColor(Color.white);

        combiner.addTextElement("(周四)", "", Font.BOLD, 60, 20, 1030)
                .setColor(Color.white);


        combiner.addTextElement("电信楼101讲学厅", "宋体", Font.BOLD, 60, 50, 1730)
                .setColor(Color.white);

        combiner.addTextElement("中山大学(东校园)", "", Font.BOLD, 40, 50, 1830)
                .setColor(Color.white);

        //添加文字,并设置为自动换行,且行宽设为0
        combiner.addTextElement("主讲人", "宋体", 50, 1250, 750)
                .setAutoBreakLine(0, 50, 60).setColor(Color.white);;

        combiner.addTextElement("欧阳上官", "宋体",50, 1180, 750)
                .setAutoBreakLine(0, 50, 60, LineAlign.Center).setColor(Color.white);;




        //执行图片合并
        combiner.combine();

        //可以获取流(并上传oss等)
        InputStream is = combiner.getCombinedImageStream();
        combiner.setQuality(1.0f);           //设置图片保存质量(0.0~1.0,Java9以下仅jpg格式有效)
        //也可以保存到本地
        combiner.save("F://image.jpg");
        InputStream download = combiner.getCombinedImageStream();
        writeFile(response, download);

        long endTime = System.nanoTime(); // 获取结束时间
        long elapsedTime = endTime - startTime; // 计算时间差(纳秒)

        // 将纳秒转换为秒
        double seconds = TimeUnit.NANOSECONDS.toSeconds(elapsedTime);

        System.out.printf(String.valueOf(seconds));
    }
    /**
     * 将输入流输出到页面
     */
    public static void writeFile(HttpServletResponse resp, InputStream inputStream) {
        OutputStream out = null;
        try {
            out = resp.getOutputStream();
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = inputStream.read(b)) != -1) {
                out.write(b, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

2:生成效果

3:第二张代码

package com.example.juc.posterTemplate;

import com.example.juc.utils.ThumbnailsUtils;
import com.freewayso.image.combiner.ImageCombiner;
import com.freewayso.image.combiner.enums.LineAlign;
import com.freewayso.image.combiner.enums.OutputFormat;
import com.freewayso.image.combiner.enums.ZoomMode;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.font.FontDesignMetrics;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("posterTwo")
public class PosterTwo {

    @GetMapping
    public void posterTwo(HttpServletResponse response) throws Exception {

        // 记录开始时间
        long startTime = System.nanoTime(); // 获取开始时间

        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        BufferedImage backgroundImage = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/75f82fa97520240426151751.png")));

//        BufferedImage backgroundImage = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/293231.jpg")));

        /**
         * 调整图片的宽高
         */
        BufferedImage image = new BufferedImage(1472, 2102, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2dd = image.createGraphics();
        g2dd.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        // 绘制原始图像到新的 BufferedImage,并进行缩放
        Image imageScaledInstance = backgroundImage.getScaledInstance(1472, 2102, Image.SCALE_SMOOTH);
        g2dd.drawImage(imageScaledInstance, 0, 0, null);
        g2dd.dispose();
        /**
         * 调整图片的宽高
         */

        Color color = new Color(0, 88, 38);

        // 创建颜色并设置透明度
        int alpha = 240; // 透明度(取值范围:0 - 255)
        // 创建 Graphics2D 对象
        Graphics2D g2d = image.createGraphics();

        // 设置透明度
        AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) alpha / 255);
        g2d.setComposite(alphaComposite);
        // 设置左上角背景色
        g2d.setColor(color);
        // 填充整个图片区域
        g2d.fillRect(0, 0, 450, 230);

        // 设置背景色
        Graphics2D g3d = image.createGraphics();
        // 设置最下面的背景色
        g3d.setColor(color);
        // 填充整个图片区域
        g3d.fillRect(0, 1980, 1472, 230);


        //合成器(指定背景图和输出格式,整个图片的宽高和相关计算依赖于背景图,所以背景图的大小是个基准)


        ImageCombiner combiner = new ImageCombiner(image,1472, 2102,ZoomMode.WidthHeight, OutputFormat.JPG);  //v1.1.4之后可以指定背景图新宽高了(不指定则默认用图片原宽高)

        //加黄色背景 图片元素
        BufferedImage backgroundImageTwo = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/d2bb4a861020240426151628.png")));

        // 添加logo 图片
        BufferedImage backgroundImageThree = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/305653dc5420240426152302.png")));

        //中山大学logo
        BufferedImage backgroundImageFour = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/cb18e0953720240426150230.png")));

        //绿色的线
        BufferedImage backgroundImageFive = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/29ba7b8c1d20240426152032.png")));

        //二维码
        BufferedImage backgroundImageSix = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/d4eaffbdb920240426150125.png")));

        //白色的圈圈

        BufferedImage backgroundImageSeven = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/fa4ae6dd3320240426152414.png")));

        // 头像
        BufferedImage backgroundImageEight = ImageIO.read(Objects.requireNonNull(loader.getResourceAsStream("images/44415e306a4802dd43c161e872a1fc56.png")));

        combiner.addImageElement(backgroundImageTwo, 0, 230, 450, 1750, ZoomMode.WidthHeight);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageThree, 0, 0, 300, 250, ZoomMode.WidthHeight);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageFour, 1200, 2020);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageFive, 38, 1400, 370, 2, ZoomMode.WidthHeight);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageFive, 38, 1600, 370, 2, ZoomMode.WidthHeight);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageSix, 50, 1750, 190, 190, ZoomMode.WidthHeight);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageSeven, 950, 400, 400, 400, ZoomMode.WidthHeight);  //(强制按指定宽度、高度缩放)
        combiner.addImageElement(backgroundImageEight, 970, 420, 360, 360, ZoomMode.WidthHeight).setRoundCorner(400);;  //(强制按指定宽度、高度缩放)

        //添加文字,并设置为自动换行,且行宽设为0
        combiner.addTextElement("主讲人", "宋体", 50, 1100, 180)
                .setAutoBreakLine(0, 50, 60).setColor(Color.white);;

        combiner.addTextElement("欧阳上官", "宋体",50, 1030, 180)
                .setAutoBreakLine(0, 50, 60, LineAlign.Center).setColor(Color.white);
        //加文本元素
        combiner.addTextElement("主讲人:", "宋体", Font.BOLD, 40, 40, 2010)
                .setColor(Color.white);

        combiner.addTextElement("李剑锋", "宋体", Font.BOLD, 40, 200, 2010)
                .setColor(Color.white);

        //加文本元素
        // TODO 标题
        Graphics2D graphics = image.createGraphics();
        drawCenteredText(graphics, "逸仙生命讲坛人第五十九讲", 100, 180);
        Graphics2D graphicsTwo = backgroundImageTwo.createGraphics();

        drawMyString(graphicsTwo, "时空相分离调控的职务细胞信号转导", color);

        //TODO 时间 2024
        Font fontYear = new Font("Source Han Sans CN", Font.PLAIN, 40);
        graphicsTwo.setColor(Color.BLACK);
        qinxie(fontYear, graphicsTwo);  //字体倾斜
        graphicsTwo.drawString("2024", 40, 1230);


        //TODO 时间 月份 10.23  01.23
        Font fontMonth = new Font("Source Han Sans CN", Font.BOLD, 70);
        graphicsTwo.setColor(Color.BLACK);
        qinxie(fontMonth, graphicsTwo);
        String posterMonth = "10.23";
        graphicsTwo.drawString(posterMonth, 230, 1230);

        //TODO 具体时间  20:12 ~ 11:00  周五
        Font fontDate = new Font("Source Han Sans CN", Font.BOLD, 43);
        graphicsTwo.setColor(Color.BLACK);
        qinxie(fontDate, graphicsTwo);
        graphicsTwo.drawString("20:12 ~ 11:00 周五", 40, 1330);

        // TODO 具体举办地点
        Font fontDd = new Font("宋体", Font.BOLD, 35);
        graphicsTwo.setFont(fontDd);
        graphicsTwo.setColor(Color.BLACK);
        qinxie(fontDd, graphicsTwo);
        drawWordAndLineFeed(graphicsTwo, fontDd,"中山大学(东校园-电信楼101讲学厅", 40, 1430, 330);

        // TODO 主讲人详细介绍信息
        drawMyLectureString(graphics, "我啥事打卡机大街上的情况为其较为气温气温其味无穷我的撒登记卡水电气网红,诶打算近期我后端数据啊按地区为背景墙网格袋安山东划算的挥洒的企鹅企鹅群。");
        // 设置字体和颜色
        Font font = new Font("宋体", Font.PLAIN, 50);
        g2d.setFont(font);
        g2d.setColor(Color.BLACK);


        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.dispose();

        //执行图片合并
        combiner.combine();

        //可以获取流(并上传oss等)

        combiner.setQuality(1.0f);           //设置图片保存质量(0.0~1.0,Java9以下仅jpg格式有效)
        //也可以保存到本地
        combiner.save("F://imageTwo.jpg");

        InputStream download = combiner.getCombinedImageStream();
        writeFile(response, download);
        long endTime = System.nanoTime(); // 获取结束时间
        long elapsedTime = endTime - startTime; // 计算时间差(纳秒)

        // 将纳秒转换为秒
        double seconds = TimeUnit.NANOSECONDS.toSeconds(elapsedTime);

        System.out.printf(String.valueOf(seconds));

    }


    public static BufferedImage resizeImageOne(BufferedImage originalImage, int targetWidth, int targetHeight) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        Thumbnails.of(originalImage)
                .size(targetWidth, targetHeight)
                .outputFormat("JPG")
                .outputQuality(1)
                .toOutputStream(outputStream);
        byte[] data = outputStream.toByteArray();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
        return ImageIO.read(inputStream);
    }


    /**
     * 将输入流输出到页面
     */
    public static void writeFile(HttpServletResponse resp, InputStream inputStream) {
        OutputStream out = null;
        try {
            out = resp.getOutputStream();
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = inputStream.read(b)) != -1) {
                out.write(b, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * &#064;Author  xialijun
     * &#064;Description  海报横向文字写字换行算法
     * &#064;Date  18:08 2024/4/24
     * &#064;Param 参数 Graphics2D 对象 、font 字体设置 、 文字、 x轴左边、 y轴坐标 、每行字体的换行宽度
     **/
    public static void drawWordAndLineFeed(Graphics2D g2d, Font font, String words, int wordsX, int wordsY, int wordsWidth) {
        FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
        // 获取字符的最高的高度
        int height = metrics.getHeight();

        int width = 0;
        int count = 0;
        int total = words.length();
        String subWords = words;
        int b = 0;
        for (int i = 0; i < total; i++) {
            // 统计字符串宽度 并与 预设好的宽度 作比较
            if (width <= wordsWidth) {
                width += metrics.charWidth(words.charAt(i)); // 获取每个字符的宽度
                count++;
            } else {
                // 画 除了最后一行的前几行
                String substring = subWords.substring(0, count);
                g2d.drawString(substring, wordsX, wordsY + (b * height));
                subWords = subWords.substring(count);
                b++;
                width = 0;
                count = 0;
            }
            // 画 最后一行字符串
            if (i == total - 1) {
                g2d.drawString(subWords, wordsX, wordsY + (b * height));
            }
        }
    }
    /**
     * 字体倾斜
     */
    public static void qinxie(Font fontMonth,Graphics2D graphics){
        // 创建 AffineTransform 对象
        AffineTransform month = new AffineTransform();
        // 设置水平倾斜
        month.shear(-0.2, 0); // + 向左  -  向右倾斜
        // 应用变换到字体
        fontMonth = fontMonth.deriveFont(month);
        graphics.setFont(fontMonth);
    }
    public static void drawCenteredText(Graphics graphics, String text, int oneY, int twoY) {
        // 设置字体和颜色
        Font font = new Font("宋体", Font.BOLD, 50);
        graphics.setFont(font);
        graphics.setColor(Color.WHITE);
        // 获取字体渲染上下文
        FontMetrics fm = graphics.getFontMetrics(font);
        // 截取第二行文字的部分
        String textTwo = text.substring(text.indexOf("第"));
        // 获取第一行文字的宽度
        String textOne = text.substring(0, text.indexOf("第"));
        // 获取第二行文字的宽度
        int textTwoWidth = fm.stringWidth(textTwo);
        // 计算第一行文字的起始x坐标,使其水平居中
        int oneX = 50;
        // 计算第二行文字的起始x坐标,使其水平居中
        int twoX = (450 - textTwoWidth) / 2;
        // 绘制第一行文字
        graphics.drawString(textOne, oneX, oneY);
        // 绘制第二行文字
        graphics.drawString(textTwo, twoX, twoY);
    }
    /**
     * 部分文字 垂直排序时,不满多列,最后一列居中显示
     * @param textGraphics Graphics 对象
     * @param text  要传入的海报描述
     */
    public static void drawMyString(Graphics textGraphics, String text,Color color) {

        // 每列显示的汉字数量
        int columnSize = 7;
        // 文字之间的垂直间距
        int verticalSpacing = 105;

        // 获取字体渲染上下文
        FontMetrics fm = textGraphics.getFontMetrics();
        // 获取字体的高度
        int fontHeight = fm.getHeight();
        System.out.println(fontHeight);
        // 计算每列的宽度
        int columnWidth = fontHeight + verticalSpacing;

        // 设置初始位置
        int x = 280;
        int y = 250;
        Font fontFour = new Font("Source Han Sans CN", Font.BOLD, 100);
        textGraphics.setFont(fontFour);
        textGraphics.setColor(color);
        // 绘制文字
        int charCount = 0;
        int totalColumns = (int)Math.ceil((double)text.length() / columnSize); // 总列数
        int totalRows = Math.min(columnSize, text.length()); // 总行数
        int remainingChars = text.length() % columnSize; // 最后一列剩余字符数

        for (int columnIndex = 0; columnIndex < totalColumns; columnIndex++) {
            for (int rowIndex = 0; rowIndex < totalRows; rowIndex++) {
                if (charCount >= text.length()) break;
                char ch = text.charAt(charCount);
                // 计算当前位置
                int cx = x - columnIndex * columnWidth;
                int cy = y + rowIndex * fontHeight + rowIndex * verticalSpacing; // 加入垂直偏移量
                // 计算当前位置
//                int cx = x - columnIndex * columnWidth;
//                int cy = y + rowIndex * fontHeight + rowIndex * verticalSpacing + columnIndex ;

                // 如果是最后一列并且不满 7 个字符,则需要将剩余字符居中
                if (columnIndex == totalColumns - 1 && remainingChars > 0) {
                    int extraVerticalSpace = (columnSize - remainingChars) * (fontHeight + verticalSpacing) / 2;
                    cy += extraVerticalSpace;
                }
                // 绘制文字
                textGraphics.drawString(String.valueOf(ch), cx, cy);
                charCount++;
            }
        }
    }
    /**
     * 在Graphics2D对象上绘制竖排文字
     *
     * @param textGraphics     Graphics2D对象
     * @param text            要绘制的文字
     */
    public static void drawMyLectureString(Graphics textGraphics, String text) {

        // 每列显示的汉字数量
        int columnSize = 25;
        // 文字之间的垂直间距
        int verticalSpacing = 20;

        // 获取字体渲染上下文
        FontMetrics fm = textGraphics.getFontMetrics();
        // 获取字体的高度
        int fontHeight = fm.getHeight() - 30;

        // 计算每列的宽度
        int columnWidth = fontHeight + verticalSpacing;

        // 设置初始位置
        int x = 800;
        int y = 230;
        Font fontFour = new Font("宋体", Font.BOLD, 40);
        textGraphics.setFont(fontFour);
        textGraphics.setColor(Color.WHITE);

        // 计算总列数
        int totalColumns = (int) Math.ceil((double) text.length() / columnSize); // 总列数

        // 绘制文字
        int charCount = 0;
        for (int rowIndex = 0; rowIndex < columnSize; rowIndex++) {
            for (int columnIndex = 0; columnIndex < totalColumns; columnIndex++) {
                int charIndex = rowIndex * totalColumns + columnIndex;
                if (charIndex >= text.length()) break;
                char ch = text.charAt(charIndex);
                // 计算当前位置
                int cx = x - columnIndex * columnWidth;
                int cy = y + rowIndex * fontHeight + rowIndex * verticalSpacing; // 加入垂直偏移量
                // 绘制文字
                textGraphics.drawString(String.valueOf(ch), cx, cy);
            }
        }
    }

}

4:生成效果

最近更新

  1. TCP协议是安全的吗?

    2024-06-07 12:40:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-07 12:40:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-07 12:40:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-07 12:40:06       18 阅读

热门阅读

  1. C++中的常见语法糖汇总

    2024-06-07 12:40:06       5 阅读
  2. 怎么保障TikTok直播网络稳定?

    2024-06-07 12:40:06       6 阅读
  3. 计算欧几里得距离

    2024-06-07 12:40:06       7 阅读
  4. Stable Diffusion详解

    2024-06-07 12:40:06       6 阅读