B4J Code Snippet Measure Text

SubName: MeasureText

Description: I came across this while searching the internet. As it seems useful I thought I'd share it. It uses TextBuilder to measure the size of text to be displayed.
It uses a Data 'Type' Structure to return the Height and Width, you can deal with this however you like.

I can't make any claims as to it's accuracy, although it seems pretty good when I am using it to draw text on a canvas.

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type TextMetric (Width As Int,Height As Int)
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show
    Dim TM As TextMetric = MeasureText("Test",fx.DefaultFont(15))
    Log(TM.Height & " " & TM.Width)

    Dim TM As TextMetric = MeasureText("Test" & CRLF & "Test2",fx.DefaultFont(15))
    Log(TM.Height & " " & TM.Width)
End Sub

Sub MeasureText(Text As String,TFont As Font) As TextMetric
    Dim TB,Bounds As JavaObject
    Dim TM As TextMetric

    TB.InitializeStatic("javafx.scene.text.TextBuilder")
    Bounds = TB.RunMethodJO("create",Null).RunMethodJO("text",Array(Text)).RunMethodJO("font",Array(TFont)).RunMethodJO("build",Null).RunMethodJO("getLayoutBounds",Null)

    TM.Width = Bounds.RunMethod("getWidth",Null)
    TM.Height = Bounds.RunMethod("getHeight",Null)
    Return TM
End Sub

Dependencies: JavaObject

Tags: B4j Measure Text Strings
 
Last edited:

nw11

Member
Licensed User
Longtime User
Is there any way to get the Height with a prefixed Width ?

i have to size a variable text lenght object into a prefixed width panel ( es. width 100dip )

Example B4A :
B4X:
EditText1.Width = 100dip
EditText1.Height = su.MeasureMultilineTextHeight(EditText1, "prova inserimento testo multilinea riga 1 prova inserimento testo multilinea riga 2")

Example B4i :
B4X:
EditText1.Width = 100dip
EditText1.Height = MeasureText("prova inserimento testo multilinea riga 1 prova inserimento testo multilinea riga 2", Font.CreateNew2("Arial", 18dip), True, EditText1.Width)

B4X:
Sub MeasureText(Text AsString,UseFont AsFont,Multiline AsBoolean, Width AsDouble) AsDouble
Dim L AsLabel
L.Initialize("")
L.Font = UseFont
L.Text = Text
L.Width = Width
L.Multiline = Multiline
L.SizeToFit
Dim Result AsDouble
' result
Result = L.Height
Return Result
End Sub

How to .. in b4J ?

Thank You in Advance

F
 

stevel05

Expert
Licensed User
Longtime User
The only way I could find was to add a label to a form with it's size set to wrap content, and measure that. So I create a dummy form and hide it off the screen. We can then add the text and measure the label size.

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type TextMetric (Width As Int,Height As Int)
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.

    MainForm.Show

    Dim TM As TextMetric = MeasureText2("Test Test Test Test Test Test",15)
    Log(TM.Width)
    Log(TM.Height)


End Sub
Sub MeasureText2(Text As String,Width As Int) As TextMetric
    Dim TM As TextMetric
    TM.Initialize
    'Create a dummy form
    Dim MForm As Form
    MForm.Initialize("",-1,-1)
    'And hide it off the screen
    MForm.WindowLeft = - Width - 200

    Dim LJO,Bounds As JavaObject
    'Create a label
    Dim L As Label
    L.Initialize("")
    'Let it wrap the text
    L.WrapText = True
    'Assign the text to test
    L.Text = Text
    'Add it to the forms rootpane
    MForm.RootPane.AddNode(L,10,10,Width,-1)
    'Show the form
    MForm.Show
    'Initialize the JavaObject
    LJO = L
    'Get the bounds
    Bounds = LJO.RunMethod("getLayoutBounds",Null)
    'Set the return values
    TM.Width = Bounds.RunMethod("getWidth",Null)
    TM.Height = Bounds.RunMethod("getHeight",Null)

    'And Close the Dummy form
    MForm.Close
    Return TM
End Sub
 

nw11

Member
Licensed User
Longtime User
Thank You for your reply,

i have tested and it seems work fine.

nb: i have only added a new field to fit the right font.

B4X:
Sub Process_Globals
Private fx As JFX
Private MainForm As Form

Type TextMetric (Width AsInt,Height AsInt)
End Sub

