【问题标题】:Convert text content to Image将文本内容转换为图像
【发布时间】:2013-09-18 23:47:45
【问题描述】:

是否有任何 Java 库允许将文本内容转换为图像文件?我只知道ImageMagick(在这种情况下为 JMagick),但我不想安装任何外部二进制文件(我的应用程序将作为 .war 文件部署在 Tomcat 服务器中,所以我不想要任何其他依赖项超过Java)。

例如,从字符串“Hello”,我想生成这个简单的图像:

【问题讨论】:

标签: java image text fonts awt


【解决方案1】:

Graphics 2D API 应该能够满足您的需求。它也有一些复杂的文本处理能力。

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class TextToGraphics {

    public static void main(String[] args) {
        String text = "Hello";

        /*
           Because font metrics is based on a graphics context, we need to create
           a small, temporary image so we can ascertain the width and height
           of the final image
         */
        BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();
        Font font = new Font("Arial", Font.PLAIN, 48);
        g2d.setFont(font);
        FontMetrics fm = g2d.getFontMetrics();
        int width = fm.stringWidth(text);
        int height = fm.getHeight();
        g2d.dispose();

        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        g2d = img.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        g2d.setFont(font);
        fm = g2d.getFontMetrics();
        g2d.setColor(Color.BLACK);
        g2d.drawString(text, 0, fm.getAscent());
        g2d.dispose();
        try {
            ImageIO.write(img, "png", new File("Text.png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

}

也可以查看Writing/Saving and Image

警告我用它生成了 90k PNG 图片,结果发现它们可以在 IE 中查看,但不能在 Chrome 版本 70.0.3538.77 中查看

上面的代码对我来说很好用(我将文本颜色更改为WHITE,所以我可以在 chrome 中看到它)

我在使用 Java 10.0.2 的 Mac OS Mojave 10.14 上使用 Chrome 70.0.3538.77。生成的图像为 4778x2411 像素...

更新...

在 IE 上是白底黑字,但在 Chrome 上是黑底白字。但是我将背景设置为白色。

所以你告诉我的是,一个透明的 PNG 在不同的浏览器上显示不同,因为浏览器使用不同的默认背景......你为什么对此感到惊讶?

最初的解决方案故意使用基于透明的图像。这一点通过在创建图像时使用BufferedImage.TYPE_INT_ARGB 很明显,它应用了基于 Alpha (A) 的RGB 颜色模型。

这是出乎意料的,因为有 g2d.setBackground(Color.white)。

不,实际上,这完全是意料之中的,只要您了解setBackground 的实际作用以及应该如何使用它即可

来自JavaDocs

设置 Graphics2D 上下文的背景颜色。背景 颜色用于清除区域。构造 Graphics2D 时 对于组件,背景颜色是从组件继承的。 在 Graphics2D 上下文中设置背景颜色只会影响 随后的 clearRect 调用,而不是 零件。要更改组件的背景,请使用适当的 组件的方法。

从事物的“声音”来看,您需要一个不透明的图像,并带有填充的背景颜色。所以,再一次,它转到JavaDocs,稍微阅读一下就会把你带到BufferedImage.TYPE_INT_RGB,它删除了Alpha通道,但你仍然需要填充图像的背景。

为此,我会使用Graphics2D#setColorGraphics2D#fillRect,只是因为它有效。

所以,你最终会得到上面的修改版本,它可能看起来像......

img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
g2d = img.createGraphics();
//...
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
try {
    ImageIO.write(img, "png", new File("Text.png"));
} catch (IOException ex) {
    ex.printStackTrace();
}

如果我更改为“jpg”,我会在 IE 和 Chrome 上看到黑色背景上的橙色/粉红色文本

这与ImageIO 中一个众所周知的常见问题/错误有关,它试图将透明颜色模型的 Alpha 通道应用于不支持 Alpha 通道的 JPG。

详情请见Issue using ImageIO.write jpg file: pink background

但基本的解决方案是要么使用支持 alpha 通道的 PNG,要么使用不透明的图像。

所以,这一切的长短是。问题不在于原始答案,也不在于ImageIOBufferedImageGraphics、AWT 库、Chrome 或 IE,而是因为您对这些 API(和示例)的工作原理缺乏了解。

【讨论】:

  • 感谢示例和最终链接资源,非常感谢
  • 关于如何处理多行字符串以适应固定区域的任何线索?
  • @Patriotic 这是 ImageIO 和基于 alpha 的图像的已知问题,改为使用 TYPE_INT_RGB(删除 alpha 支持)
  • @ilw 您需要知道文本的大小 - 对于 exampleexample
【解决方案2】:

不使用任何外部库,请执行以下操作:

  1. 以像素为单位测量文本大小(请参阅Measuring Text
  2. 为文本创建一个大小合适的 java.awt.image.BufferedImage
  3. 使用 createGraphics() 方法获取 BufferedImage 的图形对象
  4. 绘制文字
  5. 使用 javax ImageIO 类保存图像

编辑 - 修复链接

【讨论】:

    【解决方案3】:

    考虑以下 sn-p:

    public static final HashMap<RenderingHints.Key, Object> RenderingProperties = new HashMap<>();
    
    static{
        RenderingProperties.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        RenderingProperties.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        RenderingProperties.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
    }
    
    public static BufferedImage textToImage(String Text, Font f, float Size){
        //Derives font to new specified size, can be removed if not necessary.
        f = f.deriveFont(Size);
    
        FontRenderContext frc = new FontRenderContext(null, true, true);
    
        //Calculate size of buffered image.
        LineMetrics lm = f.getLineMetrics(Text, frc);
    
        Rectangle2D r2d = f.getStringBounds(Text, frc);
    
        BufferedImage img = new BufferedImage((int)Math.ceil(r2d.getWidth()), (int)Math.ceil(r2d.getHeight()), BufferedImage.TYPE_INT_ARGB);
    
        Graphics2D g2d = img.createGraphics();
    
        g2d.setRenderingHints(RenderingProperties);
    
        g2d.setBackground(Color.WHITE);
        g2d.setColor(Color.BLACK);
    
        g2d.clearRect(0, 0, img.getWidth(), img.getHeight());
    
        g2d.setFont(f);
    
        g2d.drawString(Text, 0, lm.getAscent());
    
        g2d.dispose();
    
        return img;
    }
    

    仅使用 java Graphics API 根据渲染到缓冲图像上的字体创建图像。

    【讨论】:

      【解决方案4】:

      这是一个将图形内容写入png 格式的简单程序。

      import java.awt.Graphics;
      import java.awt.Image;
      import java.awt.image.BufferedImage;
      
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import java.io.File;
      import javax.imageio.ImageIO;
      
      class ImageWriteEx extends JPanel{
      
          public void paint(Graphics g){
      
              Image img = createImageWithText();
              g.drawImage(img, 20, 20, this);
      
          }
      
          private static BufferedImage createImageWithText(){ 
      
              BufferedImage bufferedImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
              Graphics g = bufferedImage.getGraphics();
      
              g.drawString("www.stackoverflow.com", 20, 20);
              g.drawString("www.google.com", 20, 40);
              g.drawString("www.facebook.com", 20, 60);
              g.drawString("www.youtube.com", 20, 80);
              g.drawString("www.oracle.com", 20, 1000);
      
              return bufferedImage;
      
          }
      
          public static void main(String[] args){
      
              try{
                  BufferedImage bi = createImageWithText();
                  File outputfile = new File("save.png");
                  ImageIO.write(bi, "png", outputfile);
              } catch(Exception e){
                  e.printStackTrace();
              }
      
              JFrame frame = new JFrame();
              frame.getContentPane().add(new ImageWriteEx());
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setSize(300,300);
              frame.setVisible(true);
      
          }
      
      }
      

      【讨论】:

      • 什么是ImageWrite
      • 应该是ImageWriteEx(上面定义的类)。我修好了。
      【解决方案5】:

      如果有人想要多行的 TextImages。我做了一些并用

      展示了它们
      new ImageIcon(*here the image*)
      

      在 JOptionPane 中(不添加文本)。这很好地填充了整个 JOptionPane。代码如下:

      import java.awt.Color;
      import java.awt.Font;
      import java.awt.FontMetrics;
      import java.awt.Graphics2D;
      import java.awt.RenderingHints;
      import java.awt.image.BufferedImage;
      
      public class TextImage
      {
         public static BufferedImage make(String...textrows)
         {
            BufferedImage helperImg = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = helperImg.createGraphics();
            Font font = *here some font*;
            g2d.setFont(font);
            FontMetrics fm = g2d.getFontMetrics();
            String longestText = "";
            for(String row: textrows)
            {
               if(row.length()>longestText.length())
               {
                  longestText = row;
               }
            }
            int width = fm.stringWidth(longestText);
            int height = fm.getHeight()*textrows.length;
            g2d.dispose();
      
      
            BufferedImage finalImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            g2d = finalImg.createGraphics();
            g2d.setColor(*here some Color*);
            g2d.fillRect(0, 0, width, height);
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            g2d.setFont(font);
            fm = g2d.getFontMetrics();
            g2d.setColor(Color.BLACK);
            int y = fm.getAscent();
            for(String row: textrows)
            {
               g2d.drawString(row, 0, y);
               y += fm.getHeight();
            }
            g2d.dispose();
            return finalImg;
         }
      }
      

      【讨论】:

        【解决方案6】:

        demo# 用于多行文本#

        将文件作为参数传递给程序

        import javax.imageio.ImageIO;
        import java.awt.*;
        import java.awt.image.BufferedImage;
        import java.io.File;
        import java.io.FileNotFoundException;
        import java.io.IOException;
        import java.text.DateFormat;
        import java.text.SimpleDateFormat;
        import java.util.Calendar;
        import java.io.BufferedReader;
        import java.io.File;
        import java.io.FileReader;
        
        public class TextToGraphics {
            
            public static void main(String[] args){
                System.out.println(args[0]);
                File file = new File(args[0]);
                try{
                BufferedReader br = new BufferedReader(new FileReader(file));
                StringBuilder sb = new StringBuilder();
                String line;
                while((line = br.readLine())!=null){
        
                    sb.append(line).append("\n");
                }
                convert(sb.toString(),args[0]+"_img");
                System.out.println("Done.");
            }
            catch(FileNotFoundException e){
                e.printStackTrace();
            }
        
            }
        
            public static void convert(String text, String img_name) {
                String[] text_array = text.split("[\n]");
                BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = img.createGraphics();
                Font font = new Font("Consolas", Font.BOLD, 12);
                g2d.setFont(font);
                FontMetrics fm = g2d.getFontMetrics();
                int width = fm.stringWidth(getLongestLine(text_array));
                int lines = getLineCount(text);
                int height = fm.getHeight() * (lines + 4);
                g2d.dispose();
                img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                g2d = img.createGraphics();
                g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                g2d.setFont(font);
                fm = g2d.getFontMetrics();
                g2d.setColor(Color.BLACK);
        
                for (int i = 1; i <= lines; ++i) {
                    g2d.drawString(text_array[i - 1], 0, fm.getAscent() * i);
                }
                g2d.dispose();
                try {
                    String img_path = System.getProperty("user.dir") + "/" + img_name + ".png";
                    ImageIO.write(img, "png", new File(img_path));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        
            public static int getLineCount(String text) {
                return text.split("[\n]").length;
            }
        
            private static String getLongestLine(String[] arr) {
                String max = arr[0];
                for (int i = 1; i < arr.length; i++) {
                    if (max.length() < arr[i].length()) {
                        max = arr[i];
                    }
                }
                return max;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-19
          • 1970-01-01
          • 2010-11-29
          • 1970-01-01
          相关资源
          最近更新 更多