< html>中的Calibri字体文本移到组件的底部 [英] Calibri Font when in <html> text moves to the bottom part of the component

查看:106
本文介绍了< html>中的Calibri字体文本移到组件的底部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

没有太多要解释的东西.只需查看下面的MCVE/图像:

There is not a lot to explain. Just see the MCVE/image below:

public class FontExample extends JFrame {
    private static final Font FONT = new Font("Calibri", Font.PLAIN, 14);

    public FontExample() {
        super("");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        JLabel withoutHtml = new JLabel("hello stackoverflow");
        withoutHtml.setFont(FONT);
        withoutHtml.setBorder(BorderFactory.createLineBorder(Color.red));
        add(withoutHtml);

        JLabel withHtml = new JLabel("<html><body style='vertical-align:top;'>hello stackoverflow");
        withHtml.setBorder(BorderFactory.createLineBorder(Color.green));
        withHtml.setFont(FONT);
        add(withHtml);

        setLocationByPlatform(true);
        pack();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            //Make sure Calibri font is installed
            if (!"Calibri".equals(FONT.getFamily())) {
                System.err.println("Font calibri is not installed.");
                System.exit(1);
            }
            new FontExample().setVisible(true);
        });
    }
}

绿色带有<html>标记. 是否有一种解决方法?通过修复,我的意思是使它像左边的一样,没有这个愚蠢的空间吗?

The green one is with the <html> tag. Is there a way to fix it? And by fix, I mean to make it like the left one, without this stupid space?

其他字体似乎都没有发生(我测试了2-3种以上).我使用的是Windows 8和Windows 10的Java 8.

It does not seem to happen with any other font (I tested 2-3 more). I am on Java 8 with Windows 7 and Windows 10.

我试图在底部添加填充:

I tried to add padding at bottom:

JLabel withHtml = new JLabel("<html><body style='padding-bottom:5px'>hello stackoverflow");

和预期的一样,这是

其中a)将拧紧同一容器中其他组件的对齐方式(对于UI而言是不利的),并且b)从5开始,我将不得不对很多值进行硬编码,因为它适合于字体大小14.但是对于其他字体大小,则需要另一个值.

which a) will screw the alignment of other components in the same container (bad for UI purposes) and b) I will have to hard code a lot of values since 5 since to be the proper for font size 14. But for other font size, it needs another value.

@Andrew Thomson在评论中说,所有JLabel都使用HTML格式.但是,如果它们位于另一个像JTextField这样的基于文本的组件的旁边,我会得到:

@Andrew Thomson in comments said to use the HTML format for all JLabels. But then, if they are next to another text-based component like a JTextField, I get this:

显然也很糟糕.

此外,我尝试从网络上的某个地方下载Calibri字体(以及诸如"Calibri Light"之类的变体)并按照

Also, I tried to download Calibri font (among with variations like "Calibri Light", etc) somewhere from the web and install it as described in this question. I do not know if that "Overrides" the existing one, but I had the same result.

推荐答案

一行文本由3部分组成:

A line of text consists of 3 parts:

  • The ascent
  • The descent
  • The leading

为了更清楚地看到,我使用的Calibri尺寸为50.没有HTML的标签是:

To see more clearly, I used Calibri with size 50. The label without HTML is:

在HTML模式下,情况有所不同. HTML渲染器将领先的 放在首位(出于某些原因):

In HTML mode, things are different. The HTML renderer puts the leading first (for some reason):

这会给您带来令人不快的结果.

This gives the unpleasant result you have observed.

现在您会问:为什么我只能在Calibri上看到这种效果?"实际上,所有字体都具有这种效果,但是它通常要小得多,所以您不会注意到它.

Now you will ask "But why do I see that effect only with Calibri?" In fact the effect exists with all fonts, but it's usually much smaller, so you don't notice it.

这是一个输出某些常见Windows字体度量的程序:

Here is a program that outputs the metrics for some common Windows fonts:

