关于java:使用ttf字体使文本宽度灵活

Making text width flexible with ttf font

我在询问两件事。首先,我有一个 ttf 文件,它位于我的 macair 驱动器中。我不想将该文件添加到我的项目结构中。如何从中导入 True_type 字体。我尝试了各种方法将它导入我的 Java 程序。例如公共类TextFixer {
私有静态字符串 [] 名称 = { "iksswgrg.ttf" }; //这存在于我的 macair 驱动器上,我想从中创建字体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  private static Map<String, Font> cache = new ConcurrentHashMap<String, Font>(names.length);          
  static {                                                                                              
    for (String name : names) {                                                                        
      cache.put(name, getFont(name));                                                                  
    }                                                                                                  
  }                                                                                                    
  public static Font getFont(String name) {                                                            
    Font font = null;                                                                                  
    if (cache != null) {                                                                                
      if ((font = cache.get(name)) != null) {                                                          
        return font;                                                                                    
      }                                                                                                
    }                                                                                                  
    String fName ="/fonts/" + name;                                                                    
    try {                                                                                              
      InputStream is = TextFixer.class.getResourceAsStream(fName);                                      
      font = new Font("ttf", 0, 16);                                                                    
      //font = Font.createFont(Font.TRUETYPE_FONT, is);                                                
    } catch (Exception ex) {                                                                            
      ex.printStackTrace();                                                                            
      System.err.println(fName +" not loaded.  Using serif font.");                                    
      font = new Font("serif", Font.PLAIN, 24);                                                        
    }                                                                                                  
    return font;                                                                                        
  }

第二部分是我想使用图形创建一个字符串。首先我需要有 130 毫米的宽度。显示框的高度将是提供的字符串中最高的字符。字体大小在 8 到 16 之间。我有一个企业项目,负责处理 ttf 的高度和大小。我面临的问题是:我不想使用 swing/javafx 库。我想使用 Java 的 Graphics 库,使用 Graphics2D 有一个矩形。如何将其宽度设置为精确 130 毫米?然后我想根据字体使宽度变得灵活。我想画一个字符串,并且字符串应该在提供的宽度上得到调整/灵活。我可以通过 g.drawString() 绘制一个字符串,但我无法在控制台上看到它。因为我不想使用 Jframe 或任何 Swing/javaFX 库。
我知道这似乎有点长,但我希望我已经解释得足够好。我迫切需要帮助。请让我知道你们是否可以在这里帮助我。
提前感谢


First I am having an ttf file and it is located in my macair drive. I do not want to add that file into my project structure. How can i import the True_type font from it

这更多是与"如何引用文件系统上的文件"和"如何加载字体"有关的问题,因为如果你能解决第一个问题,你就可以解决第二。

1
File fontFile = new File("some/relative/path/to/your/Font.tff");

1
File fontFile = new File("/some/absolute/path/to/your/Font.tff");

就我个人而言,我都不喜欢,因为它会造成太多麻烦(工作目录、其他系统等),我更喜欢尽可能使用嵌入式资源或将文件放在公共位置。

例如,Windows 或 Mac 上的 {user.home}/AppData/Local/{application name},您可以使用 {user.home}/Library/Application Support/{application name},那么从

执行程序的位置无关紧要

加载字体比较简单。对于我的示例,我将字体文件放在程序的工作目录中

1
2
System.out.println(new File("Pacifico.ttf").exists());
Font font = Font.createFont(Font.TRUETYPE_FONT, new File("Pacifico.ttf"));

2nd part is I want to create a String by using Graphics. First I need to have width that is of 130mm. The height of the displayed box will be the tallest character in the provided string

这要复杂得多,因为图像以像素为单位。为了知道给定距离有多少像素,我们需要知道图像的 DPI。

its 72DPI

好,那么,我们就可以计算出我们需要的像素数了

1
2
3
public static double cmToPixel(double cm, double dpi) {
    return (dpi / 2.54) * cm;
}

130 毫米(13 厘米)出来是 368.503937007874 @ 72dpi。

据此,我们需要找到给定文本的字体点大小以适合此范围。

现在,有很多你可以做到这一点,你可以简单地从点 1 开始并执行线性进展,直到你通过你所追求的范围。它不是很快,而且随着大小的增加,它可能会变得有点容易出错。

我选择了分而治之的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected static int widthOfText(String text, Font font, float fontSize, Graphics2D g2d) {
    font = font.deriveFont(fontSize);
    FontMetrics fm = g2d.getFontMetrics(font);
    int textWidth = fm.stringWidth(text);
    return textWidth;
}

public static Float pointToFit(double width, String text, Font font, Graphics2D g2d, float min, float max) {
    float fontSize = min + ((max - min) / 2f);
    font = font.deriveFont(fontSize);
    FontMetrics fm = g2d.getFontMetrics(font);
    int textWidth = fm.stringWidth(text);

    if (fontSize == min || fontSize == max) {
        return fontSize;
    }

    if (textWidth < width) {
        return pointToFit(width, text, font, g2d, fontSize, max);
    } else if (textWidth > width) {
        return pointToFit(width, text, font, g2d, min, fontSize);
    }
    return fontSize;
}

需要注意的是,它并不完美,但它比线性进展更好:P

有了这个,我们就可以开始计算所需的属性了……

1
2
3
4
5
6
7
8
9
10
11
String text ="Happy, Happy, Joy, Joy";
double width = cmToPixel(13.0, 72.0);

BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
float fontSize = pointToFit(width, text, font, g2d, 0, (float)width);

font = font.deriveFont(fontSize);
FontMetrics fm = g2d.getFontMetrics(font);
int height = fm.getHeight();
g2d.dispose();

好的,这将创建一个小的 (1x1) 临时图像。我们需要 Graphics 上下文来计算所有其他属性。然后它计算字体点大小,然后可以计算文本高度

有了所有这些信息,我们就可以开始实际渲染文本了...

1
2
3
4
5
6
7
img = new BufferedImage((int) Math.round(width), height, BufferedImage.TYPE_INT_ARGB);
g2d = img.createGraphics();
g2d.setFont(font);
fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();

最终会输出这样的东西...

Happy,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

public class Test {

    public static void main(String[] args) {
        try {
            System.out.println(new File("Pacifico.ttf").exists());
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            Font font = Font.createFont(Font.TRUETYPE_FONT, new File("Pacifico.ttf"));

            String text ="Happy, Happy, Joy, Joy";
            double width = cmToPixel(13.0, 72.0);
            BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = img.createGraphics();
            float fontSize = pointToFit(width, text, font, g2d, 0, (float) width);
            System.out.println(width);
            System.out.println(fontSize);
            font = font.deriveFont(fontSize);
            FontMetrics fm = g2d.getFontMetrics(font);
            int height = fm.getHeight();
            g2d.dispose();

            img = new BufferedImage((int) Math.round(width), height, BufferedImage.TYPE_INT_ARGB);
            g2d = img.createGraphics();
            g2d.setColor(Color.RED);
            g2d.drawRect(0, 0, img.getWidth() - 1, img.getHeight() - 1);
            g2d.setFont(font);
            fm = g2d.getFontMetrics();
            g2d.setColor(Color.BLACK);
            g2d.drawString(text, 0, fm.getAscent());
            g2d.dispose();

            JOptionPane.showConfirmDialog(null, new ImageIcon(img));
        } catch (IOException | FontFormatException e) {
            //Handle exception
        }
    }

    public static Float pointToFit(double width, String text, Font font, Graphics2D g2d) {
        return pointToFit(width, text, font, g2d, 0f, Float.MAX_VALUE);
    }

    protected static int widthOfText(String text, Font font, float fontSize, Graphics2D g2d) {
        font = font.deriveFont(fontSize);
        FontMetrics fm = g2d.getFontMetrics(font);
        int textWidth = fm.stringWidth(text);
        return textWidth;
    }

    public static Float pointToFit(double width, String text, Font font, Graphics2D g2d, float min, float max) {
        float fontSize = min + ((max - min) / 2f);
        NumberFormat nf = NumberFormat.getInstance();
        font = font.deriveFont(fontSize);
        FontMetrics fm = g2d.getFontMetrics(font);
        int textWidth = fm.stringWidth(text);

        if (fontSize == min || fontSize == max) {
            return fontSize;
        }

        if (textWidth < width) {
            return pointToFit(width, text, font, g2d, fontSize, max);
        } else if (textWidth > width) {
            return pointToFit(width, text, font, g2d, min, fontSize);
        }
        return fontSize;
    }

    public static double cmToPixel(double cm, double dpi) {
        return (dpi / 2.54) * cm;
    }

}