4

With CreateFont one can specify font name and a bunch of other properties. However, what if I have a font.ttf file, and I want that particular font to be loaded by windows? How do I specify that specific file to be used?

7
  • Wouldn't CreateFontIndirectEx be an option then? There you can pick and choose. Commented Apr 22, 2011 at 16:00
  • @STATUS_ACCESS_DENIED: i don't see anywhere to specify a filename, just a 'face name', which i suspect is looked up in the system font table somewhere. maybe i could AddFontResourceEx, then use it, then RemoveFontResourceEx, but then how do i figure out under what name windows put it so i can load it with CreateFont? Commented Apr 22, 2011 at 16:04
  • Alternative; GDI+ PrivateFontCollection you can then possibly enumerate the collection & fetch the family name Commented Apr 22, 2011 at 16:15
  • @Claudiu: so you know the file name but not the font name? Or rather you have to expect that the font name and properties will not be sufficient to distinguish your (installed) font from others? I haven't found any solution to loading a font (except the old style fonts from PE resource) without installing when I looked for this a few years again. I'd put in quite some time back then. Commented Apr 22, 2011 at 16:21
  • @STATUS_ACCESS_DENIED: it's more like i have a user-supplied font file i want to use, that isn't necessarily installed. my program can install it - that's fine. but how do i get that particular installed font? the filename can be foo.ttf - it doesn't say anything about its logical nam Commented Apr 22, 2011 at 16:25

5 Answers 5

4

It's admittedly rather indirect, but you could utilize GDI interop with DWrite when running on Windows 7+.

#include <Windows.h>
#include <WindowsX.h>
#include <DWrite.h>

...

// Make the font file visible to GDI.
AddFontResourceEx(fontFileName, FR_PRIVATE, 0);
if (SUCCEEDED(GetLogFontFromFileName(fontFileName, &logFont)))
{
    logFont.lfHeight = -long(desiredPpem);
    HFONT hf = CreateFontIndirect(&logFont);
    HFONT oldFont = SelectFont(hdc, hf);
    ...
    // Do stuff...
    ...
    SelectFont(hdc, oldFont);
}
RemoveFontResource(fontFileName);

....

HRESULT GetLogFontFromFileName(_In_z_ wchar const* fontFileName, _Out_ LOGFONT* logFont)
{
    // DWrite objects
    ComPtr<IDWriteFactory> dwriteFactory;
    ComPtr<IDWriteFontFace> fontFace;
    ComPtr<IDWriteFontFile> fontFile;
    ComPtr<IDWriteGdiInterop> gdiInterop;

    // Set up our DWrite factory and interop interface.
    IFR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(IDWriteFactory),
        reinterpret_cast<IUnknown**>(&dwriteFactory)
        );
    IFR(g_dwriteFactory->GetGdiInterop(&gdiInterop));

    // Open the file and determine the font type.
    IFR(g_dwriteFactory->CreateFontFileReference(fontFileName, nullptr, &fontFile));
    BOOL isSupportedFontType = false;
    DWRITE_FONT_FILE_TYPE fontFileType;
    DWRITE_FONT_FACE_TYPE fontFaceType;
    UINT32 numberOfFaces = 0;
    IFR(fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces));

    if (!isSupportedFontType)
        return DWRITE_E_FILEFORMAT;

    // Set up a font face from the array of font files (just one)
    ComPtr<IDWriteFontFile> fontFileArray[] = {fontFile};
    IFR(g_dwriteFactory->CreateFontFace(
        fontFaceType,
        ARRAYSIZE(fontFileArray), // file count
        &fontFileArray[0], // or GetAddressOf if WRL ComPtr
        0, // faceIndex
        DWRITE_FONT_SIMULATIONS_NONE,
        &fontFace
        );

    // Get the necessary logical font information.
    IFR(gdiInterop->ConvertFontFaceToLOGFONT(fontFace, OUT logFont));

    return S_OK;
}

