character string lengths

character string lengths

I require to obtain individual character widths and character string lengths for applications such as microsoft word and autocad.

I am using the functions GetTextExtentPoint32 and getcharabcwidths to get this information. If I use fontheights of 12,18 and 24 points using Arial and Bookman old style (bookos), the ratio of the string lengths (arial/Bookos)is 1.06, 1.12 and 0.91 respectively. When I plotthe character strings with a Fortran windows application i indeed get the same ratios. However if i replicate the strings in microsoft word or microsoft paint or autocad the ratio is approx. 0.88 for all font point heights. Why is there a discrepancy ? and is there any method of obtainingcharacter widths forgeneral applications

14 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

It's difficult to tell offhand what is wrong, but I'd like to turn your attention to two finesses in font size handling:

1) Take a closer look at documentation of LOGFONT/lfHeight. When lfHeight is a positive value n, the returned font has "cell height" of n, while when lfHeight < 0, the returned font has "character height" of n. It's not clear from the text, but I think the latter is greater from the former for the size of descent. As far as I can tell, most applications use the latter (lfHeight < 0) to denote a "12pt font".

2) There might be a confusion over the default assumed screen resolution. LOGFONT documentation appears to say 72 dpi, but 96 dpi is also used; (Note: it also depends on Small/Large fonts settings on the system! don't blindly assume 72). GetDeviceCaps(LOGPIXELSY) should tell you the correct value, and that's the one that should be used when converting points to pixels.

Finally, you can use GetTextMetrics to get detailed information about the font.

Jugoslav
www.xeffort.com

SUBROUTINE fontsizes (hdc,width,height)

USE ifwin

IMPLICIT NONE

INTEGER,INTENT(IN) :: hdc

INTEGER,INTENT(OUT) :: width,height

INTEGER :: lret

TYPE(T_TEXTMETRIC) :: tm

lret = GetTextMetrics (hdc,tm)

width = tm%tmAveCharWidth

height = tm%tmHeight

END SUBROUTINE fontsizes

Thanks for the replies - the screen resolution is 96

The problem I am encountering is that I want to use Digital Fortran as a tool to generate a table of character widths which i can then multiply by the font height to get the correct character string length for use in autocad or other applications such as word for windows . In these applications a character string at 24 point heght is twice as long as that for 12 point. In programs from Fortran windows this is not the case.

I use

hfont = CreateFont(nh, 0, 0, 0, 0, 0, 0, 0, &
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, &
DEFAULT_QUALITY,INT(FIXED_PITCH) ,typeface)

to create the font substituting nh and typeface for the appropriate values

and use

ret=getcharabcwidthsfloat(hdc,1,ntv,ABC(1))
ret=getcharwidth32 (hdc,1,ntv,LOC(ABCD(1)))

to get the character widths.

If you run the SAMPLES program cmndlg the problem I am having can be demonstrated. If you insert any string and specify a 12 point and a 24 point font and measure the string length instead of the 24 point string being twice that of the 12 point string it can be say 1.8 times or 2.2 times as long.

Further if you use the above expressions and generate a table for (length of character string ) / (point height), for point heights of say 1- 200 you get an erratic set of values

In these applications a character string at 24 point height is twice as long as that for 12 point.

This should NOT be the case. Character spacing varies with font size, and will be comparatively greater with smaller sizes.

Please see the attached picture. The fonts used are Arial 8,12,16, and 24 points respectively. They appear largely identical (I think Word uses different kerning to match the selected printer). One in the background is from Fortran application, with string lengths in pixels as indicated.

This is the code I used to create the Fortran output. Note, however, that it uses XFTGDI wrappers from my Xeffort library:

CHARACTER(*), PARAMETER:: sText = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890"
INTEGER, PARAMETER:: iSize(4) = (/8,12,16,24/)
INTEGER:: i, iX, iY
REAL:: xPos

LOGICAL:: bSt

bSt = XFillViewport(xDC, X_BRUSH(BS_SOLID, XCOLOR_WHITE, 0))
xPos = 0.
CALL XSetScaling(xDC, 0., 0., MM_POINTS)
DO i=1,4
bSt = XSetFont_W(xDC, "Arial", REAL(iSize(i)))
CALL XGetTextExtent(xDC, sText, iX, iY)
bSt = XTextOut_W(xDC, 0., REAL(xPos), XString(iX))
bSt = XTextOut_W(xDC, 70., REAL(xPos), sText)
xPos = xPos + 1.1*iSize(i)
END DO

XSetScaling introduces logical coordinate system expressed in points (1/72 of an inch). XSetFont_W calls CreateFont with negative lfHeight, equal to 8*96 (screen dpi)/ 72(points per inch) = 11 pixels for 8-point font. XGetTextExtent calls GetTextExtentPoint32.

Does it shed some light?

Jugoslav
www.xeffort.com

...ah, yes, the picture :-).

