• Welcome to Theos PowerBasic Museum 2017.

Fonts Don't Seem To Scale At DPI Scaling Factor?

Started by Frederick J. Harris, November 16, 2016, 07:52:21 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

I'm insecure in my knowledge of fonts, particularly with regard to Hi-DPI Aware scaling of fonts.  I decided to put together a test app to see how stuff works, that is, how fonts scale in relation to the controls that contain text.  I suppose one way of describing my uncertainty was whether fonts scale at the same ratio as Hi-DPI Scaling Factors (I don't believe they do).  My idea was as follows.  Suppose I create a text string such as this...

"PowerBASIC : Compile Without Compromise!"

There are 40 characters in that string.  Now suppose I put an edit control on a Form/Window/Dialog and size it so that the above string fits exactly in the edit control with not one pixel extra in any direction.  In other words, I could use GetTextExtentPoint32() to determine the exact pixel height and width of that string, and I could use GetWindowRect(), GetClientRect(), and GetSystemMetrics() to come up with the exact size of the edit control for my CreateWindowEx() call to create the edit control to contain that text exactly.

The code for that is posted just below (the whole program is at the end of this post).  Here is what I did.  In my WM_CREATE handler code for the app I retrieved DPI settings and created a "Lucida Console" Bold Font of size 12.0...


hDC     = GetDC(Wea.hWnd)
dpiX    = GetDeviceCaps(hDC, %LOGPIXELSX)
dpiY    = GetDeviceCaps(hDC, %LOGPIXELSY)
rxRatio = dpiX/96
ryRatio = dpiY/96
hFont   = CreateFont
( _
  -MulDiv(12.0, dpiY, 72), _
  0, _
  0, _
  0, _
  %FW_BOLD, _
  0, _
  0, _
  0, _
  %DEFAULT_CHARSET, _
  0, _
  0, _
  0, _
  0, _
  "Lucida Console"
)


When I first ran that code I had my computer set at its standard resolution which was (its a laptop) 1920 X 1200, and in Control Panel, of the three Font sizes available (small, medium, and large), I had it set to 'small', which was the default.  My dpiX and dpiY readings were 96 with rxRatio and ryRatio both 1.0 - so there was no scaling.  If you build and run the below code the app will TextOut() a lot of size information and the top two lines of that are as follows...


1)  rxRatio                   = 1.0000   X DPI Scaling Factor
2)  ryRatio                   = 1.0000   Y DPI Scaling Factor


Next in my WM_CREATE code are lines to determine the display height and width of the text string...

"PowerBASIC : Compile Without Compromise!"

...which works out to 16 pixels high and 440 pixels wide, and GetSystemMetrics() calls to get the 3D Border widths of edit controls created with a 3D extended style...


GetTextExtentPoint32(hDC, szText, Len(szText), sz)
iCx3DBorder = GetSystemMetrics(%SM_CXEDGE)
iCy3DBorder = GetSystemMetrics(%SM_CYEDGE)
iWidth      = iCx3DBorder * 2 + sz.cx
iHeight     = iCy3DBorder * 2 + sz.cy


So for example, if my text string needs 440 pixels, and the GetSystemMetrics() calls indicate a border width of 2 pixels, then I'm going to need an edit control 440 pixels wide plus 2 pixels each for the left and right borders of the control, which comes to 444 pixels.  The same logic applied to the height yields a requirement of 20 pixels.  So my CreateWindowEx() call for the edit control, if I want to locate its top left edge 10 pixels to the right of the left edge of the containing form and 10 pixels down from the top, and I want it to exactly contain my text string with no room to spare as determined above, would be this...


hCtl = CreateWindowEx _
( _
  %WS_EX_CLIENTEDGE, _
  "edit", _
  szText, _
  %WS_CHILD Or %WS_VISIBLE, _
  SizX(10), _
  SizY(10), _
  SizX(444), _
  SizY(20), _
  Wea.hWnd, _
  %ID_TEXT, _
  Wea.hInst, _
  ByVal 0 _
)


Note that my SizX() and SizY() terms above are simple one line macros to multiply the number contained within by the rxRatio and ryRatio DPI Scaling Factors.  Since those scaling factors are 1.0 when not running in a high DPI resolution altered state, they have no effect on the numbers contained within the above CreateWindowEx() call.  When I run the below code of my app it indeed does work out perfectly and the edit control is sized perfectly to contain the text string exactly.  This is the output from the run which displays right below the edit control...


