View Full Version : Font points vs. twips and pixels
MacroShadow
05-07-2012, 12:36 AM
I managed to get very confused. Word's font and spacing settings are in points. Are they related in any way to twips and pixels (other than the fact that they are both measurements)?
In other words are Word's point sizes dependable on the monitor or printer settings?
I found a class by Joacim Andersson (attached at the end) that returns the width and height in pixels for specified string and font settings (it also measures the width and height of a string containing multiple lines).
What is the relation between Word's points and that class's pixels (or twips)?
''---------------------------------------------------------------------------------------
''---------------------------------------------------------------------------------------
'' Module : CTextSize
'' Author : Joacim Andersson @ http://www.vbforums.com
'' Date : 07/05/2012
'' Purpose : Return the width and height in pixels
'' it will also measure the width and height
'' of a string containing multiple lines
''---------------------------------------------------------------------------------------
''---------------------------------------------------------------------------------------
'' Sample usage:
''
'' Sub test()
'' Dim o As CTextSize
'' Set o = New CTextSize
'' With o.Font
'' .Name = "Times New Roman"
'' .SIZE = 12
'' .Bold = True
'' End With
'' MsgBox o.TextHeight("Hello World!")
'' End Sub
''---------------------------------------------------------------------------------------
''---------------------------------------------------------------------------------------
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(LF_FACESIZE) As Byte
End Type
Private Const FW_NORMAL = 400
Private Const FW_BOLD = 700
Private Const FF_DONTCARE = 0
Private Const DEFAULT_QUALITY = 0
Private Const DEFAULT_PITCH = 0
Private Const DEFAULT_CHARSET = 1
Private Declare Function CreateFontIndirect Lib "gdi32" _
Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
Private Declare Function GetDC Lib "user32.dll" _
(ByVal hwnd As Long) As Long
Private Declare Function MulDiv Lib "kernel32" _
(ByVal nNumber As Long, ByVal nNumerator As Long, _
ByVal nDenominator As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" _
(ByVal hObject As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Const LOGPIXELSY = 90
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function DrawText Lib "user32" _
Alias "DrawTextA" (ByVal hdc As Long, _
ByVal lpStr As String, ByVal nCount As Long, _
lpRect As RECT, ByVal wFormat As Long) As Long
Private Const DT_CALCRECT = &H400
Private Declare Function SelectObject Lib "gdi32" _
(ByVal hdc As Long, ByVal hObject As Long) As Long
Private m_Font As StdFont
Public Property Get Font() As StdFont
Set Font = m_Font
End Property
Public Function TextWidth(ByVal sText As String) As Long
Dim r As RECT
r = GetFontSize(sText)
TextWidth = r.Right
End Function
Public Function TextHeight(ByVal sText As String) As Long
Dim r As RECT
r = GetFontSize(sText)
TextHeight = r.Bottom
End Function
Private Sub OLEFontToLogFont(fntThis As StdFont, ByVal hdc As Long, tLF As LOGFONT)
Dim sFont As String
Dim iChar As Integer
Dim b() As Byte
' Convert an OLE StdFont to a LOGFONT structure:
With tLF
sFont = fntThis.Name
b = StrConv(sFont, vbFromUnicode)
For iChar = 1 To Len(sFont)
.lfFaceName(iChar - 1) = b(iChar - 1)
Next iChar
' Based on the Win32SDK documentation:
.lfHeight = -MulDiv((fntThis.SIZE), (GetDeviceCaps(hdc, LOGPIXELSY)), 72)
.lfItalic = fntThis.Italic
If (fntThis.Bold) Then
.lfWeight = FW_BOLD
Else
.lfWeight = FW_NORMAL
End If
.lfUnderline = fntThis.Underline
.lfStrikeOut = fntThis.Strikethrough
.lfCharSet = fntThis.Charset
End With
End Sub
Private Sub Class_Initialize()
'set a standard font
Set m_Font = New StdFont
m_Font.Name = "MS Sans Serif"
m_Font.SIZE = 8
End Sub
Private Function GetFontSize(ByVal sText As String) As RECT
Dim hdc As Long
Dim tLF As LOGFONT
Dim hFnt As Long, hFntOld As Long
Dim tR As RECT
hdc = GetDC(0)
OLEFontToLogFont m_Font, hdc, tLF
hFnt = CreateFontIndirect(tLF)
hFntOld = SelectObject(hdc, hFnt)
DrawText hdc, sText, -1, tR, DT_CALCRECT
SelectObject hdc, hFntOld
DeleteObject hFnt
GetFontSize = tR
End Function
Private Sub Class_Terminate()
Set m_Font = Nothing
End Sub
fumei
05-07-2012, 01:32 AM
A pixel is a single square 'picture element' (hence pix-el), i.e. a single dot in your image. A 10x10 image is made up of a set of a set of pixels in a grid 10 wide by 10 deep, totalling 100 pixels.
The 'point' (pt) on the other hand is a unit of length, commonly used to measure the height of a font, but technically capable of measuring any length. 1pt is equal to exactly 1/72th of an inch
How many pixels = 1pt depends on the resolution of your image. If your image is 72ppi (pixels per inch), then one point will equal exactly one pixel
What are you up to?
MacroShadow
05-07-2012, 05:37 AM
How many pixels = 1pt depends on the resolution of your image. In this case there aren't any images, I'm dealing with calculating the height of a specific word (more on that at the end of this post), so what is the pixel-point ratio dependable upon? Will the PointsToPixels and PixelsToPoints methods return the same result across multiple machines? How can there be online tools for converting points to pixels if pixels are machine dependent?
Are the results returned by the above class different on different machines?
I hope this clarifies my confusion.
Here's the overall picture:
I use a macro that displays the font dialog, and then loops through all paragraphs in a document, applying the chosen settings to the first word in each paragraph. Generally the user will choose a different and or larger font, and at times with a different weight.
The trouble is that the line spacing between the first and second lines will appear to be larger than the rest of the paragraph, unless the LineSpacing Rule is set to 'exactly', and the LineSpacing for those lines is set to a slightly larger spacing than the rest of the paragraph.
In order to determine by how much the LineSpacing has to be increased I must measure the height of the first word after the users settings are applied to the first word as chosen by the user, that I do using the above class, the trouble here is that the result is returned in pixels, and the LineSpacing has to be increased by points. That issue could be solved if the PixelsToPoints method does return the same results across different machines.
Thanks for your time:)
Frosty
05-07-2012, 09:28 AM
I don't think you want to involve yourself with pixels at all. Fumei has already explained, but you're describing a process by which you
a) get something's value in points
b) convert that value to pixels
c) use the class to get a measurement of something in pixels
d) convert that value back to points
e) change something's value to the new points value
Doesn't that seem a bit circuitous, even if it works (which I'm not sure it would, although I would need to test)?
For example... what happens to the "value" you get when
1. You're at something other than 100% zoom in Word
2. You change your resolutions (and don't just try appropriate ratios like 800x600 and 1280x1024... try a widescreen ratio on a square monitor and vice versa.
In general, dealing with pixels is going to be for something very specific-- graphic resolutions on web-page development.
All you want to do is change the font size of the first word of a paragraph, and then make sure the line-spacing of that paragraph looks consistent, right?
I would suggest, rather than trying to utilize a class you don't understand in a way it probably wasn't really meant for... that you work to develop a method of testing the various value combinations. I mean, there are only so many font-size and line-spacing combinations available to you in Word. And if you narrow down your initial tests to the most likely combos (guessing: something between 8pt and 24pt font size, something between 10 and 30 line spacing), it probably wouldn't take that long to test.
For example... I know I've talked about the immediate window in other posts. I would use this function in the immediate window, and just start seeing what combo's work the best.
'optional parameter means you can later pass in a paragraph, rather than always use selection
Public Sub TestingValues(siFontSize As Single, siLineSpacing As Single, Optional oPara As Paragraph)
Dim rngWhere As Range
If oPara Is Nothing Then
Set oPara = Selection.Paragraphs(1)
End If
'first word
Set rngWhere = oPara.Range
With rngWhere.Words(1).Font
'get the original value
Debug.Print "Orig Font Size: " & .Size
'and set the new one
.Size = siFontSize
End With
'line spacing
Debug.Print "Orig LineSpacing: " & oPara.LineSpacing
oPara.LineSpacing = siLineSpacing
End Sub
I could spend a bunch of time trying to explain the class to you, but I'm not sure it's the best fit for what you need, especially when it's likely to give you "bad" values in some cases (when someone deliberately, for whatever reasons, uses a "bad" screen resolution ratio).
MacroShadow
05-07-2012, 01:17 PM
Is your suggestion of testing the various value combinations effective, after all even though the font-size and line-spacing combinations available in Word are limited, the font a user may use is not, and since different fonts take up different amount of space, testing font-size and line-spacing combinations doesn't seem what I'm after.
Well I'm not sure what process you're referring to. What I would like to do doesn't seem roundabout to me, but what do I know... Please show me where I'm wrong.
Now that I seriously think about it is pretty complicated:
a) ask user to manually choose settings that appear well on their device
b) use the class to find height of first word in the paragraph (x) in pixels (for lack of another method)
c) use the class to find height of second word in the paragraph (y) in pixels (for lack of another method)
d) use this formula: x/(linespacing chosen by user)=z
e) store value of z somewhere
f) set the .LineSpacing of the two first lines as following .LineSpacing = .LineSpacing + (x-y*z)
Frosty
05-07-2012, 01:27 PM
Hmm, maybe I'm the one that's confused. Are you saying that there are fonts you have used which have different heights, even with the same font size applied? Obviously lower-case vs. upper-case is different, but I would think that no matter what font you have, you're dealing with a maximum height which is the actual font-size.
If that's not the case, then I think your problem is even worse than previously described... and you are *really* getting into the realm of trying to have Word act like Quark.
So I might re-think the whole thing, formatting-wise. You are using the first word of the sentence in some kind of "drop cap" way?
I think you need to post a couple of samples of the kind of end-result you want. Forget the VBA code for a moment... there may be a better way to do this (i.e., using a frame or some other formatting method) to get what you want.
MacroShadow
05-07-2012, 02:24 PM
Yes, different fonts have different dimensions even at the same (point) size. That can easily be proven using the class.
I thought about using a frame but dropped it for two reasons: a) I don't know how to replace the text with a frame at the same location. b) I think it will create problems with copying and pasting the document.
In the attachment please note the difference between the first two paragraphs, and all the rest.
fumei
05-07-2012, 02:24 PM
"In this case there aren't any images,"
Ummmmm, what you see on screen IS an image.
It seems you are trying to be Quark-like. Good luck with that. Do let us know how it works out. I personally think it is way more work that it is worth, and I have real issues with:
"ask user to manually choose settings that appear well on their device"
Are you serious??? There are existing ways through the Windows UI to adjust things (crudely I admit, but still...close enough). What if they change things, even zooming?
Yes, different fonts have different dimensions even at the same (point) size. That can easily be proven using the class.Yes, true. And is your point that you want to mitigate this???
MacroShadow
05-07-2012, 02:26 PM
Yes but in word what counts, at least for me if the printout.
Frosty
05-07-2012, 02:37 PM
Hmm... sorry, I don't have that much time to investigate what the class is actually proving.
Are you certain the differences in height are meaningful from Word's point of view? I could see where "W" is a slightly different height for one font vs. another at 12 pt font, but I would be surprised if values for the line spacing actually differed.
I don't think Word is that complicated on how it renders a truetype font...
A couple of things I notice from your attachment:
1. Even though you saved as a .doc... this clearly started life in Word 2007 or 2010, since all of those paragraphs have "multiple" line spacing of 1.15 (except for the first 2).
2. Since I don't have the font "Rockwell Extra Bold" on my system, I don't see the exact things you are talking about in terms of what might look better or not... all I see if you using exact line spacing of 14 pt and exact line spacing of 15 pt on the two paragraphs.
Are you sure using "at least" wouldn't serve your purposes better? I'm still not sure what you want your final look to be. Do you really want different line spacings between all these different paragraphs? Or do you want the same line spacing for each "article" in this newspaper... and from there, you simply make it consistent based on whatever was required by the first word?
It's really tough... because I don't know enough to say definitively that you're approaching this "all wrong" (my apologies for being direct), but that is the sense I'm getting.
For advanced "tweaking" methodologies... I would simply present my users with a non-modal dialog box with the values they care about readily available, with good accelerator keys. A lot can be accomplished from a good UI, rather than trying to code all of the various potentials.
Or, in summary-- wouldn't it be faster to facilitate the end-user being able to quickly tweak the document, rather than trying to code for all possibilities of tweaking a document?
fumei
05-07-2012, 03:14 PM
"Yes but in word what counts, at least for me if the printout."
Assuming that "if" is an "is", then why are you concerned (it seems, but I may very well be confused) with how it appears on screen?
Word IS designed regarding pinting. That is why everything is based on the current printer driver. And why it uses points. 1/72 of an inch is relevant when you are trying to place text on an 8.5" physical paper.
Pixels are less so, because it depends directly not on the paper to be printed on, but resolution and the actual pixel sizes of THAT monitor.
I must admit I am still struggling to get a good grasp of what you want to achieve. The attachment did not help really.
Frosty
05-07-2012, 03:57 PM
I *think* the problem MacroShadow is trying to solve is the funny look Word gives you when you have variable fonts in a single paragraph. In a paragraph with 12 pt font, at least 14 pt line spacing... make the first word 72 pt font size.
The line spacing between the 1st line and the 2nd line is a lot more than the 2nd line and the 3rd line. I've forgotten all the areas that have to do with tweaking this value, but I know kerning has something to do with. But I also think there might be something in Tools > Options which handles this formatting.
I *think* MacroShadow is trying to make the entire paragraph use the same line spacing as the distance between the 1st and 2nd lines.
I *think* hehe
fumei
05-07-2012, 09:07 PM
Kerning has a LOT to do with all of this stuff.
MacroShadow
05-08-2012, 02:57 AM
I don't know were to begin...
I guess I'll start by trying to clarify what I'm after. When the first word in a paragraph is larger or of heavier weight that the rest of the paragraph, the spacing between the first and second lines in reality is larger then the space between the other lines.
I would like to mitigate the difference.
How I accomplish that:
a) set the line spacing rule to exactly
b) set the line spacing to a larger value than the text in the paragraph (and smaller than the the size of the first word), what this value is, is what I'm trying to determine.
This being done the linespacing of the entire paragraph is uniform, and appears so too.
What I'm trying to do is determine the linespacing to use to achieve the aforementioned result.
How I thought it would work:
a) the first time the macro is run on a machine have the user manually (using Words UI) adjust the line settings to achieve the aforementioned result in the printout.
b) determine the height of the first word (x)
c) determine the height of the second word (y)
d) subtract y from x to return the difference between them (z) and store that value somewhere
e) then for all future paragraphs set the linespacing to x-y*z which hopefully should produce the desired result.
Where I got stuck:
Determining the height of the letters, since (if i understood correctly,) pixels which are the way the class returns results have nothing to do with the *real* difference in height between the words.
I hope this gets us somewhere (like on the same page;)).
Frosty
05-08-2012, 07:29 AM
Hmm...
Your "z" value would be called the Line Spacing value of a style. Maybe just normal, if people don't understand. But you don't need to store values which you're going to translate into a format value as something other than what it is-- styles.
The more you explain, the more I'm convinced that in a funny kind of way... answering your coding questions is slowing down your learning of how Word works.
VBA, first and foremost, should generally be a facilitator of using Word properly. Otherwise you spend a lot of time coding around your own ignorance.
To me, the problem you want to solve is:
1. Make it easier for the end-user to "tweak" the specific settings (font size of first word of a paragraph, and line-spacing of that same paragraph, so that it looks right).
2. Make the user not need to ever do that again for a particular document.
Okay... here's how to do it:
1. Create a User Interface form (non-modal) which sets both the value of the selection to a font-size (which allows the user to select the first word or a couple of words of the sentence). When they select the size, make sure a character style called "First Word" exists in the document... if it doesn't, create it and apply to the selection. When the user comes back... you can make sure to adjust the character style values as well as the selection values from within that dialog-- facilitating the proper use of a style.
2. Adjust the line spacing through the dialog... changing the style of either Normal or a custom paragraph style. Again, synch up with the selection's first paragraph, as well as the style.
Both of your problems are solved on a one-time per document basis, but you've created a stream-lined way of doing it.
Read up about character styles and paragraph styles... the functionality you want is already (mostly) built in... the only difference is how you determine those values you wish to store. And, in my opinion, the easiest way to determine those values is to present your own UI which gives that ability in the easiest manner.
Frosty
05-08-2012, 08:52 AM
I guess from a cost/benefit analysis of the time involved, with the approach being an easier way to use word properly, you can add in a "best guess" on line spacing based on font size (and name?), but it should leave the final tweaking (which is really just an aesthetic choice, at the end of the day) in the hands of the end user, not your coding logic.
fumei
05-08-2012, 04:07 PM
A well reasoned and expressed post.
"VBA, first and foremost, should generally be a facilitator of using Word properly. Otherwise you spend a lot of time coding around your own ignorance."
macroshadow, I am inclined to agree with Frosty, focusing so much on a coded approach reduces your deep knowledge of Word. It also complicates using VBA itself.
Paul_Hossler
05-08-2012, 05:01 PM
It's been interesting.
FWIW, I think the typeface parameters (x-height, baseheight, etc. will greatly complicate an 'automated' approach.
If you want to peruse, dispite the wise words above, this uses the vertical dpi for the monitor and the pixels from the class, multiples by 72 pts/in
I tried the same point size, different fonts, as as expected got different measurements.
24 pt Garamond came back with 30.75 and Arial Black came back with 33.75
Option Explicit
'' Sample usage:
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function GetDC Lib "user32.dll" _
(ByVal hwnd As Long) As Long
Sub test()
Const LOGPIXELSY = 90
Dim o As CTextSize
Dim iTwips As Double
Dim hDC As Long
Dim iDPI As Double
hDC = GetDC(0)
iDPI = GetDeviceCaps(hDC, LOGPIXELSY)
Set o = New CTextSize
With o.Font
.Name = Selection.Font.Name
.Size = Selection.Font.Size
.Bold = Selection.Font.Bold
End With
iTwips = o.TextHeight(Selection.Text)
MsgBox 72 * (iTwips / iDPI)
End Sub
Paul
fumei
05-08-2012, 06:29 PM
Dim o As CTextSize
What is CTextSize?
MacroShadow
05-08-2012, 09:08 PM
CTextSize is the name of the aforementioned class.
Thank you all for your input. I think I will follow Frosty's advice. As far as "coding around your own ignorance" I like that line, for it is said that about 90% of Words users know only about 10% of its capabilities.
Paul, what are you trying to accomplish, the results received depend on the monitor, so there is no way of retrieving the real height.
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.