Message Edited by JugoslavDujic on 02-21-2006 11:33 AM

Jugoslav
www.xeffort.com

I tried using
ret=GetTextExtentPoint32(HDC,charx(1:nexten), nexten,LPSIZE)

to get the lengths of the strings and for arial got

point size length length/pointsize
8 169 21
12 219 18
16 303 19
24 496 21

compared to JugoslavDujic of

point size length length/pointsize
8 243 30
12 371 31
16 490 31
24 752 31

I acknowledge there will discrepancies due to rounding but the length/pointsize in the value I obtained is not acceptable.

I also tried the code supplied by JugoslavDujic, I had to modify to this beacuse i am running version 6.0

my code for the size 8 font is

ret = xfillviewport(xDC,X_BRUSH(BS_SOLID,XCOLOR_WHITE,0))
call XsetScaling(xdc,0.,0.,MM_POINTS)

ret = XsetFont_W(xDC,"Arial",8.,0,0.,0,.false.,.false.,.false.)
call XgettextExtent(xdc,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",ix8,iy8,.false.)

write(95,*)'8.',ix8,iy8

The code compiled OK and ran but i got

8. -2147483648 -2147483648

in the Fort.95 file

The lengths given in the previous thread are for the string
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890" and not

"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

My code will probably not be of use to you as-is; it was an excerpt from a larger application (and Xeffort won't compile on 6.0 anymore). The strange -2147483648 are probably the result that xDC was not initialized -- at least, you should set xDC%hDC to your hDC value and XSetViewport to (0,0,whatever,whatever). I can't repeat your (169, 219, 303, 496) results though. Here's the supposedly equivalent Win32 code (sorry, no time to test it at the moment):

iret = SetMapMode(hDC, MM_TEXT) !this is the default, just in case
iPtSize = 8
iXSF = GetDeviceCaps(xDC%hDC, LOGPIXELSX)
iYSF = GetDeviceCaps(xDC%hDC, LOGPIXELSY)
iPxSize = NINT(iPtSize * REAL(iYSF) / 72)
hFont = CreateFont(-iPxSize...)
hOldFont = SelectObject(hDC, hFont)
...
iret = GetTextExtentPoint32(...)

For (8, 12, 16, 24) pt Arial, you should get iPxSize of (11, 16, 21, 32) pixels respectively, and (at least approximately) my results for the total length (note that my exact string was:

"ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890"

You should pass the negative value to CreateFont. I don't see how you could possibly achieve different results provided normal fonts (iYSF = 96 dpi) :-).

Jugoslav
www.xeffort.com

P.S. I can repeat your results after all (at least, very close). It seems that you made two errors:

1) did not correctly calculate pixel size on the basis of point size, i.e. you did 8 * (96/72) which is still equal to 8 pixels. Best, use floating-point operations.

2) passed the positive value to CreateFont. This yields a font of smaller size than in Word and Paint (for whatever reasons).

Jugoslav
www.xeffort.com

Using the information in the previous two threads by JugoslavDujic

I get the string lengths of 246 375 496 761 which is near enough to 243 , 371, 490 and 752 -

many thanks

PS - to incorporate the Xeffort routines I lifted the source for XFTGDI.f90 and XFTTYPES.f90 and inserted them into my source code . I had to remove a couple of the functions within XFTGDI.f90 which gave compilation errors. After modification of the routines to match the version 6 , the code compiled ok

Just releasied that I used a string length of 38 instead of 37 and accordinglymy figures in reply 11 should be less than I have posted. I think I have probably added an extra character which is about the average widtth of an arial character so my numbres will be 37 / 38 less so tallying with the other results - I can confirm this ion thurs 23rd feb

onfirm i now get the values of 243, 371,490 and 752 for the length of string

Leave a Comment

Please sign in to add a comment. Not a member? Join today