Sub AppStart (Form1 As Form, Args() AsString)
 MainForm = Form1
 MainForm.Show
 Dim TM As TextMetric = MeasureText2("Test Line Test Line Test Line Test Line Test Line Test Line", 100, fx.CreateFont("Arial", 18, False, False))
 Log(TM.Width)
 Log(TM.Height)
End Sub


Sub MeasureText2(Text As String, Width As Int, mFont as Font) As TextMetric
   
    Dim TM As TextMetric
    TM.Initialize
  
    'Create a dummy form
    Dim MForm As Form
    MForm.Initialize("",-1,-1)
    'And hide it off the screen
    MForm.WindowTop = - 1000
    'MForm.WindowLeft = - Width - 200
    Dim LJO,Bounds As JavaObject
    'Create a label
    Dim L As Label
    L.Initialize("")
    'Let it wrap the text
    L.WrapText = True
    ' font
    L.Font = mFont   
    'Assign the text to test
    L.Text = Text
    'Add it to the forms rootpane
    MForm.RootPane.AddNode(L,10,10,Width,-1)
    'Show the form
    MForm.Show
    'Initialize the JavaObject
    LJO = L
    'Get the bounds
    Bounds = LJO.RunMethod("getLayoutBounds",Null)
    'Set the return values
    TM.Width = Bounds.RunMethod("getWidth",Null)
    TM.Height = Bounds.RunMethod("getHeight",Null)
    'And Close the Dummy form
    MForm.Close

    Return TM
   
End Sub


F
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Note that the code in the first post doesn't work in Java 9+ due to the removal of the TextBuilder API.

Code such as this one can be used to measure the width:
B4X:
Private Sub MeasureTextWidth (Text As String, Font As B4XFont) As Float
   Dim lbl As Label
   lbl.Initialize("")
   lbl.Text = Text
   lbl.Font = Font
   mBase.AddView(lbl, 0, 0, -1, -1) 'mBase is a B4XView panel.
   lbl.Snapshot
   lbl.RemoveNodeFromParent
   Return lbl.Width
End Sub
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
Testing the code on Java9, the example is a little slow with the need to add labels, I found this on StackOverflow: https://stackoverflow.com/questions/41336447/determining-width-and-height-of-a-javafx-font

Which is quite a bit faster:

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type TextMetric (Width As Int,Height As Int)
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show
    Dim TM As TextMetric = MeasureText("Test",fx.DefaultFont(15))
    Log(TM.Height & " " & TM.Width)

    Dim TM As TextMetric = MeasureText("Test" & CRLF & "Test2",fx.DefaultFont(15))
    Log(TM.Height & " " & TM.Width)
End Sub

Sub MeasureText(Text As String,TFont As Font) As TextMetric
    Dim TM As TextMetric
    Dim Helper As JavaObject
    Helper.InitializeNewInstance("javafx.scene.text.Text", Array(Text))
    Helper.RunMethod("setFont",Array(TFont))
    Helper.RunMethod("setLineSpacing",Array(0.0))
    Helper.RunMethod("setWrappingWidth",Array(0.0))
    Dim Bounds As JavaObject = Helper.RunMethod("getLayoutBounds",Null)
    TM.Width = Bounds.RunMethod("getWidth",Null)
    TM.Height = Bounds.RunMethod("getHeight",Null)
    Return TM
End Sub

It seems to do the job, please consider it untested code and test it youself before using it in your app.
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
After working with Shapes, it appears that there is a simpler solution too.

Note the TextMetric definition has changed to Doubles :

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type TextMetric(Width As Double, Height As Double)
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show
    Dim TestStr As String = $"Test testest setsetset setsetest setsetset setsetsetset setset set set setsetest setestset set setsetset setsetsetsetset setsetset setse tsetsetsetset setsetsetset setseset setset set set setsetest"$
  
    Dim TM As TextMetric = MeasureText(TestStr,fx.DefaultFont(24))
    Log(TM.Width)
    Log(TM.Height)

End Sub


Sub MeasureText(Text As String,TFont As Font) As TextMetric
    Dim TM As TextMetric
    TM.Initialize
    Dim T As JavaObject
    T.InitializeNewInstance("javafx.scene.text.Text",Array(Text))
    T.RunMethod("setFont",Array(TFont))
    TM.Width = T.RunMethod("prefWidth",Array(-1.0))
    TM.Height = T.RunMethod("prefHeight",Array(TM.Width))
    Return TM
End Sub
 
Top