(see here for the first part) 

The first part of the code centers around a call to TTEmbedFont. It only runs on Vista and above (since no one else should have the font on their machine!):

IntPtr hDC = CreateDC("DISPLAY", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (hDC != IntPtr.Zero) {
    IntPtr hFont = CreateFont(MulDiv(Convert.ToInt16(siz), GetDeviceCaps(hDC, LOGPIXELSY), 72),
                              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, "Nyala");
    if (hFont != IntPtr.Zero) {
        IntPtr hFontOld = SelectObject(hDC, hFont);
        if (hFontOld != IntPtr.Zero) {
            // We are writing out the embed file info for the font if the file doesn't exist.
            uint ulStatus = 0;
            FileStream fsWrite = new FileStream(FONTNAME, FileMode.CreateNew);
            WRITEEMBEDPROC wep = new WRITEEMBEDPROC(this.WriteEmbedProc);
            TTEMBEDINFO ttie = new TTEMBEDINFO();

            ttie.usStructSize = Convert.ToUInt16(Marshal.SizeOf(ttie));
            ttie.usRootStrSize = 0;
            ttie.pusRootStr = IntPtr.Zero;
            ulPrivStatus = 0;
            ulStatus = 0;
            rc = TTEmbedFont(hDC,
                             TTEMBED.RAW | TTEMBED.TTCOMPRESSED,
                             CHARSET.UNICODE,
                             out ulPrivStatus,
                             out ulStatus,
                             wep,
                             fsWrite,
                             IntPtr.Zero,
                             0,
                             0,
                             ttie);
            fsWrite.Flush();
            fsWrite.Close();
            if (rc != E.NONE) {
                // Since creation of the file ultimately failed, delete whatever
                // interim bits might have been written.
                File.Delete(FONTNAME);
            }
            SelectObject(hDC, hFontOld);
        }
        DeleteObject(hFont);
    }
    DeleteDC(hDC);
}

You'll notice that I am passing the flags to include the raw font and not a subset of it. My initial reason for that was the suggestion from some people that the subsetting would not pick up any of the different forms of glyphs that might be available. But Sergey actually told me that the code is rather generous at including all of the alternate forms and glyphs that could potentially derived from the ones that are specified, so in the situation where the text is static, subsetting the font may be worthwhile (and will certainly make for a smaller file!).

If one was going to subset, putting all the text in a string and then changing that IntPtr in the pinvoke declare of pusCharCodeSet to a string and then passing it (after all, what else is a string but an array of ushort values?). :-)

The key piece of code that does this part of  work is that WriteEmbedProc. To be honest, I am not entirely happy with it. You may see why if you look it:

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl, CharSet=CharSet.Unicode)]
internal delegate uint WRITEEMBEDPROC(FileStream lpvWriteStream, IntPtr lpvBuffer, uint cbBuffer);

internal uint WriteEmbedProc(FileStream lpvWriteStream, IntPtr lpvBuffer, uint cbBuffer) {
    byte[] rgbyt = new byte[cbBuffer];
    Marshal.Copy(lpvBuffer, rgbyt, 0, (int)cbBuffer);
    lpvWriteStream.Write(rgbyt, 0, (int)cbBuffer);
    return cbBuffer;
}

Okay, so because I am using the .NET FileStream class to do the writing, I am forced to do that extra bit of copying into a byte array that I'd rather avoid. You know, just something to write from that lpvBuffer pointer directly to the file. But the actual hit is small (it is a small file, after all!), so I just kind of thought it would be worth earmarking as an area to potentially revisit if performance became a problem. In the meantime, it does get the job done....

I also chose not to get involved with the whole TTEMBEDINFO structure and its link checking, though people looking at the sample might see it as worthwhile to look into (this is why I bothered to define the struct rather than just making it an IntPtr and passing IntPtr.Zero in this case).

Anyway, when everything is done you end up with a nice little binary file that can be used in your application that needs to display text that may not be available....

In the next post I'll talk about the harder bit, which is actually loading that file....

 

This post brought to you by(U+1275, a.k.a. ETHIOPIC SYLLABLE TE)