16

長方形 (の長さ) に収まるようにフォントをスケーリングするコードをいくつか書きました。幅 18 から始まり、収まるまで繰り返します。

これは恐ろしく非効率に思えますが、ループしない方法が見つかりません。この行は、スケーリングするゲーム グリッド内のラベル用であるため、回避策がわかりません (四角形をラップ、カットオフ、および拡張することはすべて受け入れられません)。

それは実際にはかなり速いです。私はこれを何百もの長方形に対して行っていますが、タッチを遅くするのに十分な速さです。

誰もそれ以上のことを思いつかない場合は、テーブルから最初の推測をロードして (18 よりもはるかに近くなるように)、これを使用します。遅延を除いて、うまく機能します。

public Font scaleFont(String text, Rectangle rect, Graphics g, Font pFont) {
    float nextTry=18.0f;
    Font font=pFont;

    while(x > 4) {                             
            font=g.getFont().deriveFont(nextTry);
            FontMetrics fm=g.getFontMetrics(font);
            int width=fm.stringWidth(text);
            if(width <= rect.width)
                return font;
            nextTry*=.9;            
    }
    return font;
}
4

6 に答える 6

22

準擬似コード:

public Font scaleFont(
    String text, Rectangle rect, Graphics g, Font font) {
    float fontSize = 20.0f;

    font = g.getFont().deriveFont(fontSize);
    int width = g.getFontMetrics(font).stringWidth(text);
    fontSize = (rect.width / width ) * fontSize;
    return g.getFont().deriveFont(fontSize);
}

反復する派生:

/**
 * Adjusts the given {@link Font}/{@link String} size such that it fits
 * within the bounds of the given {@link Rectangle}.
 *
 * @param label    Contains the text and font to scale.
 * @param dst      The bounds for fitting the string.
 * @param graphics The context for rendering the string.
 * @return A new {@link Font} instance that is guaranteed to write the given
 * string within the bounds of the given {@link Rectangle}.
 */
public Font scaleFont(
    final JLabel label, final Rectangle dst, final Graphics graphics ) {
  assert label != null;
  assert dst != null;
  assert graphics != null;

  final var font = label.getFont();
  final var text = label.getText();

  final var frc = ((Graphics2D) graphics).getFontRenderContext();

  final var dstWidthPx = dst.getWidth();
  final var dstHeightPx = dst.getHeight();

  var minSizePt = 1f;
  var maxSizePt = 1000f;
  var scaledFont = font;
  float scaledPt = scaledFont.getSize();

  while( maxSizePt - minSizePt > 1f ) {
    scaledFont = scaledFont.deriveFont( scaledPt );

    final var layout = new TextLayout( text, scaledFont, frc );
    final var fontWidthPx = layout.getVisibleAdvance();

    final var metrics = scaledFont.getLineMetrics( text, frc );
    final var fontHeightPx = metrics.getHeight();

    if( (fontWidthPx > dstWidthPx) || (fontHeightPx > dstHeightPx) ) {
      maxSizePt = scaledPt;
    }
    else {
      minSizePt = scaledPt;
    }

    scaledPt = (minSizePt + maxSizePt) / 2;
  }

  return scaledFont.deriveFont( (float) Math.floor( scaledPt ) );
}

rラベルがコンポーネントの領域を完全に埋めるように、長方形の境界を持つコンポーネントにラベルを追加したいとします。次のように書くことができます。

final Font DEFAULT_FONT = new Font( "DejaVu Sans", BOLD, 12 );
final Color COLOUR_LABEL = new Color( 33, 33, 33 );

// TODO: Return a valid container component instance.
final var r = getComponent().getBounds();
final var graphics = getComponent().getGraphics();

final int width = (int) r.getWidth();
final int height = (int) r.getHeight();

final var label = new JLabel( text );
label.setFont( DEFAULT_FONT );
label.setSize( width, height );
label.setForeground( COLOUR_LABEL );

final var scaledFont = scaleFont( label, r, graphics );
label.setFont( scaledFont );
于 2009-05-18T04:41:44.213 に答える
4

より良い結果を得るには、すべての幅変数を int ではなく float に変更します。

public static Font scaleFontToFit(String text, int width, Graphics g, Font pFont)
{
    float fontSize = pFont.getSize();
    float fWidth = g.getFontMetrics(pFont).stringWidth(text);
    if(fWidth <= width)
        return pFont;
    fontSize = ((float)width / fWidth) * fontSize;
    return pFont.deriveFont(fontSize);
}
于 2011-05-06T23:22:06.913 に答える
3

補間検索を使用できます。

