2009-05-18 20 views
10

Ho appena scritto del codice per ridimensionare un font per adattarlo alla lunghezza di un rettangolo. Inizia a 18 larghezze e scorre fino a quando non si adatta.Hai bisogno di un modo per ridimensionare un font per adattarlo a un rettangolo

Questo sembra orribilmente inefficiente, ma non riesco a trovare un modo non ciclico per farlo. Questa riga è per le etichette in una griglia di gioco che si ridimensiona, quindi non riesco a vedere una soluzione di aggiramento (avvolgere, tagliare e estendere oltre il rettangolo sono inaccettabili).

In realtà è piuttosto veloce, lo sto facendo per centinaia di rettangoli ed è abbastanza veloce da rallentare un tocco.

Se nessuno riesce a fare qualcosa di meglio, mi basterà caricare l'ipotesi iniziale da un tavolo (in modo che sia molto più vicino di 18) e usarlo - tranne che per il ritardo che funziona.

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. Si scopre che Netbeans non ama troppo usare 'try' come identificatore ... – Tharwen

risposta

16

codice di semi-pseudo:

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

io non sono sicuro perché si passa pFont come non è usato ...

+0

Buona risposta. Se capisco il tuo codice, mi ricorda un'approssimazione di Newton-Raphson. Forse fatelo due volte invece di una volta, per essere più precisi, poiché potrebbe esserci qualche errore di arrotondamento. – ChrisW

+0

Una buona presa con il pFont - lo ha lanciato all'ultimo secondo dopo aver scovato questo codice da un altro metodo e non ci ha pensato molto. Questo sembra allettante! Non sono del tutto sicuro di ottenere la giusta dimensione (non sono sicuro che le dimensioni dei caratteri crescano in modo lineare), ma se non altro farà un'impressione IMPRESSIONANTE. –

+0

Sì, ho pensato al fatto che potrebbe non essere in scala in quel modo. Ho pensato che se avessi voluto che fosse della dimensione di un rettangolo senza che fosse all'interno di un riquadro di delimitazione, allora andrebbe bene. In caso contrario, i test mostreranno se è abbastanza buono e potresti quindi aggiungere un ciclo di iterazione breve per risolverlo. –

0

Un modo ovvio diverso sarebbe di avere il testo pre disegnato su una bitmap, e quindi ridurre la bitmap per adattarsi al rettangolo; ma, a causa del design dei font fatti a mano e dei suggerimenti, ecc., trovare la giusta dimensione del font produce il risultato più bello (anche se forse non il più veloce).

1

È possibile migliorare l'efficienza utilizzando un modello di ricerca binario - alto/basso con una certa granularità - 1, 0,5 o 0,25 punti.

Ad esempio, indovinare a 18, troppo alto? Sposta a 9, troppo basso? 13,5, troppo basso? 15,75, troppo alto? 14!

3

È possibile utilizzare la ricerca per interpolazione:

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

Cambia tutte le variabili di larghezza in float invece di int per ottenere un risultato migliore.

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