1)  rxRatio                   = 1.0000   X DPI Scaling Factor
2)  ryRatio                   = 1.0000   Y DPI Scaling Factor
3)  dwWndWidth                = 444      Edit Control Total Width
4)  dwWndHeight               = 20       Edit Control Total Height
5)  iCx3DBorder               = 2        Edit Control 3D Border Width
6)  iCy3DBorder               = 2        Edit Control 3D Border Height
7)  dwClientWidth             = 440      Edit Control Client Rect Width
8)  dwClientHeight            = 16       Edit Control Client Rect Height
9)  tm.tmHeight               = 16       Char Total Height At Font Size
10) tm.tmAveCharWidth         = 11       Char Total Width At Font Size
11) sz.cx                     = 440      Pixel Width of Edit Control Text
12) sz.cy                     = 16       Pixel Height of Edit Control Text


The variables dwWndWidth and dwWndHeight are the cx and cy parameters of the CreateWindowEx() call to create the edit control (444 wide and 20 high), and in the WM_PAINT handler code where the TextOut()s are located were actually acquired by GetWindowRect() calls which retrieved those values.  The iCx3DBorder and iCy3DBorder values were acquired by GetSystemMetrics() to get the pixel sizes of the edit control borders.   That works out to 2 pixels per border - so 4 across for the left/right border, and 4 up/down for the top and bottom border.  The dwClientWidth and dwClientHeight variables are the dwWndWidth and dwWndHeight numbers reduced by the border widths as just described, and were actually acquired by GetClientRect().  A call to GetTextMetrics() indicated my CreateFont() call for the "Lucida Console" Bold font of 12 Logical Units (Font Size 12.0) resulted in a tmHeight of 16 pixels with a tmAveCharWidth of 11 pixels.  This all adds up perfectly as  the MulDiv() macro call in CreateFont() resolves to this arithmetic...


MulDiv(12.0, dpiY, 72) = (12.0 * 96) / 72 = 20


...and so we see how the client rectangle within the edit control needs to be 20 pixels high, and we need a client width of 440 pixels because 40 characters times 11 pixels width for the fixed pitch Lucida Console font yields 440 pixels.  So everything is working perfectly - almost. 

A bit of a problem results when the above code is run at one of the non-standard resolution or font size settings alterable through Control Panel.  How much of a problem I'll let you all decide.  On the positive side, if you call SetProcessDPIAware(), there is no clipping of text, no detracting 'visual artifacts' resulting from font virtualization, the text output is clear and not fuzzy and everything looks otherwise OK.  Except the text string, which we took great pains to size perfectly to just fit within the edit control, no longer is sized perfectly to fit within the edit control.  There is quite a bit of white space to the right of the string between it and the right border of the edit control.  So in other words, the font did not scale at the same factor as the other elements of the application according to the DPI Scaling Factors.   Here are the results of a program run where I set the display to a larger font in Control Panel than the default...


1)  rxRatio                   = 1.5000   X DPI Scaling Factor
2)  ryRatio                   = 1.5000   Y DPI Scaling Factor
3)  dwWndWidth                = 666      Edit Control Total Width
4)  dwWndHeight               = 30       Edit Control Total Height
5)  iCx3DBorder               = 2        Edit Control 3D Border Width
6)  iCy3DBorder               = 2        Edit Control 3D Border Height
7)  dwClientWidth             = 662      Edit Control Client Rect Width
8)  dwClientHeight            = 26       Edit Control Client Rect Height
9)  tm.tmHeight               = 24       Char Total Height At Font Size
10) tm.tmAveCharWidth         = 15       Char Total Width At Font Size
11) sz.cx                     = 600      Pixel Width of Edit Control Text
12) sz.cy                     = 24       Pixel Height of Edit Control Text


As you can see above we now have a DPI Scaling Factor of 1.5.  The client rectangle within the edit control capable of receiving text is now 662 pixels.  However, a call to GetTextExtentPoint32() reveals that only 600 pixels are needed to display the 40 character text string at whatever the altered DPI setting had done to the Font.  So naturally we have that 62 pixel blank area to the right of the string.   The only conclusion one can come to is that fonts do not scale at the DPI Scaling Factors acquired like so...


Local rxRatio,ryRatio As Double
Local dpiXdpiY As Long

dpiX    = GetDeviceCaps(hDC, %LOGPIXELSX)
dpiY    = GetDeviceCaps(hDC, %LOGPIXELSY)
rxRatio = dpiX/96
ryRatio = dpiY/96


Either that or my CreateFont() call as provided above is not set up correctly.  In looking at that again...


hDC     = GetDC(Wea.hWnd)
dpiX    = GetDeviceCaps(hDC, %LOGPIXELSX)
dpiY    = GetDeviceCaps(hDC, %LOGPIXELSY)
rxRatio = dpiX/96
ryRatio = dpiY/96
hFont   = CreateFont
( _
  -MulDiv(12.0, dpiY, 72), _ 'Specifies the height, in logical units, of the font's character cell or character.
  0, _                       'Specifies the average width, in logical units, of characters in the requested font.
  0, _
  0, _
  %FW_BOLD, _
  0, _
  0, _
  0, _
  %DEFAULT_CHARSET, _
  0, _
  0, _
  0, _
  0, _
  "Lucida Console"
)


...the first parameter where I'm using the MulDiv() call specifically is referring to the height of the font - not the width.   The second parameter refers to the width, and I can see I left that blank.  So maybe that's the source of my problem.  I don't know.  As I said, I don't fully understand this.  I will say though that putting in a value of 15 for that second parameter reduces the white space.  But its still not exact.  And putting in a 16 clips the string in the edit control. 

What I'm wondering and beginning to believe might be true is that these things aren't suppossed to scale exactly.  In the documentation on CreateFont() are these words...

Quote
For all height comparisons, the font mapper looks for the largest font that does not exceed the requested size.

and these...

Quote
Specifies the average width, in logical units, of characters in the requested font. If this value is zero, the font mapper chooses a closest match value. The closest match value is determined by comparing the absolute values of the difference between the current device's aspect ratio and the digitized aspect ratio of available fonts.

Note that it is possible to make everything match up perfectly if one specifically codes sizing code.  In the app I'm posting below you'll see this CreateWindowEx() call commented out...


iCx3DBorder = GetSystemMetrics(%SM_CXEDGE)
iCy3DBorder = GetSystemMetrics(%SM_CYEDGE)
iWidth      = iCx3DBorder * 2 + sz.cx
iHeight     = iCy3DBorder * 2 + sz.cy
'hCtl       = CreateWindowEx _
( _
  %WS_EX_CLIENTEDGE, _
  "edit", _
  szText, _
  %WS_CHILD Or %WS_VISIBLE, _
  SizX(10), _
  SizY(10), _
  iWidth, _
  iHeight, _
  Wea.hWnd, _
  %ID_TEXT, _
  Wea.hInst, _
  ByVal 0 _
)


If you uncomment that one and comment out the active one below it and run the app you'll see what I was hoping would occur but doesn't through the DPI Scaling Factors.  That it doesn't possibly makes the point better that Fonts don't scale as per the DPI Scaling Factors.

I'd appreciate any clarifications corrections or opinions anyone might have regarding this or my program.  Here is the test program where these things can be examined and experimented with...


#Compile            Exe
#Dim                All
%UNICODE            = 1
#If %Def(%UNICODE)
    Macro ZStr      = WStringz
    Macro BStr      = WString
#Else
    Macro ZStr      = Asciiz
    Macro BStr      = String
#EndIf
Macro SizX(x)       = x*rxRatio
Macro SizY(y)       = y*ryRatio
#Include            "Windows.inc"
%ID_TEXT            = 1500

Type WndEventArgs
  wParam            As Long
  lParam            As Long
  hWnd              As Dword
  hInst             As Dword
End Type

Type MessageHandler
  wMessage          As Long
  dwFnPtr           As Dword
End Type

Declare Function FnPtr(wea As WndEventArgs) As Long
Declare Function SetProcessDpiAware() As Long


Function SetMyProcessDpiAware() As Long
  Local hInstance,pFn,blnReturn As Dword

  hInstance=LoadLibrary("user32.dll")
  If hInstance Then
     pFn=GetProcAddress(hInstance,"SetProcessDPIAware")
     If pFn Then
        Call Dword pFn Using SetProcessDpiAware To blnReturn
     End If
  End If

  Function=blnReturn
