Reputation: 521
I'm trying to place text accurately in the centre of a pane both horizontally and vertically. Using fontmetrics and a test program I get the following results:
This test raises the following questions:
Upvotes: 8
Views: 5434
Reputation: 159281
Here is an alternate implementation of Frank's reportSize
function:
public void reportSize(String s, Font myFont) {
Text text = new Text(s);
text.setFont(myFont);
Bounds tb = text.getBoundsInLocal();
Rectangle stencil = new Rectangle(
tb.getMinX(), tb.getMinY(), tb.getWidth(), tb.getHeight()
);
Shape intersection = Shape.intersect(text, stencil);
Bounds ib = intersection.getBoundsInLocal();
System.out.println(
"Text size: " + ib.getWidth() + ", " + ib.getHeight()
);
}
This implementation uses shape intersection to determine the size of the bounding box of the rendered shape with no whitespace. The implementation does not rely on com.sun
package classes which may not be directly accessible to user application code in Java 9+.
Upvotes: 13
Reputation: 521
After a bit of experimenting I've come up with this solution:
Here is the code that produces it:
public void getBoundingBox(String s, Font myFont) {
final FontMetrics fm = Toolkit.getToolkit().getFontLoader().getFontMetrics(myFont);
final Canvas canvas = new Canvas(fm.computeStringWidth(s), fm.getAscent() + fm.getDescent());
final GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.RED); // Just an abitrary color
gc.setTextBaseline(VPos.TOP); // This saves having to scan the bottom
gc.setFont(myFont);
gc.fillText(s, -fm.getLeading(), 0); // This saves having to scan the left
// Get a snapshot of the canvas
final WritableImage image = canvas.snapshot(null, null);
final PixelReader pr = image.getPixelReader();
final int h = (int) canvas.getHeight();
final int w = (int) canvas.getWidth();
int x;
int y = 0;
// Scan from the top down until we find a red pixel
boolean found = false;
while (y < h && !found) {
x = 0;
while (x < w && !found) {
found = pr.getColor(x, y).equals(Color.RED);
x++;
}
y++;
}
int yPos = y - 2;
// Scan from right to left until we find a red pixel
x = w;
found = false;
while (x > 0 && !found) {
y = 0;
while (y < h && !found) {
found = pr.getColor(x, y).equals(Color.RED);
y++;
}
x--;
}
int xPos = x + 3;
// Here is a visible representation of the bounding box
Rectangle mask = new Rectangle(0, yPos, xPos, h - yPos);
mask.setFill(Color.rgb(0, 0, 255, 0.25));
root.getChildren().addAll(canvas, mask); // root is a global AnchorPane
System.out.println("The width of the bounding box is " + xPos);
System.out.println("The height of the bounding box is " + (h - yPos));
}
Two imports are required for FontMetrics:
import com.sun.javafx.tk.FontMetrics;
import com.sun.javafx.tk.Toolkit;
and call the boundingbox like this for example:
Font myFont = new Font("Arial", 100.0);
getBoundingBox("Testing", myFont);
It solves my problem and I hope this is useful for others as well.
Upvotes: 4