mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
573 lines
20 KiB
C#
573 lines
20 KiB
C#
// Reference: http://support.microsoft.com/kb/65123
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Orvid.Graphics.FontSupport.fnt
|
|
{
|
|
internal class FntLoader
|
|
{
|
|
#region Fields
|
|
/// <summary>
|
|
/// 2 bytes specifying the version (0200H or 0300H) of
|
|
/// the file.
|
|
/// </summary>
|
|
public ushort Version;
|
|
/// <summary>
|
|
/// 4 bytes specifying the total size of the file in
|
|
/// bytes.
|
|
/// </summary>
|
|
public uint FileSize;
|
|
/// <summary>
|
|
/// 60 bytes specifying copyright information.
|
|
/// </summary>
|
|
public string Copyright;
|
|
/// <summary>
|
|
/// 2 bytes specifying the type of font file.
|
|
///
|
|
/// The low-order byte is exclusively for GDI use. If the
|
|
/// low-order bit of the WORD is zero, it is a bitmap
|
|
/// (raster) font file. If the low-order bit is 1, it is a
|
|
/// vector font file. The second bit is reserved and must
|
|
/// be zero. If no bits follow in the file and the bits are
|
|
/// located in memory at a fixed address specified in
|
|
/// dfBitsOffset, the third bit is set to 1; otherwise, the
|
|
/// bit is set to 0 (zero). The high-order bit of the low
|
|
/// byte is set if the font was realized by a device. The
|
|
/// remaining bits in the low byte are reserved and set to
|
|
/// zero.
|
|
///
|
|
/// The high byte is reserved for device use and will
|
|
/// always be set to zero for GDI-realized standard fonts
|
|
/// Physical fonts with the high-order bit of the low byte
|
|
/// set may use this byte to describe themselves. GDI will
|
|
/// never inspect the high byte.
|
|
/// </summary>
|
|
public ushort Type;
|
|
/// <summary>
|
|
/// 2 bytes specifying the nominal point size at which
|
|
/// this character set looks best.
|
|
/// </summary>
|
|
public ushort PointSize;
|
|
/// <summary>
|
|
/// 2 bytes specifying the nominal vertical resolution
|
|
/// (dots-per-inch) at which this character set was
|
|
/// digitized.
|
|
/// </summary>
|
|
public ushort VertDpi;
|
|
/// <summary>
|
|
/// 2 bytes specifying the nominal horizontal resolution
|
|
/// (dots-per-inch) at which this character set was
|
|
/// digitized.
|
|
/// </summary>
|
|
public ushort HorizDpi;
|
|
/// <summary>
|
|
/// 2 bytes specifying the distance from the top of a
|
|
/// character definition cell to the baseline of the
|
|
/// typographical font. It is useful for aligning the
|
|
/// baselines of fonts of different heights.
|
|
/// </summary>
|
|
public ushort Ascent;
|
|
/// <summary>
|
|
/// Specifies the amount of leading inside the bounds set
|
|
/// by dfPixHeight. Accent marks may occur in this area.
|
|
/// This may be zero at the designer's option.
|
|
/// </summary>
|
|
public ushort InternalLeading;
|
|
/// <summary>
|
|
/// Specifies the amount of extra leading that the designer
|
|
/// requests the application add between rows. Since this
|
|
/// area is outside of the font proper, it contains no
|
|
/// marks and will not be altered by text output calls in
|
|
/// either the OPAQUE or TRANSPARENT mode. This may be zero
|
|
/// at the designer's option.
|
|
/// </summary>
|
|
public ushort ExternalLeading;
|
|
public bool IsItalic;
|
|
public bool IsUnderline;
|
|
public bool IsStrikeOut;
|
|
/// <summary>
|
|
/// 2 bytes specifying the weight of the characters in the
|
|
/// character definition data, on a scale of 1 to 1000. A
|
|
/// dfWeight of 400 specifies a regular weight.
|
|
/// </summary>
|
|
public ushort Weight;
|
|
/// <summary>
|
|
/// 1 byte specifying the character set defined by this
|
|
/// font.
|
|
/// </summary>
|
|
public byte CharSet;
|
|
/// <summary>
|
|
/// 2 bytes specifing the width.
|
|
/// </summary>
|
|
public ushort PixWidth;
|
|
/// <summary>
|
|
/// 2 bytes specifying the height of the character bitmap
|
|
/// (raster fonts), or the height of the grid on which a
|
|
/// vector font was digitized.
|
|
/// </summary>
|
|
public ushort PixHeight;
|
|
/// <summary>
|
|
/// Specifies the pitch and font family. The low bit is set
|
|
/// if the font is variable pitch. The high four bits give
|
|
/// the family name of the font. Font families describe in
|
|
/// a general way the look of a font. They are intended for
|
|
/// specifying fonts when the exact face name desired is
|
|
/// not available. The families are as follows:
|
|
///
|
|
/// Family Description
|
|
/// ------ -----------
|
|
/// FF_DONTCARE (0<<4) Don't care or don't know.
|
|
/// FF_ROMAN (1<<4) Proportionally spaced fonts with serifs.
|
|
/// FF_SWISS (2<<4) Proportionally spaced fonts without serifs.
|
|
/// FF_MODERN (3<<4) Fixed-pitch fonts.
|
|
/// FF_SCRIPT (4<<4)
|
|
/// FF_DECORATIVE (5<<4)
|
|
/// </summary>
|
|
public byte PitchAndFamily;
|
|
/// <summary>
|
|
/// 2 bytes specifying the width of characters in the font.
|
|
/// For fixed-pitch fonts, this is the same as dfPixWidth.
|
|
/// For variable-pitch fonts, this is the width of the
|
|
/// character "X."
|
|
/// </summary>
|
|
public ushort AvgWidth;
|
|
/// <summary>
|
|
/// 2 bytes specifying the maximum pixel width of any
|
|
/// character in the font. For fixed-pitch fonts, this is
|
|
/// simply dfPixWidth.
|
|
/// </summary>
|
|
public ushort MaxWidth;
|
|
/// <summary>
|
|
/// 1 byte specifying the first character code defined by
|
|
/// this font. Character definitions are stored only for
|
|
/// the characters actually present in a font. Therefore,
|
|
/// use this field when calculating indexes into either
|
|
/// dfBits or dfCharOffset.
|
|
/// </summary>
|
|
public byte FirstChar;
|
|
/// <summary>
|
|
/// 1 byte specifying the last character code defined by
|
|
/// this font. Note that all the characters with codes
|
|
/// between dfFirstChar and dfLastChar must be present in
|
|
/// the font character definitions.
|
|
/// </summary>
|
|
public byte LastChar;
|
|
/// <summary>
|
|
/// 1 byte specifying the character to substitute
|
|
/// whenever a string contains a character out of the
|
|
/// range. The character is given relative to dfFirstChar
|
|
/// so that dfDefaultChar is the actual value of the
|
|
/// character, less dfFirstChar. The dfDefaultChar should
|
|
/// indicate a special character that is not a space.
|
|
/// </summary>
|
|
public byte DefaultChar;
|
|
/// <summary>
|
|
/// 1 byte specifying the character that will define word
|
|
/// breaks. This character defines word breaks for word
|
|
/// wrapping and word spacing justification. The character
|
|
/// is given relative to dfFirstChar so that dfBreakChar is
|
|
/// the actual value of the character, less that of
|
|
/// dfFirstChar. The dfBreakChar is normally (32 -
|
|
/// dfFirstChar), which is an ASCII space.
|
|
/// </summary>
|
|
public byte BreakChar;
|
|
/// <summary>
|
|
/// 2 bytes specifying the number of bytes in each row of
|
|
/// the bitmap. This is always even, so that the rows start
|
|
/// on WORD boundaries. For vector fonts, this field has no
|
|
/// meaning.
|
|
/// </summary>
|
|
public ushort WidthBytes;
|
|
/// <summary>
|
|
/// 4 bytes specifying the offset in the file to the
|
|
/// null-terminated string giving the device name.
|
|
/// For a generic font, this value is zero.
|
|
/// </summary>
|
|
public uint Device;
|
|
/// <summary>
|
|
/// 4 bytes specifying the offset in the file to the
|
|
/// null-terminated string that names the face.
|
|
/// </summary>
|
|
public uint Face;
|
|
/// <summary>
|
|
/// 4 bytes specifying the absolute machine address of
|
|
/// the bitmap. This is set by GDI at load time. The
|
|
/// dfBitsPointer is guaranteed to be even.
|
|
/// </summary>
|
|
public uint BitsPointer;
|
|
/// <summary>
|
|
/// 4 bytes specifying the offset in the file to the
|
|
/// beginning of the bitmap information. If the 04H bit in
|
|
/// the dfType is set, then dfBitsOffset is an absolute
|
|
/// address of the bitmap (probably in ROM).
|
|
///
|
|
/// For raster fonts, dfBitsOffset points to a sequence of
|
|
/// bytes that make up the bitmap of the font, whose height
|
|
/// is the height of the font, and whose width is the sum
|
|
/// of the widths of the characters in the font rounded up
|
|
/// to the next WORD boundary.
|
|
///
|
|
/// For vector fonts, it points to a string of bytes or
|
|
/// words (depending on the size of the grid on which the
|
|
/// font was digitized) that specify the strokes for each
|
|
/// character of the font. The dfBitsOffset field must be
|
|
/// even.
|
|
/// </summary>
|
|
public uint BitsOffset;
|
|
/// <summary>
|
|
/// 4 bytes specifying the bits flags, which are additional
|
|
/// flags that define the format of the Glyph bitmap, as
|
|
/// follows:
|
|
///
|
|
/// DFF_FIXED equ 0001h ; font is fixed pitch
|
|
/// DFF_PROPORTIONAL equ 0002h ; font is proportional pitch
|
|
/// DFF_ABCFIXED equ 0004h ; font is an ABC fixed font
|
|
/// DFF_ABCPROPORTIONAL equ 0008h ; font is an ABC pro-portional font
|
|
/// DFF_1COLOR equ 0010h ; font is one color
|
|
/// DFF_16COLOR equ 0020h ; font is 16 color
|
|
/// DFF_256COLOR equ 0040h ; font is 256 color
|
|
/// DFF_RGBCOLOR equ 0080h ; font is RGB color
|
|
/// </summary>
|
|
public uint Flags;
|
|
/// <summary>
|
|
/// 2 bytes specifying the global A space, if any. The
|
|
/// dfAspace is the distance from the current position to
|
|
/// the left edge of the bitmap.
|
|
/// </summary>
|
|
public ushort ASpace;
|
|
/// <summary>
|
|
/// 2 bytes specifying the global B space, if any. The
|
|
/// dfBspace is the width of the character.
|
|
/// </summary>
|
|
public ushort BSpace;
|
|
/// <summary>
|
|
/// 2 bytes specifying the global C space, if any. The
|
|
/// dfCspace is the distance from the right edge of the
|
|
/// bitmap to the new current position. The increment of a
|
|
/// character is the sum of the three spaces. These apply
|
|
/// to all glyphs and is the case for DFF_ABCFIXED.
|
|
/// </summary>
|
|
public ushort CSpace;
|
|
/// <summary>
|
|
/// 4 bytes specifying the offset to the color table for
|
|
/// color fonts, if any. The format of the bits is similar
|
|
/// to a DIB, but without the header. That is, the
|
|
/// characters are not split up into disjoint bytes.
|
|
/// Instead, they are left intact. If no color table is
|
|
/// needed, this entry is NULL.
|
|
/// </summary>
|
|
public uint ColorPointer;
|
|
|
|
public FntGlyph[] Glyphs = new FntGlyph[255];
|
|
public string DeviceName;
|
|
public string FaceName;
|
|
public FntGlyphType GlyphType;
|
|
public FntGlyph NotDefChar;
|
|
|
|
private const byte FixedFlagMask = 1;
|
|
private const byte ProportionalFlagMask = 2;
|
|
private const byte ABCFixedFlagMask = 4;
|
|
private const byte ABCProportionalFlagMask = 8;
|
|
private const byte Color1FlagMask = 16;
|
|
private const byte Color16FlagMask = 32;
|
|
private const byte Color256FlagMask = 64;
|
|
private const byte ColorRGBFlagMask = 128;
|
|
#endregion
|
|
|
|
|
|
public FntLoader()
|
|
{
|
|
}
|
|
|
|
public string GetFamily()
|
|
{
|
|
if (PitchAndFamily == 0)
|
|
{
|
|
return "None";
|
|
}
|
|
else if (PitchAndFamily == (1<<4))
|
|
{
|
|
return "Roman";
|
|
}
|
|
else if (PitchAndFamily == (2<<4))
|
|
{
|
|
return "Swiss";
|
|
}
|
|
else if (PitchAndFamily == (3<<4))
|
|
{
|
|
return "Modern";
|
|
}
|
|
else if (PitchAndFamily == (4<<4))
|
|
{
|
|
return "Script";
|
|
}
|
|
else if (PitchAndFamily == (5<<4))
|
|
{
|
|
return "Decorative";
|
|
}
|
|
else
|
|
{
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
public Rectangle GetBounds()
|
|
{
|
|
Rectangle r = new Rectangle();
|
|
r.SetBounds(this.CSpace, this.Ascent, this.MaxWidth, this.PixHeight);
|
|
return r;
|
|
}
|
|
|
|
public int GetDepth()
|
|
{
|
|
switch (GlyphType)
|
|
{
|
|
case FntGlyphType.Version2:
|
|
case FntGlyphType.Color1:
|
|
return 1;
|
|
default:
|
|
return 900;
|
|
}
|
|
}
|
|
|
|
public FontStyle GetStyle()
|
|
{
|
|
FontStyle f = FontStyle.Normal;
|
|
if (IsItalic)
|
|
f |= FontStyle.Italic;
|
|
if (IsStrikeOut)
|
|
f |= FontStyle.Strikeout;
|
|
if (IsUnderline)
|
|
f |= FontStyle.Underline;
|
|
if (Weight >= 800)
|
|
f |= FontStyle.Bold;
|
|
return f;
|
|
}
|
|
|
|
public FntGlyph GetGlyph(char c)
|
|
{
|
|
if (((int)c) > 255)
|
|
return NotDefChar;
|
|
FntGlyph g = Glyphs[((int)c) - 1];
|
|
if (g == null)
|
|
return NotDefChar;
|
|
return g;
|
|
}
|
|
|
|
public int GetSize()
|
|
{
|
|
return PointSize;
|
|
}
|
|
|
|
public void Load(Stream s)
|
|
{
|
|
BinaryReader br = new BinaryReader(s);
|
|
byte[] buf;
|
|
|
|
|
|
Version = br.ReadUInt16();
|
|
if (Version != 0x0300 && Version != 0x0200)
|
|
{
|
|
throw new Exception("Unknown format version!");
|
|
}
|
|
|
|
FileSize = br.ReadUInt32();
|
|
if (FileSize != s.Length)
|
|
{
|
|
throw new Exception("FileSize incorrect! Possible corrupt file?");
|
|
}
|
|
|
|
buf = br.ReadBytes(60);
|
|
Copyright = System.Text.ASCIIEncoding.ASCII.GetString(buf).Replace("\0", "");
|
|
buf = null;
|
|
|
|
Type = br.ReadUInt16();
|
|
if ((Type & 1) == 1)
|
|
{
|
|
throw new Exception("Can't handle Vector FNT files.");
|
|
}
|
|
PointSize = br.ReadUInt16();
|
|
VertDpi = br.ReadUInt16();
|
|
HorizDpi = br.ReadUInt16();
|
|
Ascent = br.ReadUInt16();
|
|
InternalLeading = br.ReadUInt16();
|
|
ExternalLeading = br.ReadUInt16();
|
|
|
|
if (br.ReadByte() != 0)
|
|
{
|
|
IsItalic = true;
|
|
}
|
|
else
|
|
{
|
|
IsItalic = false;
|
|
}
|
|
|
|
if (br.ReadByte() != 0)
|
|
{
|
|
IsUnderline = true;
|
|
}
|
|
else
|
|
{
|
|
IsUnderline = false;
|
|
}
|
|
|
|
if (br.ReadByte() != 0)
|
|
{
|
|
IsStrikeOut = true;
|
|
}
|
|
else
|
|
{
|
|
IsStrikeOut = false;
|
|
}
|
|
|
|
Weight = br.ReadUInt16();
|
|
CharSet = br.ReadByte();
|
|
PixWidth = br.ReadUInt16();
|
|
PixHeight = br.ReadUInt16();
|
|
PitchAndFamily = br.ReadByte();
|
|
AvgWidth = br.ReadUInt16();
|
|
MaxWidth = br.ReadUInt16();
|
|
FirstChar = br.ReadByte();
|
|
LastChar = br.ReadByte();
|
|
DefaultChar = br.ReadByte();
|
|
BreakChar = br.ReadByte();
|
|
WidthBytes = br.ReadUInt16();
|
|
|
|
long position;
|
|
string deviceName = "";
|
|
char curChar = ' ';
|
|
|
|
#region Read Device Name
|
|
Device = br.ReadUInt32();
|
|
position = br.BaseStream.Position;
|
|
br.BaseStream.Position = Device;
|
|
br.BaseStream.Flush();
|
|
while (curChar != '\u0000')
|
|
{
|
|
deviceName += curChar;
|
|
curChar = (char)br.ReadByte();
|
|
}
|
|
br.BaseStream.Position = position;
|
|
br.BaseStream.Flush();
|
|
DeviceName = deviceName.Substring(1);
|
|
#endregion
|
|
|
|
#region Read Face Name
|
|
Face = br.ReadUInt32();
|
|
deviceName = ""; // re-use the variable.
|
|
curChar = ' ';
|
|
position = br.BaseStream.Position;
|
|
br.BaseStream.Position = Face;
|
|
br.BaseStream.Flush();
|
|
// loop while it's not the null terminator.
|
|
while (curChar != '\u0000')
|
|
{
|
|
deviceName += curChar;
|
|
curChar = (char)br.ReadByte();
|
|
}
|
|
br.BaseStream.Position = position;
|
|
br.BaseStream.Flush();
|
|
// account for the extra char we added.
|
|
FaceName = deviceName.Substring(1);
|
|
#endregion
|
|
|
|
BitsPointer = br.ReadUInt32();
|
|
BitsOffset = br.ReadUInt32();
|
|
|
|
br.ReadByte(); // Not used.
|
|
|
|
if (Version == 0x0300)
|
|
{
|
|
Flags = br.ReadUInt32();
|
|
ASpace = br.ReadUInt16();
|
|
BSpace = br.ReadUInt16();
|
|
CSpace = br.ReadUInt16();
|
|
ColorPointer = br.ReadUInt32();
|
|
|
|
br.ReadBytes(16); // Not used.
|
|
}
|
|
|
|
// Next is the character table.
|
|
List<FntGlyph> chars = new List<FntGlyph>();
|
|
FntGlyph g;
|
|
uint nChars = (uint)((LastChar - FirstChar) + 2);
|
|
position = 0;
|
|
long loc;
|
|
int bytesToRead;
|
|
if (Version == 0x0200)
|
|
{
|
|
this.GlyphType = FntGlyphType.Version2;
|
|
for (uint c = 0; c < nChars; c++)
|
|
{
|
|
g = new FntGlyph();
|
|
g.Width = br.ReadUInt16();
|
|
g.Type = FntGlyphType.Version2;
|
|
g.Height = PixHeight;
|
|
g.Font = this;
|
|
|
|
#region Read Data
|
|
loc = br.ReadUInt16();
|
|
position = br.BaseStream.Position;
|
|
br.BaseStream.Position = loc;
|
|
br.BaseStream.Flush();
|
|
bytesToRead = (int)(((g.Width + 7) >> 3) * (PixHeight));
|
|
g.RawData = br.ReadBytes(bytesToRead);
|
|
br.BaseStream.Position = position;
|
|
br.BaseStream.Flush();
|
|
#endregion
|
|
|
|
chars.Add(g);
|
|
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < chars.Count; i++)
|
|
{
|
|
chars[i].Initialize();
|
|
}
|
|
Array.Copy(chars.ToArray(), 0, Glyphs, FirstChar - 1, chars.Count - 1);
|
|
//Glyphs = chars;
|
|
NotDefChar = chars[chars.Count - 1];
|
|
//throw new Exception();
|
|
|
|
//else // version == 0x0300
|
|
//{
|
|
// if ((Flags & FixedFlagMask) == FixedFlagMask || (Flags & ProportionalFlagMask) == ProportionalFlagMask)
|
|
// {
|
|
// FntGlyphType t;
|
|
// if ((Flags & FixedFlagMask) == FixedFlagMask)
|
|
// {
|
|
// t = FntGlyphType.Fixed;
|
|
// }
|
|
// else
|
|
// {
|
|
// t = FntGlyphType.Proportional;
|
|
// }
|
|
// for (uint c = 0; c < nChars; c++)
|
|
// {
|
|
// g = new FntGlyph();
|
|
// g.Type = t;
|
|
|
|
// }
|
|
// }
|
|
// else if ((Flags & ABCFixedFlagMask) == ABCFixedFlagMask || (Flags & ABCProportionalFlagMask) == ABCProportionalFlagMask)
|
|
// {
|
|
// for (uint c = 0; c < nChars; c++)
|
|
// {
|
|
|
|
// }
|
|
// }
|
|
// else // A color flag.
|
|
// {
|
|
// for (uint c = 0; c < nChars; c++)
|
|
// {
|
|
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
}
|
|
|
|
}
|
|
}
|