public static Font scaleFont(String text, Rectangle rect, Graphics g, Font pFont) {
    float min=0.1f;
    float max=72f;
    float size=18.0f;
    Font font=pFont;

    while(max - min <= 0.1) {
        font = g.getFont().deriveFont(size);
        FontMetrics fm = g.getFontMetrics(font);
        int width = fm.stringWidth(text);
        if (width == rect.width) {
            return font;
        } else {
            if (width < rect.width) {
                min = size;
            } else {
                max = size;
            }
            size = Math.min(max, Math.max(min, size * (float)rect.width / (float)width));
        }
    }
    return font;
}
于 2009-05-18T07:13:33.003 に答える
2
private Font scaleFont ( String text, Rectangle rect, Graphics gc )
{
    final float fMinimumFont = 0.1f;
    float fMaximumFont = 1000f;

    /* Use Point2d.Float to hold ( font, width of font in pixels ) pairs. */
    Point2D.Float lowerPoint = new Point2D.Float ( fMinimumFont, getWidthInPixelsOfString ( text, fMinimumFont, gc ) );
    Point2D.Float upperPoint = new Point2D.Float ( fMaximumFont, getWidthInPixelsOfString ( text, fMaximumFont, gc ) );
    Point2D.Float midPoint = new Point2D.Float ();

    for ( int i = 0; i < 50; i++ )
    {
        float middleFont = ( lowerPoint.x + upperPoint.x ) / 2;

        midPoint.setLocation ( middleFont, getWidthInPixelsOfString ( text, middleFont, gc ) );

        if ( midPoint.y >= rect.getWidth () * .95 && midPoint.y <= rect.getWidth () )
            break;
        else if ( midPoint.y < rect.getWidth () )
            lowerPoint.setLocation ( midPoint );
        else if ( midPoint.y > rect.getWidth () )
            upperPoint.setLocation ( midPoint );
    }

    fMaximumFont = midPoint.x;

    Font font = gc.getFont ().deriveFont ( fMaximumFont );

    /* Now use Point2d.Float to hold ( font, height of font in pixels ) pairs. */
    lowerPoint.setLocation ( fMinimumFont, getHeightInPixelsOfString ( text, fMinimumFont, gc ) );
    upperPoint.setLocation ( fMaximumFont, getHeightInPixelsOfString ( text, fMaximumFont, gc ) );

    if ( upperPoint.y < rect.getHeight () )
        return font;

    for ( int i = 0; i < 50; i++ )
    {
        float middleFont = ( lowerPoint.x + upperPoint.x ) / 2;

        midPoint.setLocation ( middleFont, getHeightInPixelsOfString ( text, middleFont, gc ) );

        if ( midPoint.y >= rect.getHeight () * .95 && midPoint.y <= rect.getHeight () )
            break;
        else if ( midPoint.y < rect.getHeight () )
            lowerPoint.setLocation ( midPoint );
        else if ( midPoint.y > rect.getHeight () )
            upperPoint.setLocation ( midPoint );
    }

    fMaximumFont = midPoint.x;

    font = gc.getFont ().deriveFont ( fMaximumFont );

    return font;
}


private float getWidthInPixelsOfString ( String str, float fontSize, Graphics gc )
{
    Font font = gc.getFont ().deriveFont ( fontSize );

    return getWidthInPixelsOfString ( str, font, gc );
}

private float getWidthInPixelsOfString ( String str, Font font, Graphics gc )
{
    FontMetrics fm = gc.getFontMetrics ( font );
    int nWidthInPixelsOfCurrentFont = fm.stringWidth ( str );

    return (float) nWidthInPixelsOfCurrentFont;
}


private float getHeightInPixelsOfString ( String string, float fontSize, Graphics gc )
{
    Font font = gc.getFont ().deriveFont ( fontSize );

    return getHeightInPixelsOfString ( string, font, gc );
}

private float getHeightInPixelsOfString ( String string, Font font, Graphics gc )
{
    FontMetrics metrics = gc.getFontMetrics ( font );
    int nHeightInPixelsOfCurrentFont = (int) metrics.getStringBounds ( string, gc ).getHeight () - metrics.getDescent () - metrics.getLeading ();

    return (float) nHeightInPixelsOfCurrentFont * .75f;
}
于 2013-06-05T21:32:17.060 に答える
1

バイナリ検索パターン (1、0.5、または 0.25 ポイントのいずれかの粒度で高/低) を使用して効率を向上させることができます。

たとえば、18 歳だと思いますが、高すぎますか? 9 に移動、あまりにも低いですか?13.5、低すぎる?15.75、高すぎる?14!

于 2009-05-18T04:30:23.033 に答える
0

別の明白な方法は、テキストをビットマップに事前に描画してから、ビットマップを縮小して四角形に合わせることです。しかし、手作りのフォント デザインやヒンティングなどにより、適切なフォント サイズを見つけることで、最も見栄えの良い (ただし、最速ではないかもしれません) 結果が得られます。

于 2009-05-18T04:30:19.227 に答える