java合并图片与文字

通过java来绘制海报,加载外部字体并设置样式大小与加粗、设置背景图、合并图片,下面是简单示例

import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

/** java 绘制海报
 * author xiaochi
 * date 2024/11/8
 */
public class PosterCreatorTest{

    public static void main(String[] args) throws Exception {
        // 创建字体
        // Font font = new Font("微软雅黑", Font.BOLD, 36);
        // 外部字体
        String fontPath = "D:\\workspace\\demo3\\src\\main\\resources\\font\\SmileySans2.ttf";
        Font font = Font.createFont(Font.TRUETYPE_FONT, new File(fontPath));
        // 合并到图片上的文字
        String text = "欢迎来到我的世界";

        int width = 854;// 容器宽度
        int height = 1280;// 容器高度
        BufferedImage bgImage = ImageIO.read(new File("D:\\workspace\\demo3\\test1111.png"));
        BufferedImage combiner = combiner(width, height,null, bgImage, bgImage.getWidth(),bgImage.getHeight());
        Graphics2D g2d = combiner.createGraphics();
        // 绘制文本2(不换行)
        g2d.setColor(getColor("#1bdf1a"));
        g2d.drawString(text, 100, 100);
        // 释放图形上下文
        g2d.dispose();

        // 绘制文本1(换行)
        wrapText(combiner,text,font.deriveFont(Font.BOLD,26f),100,50,10,0,null);
        // 设置文本旋转45°
        wrapText(combiner,text,font.deriveFont(26f),500,50,combiner.getWidth(),0,null);
        wrapText(combiner,text,font.deriveFont(26f),500,50,combiner.getWidth(),45,"#ea6f5a");

        // 合并图片
        BufferedImage mergeImage = ImageIO.read(new File("D:\\02.png"));
        merge(combiner,mergeImage,100,100,100,500);

        try {
            // 保存图片到文件
            //ImageIO.write(image, "PNG", new File("D:\\workspace\\demo3\\poster.png"));
            // 输出文件流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(combiner,"png",out);
            byte[] bytes = out.toByteArray();
            out.close();
            FileOutputStream fileOutputStream = new FileOutputStream("D:\\workspace\\demo3\\poster333.png");
            fileOutputStream.write(bytes);
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建容器
     * @param combinerWidth 容器宽度
     * @param combinerHeight 容器高度
     * @param bgColor 背景色(默认白色,且背景图会覆盖背景色)
     * @param bgImage 背景图
     * @param bgImageWidth 背景图宽度
     * @param bgImageHeight 背景图高度
     * @return combiner容器
     */
    public static BufferedImage combiner(int combinerWidth,int combinerHeight,String bgColor,BufferedImage bgImage,int bgImageWidth,int bgImageHeight){
        BufferedImage combiner = new BufferedImage(combinerWidth,combinerHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = combiner.createGraphics();
        // 设置背景颜色
        g2d.setColor(Color.white);
        if (null != bgColor && !"".equals(bgColor)){
            g2d.setColor(getColor(bgColor));
        }
        g2d.fillRect(0, 0, combinerWidth, combinerHeight);
        // 添加背景图片
        if (null != bgImage){
            // 添加背景图片
            g2d.drawImage(bgImage,0,0,bgImageWidth,bgImageHeight,null);
        }
        // 释放图形上下文
        g2d.dispose();
        return combiner;
    }

    /**
     * 合并图片
     * @param combiner 容器
     * @param mergeImage 待合并的图片
     * @param width  待合并的图片宽度
     * @param height 待合并的图片高度
     * @param x 待合并的图片x坐标
     * @param y 待合并的图片y坐标
     */
    public static void merge(BufferedImage combiner,BufferedImage mergeImage,int width,int height,int x,int y){
        //BufferedImage image = new BufferedImage(100,100,BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = combiner.createGraphics();
        g2d.drawImage(mergeImage,x,y,width,height,null);
        g2d.dispose();
    }

    /**
     * 绘制文字
     * @param combiner 容器
     * @param text 文字
     * @param font 字体(包括大小、样式、颜色)
     * @param x 文字x坐标
     * @param y 文字y坐标
     * @param maxWidth 文字最大宽度(0为竖排显示)
     * @param rotate 旋转度数
     * @param color 文字颜色(如:#ffffff)
     */
    public static void wrapText(BufferedImage combiner,String text,Font font,int x,int y,int maxWidth,double rotate,String color){
        Graphics2D g2d = combiner.createGraphics();
        //设置字体
        g2d.setFont(font);
        // 设置文字颜色
        if (null != color && !"".equals(color)){
            g2d.setColor(getColor(color));
        }
        // 抗锯齿属性
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //提升观感
        // 设置旋转
        AffineTransform at = new AffineTransform();
        at.rotate(Math.toRadians(rotate),x,y); // 旋转45度,旋转中心为(文字x坐标,100)
        g2d.setTransform(at);

        FontMetrics fontMetrics = g2d.getFontMetrics(font);
        String[] lines = splitText(text, maxWidth, fontMetrics); //实现文字自动换行
        int lineHeight = g2d.getFontMetrics().getHeight();
        int ystart = y;
        for (String line : lines) {
            g2d.drawString(line, x, ystart);
            ystart += lineHeight;
        }
        g2d.dispose();
    }

    /**
     * 获取颜色
     * @param color #2395439
     * @return
     */
    public static Color getColor(String color) {
        if (color.charAt(0) == '#') {
            color = color.substring(1);
        }
        if (color.length() != 6) {
            return null;
        }
        try {
            int r = Integer.parseInt(color.substring(0, 2), 16);
            int g = Integer.parseInt(color.substring(2, 4), 16);
            int b = Integer.parseInt(color.substring(4), 16);
            return new Color(r, g, b);
        } catch (NumberFormatException nfe) {
            return null;
        }
    }

    /**
     * 切割文字
     * @param text
     * @param maxWidth
     * @param fontMetrics
     * @return
     */
    private static String[] splitText(String text, int maxWidth, FontMetrics fontMetrics) {
        StringBuilder wrappedText = new StringBuilder();
        String[] words = text.split(""); //以每个字符做拆分,可根据实际需求做更改,下同
        List<String> lines = new ArrayList<>();
        for (String word : words) {
            // 检查添加新单词后是否会超过最大宽度
            if (wrappedText.length() > 0) {
                // 检查加上新单词后的总长度
                if (fontMetrics.stringWidth(wrappedText.toString() + word) > maxWidth) {
                    // 如果超过最大宽度,将当前字符串添加到行列表,并开始新的一行
                    lines.add(wrappedText.toString());
                    wrappedText = new StringBuilder(word);
                } else {
                    // 如果不超过最大宽度,添加新单词
                    wrappedText.append(word);
                }
            } else {
                wrappedText.append(word);
            }
        }
        // 添加最后一行
        if (wrappedText.length() > 0) {
            lines.add(wrappedText.toString());
        }
        // 将行列表转换为数组
        return lines.toArray(new String[0]);
    }
}

OK了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342