It is currently Sun Dec 15, 2019 3:26 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 1:18 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10527
Location: Beyond the pale...
**EDIT : code updated with assistance from Rashad.

==================================

Original post :

I just found myself needing to draw a single line of text containing multiple fonts.

Problem is that PB's 2D drawing lib does not allow us to draw along a common baseline because we cannot calculate the necessary font metrics without dipping into Win API and GetTextMetrics_(). Using DrawText() repeatedly to create a single line of text containing multiple fonts produces a lump of text sharing the same top line which is useless and very very ugly.

Since I need this to be cross-platform, I hacked up the following which makes simple use of the vector lib to calculate the necessary metrics which we can then use in the regular 2D drawing lib. Only tested on Windows and Ubuntu, but I see no reason why it should not work on the other platforms.

Note that the vector lib returns slightly different values for text ascents than the Win API GetTextMetrics_(), but these differences are small and possibly due to rounding errors because the vector lib uses floating point values etc. Doesn't seem to impact my tests thus far.

Thought this might be useful.

Code:
;/////////////////////////////////////////////////////////////////////////////////
;Font ascent / Descent.
;======================
;srod + Rashad : Jan 2019.
;
;With PB's vector library it is possible to draw text in multiple fonts on a single line sharing a common baseline.
;This is not currently possible with PB's 2D Drawing lib (not in a cross-platform way) since we do not have access to the necessary text metrics (ascent, descent, internal leading etc.)
;This little prog shows how to use the cross-platform vector lib to 'estimate' the ascents/descents of a given font for subsequent use
;with PB's basic 2D Drawing lib.
;With this info it is then possible to use the 2D drawing lib to draw text in multiple fonts on a single line sharing a common baseline as we demonstrate.
;
;Platforms : ALL.  Tested on Windows/Ubuntu (GTK3 and QT) only.
;
;NOTES.
;
;     i)  During testing we found an incompatibility between PB's vector library and PB's 2D Drawing lib with certain 'wayward' fonts.
;         This necessitated a Rashad inspired workaround which results in 'estimates' for the required font ascents etc.
;         These estimates are looking very good though. Certainly good enough for the purposes of this utility.
;/////////////////////////////////////////////////////////////////////////////////


;The following global records the max ascent of the various fonts we are using.
;This will allow us to draw a single line of text along a common baseline with multiple fonts etc.
  Global gMaxAscent.d

;The following structure will hold ascent/descent info on each of our fonts.
  Structure fontData
    font.i
    ascent.d
  EndStructure

