As pointed out in a comment (with a link to a truly excellent discussion of font metrics), the size of a font is not the height of its capital letters.
Probably (I would be interested to see other suggestions) the best way to achieve what you want is to define a custom Region
to hold your text node, which overrides layoutChildren()
so that it adjusts the font size so that the height fits the size of the region.
A couple of implementation notes:
Text.setBoundsType()
determines how the bounds of the text are measured. The default is LOGICAL
, which bases the bounds on the metrics of the font, rather than the actual text being rendered. I think what you want here are VISUAL
bounds.
- This implementation is content-biased to vertical; meaning that its width depends on its height. Once the height is known, the font size is computed and the preferred width is calculated as the width of the text.
This is somewhat limited (no multiline text, etc.) but should give you a starting point if you want something more sophisticated.
public class TextPane extends Region {
private final Text text ;
public TextPane(Text text) {
this.text = text ;
getChildren().add(text);
}
@Override
protected void layoutChildren() {
adjustFontSize(getHeight());
text.setY(getHeight());
text.setX(0);
}
private void adjustFontSize(double height) {
double textHeight = text.getBoundsInLocal().getHeight();
if (Math.abs(height - textHeight) > 1) {
Font currentFont = text.getFont() ;
double fontSize = currentFont.getSize() ;
text.setFont(Font.font(currentFont.getFamily(), height * fontSize / textHeight));
}
}
@Override
protected double computePrefWidth(double height) {
adjustFontSize(height);
return text.getBoundsInLocal().getWidth();
}
@Override
public Orientation getContentBias() {
return Orientation.VERTICAL;
}
}
Here's an example which basically implements what you were looking to do, by fixing the height of the text pane to 100
:
public class TextHeight extends javafx.application.Application {
@Override
public void start(Stage stage) {
Text text = new Text("EXAMPLE");
text.setBoundsType(TextBoundsType.VISUAL);
text.setTextOrigin(VPos.BOTTOM);
text.setFill(Color.BLUE);
TextPane textPane = new TextPane(text);
textPane.setMinHeight(100);
textPane.setMaxHeight(100);
Pane root = new Pane(textPane, new Rectangle(620, 0, 10, 100));
stage.setScene(new Scene(root, 700, 120));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

If the text has glyphs with descent (e.g. if you change "EXAMPLE"
to "Example"
, so the p
descends below the baseline), the total height of the text will include the descent:

And here's an example which uses the text pane as the root node (so it's always the same size as the scene). Changing the window size will result in the text automatically updating to vertically fill the scene:
public class TextHeight extends javafx.application.Application {
@Override
public void start(Stage stage) {
Text text = new Text("Example");
text.setBoundsType(TextBoundsType.VISUAL);
text.setTextOrigin(VPos.BOTTOM);
text.setFill(Color.BLUE);
TextPane textPane = new TextPane(text);
textPane.setPrefHeight(100);
stage.setScene(new Scene(textPane));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}