Where IFR is just a failure macro that returns on a FAILED HRESULT, and ComPtr is a helper smart pointer class (substitute with your own, or ATL CComPtr, WinRT ComPtr, VS2013 _com_ptr_t...).

Sign up to request clarification or add additional context in comments.

Comments

3

I'm pretty sure you can't. All requests for fonts go through the font mapper, and it picks out the font file that comes the closest to meeting the specifications you've given. Though I'm not sure it even does in reality, it could at least theoretically use (for example) data from two entirely separate font files to create one logical font.

4 Comments

would there be a way to parse the ttf file to get all of its properties such that i could install it, then pass those properties to windows so as to get that particular font?
@Claudiu: yes, but it's definitely not trivial to do it on your own. I'd probably use something like Freetype (Freetype 2, really) to access the data from the font files.
yep i ended up just enumerating the existing windows fonts to solve my problem.
AddFontResourceEx("data/fonts/quantico.ttf", FR_PRIVATE) seems to make embedded Webkit to recognize the font-family:Quantico without the font being actually available on my system in my game engine.
2

One possibility is to EnumFonts(), save the results. Then add your private font with AddFontResourceEx(), and EnumFonts() again, the difference is what you added. Note that TTF and bitmap fonts enumerate differently, but for this test, that shouldn't matter.

If you were using bitmap fonts, they could be easily parsed (.FNT and .FON). TTF you'd likely have to build (or borrow, as another commenter suggested FreeType) a parser to pull the "name" table out of the TTF file.

That seems like a lot of work for a font you're controlling or supplying with your app.

We use AddFontResourceEx() to add a private font, but since we control the font we're adding, we just hardcode the fontname passed to CreateFontIndirect() to match.

2 Comments

The trouble with this is if you add a font that is already installed then the difference will be empty.
If you mark the font as FR_PRIVATE and then CreateFontIndirect with its well-known name, your app will use it, overriding the system font with same name.
1

If you dont care about installing the font you can do so with AddFontResource then you can fetch the relationship between the physical .TTF and it logical/family name by looking at the mappings in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts.

I mentioned PrivateFontCollection in my comment because I thought you wanted to do this temporarily; you can load a TTF into a PFC with PrivateFontCollection::AddFontFile, fetch back the new FontFamily object from the collection & examime GetFamilyName. (I've done similar with the .net implementation of this but not the raw API)

2 Comments

AddFontResource does not actually persist the addition to the registry for reboots, just temporarily to GDI's system table. When you install a font via the shell (by right-clicking on the file), it calls AddFontResource, updates the registry key too, and broadcasts WM_FONTCHANGE. msdn.microsoft.com/en-us/library/windows/desktop/…
That is why a ttf file not marked as FR_PRIVATE is taken by PID 4 :P
0

In order to get the .ttf name/family, the GetFontResourceInfo api should be used:

function GetFontResourceInfo(Name: PWideChar; var BufSize: Cardinal;
    Buffer: Pointer; InfoType: Cardinal): BOOL; stdcall; external 'gdi32.dll' name 'GetFontResourceInfoW'; 

function GetFontName(filename:string):string;
    const
      GFRI_DESCRIPTION = 1;
    var
     BufferSize: DWORD;
     Buffer: array [0..LF_FACESIZE - 1] of Char;
     a: ansistring;
    begin
     BufferSize := SizeOf(Buffer);
     FillChar(buffer, BufferSize, 0);
     if GetFontResourceInfo(PChar(filename), BufferSize, @Buffer, GFRI_DESCRIPTION)
      then SetString(a, PChar(@Buffer[0]), BufferSize)
      else a := 'unk?!';
     result := pansichar(a);
    end;

GetFontName('xyz.ttf') = 'My Cool Font Regular'

Now when you know the font face name, load it with AddFontResource('xyz.ttf') and use where you need it: Createfont (.., .., 'My Cool Font Regular')

3 Comments

This doesn't attempt to answer the question asked.
The font mapper might still choose a different font so that naive solution won't work.
But the only reason for that (of what I know) is missing symbols for the codepage requested. In other situations it's gonna be fine

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.