2009-05-18 10 views
10

Je viens d'écrire du code pour redimensionner une police afin qu'elle corresponde à (la longueur) d'un rectangle. Il commence à 18 largeurs et se rétrécit jusqu'à ce qu'il s'adapte.J'ai besoin d'un moyen de mettre à l'échelle une police pour l'adapter à un rectangle

Cela semble terriblement inefficace, mais je ne trouve pas de moyen de le faire en boucle. Cette ligne est pour les étiquettes dans une grille de jeu qui évolue, donc je ne peux pas voir une solution de contournement (emballer, couper et dépasser le rectangle sont tous inacceptables).

En fait, c'est assez rapide, je le fais pour des centaines de rectangles et c'est assez rapide pour le ralentir au toucher.

Si personne ne trouve quelque chose de mieux, je vais juste charger le devin de départ à partir d'une table (de sorte qu'il est beaucoup plus proche que 18) et l'utiliser - sauf pour le décalage ça marche très bien.

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

Huh. Il s'avère que Netbeans n'aime pas trop utiliser 'try' comme identifiant ... – Tharwen

Répondre

16

Code semi-psuedo:

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

Je ne sais pas pourquoi vous passez pFont comme il n'a pas l'habitude ...

+0

Bonne réponse. Si je comprends votre code, cela me rappelle une approximation de Newton-Raphson. Peut-être faire deux fois au lieu d'une fois, pour être plus précis, car il peut y avoir une erreur d'arrondi. – ChrisW

+0

Bonne prise avec le pFont - jeté cela à la dernière seconde après avoir creusé ce code d'une autre méthode et n'y a pas beaucoup réfléchi. Cela semble tentant! Je ne suis pas tout à fait sûr d'obtenir la bonne taille (pas sûr que les tailles de police augmentent linéairement) mais si rien d'autre, il fera une première conjecture IMPRESSIONNANTE. –

+0

Ouais, j'ai pensé au fait que ça pourrait ne pas évoluer comme ça. Je me suis dit que si vous vouliez avoir la taille d'un rectangle sans qu'il soit dans une boîte englobante, alors ce serait bien. Sinon, vos tests montreront si c'est assez bon et vous pourriez alors ajouter une boucle d'itération courte pour le réparer. –

0

Une autre, de façon évidente serait d'avoir le texte pré-dessiné sur un bitmap, puis réduire le bitmap pour adapter le rectangle; mais, en raison de la conception de police et de l'allusion à la main, etc., trouver la bonne taille de police produit le meilleur résultat (mais peut-être pas le plus rapide).

1

Vous pouvez améliorer l'efficacité en utilisant un modèle de recherche binaire - haut/bas avec une certaine granularité - soit 1, 0,5 ou 0,25 points. Par exemple, devinez 18, trop élevé?

Passer à 9, trop bas? 13,5, trop bas? 15h55, trop haut? 14!

3

Vous pouvez utiliser la recherche d'interpolation:

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

Changer toutes les variables de largeur pour flotter au lieu de int pour un meilleur résultat.

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