import java.awt.*;
import javax.swing.JLabel;

public class FontInfo
{
    static void info(String family, int size)
    {
        Font font = new Font(family, Font.PLAIN, size);
        if(!font.getFamily().equals(family))
            throw new RuntimeException("Font not available: "+family);
        FontMetrics fm = new JLabel().getFontMetrics(font);
        System.out.printf("%-16s %2d %2d %2d\n", family, fm.getAscent(), fm.getDescent(), fm.getLeading());
    }

    public static void main(String[] args)
    {
        String[] fonts = {"Arial", "Calibri", "Courier New", "Segoe UI", "Tahoma", "Times New Roman", "Verdana"};
        System.out.printf("%-16s %s\n", "", " A  D  L");
        for(String f : fonts)
            info(f, 50);
    }
}

对于尺寸50,结果为:

For size 50, the results are:


                  A  D  L
Arial            46 11  2
Calibri          38 13 11
Courier New      42 15  0
Segoe UI         54 13  0
Tahoma           50 11  0
Times New Roman  45 11  2
Verdana          51 11  0

如您所见,与其他字体相比,Calibri的领先优势很大.

As you can see, the leading for Calibri is huge compared to the other fonts.

对于尺寸14,结果为:

For size 14, the results are:


                  A  D  L
Arial            13  3  1
Calibri          11  4  3
Courier New      12  5  0
Segoe UI         16  4  0
Tahoma           14  3  0
Times New Roman  13  3  1
Verdana          15  3  0

Calibri的领先优势仍然是3像素.其他字体具有0或1,这意味着它们的效果是不可见的或很小的.

The leading for Calibri is still 3 pixels. Other fonts have 0 or 1, which means the effect for them is invisible or very small.

似乎无法更改HTML渲染器的行为.但是,如果目标是对齐相邻组件的基线,则有可能.您使用的FlowLayout具有

It doesn't seem possible to change the behavior of the HTML renderer. However, if the goal is to align the baselines of adjacent components, then it is possible. The FlowLayout you have used has an alignOnBaseline property. If you enable it, it does align the components correctly:

更新1

这是一个JFixedLabel类,无论包含HTML还是纯文本,它都给出相同的结果.在HTML模式下,它会根据前导值转换Graphics:

Here's a JFixedLabel class that gives the same result, whether it contains HTML or plain text. It translates the Graphics by the leading value when in HTML mode:

import java.awt.Graphics;
import javax.swing.JLabel;
import javax.swing.plaf.basic.BasicHTML;

public class JFixedLabel extends JLabel
{
    public JFixedLabel(String text)
    {
        super(text);
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        int dy;
        if(getClientProperty(BasicHTML.propertyKey)!=null)
            dy = getFontMetrics(getFont()).getLeading();
        else
            dy = 0;
        g.translate(0, -dy);
        super.paintComponent(g);
        g.translate(0, dy);
    }
}

结果:

更新2

先前的解决方案在图标方面存在问题,因此这是一个新的解决方案,可以同时处理文本和图标.在这里,我们不扩展JLabel,而是定义一个新的UI类:

The previous solution had an issue with icons, so here's a new one that handles both text and icons. Here we don't extend JLabel, instead we define a new UI class:

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.metal.MetalLabelUI;

public class FixedLabelUI extends MetalLabelUI
{
    @Override
    protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text, Icon icon,
        Rectangle viewR, Rectangle iconR, Rectangle textR)
    {
        String res = super.layoutCL(label, fontMetrics, text, icon, viewR, iconR, textR);
        if(label.getClientProperty(BasicHTML.propertyKey)!=null)
            textR.y -= fontMetrics.getLeading();
        return res;
    }
}

要将UI分配给标签,请执行以下操作:

To assign the UI to a label, do like this:

JLabel label = new JLabel();
label.setUI(new FixedLabelUI());

这篇关于&lt; html&gt;中的Calibri字体文本移到组件的底部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