Sign in to follow this  
andy82

CreateFont() paranoia

Recommended Posts

Hi, I'm trying to break down a full font specification string (containing the font's face name _and_ its styles) into its constituents, so that I can pass it to the CreateFont() Win32 API function. For example, consider the following full font specification string: "Arial Bold Italic" This should be broken down into face name "Arial", stem weight FW_BOLD and Italic set to TRUE. This is of course very easy to do, but now I discovered that there are actually fonts which use "Bold" or "Italic" in their face names (!). For example, "Adobe Caslon Pro Bold" is such a face name. In that case, I must not cut that "Bold" off in this case and use "Adobe Caslon Pro" as face name. That wouldn't work because the face name is actually "Adobe Caslon Pro Bold"! So how am I supposed to handle this case? Background for my concern: Under Mac OS X, this is much easier. OS X has the ATSUFindFontFromName() API which allows me to specify a full font specification and then OS X does everything else automatically. Under Win32, however, it is expected that face name and style settings are specified separately. I'm porting an application from OS X to Win32 and my app uses these full font specifications to refer to fonts, so I somehow need to find a way to break these down into something CreateFont() can understand so that I can open the fonts under Win32, too. Does someone have an idea how I could do that? To sum up my concern, what I'm looking for is an algorithm to break full font specification strings down into something that CreateFont() can understand (or alternatively, break these strings down into a LOGFONT structure for CreateFontIndirect()). Here are some examples of full font specifications that my algorithm would need to handle: Arial Bold Italic --> must be broken down into: lfFaceName="Arial" lfWeight=FW_BOLD lfItalic=TRUE Adobe Caslon Pro Bold Italic --> must be broken down into: lfFaceName="Adobe Caslon Pro Bold" (sic!) lfWeight=FW_BOLD lfItalic=TRUE Adobe Caslon Pro Semi-Bold --> must be broken down into: lfFaceName="Adobe Caslon Pro" (sic!) lfWeight=FW_SEMIBOLD lfItalic=FALSE Times New Roman Bold --> must be broken down into: lfFaceName="Times New Roman" lfWeight=FW_BOLD lfItalic=FALSE Adobe Song Std L Light --> must be broken down into: lfFaceName="Adobe Song Std L" lfWeight=FW_LIGHT lfItalic=FALSE etc. My only proposal to solve this problem is to enumerate all fonts in the system and start searching for the full font specification first. If it is not found, remove one style constituent from the string (e.g. remove "Italic") and start searching all over again. If nothing is found, remove the next style constituent (e.g. remove "Bold") and start all over again. But still, this is not a perfect algorithm because I actually would have to try all possible combinations. E.g. given the specification Adobe Caslon Pro Bold Italic I'd have to search for the following face names: 1) Adobe Caslon Pro Bold Italic 2) Adobe Caslon Pro Bold 3) Adobe Caslon Pro Italic 4) Adobe Caslon Pro I guess it would be possible this way but this really leads me to the question: Is there really no easier way to do what I want to do? Doesn't Win32 offer an API to get a LOGFONT from a full font specification? Do I really have to do this manually? Thanks for reading this long post and please post any suggestions you may have. Thanks, Andy

Share this post


