2008-12-15 9 views

Répondre

19

La méthode getStringBounds() ci-dessous est basée sur la GlyphVector pour la police Graphics2D actuelle, qui fonctionne très bien pour une chaîne de ligne de texte:

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

Notez que j'ai omis les importations pour plus de clarté.

Le résultat:

Result screenshot.

+1

@nierito Bonne réponse et bonne démo! Sur la base de ce que vous avez dit, j'ai essayé de raccourcir votre code (en supposant que l'on ait défini une «police» et obtenu le 'FontMetrics'). Cela fonctionne plutôt bien. 'police.createGlyphVector (fontMetrics.getFontRenderContext(), texte) .getVisualBounds(). getHeight(); ' –

+0

Comment l'utilisation de GlyphVector.getPixelBounds se compare-t-elle à Font.getStringBounds et TextLayout.getBounds? Y a-t-il un gros avantage au code plus complexe? Dans un test simple, j'ai trouvé que la méthode Font sur-reporté la hauteur par rapport aux deux autres. J'aime ça et le TextLayout ne nécessite pas de point x et y. – jla

+0

c'est 8 ans, mais quand j'utilise g2d.scale (x, x); il ne peut pas s'afficher correctement! – yelliver

11

Qu'est-ce qui vous fait penser qu'il renvoie une valeur erronée? Il est beaucoup plus probable que votre attente de ce qu'elle renvoie ne correspond pas à la spécification. Notez que c'est parfaitement correct si certains glyphes dans la police dépassent ces valeurs.

getMaxDescent() et getMaxAscent() doivent vous indiquer les valeurs maximales absolues de ces champs pour tout glyphe de la police.

Si vous voulez connaître les métriques pour une chaîne spécifique, alors vous voulez absolument appeler getLineMetrics().

2

getHeight() ne peut pas couper les descendants d'une chaîne, seul le dessin de la chaîne peut le faire. Vous utilisez la hauteur retournée par getHeight pour dessiner la corde d'une manière ou d'une autre, et vous utilisez probablement la hauteur. Par exemple, si vous positionnez le point de départ de la chaîne au bas d'une case getHeight() haute, la ligne de base de votre texte sera placée sur le bord inférieur de la boîte, et très probablement les descendeurs seront coupés.

La géométrie du texte est un sujet complexe, infusé d'artefacts historiques bizarres. Comme d'autres l'ont suggéré, utilisez getAscent et getDescent pour essayer de positionner correctement la ligne de base dans votre boîte.

3

J'ai écrit récemment le code ci-dessous comme je l'ai besoin des mesures de pixels en hauteur parfaites pour des gammes spécifiques de la police (par exemple: tous les caractères inférieurs, ou tous les numéros).

Si vous avez besoin d'un code plus rapide (le mien a pour boucles), je recommande de l'exécuter une fois au démarrage pour obtenir toutes les valeurs (par exemple de 1 à 100) dans un tableau.

Le code tire essentiellement tous les caractères de la chaîne d'entrée au même endroit sur un bitmap 250x250 (augmenter ou réduire si nécessaire), il commence à rechercher les pixels du haut, puis du bas . Il fonctionne avec des chaînes normales même s'il a été conçu pour des plages de caractères. Cela signifie qu'il existe une sorte de redondance lors de l'évaluation de chaînes régulières, comme le répètent certains caractères. Donc, si votre chaîne d'imputation dépasse le nombre d'alphabets (26), utilisez comme imputation 'tRange': "abcd ... z" et d'autres caractères qui peuvent être utilisés. C'est plus rapide.

Espérons que ça aide.

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