文字列の高さを取得するために使用FontMetrics.getHeight()
していますが、間違った値が表示され、文字列文字の子孫が切り捨てられます。私が使用できるより良い機能はありますか?
5 に答える
以下の方法は、現在のフォントの にgetStringBounds()
基づいており、テキストの 1 行の文字列に対して非常にうまく機能します。GlyphVector
Graphics2D
public class StringBoundsPanel extends JPanel
{
public StringBoundsPanel()
{
setBackground(Color.white);
setPreferredSize(new Dimension(400, 247));
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// must be called before getStringBounds()
g2.setFont(getDesiredFont());
String str = "My Text";
float x = 140, y = 128;
Rectangle bounds = getStringBounds(g2, str, x, y);
g2.setColor(Color.red);
g2.drawString(str, x, y);
g2.setColor(Color.blue);
g2.draw(bounds);
g2.dispose();
}
private Rectangle getStringBounds(Graphics2D g2, String str,
float x, float y)
{
FontRenderContext frc = g2.getFontRenderContext();
GlyphVector gv = g2.getFont().createGlyphVector(frc, str);
return gv.getPixelBounds(null, x, y);
}
private Font getDesiredFont()
{
return new Font(Font.SANS_SERIF, Font.BOLD, 28);
}
private void startUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception
{
final StringBoundsPanel tb = new StringBoundsPanel();
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
tb.startUI();
}
});
}
}
わかりやすくするために、インポートを省略していることに注意してください。
結果:
間違った値を返すと思う理由は何ですか?返されるものに対する期待が仕様と一致しない可能性がはるかに高くなります。フォント内の一部のグリフがこれらの値を上回ったり下回ったりしても、まったく問題がないことに注意してください。
getMaxDescent()
getMaxAscent()
フォント内のグリフのこれらのフィールドの絶対最大値を教えてくれるはずです。
特定の文字列のメトリックを知りたい場合は、必ず。を呼び出しますgetLineMetrics()
。
FontMetrics.getAscent()とFontMetrics.getDescent()がうまくいくかもしれません。
フォントの特定の範囲 (たとえば、すべての下位文字、またはすべての数字) のピクセル単位の高さを測定する必要があったため、最近、以下のコードを作成しました。
より高速なコードが必要な場合 (私のコードには for ループがあります)、起動時に一度実行して配列内のすべての値 (たとえば 1 から 100 まで) を取得し、代わりに配列を使用することをお勧めします。
このコードは基本的に、入力文字列から 250x250 のビットマップに重ねられた同じ場所にすべての文字を描画し (必要に応じて増減します)、上からピクセルを探し始め、次に下からピクセルを探し始め、見つかった最大の高さを返します。文字範囲用に設計されている場合でも、通常の文字列で機能します。これは、一部の文字が繰り返されるため、通常の文字列を評価するときに一種の冗長性があることを意味します。したがって、入力文字列がアルファベットの数 (26) を超える場合は、'tRange' 入力として使用します: "abcd...z" および使用される可能性のあるその他の文字。より高速です。
それが役立つことを願っています。
public int getFontPixelHeight(float inSize, Paint sourcePaint, String tRange)
{
// It is assumed that the font is already set in the sourcePaint
int bW = 250, bH = 250; // bitmap's width and height
int firstContact = -1, lastContact = -2; // Used when scanning the pixel rows. Initial values are set so that if no pixels found, the returned result is zero.
int tX = (int)(bW - inSize)/2, tY = (int)(bH - inSize)/2; // Used for a rough centering of the displayed characters
int tSum = 0;
// Preserve the original paint attributes
float oldSize = sourcePaint.getTextSize();
int oldColor = sourcePaint.getColor();
// Set the size/color
sourcePaint.setTextSize(inSize); sourcePaint.setColor(Color.WHITE);
// Create the temporary bitmap/canvas
Bitmap.Config bConf = Bitmap.Config.ARGB_8888;
Bitmap hld = Bitmap.createBitmap(250, 250, bConf);
Canvas canv = new Canvas(hld);
for (int i = 0; i < bH; i++)
{
for (int j = 0; j < bW; j++)
{
hld.setPixel(j, i, 0); // Zero all pixel values. This might seem redundant, but I am not quite sure that creating a blank bitmap means the pixel color value is indeed zero, and I need them to be zero so the addition performed below is correct.
}
}
// Display all characters overlapping at the same position
for (int i = 0; i < tRange.length(); i++)
{
canv.drawText("" + tRange.charAt(i), tX, tY, sourcePaint);
}
for (int i = 0; i < bH; i++)
{
for (int j = 0; j < bW; j++)
{
tSum = tSum + hld.getPixel(j, i);
}
if (tSum > 0) // If we found at least a pixel, save row index and exit loop
{
firstContact = i;
tSum = 0; // Reset
break;
}
}
for (int i = bH - 1; i > 0 ; i--)
{
for (int j = 0; j < bW; j++)
{
tSum = tSum + hld.getPixel(j, i);
}
if (tSum > 0) // If we found at least a pixel, save row index and exit loop
{
lastContact = i;
break;
}
}
// Restore the initial attributes, just in case the paint was passed byRef somehow
sourcePaint.setTextSize(oldSize);
sourcePaint.setColor(oldColor);
return lastContact - firstContact + 1;
}
getHeight()
文字列のディセンダーを切り取ることはできません。文字列を描画するだけでそれを行うことができます。から返された高さを使用してgetHeight
何らかの方法で文字列を描画していますが、高さを誤用している可能性があります。たとえば、文字列の開始点を getHeight() の高さのボックスの下部に配置すると、テキストのベースラインがボックスの下端に配置され、ディセンダーがクリップされる可能性が非常に高くなります。
テキストのジオメトリは複雑なトピックであり、奇妙な歴史的遺物が吹き込まれています。他の人が示唆しているように、 と を使用getAscent
しgetDescent
て、ベースラインをボックス内に適切に配置してください。