Custom line height and multi-line text measurement

Jungle Ide Home Forums FontMachine FontMachine open source module Custom line height and multi-line text measurement

This topic contains 8 replies, has 2 voices, and was last updated by  Ziggy 7 years, 9 months ago.

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #2577

    secondgear
    Participant

    I tried to solve 2 problems:
    (1) There is no methods in fontmachine to measure text with line breaks.
    (2) There is no way to adjust line spacing for multi-line text

    I added the following code to bitmapfont.monkey

    Code:
    Private
    Field _useCustomLineHeight:Bool
    Field _customLineHeight:Float
    Field _customLineOffset:Float
    Public

    Method SetCustomLineHeight:Void(lineHeight:Float)
    _useCustomLineHeight = True
    _customLineHeight = lineHeight
    _customLineOffset = (lineHeight-faceChars[32].drawingMetrics.drawingSize.y)*0.5
    End

    Method DisableCustomLineHeight:Void()
    _useCustomLineHeight = False
    End

    #rem
    summary:This method returns width in pixels for the given single- or multi-line string
    #end
    Method GetWrappedTextWidth:Int(text:String)
    If (text.Length = 0) Return 0
    Local lineSep:String = String.FromChar(10)
    Local breakPos:Int = text.Find(lineSep)
    If breakPos>=0
    Local textStart:Int
    Local textWidth:Int
    While breakPos>=0
    Local text2:String = text[textStart .. breakPos]
    Local text2width:Int = GetTxtWidth(text2)
    If text2width>textWidth Then textWidth = text2width
    textStart = breakPos+1
    breakPos = text.Find(lineSep, textStart)
    Wend
    If (textStart < text.Length)
    Local lastLineWidth:Int = GetTxtWidth(text[textStart..])
    If lastLineWidth>textWidth Then textWidth = lastLineWidth
    EndIf
    Return textWidth
    Else
    Return GetTxtWidth(text)
    EndIf
    End

    #rem
    summary:This method returns height in pixels for the given single- or multi-line string
    #end
    Method GetWrappedTextHeight:Int(text:String)
    Local lineHeight:Int = GetFontHeight()
    Local totalHeight:Int = lineHeight
    For Local i:Int = 0 Until text.Length
    If text = 10 Then totalHeight += lineHeight
    Next
    Return totalHeight
    End

    and then modified the following methods:

    Code:
    Method GetFontHeight:Int()
    ‘ >>> added by SGG
    If _useCustomLineHeight
    Return _customLineHeight
    Else
    ‘ <<< end
    If faceChars[32] = Null Then Return 0
    Return faceChars[32].drawingMetrics.drawingSize.y
    ‘ >>> added by SGG
    EndIf
    ‘ <<< end
    End Method
    Code:
    Method DrawCharsText(text:String,x#,y#, target:BitMapChar[] , align:Int)
    Local drx:Float = x, dry:Float = y
    Local oldX:Float = x
    Local xOffset:Int = 0
    Local lineSep:String = String.FromChar(10)
    Select align
    Case eDrawAlign.CENTER
    if text.Find(lineSep)>=0 Then
    Local text2:String = text[.. text.Find(lineSep)]
    xOffset = Self.GetTxtWidth(text2) / 2
    Else
    xOffset = Self.GetTxtWidth(text) / 2
    endif
    Case eDrawAlign.RIGHT
    if text.Find(lineSep)>=0 Then
    Local text2:String = text[.. text.Find(lineSep)]
    xOffset = Self.GetTxtWidth(text2)
    Else
    xOffset = Self.GetTxtWidth(text)
    endif
    End select

    ‘ >>> added by SGG
    If _useCustomLineHeight Then dry += _customLineOffset
    ‘ <<< end

    For Local i:Int = 1 to text.Length
    Local char:Int = text[i-1]
    if char>=0 And char<=target.Length Then
    if char = 10 Then
    ‘ >>> added by SGG
    If _useCustomLineHeight
    dry+=_customLineHeight-_customLineOffset
    Else
    ‘ <<< end
    dry+=faceChars[32].drawingMetrics.drawingSize.y ‘ + verticalkerning!
    ‘ >>> added by SGG
    EndIf
    ‘ <<< end
    Self.DrawCharsText(text,oldX ,dry,target,align)
    return
    ElseIf target[char] <> null Then
    if target[char].CharImageLoaded() = false Then
    target[char].LoadCharImage()
    End
    if target[char].image <> null Then
    DrawImage(target[char].image,drx-xOffset,dry)
    ElseIf target[char].packedFontIndex > 0 Then
    DrawImageRect(packedImages[target[char].packedFontIndex],-xOffset+drx+target[char].drawingMetrics.drawingOffset.x,dry+target[char].drawingMetrics.drawingOffset.y,target[char].packedPosition.x,target[char].packedPosition.y,target[char].packedSize.x,target[char].packedSize.y)
    Endif
    drx+=faceChars[char].drawingMetrics.drawingWidth ‘+HKERNING
    endif
    Else
    ‘ Print “Char ” + char + ” out of scope.”
    EndIf
    Next
    End

    I hope you might consider including those ideas in the next release.

    #3262

    Ziggy
    Keymaster

    I like the ideas and will definitevlly add somethig like this in the official release. the HKerning and VKerning commented vars in the drawtext comment are there, I just need to implement them properly. Also, the GetWrappedTextWidth, I don’t think I like the idea of having two functions to measure text. The regular function should take into consideration line breaks. I’ll add this too.

    #3264

    Ziggy
    Keymaster
    #3265

    secondgear
    Participant

    Thank you for taking care of this. There is one problem still (at least I see it as a problem).

    When you render text, you apply vertical kerning after encountering a line break (line 624 of bitmapfont.monkey). It means that you render the first line without kerning. GetTxtHeight() correctly calculates text height with kerning applied to all lines.

    I think the same problem exists with horizontal kerning as the initial value of drx is not adjusted for kerning in DrawCharsText().

    #3266

    Ziggy
    Keymaster
    Quote:
    When you render text, you apply vertical kerning after encountering a line break (line 624 of bitmapfont.monkey). It means that you render the first line without kerning.

    That’s correct and this is expected behavior.Kerning has to be applied between letters.

    Quote:
    GetTxtHeight() correctly calculates text height with kerning applied to all lines.

    I need to add a small fix to this calculation. you’re right I should not be adding the kerning the the first line.

    Quote:
    I think the same problem exists with horizontal kerning as the initial value of drx is not adjusted for kerning in DrawCharsText().

    AFAIK this is expected bahevior too. First letter does not have to add leading space from previous letter, as there is no previous letter.

    #3267

    secondgear
    Participant

    I think all lines should be the same “height”, therefore adding kerning to the first line makes more sense. For practical purposes I really need to eliminate the white space above the first line and to the left of the first character. Otherwise positioning text relative to other game elements becomes very inconsistent between different fonts.

    Quite frankly, I am struggling conceptually right now, so I don’t have a concrete solution to propose.

    #3268

    Ziggy
    Keymaster

    @sgg_ But that’s not kerning. you can modify all characters of a font by modifying all the Metrics of them. You can use the GetFaceInfo, GetShadowInfo, etc. methods when loading a font, and modify their drawingheight parameter. No need kerning for this. Kerning is to manipulate space between chars.

    This is what needs to be changed on the bitmapfont class:

    Code:
    #rem
    summary:This method returns the width in graphic units of the given string.
    You can see [a ../sample programs/sample03.monkey]a sample application that uses the GetTxtWidth function here.[/a]
    #end
    Method GetTxtWidth:Float(text:String)
    Local twidth:Float
    Local MaxWidth:Float = 0
    Local char:Int
    Local lastchar:Int = 0
    For Local i:Int = 1 To text.Length
    char = text[i-1]
    If char >= 0 And char < faceChars.Length() and char<> 10 And char<>13 Then
    If faceChars[char] <> Null Then
    lastchar = char
    twidth = twidth + faceChars[char].drawingMetrics.drawingWidth + Kerning.x
    End If
    ElseIf char = 10
    If Abs(MaxWidth)<Abs(twidth) Then MaxWidth = twidth – Kerning.x – faceChars[lastchar].drawingMetrics.drawingWidth + faceChars[lastchar].drawingMetrics.drawingSize.x
    twidth = 0
    lastchar = char
    End If
    Next
    If lastchar >= 0 And lastchar < faceChars.Length() Then
    If faceChars[lastchar] <> Null Then
    twidth = twidth – faceChars[lastchar].drawingMetrics.drawingWidth
    twidth = twidth + faceChars[lastchar].drawingMetrics.drawingSize.x
    End If
    End If
    If Abs(MaxWidth)<Abs(twidth ) Then MaxWidth = twidth – Kerning.x ‘- faceChars[lastchar].drawingMetrics.drawingWidth + faceChars[lastchar].drawingMetrics.drawingSize.x
    Return MaxWidth ‘twidth
    End Method

    #rem
    summary:This method returns the height in graphic units of the given string.
    #end
    Method GetTxtHeight:Float(Text:String)
    ‘Too agreesive as it generates an array, but it is simple in calculation:
    ‘return (Text.Split(“~n”).Length + 1) * (faceChars[32].drawingMetrics.drawingSize.y + Kerning.y)

    ‘Alternative:
    Local count:int = 0
    For Local i=0 until Text.Length
    if Text = 10 Then
    count+=1
    EndIf
    Next
    Return count * (faceChars[32].drawingMetrics.drawingSize.y + Kerning.y) + GetFontHeight()
    End

    #3270

    secondgear
    Participant

    Correct me if I am wrong, but changing height in metrics has no effect on the first line. I applied the patch above and then modified firstsample.monkey the following way:

    Code:
    font = New BitmapFont(“bluesky/bluesky.txt”, False)
    font.DrawShadow = False
    font.DrawBorder = False
    ‘font.Kerning.x = 10
    ‘font.Kerning.y = 20
    Print “Font loaded!”
    Local fi:BitMapCharMetrics = font.GetFaceInfo(32)
    Print “height:” + fi.drawingSize.y ‘prints 44
    fi.drawingSize.y = 32

    The second line moves up, the first line stays where it is.

    #3271

    Ziggy
    Keymaster

    You have to modify the drawingoffset of each character, to make them draw with a given X or Y offset, and then modify the height of the 32 character to modify the lineheight. There are 2 separate things:
    1.- The offset of each letter
    2.- The space between lines
    the first one can be modified per-char using the drawingoffset values, the second one can be handed by Kerning, ot by modifying the height of the char index 32.

Viewing 9 posts - 1 through 9 (of 9 total)

You must be logged in to reply to this topic.