;We place all our fonts in an array for convenience.
  Dim fonts.fontData(3)
  fonts(1)\font = LoadFont(#PB_Any, "Monotype Corsiva", 45)
  fonts(2)\font = LoadFont(#PB_Any, "Times New Roman", 30, #PB_Font_Italic)
  fonts(3)\font = LoadFont(#PB_Any, "Arial", 11)

Declare GetFontData(Array fonts.fontData(1))

;Calculate font ascents/descents for all of our fonts.
  Global text$ = "ABCDefghIJK"
  GetFontData(fonts())

;A quick test to check all is well.
;We simply use DrawText() 3 times (once for each font) to display a single line of repeated text adjusting the vertical position to ensure
;all the text shares the same baseline.
Define.d tempAscent
If OpenWindow(0, 0, 0, 800, 400, "Font Data", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(0, 10, 10, 780, 380)
  If StartDrawing(CanvasOutput(0))
    ;Draw a single line of text in the various fonts sharing the same baseline.
      DrawingMode(#PB_2DDrawing_Transparent)
      For i = 1 To ArraySize(fonts())     
        DrawingFont(FontID(fonts(i)\font))
        x=DrawText(x, 20 + gMaxAscent - fonts(i)\ascent, text$, 0)
      Next
    ;Draw the baseline.
      Line(0, 20 + gMaxAscent, x, 1, $0000FF)
    StopDrawing()
  EndIf
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf

End

;The following procedure calculates all font ascents/descents.
;It uses the vector lib for cross platform purposes.
Procedure GetFontData(Array fonts.fontData(1))
  Protected image, i, TextHeight, visHeight.d, ascentScale.d
  ;Create a dummy image.
    image = CreateImage(#PB_Any, 1, 1)
  If image
    If StartVectorDrawing(ImageVectorOutput(image))
      If StartDrawing(ImageOutput(image))
        For i = 1 To ArraySize(fonts()) 
          VectorFont(FontID(fonts(i)\font))
          visHeight = VectorTextHeight(text$, #PB_VectorText_Visible) + VectorTextHeight(text$, #PB_VectorText_Visible|#PB_VectorText_Offset)
          If visHeight
            ascentScale = VectorTextHeight(" ", #PB_VectorText_Baseline) / visHeight
          Else
            ascentScale = 1
          EndIf
          DrawingFont(FontID(fonts(i)\font))
          fonts(i)\ascent = ascentScale * TextHeight(" ")
          If fonts(i)\ascent > gMaxAscent
            gMaxAscent = fonts(i)\ascent
          EndIf
        Next
        StopDrawing()
      EndIf
      StopVectorDrawing()
    EndIf
    FreeImage(image) 
  EndIf
EndProcedure

_________________
I may look like a mule, but I'm not a complete ass.


Last edited by srod on Thu Jan 17, 2019 10:36 am, edited 6 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 1:40 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Fri Dec 10, 2010 6:31 pm
Posts: 153
excellent.
I really needed these codes.I think with this function i can simulate the markup-text feature.
Thanks a lot.

_________________
Sorry for my bad English.

Amitris


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 1:49 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10527
Location: Beyond the pale...
mohsen wrote:
excellent.
I really needed these codes.I think with this function i can simulate the markup-text feature.
Thanks a lot.


You're welcome Mohsen. Just remember to recalculate the ascent/descents every time you alter a font etc.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 2:21 pm 
Offline
Addict
Addict
User avatar

Joined: Sun Nov 05, 2006 11:42 pm
Posts: 4548
Location: Lyon - France
Hello SROD always happy to read you :D
Works perfectly here with W7 v5.70
Thanks you for sharing 8)

_________________
ImageThe happiness is a road...
Not a destination


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 2:24 pm 
Offline
Addict
Addict

Joined: Fri Nov 09, 2012 11:04 pm
Posts: 1715
Location: Uttoxeter, UK
@srod,

Checked on Windows 10.
Checked it on my MacBook Pro, too.
Looks pretty much the same to me.

Thank you for sharing. :D

_________________
DE AA EB


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 6:18 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Apr 12, 2009 6:27 am
Posts: 3480
Hi srod
Simple using only the VectorLib

Code:
  LoadFont(0, "Arial", 42)
  LoadFont(1, "Times New Roman", 20, #PB_Font_Italic)
  LoadFont(2, "Arial", 30)
 
  If OpenWindow(0, 0, 0, 610, 200, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    CanvasGadget(0, 0, 0, 610, 200)
   
    If StartVectorDrawing(CanvasVectorOutput(0))
      text$ = "ABCDefghIJK"
      VectorFont(FontID(0), 40)
      x1 = VectorTextWidth(Text$,#PB_VectorText_Visible)
      h1 = VectorTextHeight(Text$,#PB_VectorText_Visible)
      VectorSourceColor(RGBA(0, 0, 0, 250))
      MovePathCursor(10 ,10)
      DrawVectorText(Text$)
           
      VectorFont(FontID(1), 20)
      VectorSourceColor(RGBA(0, 250, 0, 250))
      x2 = VectorTextWidth(Text$,#PB_VectorText_Visible)
      h2 = VectorTextHeight(Text$,#PB_VectorText_Visible) + 1
      MovePathCursor(x1+12 ,11 + h1-h2)
      DrawVectorText(Text$)
     
      VectorFont(FontID(2), 35)
      VectorSourceColor(RGBA(0, 0, 250, 250))
      x3 = VectorTextWidth(Text$,#PB_VectorText_Visible)
      h3 = VectorTextHeight(Text$,#PB_VectorText_Visible) + 1
      MovePathCursor(x1+x2+14,12 + h1-h3)
      DrawVectorText(Text$)
     
      ResetCoordinates()
      VectorSourceColor(RGBA(250, 0, 0, 250))
      MovePathCursor(10 ,8 + h1)     
      AddPathLine(x1+x2+x3+14,10 + h1)
      StrokePath(1)     
      StopVectorDrawing()
    EndIf
   
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
  EndIf


Edit : Modified

_________________
Egypt my love


Last edited by RASHAD on Wed Jan 16, 2019 7:06 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 6:30 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10527
Location: Beyond the pale...
I needed the calculations for use with the 2D Drawing lib, not the vector library.

I use the 2D Drawing lib instead of the vector lib when speed is critical.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 6:43 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Apr 12, 2009 6:27 am
Posts: 3480
Got it
Thanks
Any way I archived it (I knew that srod can not do it for nothing :) )

_________________
Egypt my love


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 6:47 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Dec 23, 2009 10:14 pm
Posts: 3153
Location: Boston, MA
Yes, RASHAD, I use the vector lib for anti-aliased text.
Please delete this line from your example to avoid confusion:
LoadFont(0, "Tahoma", 20)

_________________
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 7:02 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10527
Location: Beyond the pale...
I must admit Rashad that I don't see how yours should work - doesn't make sense to me. In fact it fails with the Monotype Corsiva font.

Then again my code fails as well with this font! :) Seems that the baseline height returned from the VectorTextHeight() function differs too much from that returned for the true text ascent with GetTextMetrics_().

Will need to examine this a bit more.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 7:12 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Apr 12, 2009 6:27 am
Posts: 3480
@skywalk
Hi
Previous post updated
Fixed also Line width using Old workaround

@srod
I never found any standard way to get the perfect text width & height
Specially with different font sizes
Glad to see you back active

_________________
Egypt my love


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 7:20 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10527
Location: Beyond the pale...
Found the problem with my code. There seems to be a distinct incompatibility/discrepancy between the regular DrawText() and DrawVectorText() which you can see in the following. The left canvas is drawn with the vector lib and the right canvas with the regular 2D lib.

Code:
If OpenWindow(0, 0, 0, 800, 250, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(0, 0, 0, 350, 250)
  CanvasGadget(1, 400, 0, 350, 250)
  LoadFont(0, "Monotype Corsiva", 45, #PB_Font_Italic)
  Text$ = "ABCDefghIJK"
  If StartVectorDrawing(CanvasVectorOutput(0))
    VectorFont(FontID(0))
    MovePathCursor(0, 0)
    DrawVectorText(Text$)
    StopVectorDrawing()
  EndIf
  If StartDrawing(CanvasOutput(1))
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawingFont(FontID(0))
    DrawText(0, 0, Text$, 0)       
    StopDrawing()
  EndIf
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf


I would have expected both text drawing's to have been placed identically. If this is an issue then it seems to be with the vector lib as the ascent reported by GetTextMetrics_() is totally correct with the right canvas and DrawText().

Puzzling.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 7:44 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Apr 12, 2009 6:27 am
Posts: 3480
Using Font Base Line as origin
Code:
  LoadFont(0, "Arial", 42)
  LoadFont(1, "Times New Roman", 20, #PB_Font_Italic)
  LoadFont(2, "Monotype Corsiva", 30)
 
  If OpenWindow(0, 0, 0, 610, 200, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    CanvasGadget(0, 0, 0, 610, 200)
   
    If StartVectorDrawing(CanvasVectorOutput(0))
      text$ = "ABCDefghIJK"
      VectorFont(FontID(0), 40)
      x1 = VectorTextWidth(Text$,#PB_VectorText_Visible)
      hb1 = VectorTextHeight(Text$,#PB_VectorText_Baseline)
      VectorSourceColor(RGBA(0, 0, 0, 250))
      MovePathCursor(10 ,10)
      DrawVectorText(Text$)
           
      VectorFont(FontID(1), 20)
      VectorSourceColor(RGBA(0, 250, 0, 250))
      x2 = VectorTextWidth(Text$,#PB_VectorText_Visible)
      hb2 = VectorTextHeight(Text$,#PB_VectorText_Baseline)
      MovePathCursor(x1+12 ,10 + hb1 - hb2)
      DrawVectorText(Text$)
     
      VectorFont(FontID(2), 35)
      VectorSourceColor(RGBA(0, 0, 250, 250))
      x3 = VectorTextWidth(Text$,#PB_VectorText_Visible)
      hb3 = VectorTextHeight(Text$,#PB_VectorText_Baseline)
      MovePathCursor(x1+x2+14,10 + hb1 - hb3)
      DrawVectorText(Text$)
     
      ResetCoordinates()
      VectorSourceColor(RGBA(250, 0, 0, 250))
      MovePathCursor(10 ,8 + hb1)     
      AddPathLine(x1+x2+x3+14,10 + hb1)
      StrokePath(1)     
      StopVectorDrawing()
    EndIf
   
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
  EndIf

_________________
Egypt my love


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 8:13 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10527
Location: Beyond the pale...
Yes there is no problem with these fonts when using the vector lib and the baseline height - providing you don't then switch across to the 2D lib! :) That was why I didn't understand your original code because you were not using the baseline height.

Have tested with more fonts and similar problems arise with the 'italicised' type fonts which seem to be rendered by GDI with a huge internal leading space which is not happening with the vector drawing.

Everything works fine with the more common fonts and I can get these 'bad' fonts to work fine if I use API to determine the font ascents and baselines etc. Even tested with GDI directly and exactly the same as using PB's 2D drawing. Yes, would seem that the vector lib is stripping out this additional leading space somehow and for some reason.

Was hoping for a fool proof cross-platform solution which, sadly, is not happening right now.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Cross platform font ascent/descents!
PostPosted: Wed Jan 16, 2019 9:31 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Apr 12, 2009 6:27 am
Posts: 3480
Workaround
Code:
   LoadFont(0, "Arial", 20)
  LoadFont(1, "Times New Roman", 14, #PB_Font_Italic)
  LoadFont(2, "Monotype Corsiva", 24)
 
  If OpenWindow(0, 0, 0, 500, 200, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    CanvasGadget(0, 0, 0, 500, 200)
    text$ = "ABCDefghIJK"
   
    If StartVectorDrawing(CanvasVectorOutput(0))     
      VectorFont(FontID(0), 120)
      h1.f = VectorTextHeight(Text$,#PB_VectorText_Visible)
      hb1.f = VectorTextHeight(Text$,#PB_VectorText_Baseline)
      scale1.f = hb1/h1
               
      VectorFont(FontID(1), 120)
      h2.f = VectorTextHeight(Text$,#PB_VectorText_Visible)
      hb2.f = VectorTextHeight(Text$,#PB_VectorText_Baseline)
      scale2.f = hb2/h2
     
      VectorFont(FontID(2), 120)
      h3.f = VectorTextHeight(Text$,#PB_VectorText_Visible)
      hb3.f = VectorTextHeight(Text$,#PB_VectorText_Baseline)
      scale3.f = hb3/h3
      StopVectorDrawing()
    EndIf
   
    If StartDrawing(CanvasOutput(0))
      DrawingMode(#PB_2DDrawing_Transparent)
      DrawingFont(FontID(0))
      x1.f = TextWidth(Text$)
      h1.f = TextHeight(Text$)
      DrawText(10 ,10 ,Text$,$0)
           
      DrawingFont(FontID(1))
      x2.f = TextWidth(Text$)
      h2.f = TextHeight(Text$)
      DrawText(12+x1,10 + h1*scale1 - h2*scale2 , Text$ , $00FF00)
     
      DrawingFont(FontID(2))
      x3.f = TextWidth(Text$)
      h3.f = TextHeight(Text$)
      DrawText(14+x1+x2,10 + h1*scale1- h3*scale3 , Text$ ,$0000FF)
     
      Line(10,10+h1*scale3,x1+x2+x3,1,$FF0000)     
      StopDrawing()
    EndIf
   
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
  EndIf

_________________
Egypt my love


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 10 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
cron

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye