Rotated Text Help Needed
- From: "Mike Williams" <Mike@xxxxxxxxxxxxxxxxx>
- Date: Thu, 6 Oct 2005 20:05:06 +0100
Hi everyone
I've got a little query about rotated fonts that I've never noticed before
and I hope that some of you may be able to help me with it. Actually, I
think it might be just WinXP that has the problem and not Win98, but I can't
check my Win98 machine at the moment. It's a bit of a "long winded"
explanation, but I hope you'll bear with me and check it out for me. Anyway,
here goes . . .
Earlier today I was writing some code that I intended to post in response to
a recent newsgroup question. The code is a simple example of how to produce
text that "wraps around a circle" on a printer (although the same principle,
and the same problem, also occurs when drawing it to the screen). I haven't
yet bothered about aligning the characters so that they "centralise
themselves" against the desired point on the circumference, or so that they
position the "glyph" rather than the character cell at the desired position
(although I will do later) and so the example currently positions the
characters such that the top left corner of the character cell sits at the
desired position. Anyway, here's the problem . . .
Paste the following code into a standard VB Form containing a command button
and then run the project and click the button (make sure you are using an A4
or a US Letter or similar size paper in your printer). As you will see, the
code draws the individual characters of a text string in a circle, with each
character being spaced out by exactly the same amount from the previous
character. It also draws a light grey background rectangle so that you can
see what is going on and it draws a very small circle at each of the
calculated positions around the circumference. At each of those positions it
draws a character from the string, such that the top left corner of each
character cell is positioned at its appropriate point. Additionally, I have
deliberately set the font transparency to False, so that you can see the
character cell as well as the character glyph itself. Okay, so far so good.
Now look carefully at each of the characters. You will (I hope) see that all
of the character cells have exactly the same "cell height" (as you would of
course expect) *except for* the characters that are positioned such that
their rotation is an exact multiple of 90 degrees (top, bottom, left and
right). On my own WinXP machine those four character cells are smaller in
height than the others, by an amount which is significant! The character
"glyph" itself is the same height (that is the height of the actual
character glyph is the same height as other similar character glyphs in
different positions) but the height of the "character cell" itself is quite
different (quite a bit smaller). This means that since the top left corner
of the "character cell" is positioned where it should be, and since the
glyph in each case sits more or less centrally within the cell, the glyph
itself ends up in the wrong position, making it look a bit odd when compared
to the others. Not quite in a circle. Now I know that there are various ways
to code around this, but I don't want to do that until (or unless) I know
that it is a problem which occurs on all machines (or at least on all
machines running the same version of Windows). On a "clock face" generator
that I once wrote the same problem must have existed, but I didn't realise
it because I had included code which moved the character pixel by pixel (in
a hidden picture box) until the glyph itself touched the circle, and so the
clock printed out exactly and I didn't realise that the problem even
existed.
On my WinXP machine, if you look at the printed output you can see that the
"I", "T", "O" and "Space" that sit at each of the four "exact multiple of 90
degree" positions are not quite where they should be. The top left corner of
the character cell is exactly positioned, but the fact that the cell is
slightly less high means that the glyph is not.
Having carried out a quick check (using much larger font sizes) I can see
that in general "rotated fonts" have slightly different cell heights and
glyph heights than character which are printed normally (without using the
CreatFontIndirect API), but that in general these differences are consistent
and so are not a problem. However, the "exact multiple of 90 degree" rotated
fonts are something different, and they have neither a cell height or a
glyph height that is the same as either a standard printed font or a
"rotated font that is not at an exact multiple of 90 degrees". Puzzling.
Sorry for the long posting, but I really would appreciate it if you could
run the following code for me and tell me what result you get (examine the
printed output very carefully). Also, I would be grateful if you could tell
me which version of Windows you are using.
By the way, yoy may find that the first time you click the button you get a
page showing the large circle and the small little position circles and the
text itself (without the grey background rectangle so that you cannot see
the white character cells around each character). If that happens then click
the button again and you should get the output I intended. (Now I remember
why I decided a while ago to ditch the VB printer object and instead to use
the API printer functions for all my printing needs!)
Anyway, here's the code. Please examine the printed output carefully and
tell me in detail what you see. Sorry for being a bit "pushy", bit I really
would appreciate an early response :-)
Mike
Option Explicit
Private Const LF_FACESIZE = 32
Private Type LOGFONT
lfHeight As Long
lfWidth As Long
lfEscapement As Long
lfOrientation As Long
lfWeight As Long
lfItalic As Byte
lfUnderline As Byte
lfStrikeOut As Byte
lfCharSet As Byte
lfOutPrecision As Byte
lfClipPrecision As Byte
lfQuality As Byte
lfPitchAndFamily As Byte
lfFaceName As String * LF_FACESIZE
End Type
Private Declare Function CreateFontIndirect Lib "gdi32" _
Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
Private Declare Function SelectObject Lib "gdi32" _
(ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" _
(ByVal hObject As Long) As Long
Private Declare Function TextOut Lib "gdi32" Alias _
"TextOutA" (ByVal hdc As Long, ByVal x As Long, ByVal _
y As Long, ByVal lpString As String, ByVal nCount _
As Long) As Long
Private Declare Function SetBkMode Lib "gdi32" _
(ByVal hdc As Long, ByVal nBkMode As Long) As Long
Private Const TRANSPARENT = 1
Private Const OPAQUE = 2
Private Const RadToTenthDegree As Single = 572.957795
Private Const pi = 3.14159
Private myhDC As Long
Private new_font As Long, old_font As Long
Private Sub RotateFont(outDevice As Object, angle As Single)
Dim myAngle As Long
myhDC = outDevice.hdc
myAngle = angle * RadToTenthDegree ' convert from radians
Dim log_font As LOGFONT
With log_font
.lfEscapement = myAngle
.lfHeight = outDevice.ScaleY(outDevice.Font.Size * 20, vbTwips, vbPixels)
.lfFaceName = outDevice.Font.Name & vbNullChar
If outDevice.Font.Bold = True Then
.lfWeight = 700
Else
.lfWeight = 400
End If
.lfItalic = outDevice.Font.Italic
.lfUnderline = outDevice.Font.Underline
End With
new_font = CreateFontIndirect(log_font)
old_font = SelectObject(myhDC, new_font)
End Sub
Private Sub CircleText(obj As Object, x1 As Single, y1 As Single, r1 As
Single, s1 As String)
' add code later to check for valid object type
Dim angle As Single, p As Long, n As Long
Dim xp As Single, yp As Single, position As Single
Dim myhDC As Long, ret As Long
obj.ScaleMode = vbInches
p = Len(s1)
angle = (2 * pi) / p
position = pi / 2
myhDC = obj.hdc
For n = 0 To p - 1
xp = x1 - (r1 * Sin(position))
yp = y1 - (r1 * Cos(position))
' hyp = Sqr(testradius * testradius + (xd / 2) * (xd / 2))
' tempangle = ArcSin((xd / 2) / hyp)
' tempangle = tempangle + angle
' (more stuff goes in here later)
xp = obj.ScaleX(xp, vbInches, vbPixels)
yp = obj.ScaleY(yp, vbInches, vbPixels)
RotateFont obj, position
ret = TextOut(myhDC, xp, yp, Mid$(s1, n + 1, 1), 1)
' change the font back and get rid of the new font
SelectObject myhDC, old_font
DeleteObject new_font
' draw a small circle at each position for test purposes
Printer.ScaleMode = vbPixels
Printer.Circle (xp, yp), 12
Printer.ScaleMode = vbInches
position = position - angle
Next n
End Sub
Private Sub Command1_Click()
Printer.Print
Printer.Font.Name = "Arial"
Printer.Font.Size = 32
' set to FontTransparent = False just for test purposes so
' that we can see the actual top left corner of each
' character block (note we use SetBkMode instead of using
' the Printer.FontTransparent property becase the
' FontTransparent property has some odd behaviour when
' printing rotated text characters)
SetBkMode Printer.hdc, OPAQUE
Printer.ForeColor = vbBlack
' draw a light grey rectangle just for test purposes
Printer.FillStyle = vbFSSolid
Printer.Line (0, 0)-(8, 8), RGB(128, 128, 128), BF
Printer.FillStyle = vbFSTransparent
Dim s1 As String
s1 = "IS THIS TEXT PRINTED CORRECTLY OR IS IT NOT "
CircleText Printer, 4, 4, 3.8, s1
' draw a bounding circle just for test purposes
Printer.Circle (4, 4), 3.8
' draw a regular character just for test purposes
Printer.CurrentX = 0: Printer.CurrentY = 0
Printer.Print "I"
Printer.EndDoc
End Sub
.
- Follow-Ups:
- Re: Rotated Text Help Needed
- From: Saga
- Re: Rotated Text Help Needed
- From: Rick Rothstein [MVP - Visual Basic]
- Re: Rotated Text Help Needed
- Prev by Date: Re: Populate A ListBox!
- Next by Date: Change Open/Save dialog path in IDE
- Previous by thread: Populate A ListBox!
- Next by thread: Re: Rotated Text Help Needed
- Index(es):