图片中的字体值 returns 意外值 ("Dialog")

Font face value returns unexpected value ("Dialog") from image

此代码段创建包含文本的图像。我将字体设置为Serif。但是,当我稍后查询结果图像的字体时,它 returns Dialog。我不明白这是为什么。

BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(img, 0, 0, 200, 100, this); // "this" refers to my custom JPanel which I am setting in the JFrame. 
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
System.out.println("from g2d object: " + g2d.getFont().getFamily()); // outputs "Serif"
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
g2d.dispose();
System.out.println("from image: " + img.getGraphics().getFont().getFamily()); // outputs "Dialog" (expected "Serif")

我理解 Dialog 是 Java 中的逻辑字体之一,但如果字体设置为其他字体,并且 Font.getFontName() returns 给定字体的 font face,为什么它不返回 Graphics2D 对象中设置的 Serif

UPDATE:在最后一个 System.out.println() 之前或之后调用 g2d.dispose() 没有区别。这两种方式,它仍然打印出 Dialog.

BufferedImage.getGraphics() returns BufferedImage.createGraphics() 的结果。而且,沿着这条路,我们最终得到了 SunGraphicsEnvironment:

的那个方法
   /**
     * Returns a Graphics2D object for rendering into the
     * given BufferedImage.
     * @throws NullPointerException if BufferedImage argument is null
     */
    public Graphics2D createGraphics(BufferedImage img) {
        if (img == null) {
            throw new NullPointerException("BufferedImage cannot be null");
        }
        SurfaceData sd = SurfaceData.getPrimarySurfaceData(img);
        return new SunGraphics2D(sd, Color.white, Color.black, defaultFont);
    }

我们可以清楚地看到它使用defaultFont,几乎连接图像(除非getPrimarySurface()确实改变了defaultFont -我猜不是 [我找不到它被更改])。

可以找到源代码here

设置 Graphics 的字体不会改变 BufferedImage 中的任何内容。 FontGraphics 使用 将文本绘制 到图像上。如果从图像中获得新的 Graphics(使用 getGraphics()createGraphics()),它将具有 GraphicsEnvironment.[=27 中定义的 defaultFont =]

这样做:

BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(img, 0, 0, 200, 100, null); // "this" refers to my custom JPanel which I am setting in the JFrame. 
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
System.out.println("from g2d object: " + g2d.getFont().getFamily()); // outputs "Serif"
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
System.out.println(g2d.getFont().toString()+"-"+img.getGraphics().getFont().toString()+" from image: " + img.getGraphics().getFont().getFamily());
g2d.dispose();
System.out.println("from image: " + img.getGraphics().getFont().getFamily());

如您所见,g2d 保留了其深受喜爱的 Serif 符号。图像本身就是一个东西。每次对图像调用 createGraphics 时,您都会得到一个新东西。

TL:DR 该字体无法在 GraphicsGraphics2D.

中访问

无论是直接调用 BufferedImage#createGraphics() 还是 BufferedImage#getGraphics() 最终结果都是一样的:它们都创建了一个新的图形对象。更具体地说,前者 returns 一个新的 Graphics 对象,后者 returns 一个 Graphics2D 对象。两者都是返回的具体类型的抽象,它是 SunGraphics2D 的一个实例。遗憾的是,此对象不存储图形对象中与字体相关的任何信息集。所以基本上,我无法从图像对象中获取我需要的数据。

相比之下,ProxyGraphics2DPeekGraphics - 抽象的两个子类型 class Graphics2D class 确实将此类信息存储在 Graphics2D 名为 mGraphics 的全局变量。该对象是通过 getDelegate() 方法访问的,显然 不是 GraphicsGraphics2D API 的 部分。不幸的是,这两个子classes 都是sun 包的一部分,不再可以访问。检查这些 classes 清楚地表明像 setFont() 这样的方法确实将字体保存在此委托 (Graphics2D) 对象中。

每个人都让事情变得比需要的更复杂。答案很简单:Image 的 getGraphics() 方法总是创建一个全新的 Graphics 对象。来自 the documentation:

Creates a graphics context for drawing to an off-screen image.

仅此而已。您在一个 Graphics 对象中设置字体,然后创建另一个 Graphics 对象。当然,新的 Graphics 对象对第一个 Graphics 对象的状态一无所知。这就像期望打开一辆车的前灯会以某种方式导致所有汽车都打开前灯。

只需调用 getGraphics() 一次即可获得预期结果。

System.out.println("from image: " + g2d.getFont().getFamily());