Нужен способ масштабировать шрифт так, чтобы он соответствовал прямоугольнику

StackOverflow https://stackoverflow.com/questions/876234

  •  22-08-2019
  •  | 
  •  

Вопрос

Я только что написал некоторый код для масштабирования шрифта так, чтобы он помещался в пределах (длины) прямоугольника.Он начинается с ширины 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;
}
Это было полезно?

Решение

полу-псевдокод:

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

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

я не уверен, почему вы передаете pFont, поскольку он не используется...

Другие советы

Вы могли бы использовать интерполяционный поиск:

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;
}

Измените все переменные ширины на float вместо int для лучшего результата.

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);
}
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;
}

Вы можете повысить эффективность, используя бинарный шаблон поиска - высокий / низкий с некоторой степенью детализации - 1, 0,5 или 0,25 балла.

Например, угадайте, что 18 - это слишком высоко?Перейти на 9, слишком низко?13,5, слишком низкий?15,75, слишком высоко?14!

Другим, очевидным способом было бы предварительно нарисовать текст на растровом изображении, а затем уменьшить растровое изображение, чтобы оно соответствовало размеру прямоугольника;но из-за разработанного вручную дизайна шрифта, подсказок и т.д. поиск правильного размера шрифта приводит к наиболее привлекательному (хотя, возможно, и не самому быстрому) результату.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top