B4J Code Snippet Measure Text

Discussion in 'B4J Code Snippets' started by stevel05, Oct 17, 2014.

  1. stevel05

    stevel05 Expert Licensed User

    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.

    Code:
    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 FontAs 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: Oct 20, 2014
  2. nw11

    nw11 Member Licensed 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 :
    Code:
    EditText1.Width = 100dip
    EditText1.Height = su.MeasureMultilineTextHeight(EditText1, 
    "prova inserimento testo multilinea riga 1 prova inserimento testo multilinea riga 2")
    Example B4i :
    Code:
    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)
    Code:
    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
     
    B4JExplorer likes this.
  3. stevel05

    stevel05 Expert Licensed 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.

    Code:
    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
     
    B4JExplorer and GGSoft like this.
  4. nw11

    nw11 Member Licensed 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.

    Code:
    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"18FalseFalse))
     
    Log(TM.Width)
     
    Log(TM.Height)
    End Sub


    Sub MeasureText2(Text As String, Width As Int, mFont as FontAs 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
     
    B4JExplorer, GGSoft and stevel05 like this.
  5. Erel

    Erel Administrator Staff Member Licensed 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:
    Code:
    Private Sub MeasureTextWidth (Text As StringFont As B4XFont) As Float
       
    Dim lbl As Label
       lbl.Initialize(
    "")
       lbl.Text = Text
       lbl.Font = 
    Font
       mBase.AddView(lbl, 
    00, -1, -1'mBase is a B4XView panel.
       lbl.Snapshot
       lbl.RemoveNodeFromParent
       
    Return lbl.Width
    End Sub
     
    Last edited: Mar 15, 2018
    Mashiane and stevel05 like this.
  6. stevel05

    stevel05 Expert Licensed 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:

    Code:
    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 FontAs 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: Mar 16, 2018
    Erel likes this.
  7. stevel05

    stevel05 Expert Licensed User

    After working with Shapes, it appears that there is a simpler solution too.

    Note the TextMetric definition has changed to Doubles :

    Code:
    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 FontAs 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
     
    keirS and Erel like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice