by Michael S. Kaplan, published on 2007/04/14 12:22 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2007/04/14/2133650.aspx
(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)
# Jason Truesdell on 14 Apr 2007 7:11 PM:
Ack! You've succumbed to the bizarre dialect of Group Program Manager/Vice President English!
I'm not usually not so much of a language purist, but one of the things adequately beaten into my little head during a a class with my university's most dedicated professor of literature was this: geometrically speaking, a center is a single point. Therefore, things do not center around anything; they center upon something. It's just like focus: you can only focus upon one point, you can't focus around something.
Examples of misuse are widespread, but "to center around" seems particularly pervasive in corporate jargon.
My own writing is still full of language barbarisms and mishaps, but that one mistake I can never make again. Once, I was listening to a a GPM at Microsoft talking about our product strategy, and I bit my tongue and clenched my teeth as he used the phrase repeatedly.
# Mihai on 16 Apr 2007 5:03 PM:
<<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>>
Trap here!
Do not try collecting all the characters that are used in a string, use the real text.
So don't use ": Cadefhiklnost" for "delete all the files on disk C:," because it will not include the ligatures glyphs (ie "fi" in file) and will mess contextual shaping, which might be needed.
Alemayehu on 8 Feb 2010 6:16 PM:
Well done. I have no words to appreciate your efforts.
referenced by
2013/10/23 I finally got to meet ali eteraz in person...
2007/12/12 SiaO as The Red Carpet (aka Characters just want to be seen)
2007/04/15 Rhymes with Amharic #5 (a.k.a. [Sub]setting up this code where it can do the most good?)
2007/04/14 Rhymes with Amharic #4 (a.k.a. we're all [sub]set so turning out the lights and going to [em]bed!)
2007/04/14 Rhymes with Amharic #3 (a.k.a. Read and write a language w/o even getting out of my [em]bed? Kewl!)