End Function


Function fnWndProc_OnCreate(Wea As WndEventArgs) As Long
  Local iCx3DBorder,iCy3DBorder,iWidth,iHeight,dpiX,dpiY As Long
  Local pCreateStruct As CREATESTRUCT Ptr
  Local hCtl,hDC,hFont,hTmp As Dword
  Local rxRatio,ryRatio As Single
  Local szText As ZStr*64
  Local sz As SIZE

  pCreateStruct=Wea.lParam : Wea.hInst=@pCreateStruct.hInstance
  hDC=GetDC(Wea.hWnd)
  dpiX=GetDeviceCaps(hDC, %LOGPIXELSX)
  dpiY=GetDeviceCaps(hDC, %LOGPIXELSY)
  rxRatio = dpiX/96
  ryRatio = dpiY/96
  Call MoveWindow(Wea.hWnd, SizX(200), SizY(100), SizX(775), SizY(475), %FALSE)
  hFont=CreateFont(-MulDiv(12.0, dpiY, 72),0,0,0,%FW_BOLD,0,0,0,%DEFAULT_CHARSET,0,0,0,0,"Lucida Console")
  SetWindowLong(Wea.hWnd,0,hFont)
  hTmp=SelectObject(hDC,hFont)
  szText="PowerBASIC : Compile Without Compromise!"
  GetTextExtentPoint32(hDC,szText,Len(szText),sz)
  iCx3DBorder=GetSystemMetrics(%SM_CXEDGE)
  iCy3DBorder=GetSystemMetrics(%SM_CYEDGE)
  iWidth  = iCx3DBorder * 2 + sz.cx
  iHeight = iCy3DBorder * 2 + sz.cy
  'hCtl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit",szText,%WS_CHILD Or %WS_VISIBLE,SizX(10),SizY(10),iWidth,iHeight,Wea.hWnd,%ID_TEXT,Wea.hInst,ByVal 0)
  hCtl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit",szText,%WS_CHILD Or %WS_VISIBLE,SizX(10),SizY(10),SizX(444),SizY(20),Wea.hWnd,%ID_TEXT,Wea.hInst,ByVal 0)
  SendMessage(hCtl,%WM_SETFONT,hFont,0)
  SelectObject(hDC,hTmp)
  ReleaseDC(Wea.hWnd,hDC)

  fnWndProc_OnCreate=0
End Function