Link to post
Share on other sites
Quote:
a full font specification string (containing the font's face name _and_ its styles)

It appears that you've defined what you consider to be the specification. Can you just change the specification to include some sort of start and stop characters for the face name, such as angle brackets? E.g., "<Arial> Bold Italic", "<Adobe Caslon Pro Bold>"

If you're using the Win32 API, can you use the Font dialog?

Share this post


Link to post
Share on other sites
Quote:

It appears that you've defined what you consider to be the specification.


Might be true, but the ATSUI API under OS X understands this specification so it appears to be that I'm not the only one who uses it.

Quote:
Can you just change the specification to include some sort of start and stop characters for the face name, such as angle brackets? E.g., "<Arial> Bold Italic", "<Adobe Caslon Pro Bold>"

If you're using the Win32 API, can you use the Font dialog?


Unfortunately, I can do neither of these. The app that I'm porting over from OS X saves its project files in XML format... so I'm reading the font specifications just from single XML tags. Under OS X I can simply pass the font specifications obtained from the XML parser directly to the text API but under Win32 I somehow need to find a way to turn strings like "Adobe Caslon Pro Bold Italic" into a LOGFONT structure or the CreateFont() equivalents.

Share this post


Link to post
Share on other sites
I would have said quotes around the font name IF it is more than 1 word as well. Same way command line switches normally work. What in XML again doesn't let you have a tag that says for instance
"Arial Bold" Bold Italic
as a tag for a bold+italic "Arial Bold" font?

Then for non-more-than-1-word-font names you don't even need to worry about it.
Arial Bold Italic - Arial font, bold and italic
"Arial Bold" Italic - Arial bold font, italic

Share this post


Link to post
Share on other sites
The problem is that I can't just go and change the syntax of the font specification in the XML because my program has been available for OS X for several years, so there are many existing XML projects which use the font specification as described above. All these projects currently fail to load on Win32 because of the different font specification. Of course, I could introduce a new font specification but if I would do that, it would break the compatibility with older versions of my software and this is really not something I'd like to sacrifice...

Share this post


Link to post
Share on other sites
You'll have to give somewhere then. There is no way to make it so older versions in windows will load the correct font obviously, but you could, in newer versions, have 2 tags. A old-version tag and a new-version tag. Always save both versions. For the old-version tag, if you have a font such as "Arial Bold" you'll have to save it as arial, and let bold be what it already is.

Not sure there are other options given your constraints, but I do feel your pain (working on 20 yr old software here......)

[edit] That was assuming the older versions skip unknown tags

Share this post


Link to post
Share on other sites
Quote:

There is no way to make it so older versions in windows will load the correct font obviously


Well, I think the solution I described in my initial posting could do the job for 90% of all cases. But it's probably very slow because it will enumerate all fonts in the system. I probably would have to employ some caching mechanism then... but I think it would work. But of course it's kind of hackish...

Share this post


Link to post
Share on other sites
I just checked, and calling CreateFontIndirect with face parameter = "Arial Bold Italic", seems to be working properly. This is under Windows 7, and there are indeed separate versions of the Arial font, including Arial Bold, Arial Bold Italic, etc, etc. So there's no need to set the weight and italic parameters for that font.
So is your problem that the Windows system you're working on doesn't have "specializations" of the fonts that are included with OSX? For example it might only have Arial, in which case asking for "Arial Bold Italic" will fail to match anything. In which case you'll need to enumerate all fonts on the system, as you have already suggested.
You can use EnumFontFamiliesEx for this. You'd first try to match "Arial Bold Italic", and if it isn't there, then you'd use "Arial" with the weight and italic parameters set to FW_BOLD and true respectively. But there mightn't be any guarantee that "<font>" with the FW_BOLD parameter is identical to the "<font> Bold" font. Not for all of them anyway.

Share this post


Link to post
Share on other sites
Are you sure that it is working correctly under Windows 7? Did you actually draw some text to see if you really got a bold & italic font from CreateFontIndirect() when specifying "Arial Bold Italic" as face name? I'm asking because here under XP, calling CreateFontIndirect() with "Arial Bold Italic" is working too, of course, except that the font I'm getting is standard Arial without any bold and italic. But my system has the bold & italic variants of Arial installed! Still, I'm getting standard Arial when specifying "Arial Bold Italic" or "Arial Bold" etc. The only way to open the bold & italic variant here under XP is using "Arial" as face name with style set to FW_BOLD and fwItalic to TRUE.

So I'm suspecting that you are getting standard Arial too under Windows 7 because CreateFontIndirect() by design just never fails. I.e. you can also pass "My Non-Existing Dummy Font" as face name and it will succeed. It will just open an other font that matches the specified characteristics.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this