feature. See also
. The project being documented here (as the example) is the Zig library itself.
compile.FontDir
pub const FontDir = struct
File
Code
pub const FontDir = struct {
fonts: std.ArrayList(Font) = .empty,
ids: std.AutoHashMapUnmanaged(u16, Token) = .empty,
pub const Font = struct {
id: u16,
header_bytes: [148]u8,
};
pub fn deinit(self: *FontDir, allocator: Allocator) void {
self.fonts.deinit(allocator);
}
pub fn add(self: *FontDir, allocator: Allocator, font: Font, id_token: Token) !void {
try self.ids.putNoClobber(allocator, font.id, id_token);
try self.fonts.append(allocator, font);
}
pub fn writeResData(self: *FontDir, compiler: *Compiler, writer: *std.Io.Writer) !void {
if (self.fonts.items.len == 0) return;
// must have a valid and unique u16 ordinal ID (trying to specify a FONT
// with e.g. id 65537 will wrap around to 1 and be ignored if there's already
// a font with that ID in the file).
const num_fonts: u16 = @intCast(self.fonts.items.len);
// Note: This works out to a maximum data_size of 9,961,322.
const data_size: u32 = 2 + (2 + 150) * num_fonts;
var header = Compiler.ResourceHeader{
.name_value = try NameOrOrdinal.nameFromString(compiler.allocator, .{ .slice = "FONTDIR", .code_page = .windows1252 }),
.type_value = NameOrOrdinal{ .ordinal = @backingInt(res.RT.FONTDIR) },
.memory_flags = res.MemoryFlags.defaults(res.RT.FONTDIR),
.language = compiler.state.language,
.version = compiler.state.version,
.characteristics = compiler.state.characteristics,
.data_size = data_size,
};
defer header.deinit(compiler.allocator);
try header.writeAssertNoOverflow(writer);
try writer.writeInt(u16, num_fonts, .little);
for (self.fonts.items) |font| {
// Technically, each FONT is seemingly meant to be written as a
// FONTDIRENTRY with two trailing NUL-terminated strings corresponding to
// the 'device name' and 'face name' of the .FNT file, but:
//
// 1. When dealing with .FNT files, the Win32 implementation
// gets the device name and face name from the wrong locations,
// so it's basically never going to write the real device/face name
// strings.
// 2. When dealing with files 76-140 bytes long, the Win32 implementation
// can just crash (if there are no NUL bytes in the file).
// 3. The 32-bit Win32 rc.exe uses a 148 byte size for the portion of
// the FONTDIRENTRY before the NUL-terminated strings, which
// does not match the documented FONTDIRENTRY size that (presumably)
// this format is meant to be using, so anything iterating the
// FONTDIR according to the available documentation will get bogus results.
// 4. The FONT resource can be used for non-.FNT types like TTF and OTF,
// in which case emulating the Win32 behavior of unconditionally
// interpreting the bytes as a .FNT and trying to grab device/face names
// from random bytes in the TTF/OTF file can lead to weird behavior
// and errors in the Win32 implementation (for example, the device/face
// name fields are offsets into the file where the NUL-terminated
// string is located, but the Win32 implementation actually treats
// them as signed so if they are negative then the Win32 implementation
// will error; this happening for TTF fonts would just be a bug
// since the TTF could otherwise be valid)
// 5. The FONTDIR resource doesn't actually seem to be used at all by
// anything that I've found, and instead in Windows 3.0 and newer
// it seems like the FONT resources are always just iterated/accessed
// directly without ever looking at the FONTDIR.
//
// All of these combined means that we:
// - Do not need or want to emulate Win32 behavior here
// - For maximum simplicity and compatibility, we just write the first
// 148 bytes of the file without any interpretation (padded with
// zeroes to get up to 148 bytes if necessary), and then
// unconditionally write two NUL bytes, meaning that we always
// write 'device name' and 'face name' as if they were 0-length
// strings.
//
// This gives us byte-for-byte .RES compatibility in the common case while
// allowing us to avoid any erroneous errors caused by trying to read
// the face/device name from a bogus location. Note that the Win32
// implementation never actually writes the real device/face name here
// anyway (except in the bizarre case that a .FNT file has the proper
// device/face name offsets within a reserved section of the .FNT file)
// so there's no feasible way that anything can actually think that the
// device name/face name in the FONTDIR is reliable.
// First, the ID is written, though
try writer.writeInt(u16, font.id, .little);
try writer.writeAll(&font.header_bytes);
try writer.splatByteAll(0, 2);
}
try Compiler.writeDataPadding(writer, data_size);
}
}