Function fnWndProc_OnPaint(Wea As WndEventArgs) As Long
  Local dwWndHeight,dwWndWidth,dwClientHeight,dwClientWidth As Dword
  Local iBkMode,dpiX,dpiY,iCx3DBorder,iCy3DBorder As Long
  Local szBuffer,szText As ZStr*80
  Local rxRatio,ryRatio As Double
  Local hDC,hFont,hTmp As Dword
  Local ps As PAINTSTRUCT
  Local tm As TEXTMETRIC
  Local hCtl As Dword
  Local rc As RECT
  Local sz As SIZE

  hDC=BeginPaint(Wea.hWnd,ps)
  iBkMode=SetBkMode(hDC,%TRANSPARENT)
  dpiX=GetDeviceCaps(hDC, %LOGPIXELSX)
  dpiY=GetDeviceCaps(hDC, %LOGPIXELSY)
  rxRatio = dpiX/96
  ryRatio = dpiY/96
  hFont=GetWindowLong(Wea.hWnd,0)
  hTmp=SelectObject(hDC,hFont)

  ' Output rxRatio/ryRatio, i.e., DPI Scaling Factors
  szBuffer="1)  rxRatio           = " & Format$(rxRatio,"#.0000")       & "  X DPI Scaling Factor"                  : TextOut(hDC,SizX(10),SizY(55),szBuffer,Len(szBuffer))
  szBuffer="2)  ryRatio           = " & Format$(ryRatio,"#.0000")       & "  Y DPI Scaling Factor"                  : TextOut(hDC,SizX(10),SizY(85),szBuffer,Len(szBuffer))

  ' Get HWND of Edit Control And Get Overall Size Of Edit Control As When It Was Created With CreateWindowEx() in fnWndProc_OnCreate().
  hCtl=GetDlgItem(Wea.hWnd,%ID_TEXT)
  GetWindowRect(hCtl,rc)
  dwWndWidth=rc.Right-rc.Left
  szBuffer="3)  dwWndWidth        = " & LTrim$(Str$(dwWndWidth))        & "     Edit Control Total Width"           : TextOut(hDC, SizX(10), SizY(115), szBuffer, Len(szBuffer))
  dwWndHeight=rc.Bottom-rc.Top
  szBuffer="4)  dwWndHeight       = " & LTrim$(Str$(dwWndHeight))       & "      Edit Control Total Height"         : TextOut(hDC, SizX(10), SizY(145), szBuffer, Len(szBuffer))

  ' Get From GetSystemMetrics() The 3D Size of the Edit Control Borders, Which Reduces The Usable Space Inside The Edit Control For Text By Several Pixels.
  iCx3DBorder=GetSystemMetrics(%SM_CXEDGE)
  iCy3DBorder=GetSystemMetrics(%SM_CYEDGE)
  szBuffer="5)  iCx3DBorder       = " & LTrim$(Str$(iCx3DBorder))       & "       Edit Control 3D Border Width"     : TextOut(hDC,SizX(10), SizY(175), szBuffer, Len(szBuffer))
  szBuffer="6)  iCY3DBorder       = " & LTrim$(Str$(iCy3DBorder))       & "       Edit Control 3D Border Height"    : TextOut(hDC,SizX(10), SizY(205), szBuffer, Len(szBuffer))

  ' Get The Usable Space Inside The Edit Control For Text, which numbers can be gotten from GetClientRect().
  GetClientRect(hCtl,rc)
  dwClientWidth=rc.Right-rc.Left
  szBuffer="7)  dwClientWidth     = " & LTrim$(Str$(dwClientWidth))     & "     Edit Control Client Rect Width"     : TextOut(hDC, SizX(10), SizY(235), szBuffer, Len(szBuffer))
  dwClientHeight=rc.Bottom-rc.Top
  szBuffer="8)  dwClientHeight    = " & LTrim$(Str$(dwClientHeight))    & "      Edit Control Client Rect Height"   : TextOut(hDC, SizX(10), SizY(265), szBuffer, Len(szBuffer))

  ' Get The Size, i.e., TEXTMETRICS, Of The Font Currently Selected Into The Device Context.
  Call GetTextMetrics(hDC,tm)
  szBuffer="9)  tm.tmHeight       = " & LTrim$(Str$(tm.tmHeight))       & "      Char Total Height At Font Size"    : TextOut(hDC, SizX(10), SizY(295), szBuffer, Len(szBuffer))
  szBuffer="10) tm.tmAveCharWidth = " & LTrim$(Str$(tm.tmAveCharWidth)) & "      Char Total Width At Font Size"     : TextOut(hDC, SizX(10), SizY(325), szBuffer, Len(szBuffer))

  ' Get The Text Out Of The Edit Control And Determine Its Length And Height In Pixels With GetTextExtentPoints32().
  GetWindowText(GetDlgItem(Wea.hWnd,%ID_TEXT),szBuffer,64)
  GetTextExtentPoint32(hDC,szBuffer,Len(szBuffer),sz)
  szBuffer="11) sz.cx             = " & LTrim$(Str$(sz.cx))             & "     Pixel Width  of Edit Control Text"  : TextOut(hDC,SizX(10), SizY(355), szBuffer, Len(szBuffer))
  szBuffer="12) sz.cy             = " & LTrim$(Str$(sz.cy))             & "      Pixel Height of Edit Control Text" : TextOut(hDC,SizX(10), SizY(385), szBuffer, Len(szBuffer))

  ' Close Out, Restore Device Context, And Exit Procedure.
  Call SetBkMode(hDC,iBkMode)
  SelectObject(hDC,hTmp)
  Call EndPaint(Wea.hWnd,ps)

  fnWndProc_OnPaint=0
End Function


Function fnWndProc_OnDestroy(Wea As WndEventArgs) As Long
  Local blnFree As Long
  Local hFont As Dword

  hFont=GetWindowLong(Wea.hWnd,0)
  blnFree=DeleteObject(hFont)
  Call PostQuitMessage(0)

  Function=0
End Function


Sub AttachMessageHandlers()
  Dim MsgHdlr(2) As Global MessageHandler 'Associate Windows Message With Message Handlers
  MsgHdlr(0).wMessage=%WM_CREATE          : MsgHdlr(0).dwFnPtr=CodePtr(fnWndProc_OnCreate)
  MsgHdlr(1).wMessage=%WM_PAINT           : MsgHdlr(1).dwFnPtr=CodePtr(fnWndProc_OnPaint)
  MsgHdlr(2).wMessage=%WM_DESTROY         : MsgHdlr(2).dwFnPtr=CodePtr(fnWndProc_OnDestroy)
End Sub


Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
  Local Wea As WndEventArgs
  Register iReturn As Long
  Register i As Long

  For i=0 To 2
    If wMsg=MsgHdlr(i).wMessage Then
       Wea.hWnd=hWnd: Wea.wParam=wParam: Wea.lParam=lParam
       Call Dword MsgHdlr(i).dwFnPtr Using FnPtr(Wea) To iReturn
       fnWndProc=iReturn
       Exit Function
    End If
  Next i

  fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function


Function WinMain(ByVal hInstance As Long, ByVal hPrevIns As Long, ByVal lpCmdLn As ZStr Ptr, ByVal iShowWnd As Long) As Long
  Local szAppName As ZStr*16
  Local wc As WNDCLASSEX
  Local hWnd As Dword
  Local Msg As tagMsg

  SetMyProcessDpiAware()                                 : Call AttachMessageHandlers()
  szAppName        = "Font2"                             : wc.lpszClassName = VarPtr(szAppName)
  wc.lpfnWndProc   = CodePtr(fnWndProc)                  : wc.hInstance     = hInstance
  wc.hCursor       = LoadCursor(%NULL, ByVal %IDC_ARROW) : wc.hbrBackground = %COLOR_BTNFACE+1
  wc.cbSize        = sizeof(WNDCLASSEX)                  : wc.cbWndExtra    = 4
  Call RegisterClassEx(wc)
  hWnd=CreateWindowEx(%WS_EX_CLIENTEDGE,szAppName,szAppName,%WS_OVERLAPPEDWINDOW,0,0,0,0,0,0,hInstance,ByVal 0)
  Call ShowWindow(hWnd,iShowWnd)
  While GetMessage(Msg,%NULL,0,0)
    TranslateMessage Msg
    DispatchMessage Msg
  Wend

  Function=msg.wParam
End Function


Patrice Terrier

#1
Welcome to the DPI change nightmare.

From my own test i found that the scalling factor that looks good for one font, doesn't produce necessarily the same result with another, because of the font chasse being used (try mixing with italic characters to see what i mean).

The only way to produce a good result is to use your own virtualization system, aka: a back buffer to render the font at 200% (usually the maximum DPI value), then stretchblit the resulting DIB in HALFTONE mode to the final DC (subclassing), then you can have exactly the good pixel size you want.

But from my opinion, the best is to avoid changing the default DPI setting of the display, especially when running other applications that are not DPI aware. And buy an external display large enough, for those suffering with eye disability.
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Frederick J. Harris

Thanks for the input Patrice.  I know this is something you know much more about than I.

From your response I gather that my conclusion is correct, and that I should not expect perfect pixel by pixel scaling.  And further, there is nothing of universal applicability that can be done to those first two parameters of the CreateFont() call, such as multiplication by some simple to obtain ratio, that will cause everything to match up perfectly.  In my case above with the 1.5 scaling factor, it appears the scaling factor on the font might have been like 1.4 maybe, or whatever.  In any case, different from the DPI scaling factor of 1.5.

The resulting application turned out OK in any case.  I had a version of the app where I didn't call SetProcessDPIAware(), and of course that turned out extremely poorly when the DPI settings were altered from the default.  The text in the edit control was badly clipped. 

I always run my computers with everything set to default, but I have noted already folks who use my programs alter their DPI settings, and that sometimes messes up my displays.  So I'm trying to code everything DPI Aware now.     

Patrice Terrier

#3
One solution i like to use with my graphic applications is to work with my own set of private font.

Giving me the insurance that they are always available whatever the computer being used.

You can see an example of such fonts scalled in real time in this project.
http://www.jose.it-berater.org/smfforum/index.php?topic=4896.0

Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com