chore: import upstream snapshot with attribution
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const Error = @import("errors.zig").Error;
|
||||
|
||||
/// Data type holding the memory modes available to client programs.
|
||||
///
|
||||
/// Regarding these various memory-modes:
|
||||
///
|
||||
/// - In no case shall the HarfBuzz client modify memory that is passed to
|
||||
/// HarfBuzz in a blob. If there is any such possibility,
|
||||
/// HB_MEMORY_MODE_DUPLICATE should be used such that HarfBuzz makes a
|
||||
/// copy immediately,
|
||||
///
|
||||
/// - Use HB_MEMORY_MODE_READONLY otherwise, unless you really really really
|
||||
/// know what you are doing,
|
||||
///
|
||||
/// - HB_MEMORY_MODE_WRITABLE is appropriate if you really made a copy of
|
||||
/// data solely for the purpose of passing to HarfBuzz and doing that
|
||||
/// just once (no reuse!),
|
||||
///
|
||||
/// - If the font is mmap()ed, it's okay to use
|
||||
/// HB_MEMORY_READONLY_MAY_MAKE_WRITABLE , however, using that mode
|
||||
/// correctly is very tricky. Use HB_MEMORY_MODE_READONLY instead.
|
||||
pub const MemoryMode = enum(u2) {
|
||||
/// HarfBuzz immediately makes a copy of the data.
|
||||
duplicate = c.HB_MEMORY_MODE_DUPLICATE,
|
||||
|
||||
/// HarfBuzz client will never modify the data, and HarfBuzz will never
|
||||
/// modify the data.
|
||||
readonly = c.HB_MEMORY_MODE_READONLY,
|
||||
|
||||
/// HarfBuzz client made a copy of the data solely for HarfBuzz, so
|
||||
/// HarfBuzz may modify the data.
|
||||
writable = c.HB_MEMORY_MODE_WRITABLE,
|
||||
|
||||
/// See above
|
||||
readonly_may_make_writable = c.HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
|
||||
};
|
||||
|
||||
/// Blobs wrap a chunk of binary data to handle lifecycle management of data
|
||||
/// while it is passed between client and HarfBuzz. Blobs are primarily
|
||||
/// used to create font faces, but also to access font face tables, as well as
|
||||
/// pass around other binary data.
|
||||
pub const Blob = struct {
|
||||
handle: *c.hb_blob_t,
|
||||
|
||||
/// Creates a new "blob" object wrapping data . The mode parameter is used
|
||||
/// to negotiate ownership and lifecycle of data .
|
||||
///
|
||||
/// Note that this function returns a freshly-allocated empty blob even
|
||||
/// if length is zero. This is in contrast to hb_blob_create(), which
|
||||
/// returns the singleton empty blob (as returned by hb_blob_get_empty())
|
||||
/// if length is zero.
|
||||
pub fn create(data: []const u8, mode: MemoryMode) Error!Blob {
|
||||
const handle = c.hb_blob_create_or_fail(
|
||||
data.ptr,
|
||||
@intCast(data.len),
|
||||
@intFromEnum(mode),
|
||||
null,
|
||||
null,
|
||||
) orelse return Error.HarfbuzzFailed;
|
||||
|
||||
return Blob{ .handle = handle };
|
||||
}
|
||||
|
||||
/// Decreases the reference count on blob , and if it reaches zero,
|
||||
/// destroys blob , freeing all memory, possibly calling the
|
||||
/// destroy-callback the blob was created for if it has not been
|
||||
/// called already.
|
||||
pub fn destroy(self: *Blob) void {
|
||||
c.hb_blob_destroy(self.handle);
|
||||
}
|
||||
|
||||
/// Attaches a user-data key/data pair to the specified blob.
|
||||
pub fn setUserData(
|
||||
self: Blob,
|
||||
comptime T: type,
|
||||
key: ?*anyopaque,
|
||||
ptr: ?*T,
|
||||
comptime destroycb: ?*const fn (?*T) callconv(.c) void,
|
||||
replace: bool,
|
||||
) bool {
|
||||
const Callback = struct {
|
||||
pub fn callback(data: ?*anyopaque) callconv(.c) void {
|
||||
@call(.{ .modifier = .always_inline }, destroycb, .{
|
||||
@as(?*T, @ptrCast(@alignCast(data))),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return c.hb_blob_set_user_data(
|
||||
self.handle,
|
||||
@ptrCast(key),
|
||||
ptr,
|
||||
if (destroycb != null) Callback.callback else null,
|
||||
if (replace) 1 else 0,
|
||||
) > 0;
|
||||
}
|
||||
|
||||
/// Fetches the user data associated with the specified key, attached to
|
||||
/// the specified font-functions structure.
|
||||
pub fn getUserData(
|
||||
self: Blob,
|
||||
comptime T: type,
|
||||
key: ?*anyopaque,
|
||||
) ?*T {
|
||||
const opt = c.hb_blob_get_user_data(self.handle, @ptrCast(key));
|
||||
if (opt) |ptr|
|
||||
return @ptrCast(@alignCast(ptr))
|
||||
else
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
|
||||
const data = "hello";
|
||||
var blob = try Blob.create(data, .readonly);
|
||||
defer blob.destroy();
|
||||
|
||||
var userdata: u8 = 127;
|
||||
var key: u8 = 0;
|
||||
try testing.expect(blob.setUserData(u8, &key, &userdata, null, false));
|
||||
try testing.expect(blob.getUserData(u8, &key).?.* == 127);
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const common = @import("common.zig");
|
||||
const Error = @import("errors.zig").Error;
|
||||
const Direction = common.Direction;
|
||||
const Script = common.Script;
|
||||
const Language = common.Language;
|
||||
|
||||
/// Buffers serve a dual role in HarfBuzz; before shaping, they hold the
|
||||
/// input characters that are passed to hb_shape(), and after shaping they
|
||||
/// hold the output glyphs.
|
||||
pub const Buffer = struct {
|
||||
handle: *c.hb_buffer_t,
|
||||
|
||||
/// Creates a new hb_buffer_t with all properties to defaults.
|
||||
pub fn create() Error!Buffer {
|
||||
const handle = c.hb_buffer_create() orelse return Error.HarfbuzzFailed;
|
||||
return Buffer{ .handle = handle };
|
||||
}
|
||||
|
||||
/// Deallocate the buffer . Decreases the reference count on buffer by one.
|
||||
/// If the result is zero, then buffer and all associated resources are
|
||||
/// freed. See hb_buffer_reference().
|
||||
pub fn destroy(self: *Buffer) void {
|
||||
c.hb_buffer_destroy(self.handle);
|
||||
}
|
||||
|
||||
/// Resets the buffer to its initial status, as if it was just newly
|
||||
/// created with hb_buffer_create().
|
||||
pub fn reset(self: Buffer) void {
|
||||
c.hb_buffer_reset(self.handle);
|
||||
}
|
||||
|
||||
/// Returns the number of items in the buffer.
|
||||
pub fn getLength(self: Buffer) u32 {
|
||||
return c.hb_buffer_get_length(self.handle);
|
||||
}
|
||||
|
||||
/// Sets the type of buffer contents. Buffers are either empty, contain
|
||||
/// characters (before shaping), or contain glyphs (the result of shaping).
|
||||
pub fn setContentType(self: Buffer, ct: ContentType) void {
|
||||
c.hb_buffer_set_content_type(self.handle, @intFromEnum(ct));
|
||||
}
|
||||
|
||||
/// Fetches the type of buffer contents. Buffers are either empty, contain
|
||||
/// characters (before shaping), or contain glyphs (the result of shaping).
|
||||
pub fn getContentType(self: Buffer) ContentType {
|
||||
return @enumFromInt(c.hb_buffer_get_content_type(self.handle));
|
||||
}
|
||||
|
||||
/// Appends a character with the Unicode value of codepoint to buffer,
|
||||
/// and gives it the initial cluster value of cluster . Clusters can be
|
||||
/// any thing the client wants, they are usually used to refer to the
|
||||
/// index of the character in the input text stream and are output in
|
||||
/// hb_glyph_info_t.cluster field.
|
||||
///
|
||||
/// This function does not check the validity of codepoint, it is up to
|
||||
/// the caller to ensure it is a valid Unicode code point.
|
||||
pub fn add(self: Buffer, cp: u32, cluster: u32) void {
|
||||
c.hb_buffer_add(self.handle, cp, cluster);
|
||||
}
|
||||
|
||||
/// Appends characters from text array to buffer . The item_offset is the
|
||||
/// position of the first character from text that will be appended, and
|
||||
/// item_length is the number of character. When shaping part of a larger
|
||||
/// text (e.g. a run of text from a paragraph), instead of passing just
|
||||
/// the substring corresponding to the run, it is preferable to pass the
|
||||
/// whole paragraph and specify the run start and length as item_offset and
|
||||
/// item_length , respectively, to give HarfBuzz the full context to be
|
||||
/// able, for example, to do cross-run Arabic shaping or properly handle
|
||||
/// combining marks at stat of run.
|
||||
///
|
||||
/// This function does not check the validity of text , it is up to the
|
||||
/// caller to ensure it contains a valid Unicode code points.
|
||||
pub fn addCodepoints(self: Buffer, text: []const u32) void {
|
||||
c.hb_buffer_add_codepoints(
|
||||
self.handle,
|
||||
text.ptr,
|
||||
@intCast(text.len),
|
||||
0,
|
||||
@intCast(text.len),
|
||||
);
|
||||
}
|
||||
|
||||
/// See hb_buffer_add_codepoints().
|
||||
///
|
||||
/// Replaces invalid UTF-32 characters with the buffer replacement code
|
||||
/// point, see hb_buffer_set_replacement_codepoint().
|
||||
pub fn addUTF32(self: Buffer, text: []const u32) void {
|
||||
c.hb_buffer_add_utf32(
|
||||
self.handle,
|
||||
text.ptr,
|
||||
@intCast(text.len),
|
||||
0,
|
||||
@intCast(text.len),
|
||||
);
|
||||
}
|
||||
|
||||
/// See hb_buffer_add_codepoints().
|
||||
///
|
||||
/// Replaces invalid UTF-16 characters with the buffer replacement code
|
||||
/// point, see hb_buffer_set_replacement_codepoint().
|
||||
pub fn addUTF16(self: Buffer, text: []const u16) void {
|
||||
c.hb_buffer_add_utf16(
|
||||
self.handle,
|
||||
text.ptr,
|
||||
@intCast(text.len),
|
||||
0,
|
||||
@intCast(text.len),
|
||||
);
|
||||
}
|
||||
|
||||
/// See hb_buffer_add_codepoints().
|
||||
///
|
||||
/// Replaces invalid UTF-8 characters with the buffer replacement code
|
||||
/// point, see hb_buffer_set_replacement_codepoint().
|
||||
pub fn addUTF8(self: Buffer, text: []const u8) void {
|
||||
c.hb_buffer_add_utf8(
|
||||
self.handle,
|
||||
text.ptr,
|
||||
@intCast(text.len),
|
||||
0,
|
||||
@intCast(text.len),
|
||||
);
|
||||
}
|
||||
|
||||
/// Similar to hb_buffer_add_codepoints(), but allows only access to first
|
||||
/// 256 Unicode code points that can fit in 8-bit strings.
|
||||
pub fn addLatin1(self: Buffer, text: []const u8) void {
|
||||
c.hb_buffer_add_latin1(
|
||||
self.handle,
|
||||
text.ptr,
|
||||
@intCast(text.len),
|
||||
0,
|
||||
@intCast(text.len),
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the text flow direction of the buffer. No shaping can happen
|
||||
/// without setting buffer direction, and it controls the visual direction
|
||||
/// for the output glyphs; for RTL direction the glyphs will be reversed.
|
||||
/// Many layout features depend on the proper setting of the direction,
|
||||
/// for example, reversing RTL text before shaping, then shaping with LTR
|
||||
/// direction is not the same as keeping the text in logical order and
|
||||
/// shaping with RTL direction.
|
||||
pub fn setDirection(self: Buffer, dir: Direction) void {
|
||||
c.hb_buffer_set_direction(self.handle, @intFromEnum(dir));
|
||||
}
|
||||
|
||||
/// See hb_buffer_set_direction()
|
||||
pub fn getDirection(self: Buffer) Direction {
|
||||
return @enumFromInt(c.hb_buffer_get_direction(self.handle));
|
||||
}
|
||||
|
||||
/// Sets the script of buffer to script.
|
||||
///
|
||||
/// Script is crucial for choosing the proper shaping behaviour for
|
||||
/// scripts that require it (e.g. Arabic) and the which OpenType features
|
||||
/// defined in the font to be applied.
|
||||
///
|
||||
/// You can pass one of the predefined hb_script_t values, or use
|
||||
/// hb_script_from_string() or hb_script_from_iso15924_tag() to get the
|
||||
/// corresponding script from an ISO 15924 script tag.
|
||||
pub fn setScript(self: Buffer, script: Script) void {
|
||||
c.hb_buffer_set_script(self.handle, @intFromEnum(script));
|
||||
}
|
||||
|
||||
/// See hb_buffer_set_script()
|
||||
pub fn getScript(self: Buffer) Script {
|
||||
return @enumFromInt(c.hb_buffer_get_script(self.handle));
|
||||
}
|
||||
|
||||
/// Sets the language of buffer to language .
|
||||
///
|
||||
/// Languages are crucial for selecting which OpenType feature to apply to
|
||||
/// the buffer which can result in applying language-specific behaviour.
|
||||
/// Languages are orthogonal to the scripts, and though they are related,
|
||||
/// they are different concepts and should not be confused with each other.
|
||||
///
|
||||
/// Use hb_language_from_string() to convert from BCP 47 language tags to
|
||||
/// hb_language_t.
|
||||
pub fn setLanguage(self: Buffer, language: Language) void {
|
||||
c.hb_buffer_set_language(self.handle, language.handle);
|
||||
}
|
||||
|
||||
/// See hb_buffer_set_language()
|
||||
pub fn getLanguage(self: Buffer) Language {
|
||||
return Language{ .handle = c.hb_buffer_get_language(self.handle) };
|
||||
}
|
||||
|
||||
/// Returns buffer glyph information array. Returned pointer is valid as
|
||||
/// long as buffer contents are not modified.
|
||||
pub fn getGlyphInfos(self: Buffer) []GlyphInfo {
|
||||
var length: u32 = 0;
|
||||
const ptr: [*c]GlyphInfo = @ptrCast(c.hb_buffer_get_glyph_infos(self.handle, &length));
|
||||
return ptr[0..length];
|
||||
}
|
||||
|
||||
/// Returns buffer glyph position array. Returned pointer is valid as
|
||||
/// long as buffer contents are not modified.
|
||||
///
|
||||
/// If buffer did not have positions before, the positions will be
|
||||
/// initialized to zeros, unless this function is called from within a
|
||||
/// buffer message callback (see hb_buffer_set_message_func()), in which
|
||||
/// case NULL is returned.
|
||||
pub fn getGlyphPositions(self: Buffer) ?[]GlyphPosition {
|
||||
var length: u32 = 0;
|
||||
|
||||
if (c.hb_buffer_get_glyph_positions(self.handle, &length)) |positions| {
|
||||
const ptr: [*]GlyphPosition = @ptrCast(positions);
|
||||
return ptr[0..length];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sets unset buffer segment properties based on buffer Unicode contents.
|
||||
/// If buffer is not empty, it must have content type
|
||||
/// HB_BUFFER_CONTENT_TYPE_UNICODE.
|
||||
///
|
||||
/// If buffer script is not set (ie. is HB_SCRIPT_INVALID), it will be set
|
||||
/// to the Unicode script of the first character in the buffer that has a
|
||||
/// script other than HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, and
|
||||
/// HB_SCRIPT_UNKNOWN.
|
||||
///
|
||||
/// Next, if buffer direction is not set (ie. is HB_DIRECTION_INVALID), it
|
||||
/// will be set to the natural horizontal direction of the buffer script as
|
||||
/// returned by hb_script_get_horizontal_direction(). If
|
||||
/// hb_script_get_horizontal_direction() returns HB_DIRECTION_INVALID,
|
||||
/// then HB_DIRECTION_LTR is used.
|
||||
///
|
||||
/// Finally, if buffer language is not set (ie. is HB_LANGUAGE_INVALID), it
|
||||
/// will be set to the process's default language as returned by
|
||||
/// hb_language_get_default(). This may change in the future by taking
|
||||
/// buffer script into consideration when choosing a language. Note that
|
||||
/// hb_language_get_default() is NOT threadsafe the first time it is
|
||||
/// called. See documentation for that function for details.
|
||||
pub fn guessSegmentProperties(self: Buffer) void {
|
||||
c.hb_buffer_guess_segment_properties(self.handle);
|
||||
}
|
||||
|
||||
/// Sets the cluster level of a buffer. The `ClusterLevel` dictates one
|
||||
/// aspect of how HarfBuzz will treat non-base characters during shaping.
|
||||
pub fn setClusterLevel(self: Buffer, level: ClusterLevel) void {
|
||||
c.hb_buffer_set_cluster_level(self.handle, @intFromEnum(level));
|
||||
}
|
||||
};
|
||||
|
||||
/// The type of hb_buffer_t contents.
|
||||
pub const ContentType = enum(u2) {
|
||||
/// Initial value for new buffer.
|
||||
invalid = c.HB_BUFFER_CONTENT_TYPE_INVALID,
|
||||
|
||||
/// The buffer contains input characters (before shaping).
|
||||
unicode = c.HB_BUFFER_CONTENT_TYPE_UNICODE,
|
||||
|
||||
/// The buffer contains output glyphs (after shaping).
|
||||
glyphs = c.HB_BUFFER_CONTENT_TYPE_GLYPHS,
|
||||
};
|
||||
|
||||
/// Data type for holding HarfBuzz's clustering behavior options. The cluster
|
||||
/// level dictates one aspect of how HarfBuzz will treat non-base characters
|
||||
/// during shaping.
|
||||
pub const ClusterLevel = enum(u2) {
|
||||
/// In `monotone_graphemes`, non-base characters are merged into the
|
||||
/// cluster of the base character that precedes them. There is also cluster
|
||||
/// merging every time the clusters will otherwise become non-monotone.
|
||||
/// This is the default cluster level.
|
||||
monotone_graphemes = c.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES,
|
||||
|
||||
/// In `monotone_characters`, non-base characters are initially assigned
|
||||
/// their own cluster values, which are not merged into preceding base
|
||||
/// clusters. This allows HarfBuzz to perform additional operations like
|
||||
/// reorder sequences of adjacent marks. The output is still monotone, but
|
||||
/// the cluster values are more granular.
|
||||
monotone_characters = c.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
|
||||
|
||||
/// In `characters`, non-base characters are assigned their own cluster
|
||||
/// values, which are not merged into preceding base clusters. Moreover,
|
||||
/// the cluster values are not merged into monotone order. This is the most
|
||||
/// granular cluster level, and it is useful for clients that need to know
|
||||
/// the exact cluster values of each character, but is harder to use for
|
||||
/// clients, since clusters might appear in any order.
|
||||
characters = c.HB_BUFFER_CLUSTER_LEVEL_CHARACTERS,
|
||||
|
||||
/// In `graphemes`, non-base characters are merged into the cluster of the
|
||||
/// base character that precedes them. This is similar to the Unicode
|
||||
/// Grapheme Cluster algorithm, but it is not exactly the same. The output
|
||||
/// is not forced to be monotone. This is useful for clients that want to
|
||||
/// use HarfBuzz as a cheap implementation of the Unicode Grapheme Cluster
|
||||
/// algorithm.
|
||||
graphemes = c.HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES,
|
||||
};
|
||||
|
||||
/// The hb_glyph_info_t is the structure that holds information about the
|
||||
/// glyphs and their relation to input text.
|
||||
pub const GlyphInfo = extern struct {
|
||||
/// either a Unicode code point (before shaping) or a glyph index (after shaping).
|
||||
codepoint: u32,
|
||||
_mask: u32,
|
||||
|
||||
/// the index of the character in the original text that corresponds to
|
||||
/// this hb_glyph_info_t, or whatever the client passes to hb_buffer_add().
|
||||
/// More than one hb_glyph_info_t can have the same cluster value, if they
|
||||
/// resulted from the same character (e.g. one to many glyph substitution),
|
||||
/// and when more than one character gets merged in the same glyph (e.g.
|
||||
/// many to one glyph substitution) the hb_glyph_info_t will have the
|
||||
/// smallest cluster value of them. By default some characters are merged
|
||||
/// into the same cluster (e.g. combining marks have the same cluster as
|
||||
/// their bases) even if they are separate glyphs, hb_buffer_set_cluster_level()
|
||||
/// allow selecting more fine-grained cluster handling.
|
||||
cluster: u32,
|
||||
_var1: u32,
|
||||
_var2: u32,
|
||||
};
|
||||
|
||||
/// The hb_glyph_position_t is the structure that holds the positions of the
|
||||
/// glyph in both horizontal and vertical directions. All positions in
|
||||
/// hb_glyph_position_t are relative to the current point.
|
||||
pub const GlyphPosition = extern struct {
|
||||
/// how much the line advances after drawing this glyph when setting text
|
||||
/// in horizontal direction.
|
||||
x_advance: i32,
|
||||
|
||||
/// how much the line advances after drawing this glyph when setting text
|
||||
/// in vertical direction.
|
||||
y_advance: i32,
|
||||
|
||||
/// how much the glyph moves on the X-axis before drawing it, this should
|
||||
/// not affect how much the line advances.
|
||||
x_offset: i32,
|
||||
|
||||
/// how much the glyph moves on the Y-axis before drawing it, this should
|
||||
/// not affect how much the line advances.
|
||||
y_offset: i32,
|
||||
|
||||
_var: u32,
|
||||
};
|
||||
|
||||
test "create" {
|
||||
const testing = std.testing;
|
||||
|
||||
var buffer = try Buffer.create();
|
||||
defer buffer.destroy();
|
||||
buffer.reset();
|
||||
|
||||
// Content type
|
||||
buffer.setContentType(.unicode);
|
||||
try testing.expectEqual(ContentType.unicode, buffer.getContentType());
|
||||
|
||||
// Try add functions
|
||||
buffer.add('🥹', 27);
|
||||
var utf32 = [_]u32{ 'A', 'B', 'C' };
|
||||
var utf16 = [_]u16{ 'A', 'B', 'C' };
|
||||
var utf8 = [_]u8{ 'A', 'B', 'C' };
|
||||
buffer.addCodepoints(&utf32);
|
||||
buffer.addUTF32(&utf32);
|
||||
buffer.addUTF16(&utf16);
|
||||
buffer.addUTF8(&utf8);
|
||||
buffer.addLatin1(&utf8);
|
||||
|
||||
// Guess properties first
|
||||
buffer.guessSegmentProperties();
|
||||
|
||||
// Try to set properties
|
||||
buffer.setDirection(.ltr);
|
||||
try testing.expectEqual(Direction.ltr, buffer.getDirection());
|
||||
|
||||
buffer.setScript(.arabic);
|
||||
try testing.expectEqual(Script.arabic, buffer.getScript());
|
||||
|
||||
buffer.setLanguage(Language.fromString("en"));
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
const std = @import("std");
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const coretext_enabled = b.option(bool, "enable-coretext", "Build coretext") orelse false;
|
||||
const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
|
||||
|
||||
const freetype = b.dependency("freetype", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.@"enable-libpng" = true,
|
||||
});
|
||||
const macos = b.dependency("macos", .{ .target = target, .optimize = optimize });
|
||||
|
||||
const module = harfbuzz: {
|
||||
const module = b.addModule("harfbuzz", .{
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "freetype", .module = freetype.module("freetype") },
|
||||
.{ .name = "macos", .module = macos.module("macos") },
|
||||
},
|
||||
});
|
||||
|
||||
const options = b.addOptions();
|
||||
options.addOption(bool, "coretext", coretext_enabled);
|
||||
options.addOption(bool, "freetype", freetype_enabled);
|
||||
module.addOptions("build_options", options);
|
||||
break :harfbuzz module;
|
||||
};
|
||||
|
||||
// For dynamic linking, we prefer dynamic linking and to search by
|
||||
// mode first. Mode first will search all paths for a dynamic library
|
||||
// before falling back to static.
|
||||
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
|
||||
.preferred_link_mode = .dynamic,
|
||||
.search_strategy = .mode_first,
|
||||
};
|
||||
|
||||
const test_exe = b.addTest(.{
|
||||
.name = "test",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
|
||||
{
|
||||
var it = module.import_table.iterator();
|
||||
while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
|
||||
if (b.systemIntegrationOption("freetype", .{})) {
|
||||
test_exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
|
||||
} else {
|
||||
test_exe.linkLibrary(freetype.artifact("freetype"));
|
||||
}
|
||||
const tests_run = b.addRunArtifact(test_exe);
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&tests_run.step);
|
||||
}
|
||||
|
||||
if (b.systemIntegrationOption("harfbuzz", .{})) {
|
||||
module.linkSystemLibrary("harfbuzz", dynamic_link_opts);
|
||||
test_exe.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
|
||||
} else {
|
||||
const lib = try buildLib(b, module, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
|
||||
.coretext_enabled = coretext_enabled,
|
||||
.freetype_enabled = freetype_enabled,
|
||||
|
||||
.dynamic_link_opts = dynamic_link_opts,
|
||||
});
|
||||
|
||||
test_exe.linkLibrary(lib);
|
||||
}
|
||||
}
|
||||
|
||||
fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
|
||||
const target = options.target;
|
||||
const optimize = options.optimize;
|
||||
|
||||
const coretext_enabled = options.coretext_enabled;
|
||||
const freetype_enabled = options.freetype_enabled;
|
||||
|
||||
const freetype = b.dependency("freetype", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.@"enable-libpng" = true,
|
||||
});
|
||||
|
||||
const lib = b.addLibrary(.{
|
||||
.name = "harfbuzz",
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibC();
|
||||
// On MSVC, we must not use linkLibCpp because Zig unconditionally
|
||||
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
|
||||
// include paths, which conflict with MSVC's own C++ runtime headers.
|
||||
// The MSVC SDK include directories (added via linkLibC) contain
|
||||
// both C and C++ headers, so linkLibCpp is not needed.
|
||||
if (target.result.abi != .msvc) {
|
||||
lib.linkLibCpp();
|
||||
}
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
const dynamic_link_opts = options.dynamic_link_opts;
|
||||
|
||||
var flags: std.ArrayList([]const u8) = .empty;
|
||||
defer flags.deinit(b.allocator);
|
||||
try flags.appendSlice(b.allocator, &.{
|
||||
"-DHAVE_STDBOOL_H",
|
||||
});
|
||||
// Disable ubsan for MSVC: Zig's ubsan runtime cannot be bundled
|
||||
// on Windows (LNK4229), leaving __ubsan_handle_* unresolved when
|
||||
// the static archive is consumed by an external linker.
|
||||
if (target.result.abi == .msvc) {
|
||||
try flags.appendSlice(b.allocator, &.{
|
||||
"-fno-sanitize=undefined",
|
||||
"-fno-sanitize-trap=undefined",
|
||||
});
|
||||
}
|
||||
if (target.result.os.tag != .windows) {
|
||||
try flags.appendSlice(b.allocator, &.{
|
||||
"-DHAVE_UNISTD_H",
|
||||
"-DHAVE_SYS_MMAN_H",
|
||||
"-DHAVE_PTHREAD=1",
|
||||
});
|
||||
}
|
||||
|
||||
// Freetype
|
||||
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
|
||||
if (freetype_enabled) {
|
||||
try flags.appendSlice(b.allocator, &.{
|
||||
"-DHAVE_FREETYPE=1",
|
||||
|
||||
// Let's just assume a new freetype
|
||||
"-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1",
|
||||
"-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1",
|
||||
"-DHAVE_FT_DONE_MM_VAR=1",
|
||||
"-DHAVE_FT_GET_TRANSFORM=1",
|
||||
});
|
||||
|
||||
if (b.systemIntegrationOption("freetype", .{})) {
|
||||
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
|
||||
module.linkSystemLibrary("freetype2", dynamic_link_opts);
|
||||
} else {
|
||||
lib.linkLibrary(freetype.artifact("freetype"));
|
||||
|
||||
if (freetype.builder.lazyDependency(
|
||||
"freetype",
|
||||
.{},
|
||||
)) |freetype_dep| {
|
||||
module.addIncludePath(freetype_dep.path("include"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coretext_enabled) {
|
||||
try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"});
|
||||
lib.linkFramework("CoreText");
|
||||
module.linkFramework("CoreText", .{});
|
||||
}
|
||||
|
||||
if (b.lazyDependency("harfbuzz", .{})) |upstream| {
|
||||
lib.addIncludePath(upstream.path("src"));
|
||||
module.addIncludePath(upstream.path("src"));
|
||||
lib.addCSourceFile(.{
|
||||
.file = upstream.path("src/harfbuzz.cc"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("src"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(lib);
|
||||
|
||||
return lib;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
.{
|
||||
.name = .harfbuzz,
|
||||
.version = "11.0.0",
|
||||
.fingerprint = 0xbd60917cd18865d8,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
// harfbuzz/harfbuzz
|
||||
.harfbuzz = .{
|
||||
.url = "https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz",
|
||||
.hash = "N-V-__8AAG02ugUcWec-Ndp-i7JTsJ0dgF8nnJRUInkGLG7G",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.freetype = .{ .path = "../freetype" },
|
||||
.macos = .{ .path = "../macos" },
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
const builtin = @import("builtin");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("hb.h");
|
||||
if (build_options.freetype) @cInclude("hb-ft.h");
|
||||
if (build_options.coretext) @cInclude("hb-coretext.h");
|
||||
});
|
||||
@@ -0,0 +1,247 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// The direction of a text segment or buffer.
|
||||
///
|
||||
/// A segment can also be tested for horizontal or vertical orientation
|
||||
/// (irrespective of specific direction) with HB_DIRECTION_IS_HORIZONTAL()
|
||||
/// or HB_DIRECTION_IS_VERTICAL().
|
||||
pub const Direction = enum(u3) {
|
||||
invalid = c.HB_DIRECTION_INVALID,
|
||||
ltr = c.HB_DIRECTION_LTR,
|
||||
rtl = c.HB_DIRECTION_RTL,
|
||||
ttb = c.HB_DIRECTION_TTB,
|
||||
bit = c.HB_DIRECTION_BTT,
|
||||
};
|
||||
|
||||
/// Data type for scripts. Each hb_script_t's value is an hb_tag_t
|
||||
/// corresponding to the four-letter values defined by ISO 15924.
|
||||
///
|
||||
/// See also the Script (sc) property of the Unicode Character Database.
|
||||
pub const Script = enum(u31) {
|
||||
common = c.HB_SCRIPT_COMMON,
|
||||
inherited = c.HB_SCRIPT_INHERITED,
|
||||
unknown = c.HB_SCRIPT_UNKNOWN,
|
||||
arabic = c.HB_SCRIPT_ARABIC,
|
||||
armenian = c.HB_SCRIPT_ARMENIAN,
|
||||
bengali = c.HB_SCRIPT_BENGALI,
|
||||
cyrillic = c.HB_SCRIPT_CYRILLIC,
|
||||
devanagari = c.HB_SCRIPT_DEVANAGARI,
|
||||
georgian = c.HB_SCRIPT_GEORGIAN,
|
||||
greek = c.HB_SCRIPT_GREEK,
|
||||
gujarati = c.HB_SCRIPT_GUJARATI,
|
||||
gurmukhi = c.HB_SCRIPT_GURMUKHI,
|
||||
hangul = c.HB_SCRIPT_HANGUL,
|
||||
han = c.HB_SCRIPT_HAN,
|
||||
hebrew = c.HB_SCRIPT_HEBREW,
|
||||
hiragana = c.HB_SCRIPT_HIRAGANA,
|
||||
kannada = c.HB_SCRIPT_KANNADA,
|
||||
katakana = c.HB_SCRIPT_KATAKANA,
|
||||
lao = c.HB_SCRIPT_LAO,
|
||||
latin = c.HB_SCRIPT_LATIN,
|
||||
malayalam = c.HB_SCRIPT_MALAYALAM,
|
||||
oriya = c.HB_SCRIPT_ORIYA,
|
||||
tamil = c.HB_SCRIPT_TAMIL,
|
||||
telugu = c.HB_SCRIPT_TELUGU,
|
||||
thai = c.HB_SCRIPT_THAI,
|
||||
tibetan = c.HB_SCRIPT_TIBETAN,
|
||||
bopomofo = c.HB_SCRIPT_BOPOMOFO,
|
||||
braille = c.HB_SCRIPT_BRAILLE,
|
||||
canadian_syllabics = c.HB_SCRIPT_CANADIAN_SYLLABICS,
|
||||
cherokee = c.HB_SCRIPT_CHEROKEE,
|
||||
ethiopic = c.HB_SCRIPT_ETHIOPIC,
|
||||
khmer = c.HB_SCRIPT_KHMER,
|
||||
mongolian = c.HB_SCRIPT_MONGOLIAN,
|
||||
myanmar = c.HB_SCRIPT_MYANMAR,
|
||||
ogham = c.HB_SCRIPT_OGHAM,
|
||||
runic = c.HB_SCRIPT_RUNIC,
|
||||
sinhala = c.HB_SCRIPT_SINHALA,
|
||||
syriac = c.HB_SCRIPT_SYRIAC,
|
||||
thaana = c.HB_SCRIPT_THAANA,
|
||||
yi = c.HB_SCRIPT_YI,
|
||||
deseret = c.HB_SCRIPT_DESERET,
|
||||
gothic = c.HB_SCRIPT_GOTHIC,
|
||||
old_italic = c.HB_SCRIPT_OLD_ITALIC,
|
||||
buhid = c.HB_SCRIPT_BUHID,
|
||||
hanunoo = c.HB_SCRIPT_HANUNOO,
|
||||
tagalog = c.HB_SCRIPT_TAGALOG,
|
||||
tagbanwa = c.HB_SCRIPT_TAGBANWA,
|
||||
cypriot = c.HB_SCRIPT_CYPRIOT,
|
||||
limbu = c.HB_SCRIPT_LIMBU,
|
||||
linear_b = c.HB_SCRIPT_LINEAR_B,
|
||||
osmanya = c.HB_SCRIPT_OSMANYA,
|
||||
shavian = c.HB_SCRIPT_SHAVIAN,
|
||||
tai_le = c.HB_SCRIPT_TAI_LE,
|
||||
ugaritic = c.HB_SCRIPT_UGARITIC,
|
||||
buginese = c.HB_SCRIPT_BUGINESE,
|
||||
coptic = c.HB_SCRIPT_COPTIC,
|
||||
glagolitic = c.HB_SCRIPT_GLAGOLITIC,
|
||||
kharoshthi = c.HB_SCRIPT_KHAROSHTHI,
|
||||
new_tai_lue = c.HB_SCRIPT_NEW_TAI_LUE,
|
||||
old_persian = c.HB_SCRIPT_OLD_PERSIAN,
|
||||
syloti_nagri = c.HB_SCRIPT_SYLOTI_NAGRI,
|
||||
tifinagh = c.HB_SCRIPT_TIFINAGH,
|
||||
balinese = c.HB_SCRIPT_BALINESE,
|
||||
cuneiform = c.HB_SCRIPT_CUNEIFORM,
|
||||
nko = c.HB_SCRIPT_NKO,
|
||||
phags_pa = c.HB_SCRIPT_PHAGS_PA,
|
||||
phoenician = c.HB_SCRIPT_PHOENICIAN,
|
||||
carian = c.HB_SCRIPT_CARIAN,
|
||||
cham = c.HB_SCRIPT_CHAM,
|
||||
kayah_li = c.HB_SCRIPT_KAYAH_LI,
|
||||
lepcha = c.HB_SCRIPT_LEPCHA,
|
||||
lycian = c.HB_SCRIPT_LYCIAN,
|
||||
lydian = c.HB_SCRIPT_LYDIAN,
|
||||
ol_chiki = c.HB_SCRIPT_OL_CHIKI,
|
||||
rejang = c.HB_SCRIPT_REJANG,
|
||||
saurashtra = c.HB_SCRIPT_SAURASHTRA,
|
||||
sundanese = c.HB_SCRIPT_SUNDANESE,
|
||||
vai = c.HB_SCRIPT_VAI,
|
||||
avestan = c.HB_SCRIPT_AVESTAN,
|
||||
bamum = c.HB_SCRIPT_BAMUM,
|
||||
egyptian_hieroglyphs = c.HB_SCRIPT_EGYPTIAN_HIEROGLYPHS,
|
||||
imperial_aramaic = c.HB_SCRIPT_IMPERIAL_ARAMAIC,
|
||||
inscriptional_pahlavi = c.HB_SCRIPT_INSCRIPTIONAL_PAHLAVI,
|
||||
inscriptional_parthian = c.HB_SCRIPT_INSCRIPTIONAL_PARTHIAN,
|
||||
javanese = c.HB_SCRIPT_JAVANESE,
|
||||
kaithi = c.HB_SCRIPT_KAITHI,
|
||||
lisu = c.HB_SCRIPT_LISU,
|
||||
meetei_mayek = c.HB_SCRIPT_MEETEI_MAYEK,
|
||||
old_south_arabian = c.HB_SCRIPT_OLD_SOUTH_ARABIAN,
|
||||
old_turkic = c.HB_SCRIPT_OLD_TURKIC,
|
||||
samaritan = c.HB_SCRIPT_SAMARITAN,
|
||||
tai_tham = c.HB_SCRIPT_TAI_THAM,
|
||||
tai_viet = c.HB_SCRIPT_TAI_VIET,
|
||||
batak = c.HB_SCRIPT_BATAK,
|
||||
brahmi = c.HB_SCRIPT_BRAHMI,
|
||||
mandaic = c.HB_SCRIPT_MANDAIC,
|
||||
chakma = c.HB_SCRIPT_CHAKMA,
|
||||
meroitic_cursive = c.HB_SCRIPT_MEROITIC_CURSIVE,
|
||||
meroitic_hieroglyphs = c.HB_SCRIPT_MEROITIC_HIEROGLYPHS,
|
||||
miao = c.HB_SCRIPT_MIAO,
|
||||
sharada = c.HB_SCRIPT_SHARADA,
|
||||
sora_sompeng = c.HB_SCRIPT_SORA_SOMPENG,
|
||||
takri = c.HB_SCRIPT_TAKRI,
|
||||
bassa_vah = c.HB_SCRIPT_BASSA_VAH,
|
||||
caucasian_albanian = c.HB_SCRIPT_CAUCASIAN_ALBANIAN,
|
||||
duployan = c.HB_SCRIPT_DUPLOYAN,
|
||||
elbasan = c.HB_SCRIPT_ELBASAN,
|
||||
grantha = c.HB_SCRIPT_GRANTHA,
|
||||
khojki = c.HB_SCRIPT_KHOJKI,
|
||||
khudawadi = c.HB_SCRIPT_KHUDAWADI,
|
||||
linear_a = c.HB_SCRIPT_LINEAR_A,
|
||||
mahajani = c.HB_SCRIPT_MAHAJANI,
|
||||
manichaean = c.HB_SCRIPT_MANICHAEAN,
|
||||
mende_kikakui = c.HB_SCRIPT_MENDE_KIKAKUI,
|
||||
modi = c.HB_SCRIPT_MODI,
|
||||
mro = c.HB_SCRIPT_MRO,
|
||||
nabataean = c.HB_SCRIPT_NABATAEAN,
|
||||
old_north_arabian = c.HB_SCRIPT_OLD_NORTH_ARABIAN,
|
||||
old_permic = c.HB_SCRIPT_OLD_PERMIC,
|
||||
pahawh_hmong = c.HB_SCRIPT_PAHAWH_HMONG,
|
||||
palmyrene = c.HB_SCRIPT_PALMYRENE,
|
||||
pau_cin_hau = c.HB_SCRIPT_PAU_CIN_HAU,
|
||||
psalter_pahlavi = c.HB_SCRIPT_PSALTER_PAHLAVI,
|
||||
siddham = c.HB_SCRIPT_SIDDHAM,
|
||||
tirhuta = c.HB_SCRIPT_TIRHUTA,
|
||||
warang_citi = c.HB_SCRIPT_WARANG_CITI,
|
||||
ahom = c.HB_SCRIPT_AHOM,
|
||||
anatolian_hieroglyphs = c.HB_SCRIPT_ANATOLIAN_HIEROGLYPHS,
|
||||
hatran = c.HB_SCRIPT_HATRAN,
|
||||
multani = c.HB_SCRIPT_MULTANI,
|
||||
old_hungarian = c.HB_SCRIPT_OLD_HUNGARIAN,
|
||||
signwriting = c.HB_SCRIPT_SIGNWRITING,
|
||||
adlam = c.HB_SCRIPT_ADLAM,
|
||||
bhaiksuki = c.HB_SCRIPT_BHAIKSUKI,
|
||||
marchen = c.HB_SCRIPT_MARCHEN,
|
||||
osage = c.HB_SCRIPT_OSAGE,
|
||||
tangut = c.HB_SCRIPT_TANGUT,
|
||||
newa = c.HB_SCRIPT_NEWA,
|
||||
masaram_gondi = c.HB_SCRIPT_MASARAM_GONDI,
|
||||
nushu = c.HB_SCRIPT_NUSHU,
|
||||
soyombo = c.HB_SCRIPT_SOYOMBO,
|
||||
zanabazar_square = c.HB_SCRIPT_ZANABAZAR_SQUARE,
|
||||
dogra = c.HB_SCRIPT_DOGRA,
|
||||
gunjala_gondi = c.HB_SCRIPT_GUNJALA_GONDI,
|
||||
hanifi_rohingya = c.HB_SCRIPT_HANIFI_ROHINGYA,
|
||||
makasar = c.HB_SCRIPT_MAKASAR,
|
||||
medefaidrin = c.HB_SCRIPT_MEDEFAIDRIN,
|
||||
old_sogdian = c.HB_SCRIPT_OLD_SOGDIAN,
|
||||
sogdian = c.HB_SCRIPT_SOGDIAN,
|
||||
elymaic = c.HB_SCRIPT_ELYMAIC,
|
||||
nandinagari = c.HB_SCRIPT_NANDINAGARI,
|
||||
nyiakeng_puachue_hmong = c.HB_SCRIPT_NYIAKENG_PUACHUE_HMONG,
|
||||
wancho = c.HB_SCRIPT_WANCHO,
|
||||
chorasmian = c.HB_SCRIPT_CHORASMIAN,
|
||||
dives_akuru = c.HB_SCRIPT_DIVES_AKURU,
|
||||
khitan_small_script = c.HB_SCRIPT_KHITAN_SMALL_SCRIPT,
|
||||
yezidi = c.HB_SCRIPT_YEZIDI,
|
||||
cypro_minoan = c.HB_SCRIPT_CYPRO_MINOAN,
|
||||
old_uyghur = c.HB_SCRIPT_OLD_UYGHUR,
|
||||
tangsa = c.HB_SCRIPT_TANGSA,
|
||||
toto = c.HB_SCRIPT_TOTO,
|
||||
vithkuqi = c.HB_SCRIPT_VITHKUQI,
|
||||
math = c.HB_SCRIPT_MATH,
|
||||
invalid = c.HB_SCRIPT_INVALID,
|
||||
};
|
||||
|
||||
/// Data type for languages. Each hb_language_t corresponds to a BCP 47
|
||||
/// language tag.
|
||||
pub const Language = struct {
|
||||
handle: c.hb_language_t,
|
||||
|
||||
/// Converts str representing a BCP 47 language tag to the corresponding
|
||||
/// hb_language_t.
|
||||
pub fn fromString(str: []const u8) Language {
|
||||
return .{
|
||||
.handle = c.hb_language_from_string(str.ptr, @intCast(str.len)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts an hb_language_t to a string.
|
||||
pub fn toString(self: Language) [:0]const u8 {
|
||||
return std.mem.span(@as(
|
||||
[*:0]const u8,
|
||||
@ptrCast(c.hb_language_to_string(self.handle)),
|
||||
));
|
||||
}
|
||||
|
||||
/// Fetch the default language from current locale.
|
||||
pub fn getDefault() Language {
|
||||
return .{ .handle = c.hb_language_get_default() };
|
||||
}
|
||||
};
|
||||
|
||||
/// The hb_feature_t is the structure that holds information about requested
|
||||
/// feature application. The feature will be applied with the given value to
|
||||
/// all glyphs which are in clusters between start (inclusive) and end
|
||||
/// (exclusive). Setting start to HB_FEATURE_GLOBAL_START and end to
|
||||
/// HB_FEATURE_GLOBAL_END specifies that the feature always applies to the
|
||||
/// entire buffer.
|
||||
pub const Feature = extern struct {
|
||||
tag: c.hb_tag_t,
|
||||
value: u32,
|
||||
start: c_uint,
|
||||
end: c_uint,
|
||||
|
||||
pub fn fromString(str: []const u8) ?Feature {
|
||||
var f: c.hb_feature_t = undefined;
|
||||
return if (c.hb_feature_from_string(
|
||||
str.ptr,
|
||||
@intCast(str.len),
|
||||
&f,
|
||||
) > 0)
|
||||
@bitCast(f)
|
||||
else
|
||||
null;
|
||||
}
|
||||
|
||||
pub fn toString(self: *Feature, buf: []u8) void {
|
||||
c.hb_feature_to_string(self, buf.ptr, @intCast(buf.len));
|
||||
}
|
||||
};
|
||||
|
||||
test "feature from string" {
|
||||
const testing = std.testing;
|
||||
try testing.expect(Feature.fromString("dlig") != null);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
const macos = @import("macos");
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const Face = @import("face.zig").Face;
|
||||
const Font = @import("font.zig").Font;
|
||||
const Error = @import("errors.zig").Error;
|
||||
|
||||
// Use custom extern functions so that the proper CoreText structs are used
|
||||
// without a ptrcast.
|
||||
extern fn hb_coretext_font_create(ct_face: *macos.text.Font) ?*c.hb_font_t;
|
||||
|
||||
/// Creates an hb_font_t font object from the specified CTFontRef.
|
||||
pub fn createFont(face: *macos.text.Font) Error!Font {
|
||||
const handle = hb_coretext_font_create(face) orelse return Error.HarfbuzzFailed;
|
||||
return Font{ .handle = handle };
|
||||
}
|
||||
|
||||
test {
|
||||
if (!@hasDecl(c, "hb_coretext_font_create")) return error.SkipZigTest;
|
||||
|
||||
const name = try macos.foundation.String.createWithBytes("Monaco", .utf8, false);
|
||||
defer name.release();
|
||||
const desc = try macos.text.FontDescriptor.createWithNameAndSize(name, 12);
|
||||
defer desc.release();
|
||||
const font = try macos.text.Font.createWithFontDescriptor(desc, 12);
|
||||
defer font.release();
|
||||
|
||||
var hb_font = try createFont(font);
|
||||
defer hb_font.destroy();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub const Error = error{
|
||||
/// Not very descriptive but harfbuzz doesn't actually have error
|
||||
/// codes so the best we can do!
|
||||
HarfbuzzFailed,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// A font face is an object that represents a single face from within a font family.
|
||||
///
|
||||
/// More precisely, a font face represents a single face in a binary font file.
|
||||
/// Font faces are typically built from a binary blob and a face index.
|
||||
/// Font faces are used to create fonts.
|
||||
pub const Face = struct {
|
||||
handle: *c.hb_face_t,
|
||||
|
||||
/// Decreases the reference count on a face object. When the reference
|
||||
/// count reaches zero, the face is destroyed, freeing all memory.
|
||||
pub fn destroy(self: *Face) void {
|
||||
c.hb_face_destroy(self.handle);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const Face = @import("face.zig").Face;
|
||||
const Error = @import("errors.zig").Error;
|
||||
|
||||
pub const Font = struct {
|
||||
handle: *c.hb_font_t,
|
||||
|
||||
/// Constructs a new font object from the specified face.
|
||||
pub fn create(face: Face) Error!Font {
|
||||
const handle = c.hb_font_create(face.handle) orelse return Error.HarfbuzzFailed;
|
||||
return Font{ .handle = handle };
|
||||
}
|
||||
|
||||
/// Decreases the reference count on the given font object. When the
|
||||
/// reference count reaches zero, the font is destroyed, freeing all memory.
|
||||
pub fn destroy(self: *Font) void {
|
||||
c.hb_font_destroy(self.handle);
|
||||
}
|
||||
|
||||
pub fn setScale(self: *Font, x: u32, y: u32) void {
|
||||
c.hb_font_set_scale(
|
||||
self.handle,
|
||||
@intCast(x),
|
||||
@intCast(y),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
const freetype = @import("freetype");
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const Face = @import("face.zig").Face;
|
||||
const Font = @import("font.zig").Font;
|
||||
const Error = @import("errors.zig").Error;
|
||||
|
||||
// Use custom extern functions so that the proper freetype structs are used
|
||||
// without a ptrcast. These are only needed when interacting with freetype
|
||||
// C structs.
|
||||
extern fn hb_ft_face_create_referenced(ft_face: freetype.c.FT_Face) ?*c.hb_face_t;
|
||||
extern fn hb_ft_font_create_referenced(ft_face: freetype.c.FT_Face) ?*c.hb_font_t;
|
||||
extern fn hb_ft_font_get_face(font: ?*c.hb_font_t) freetype.c.FT_Face;
|
||||
|
||||
/// Creates an hb_face_t face object from the specified FT_Face.
|
||||
///
|
||||
/// This is the preferred variant of the hb_ft_face_create* function
|
||||
/// family, because it calls FT_Reference_Face() on ft_face , ensuring
|
||||
/// that ft_face remains alive as long as the resulting hb_face_t face
|
||||
/// object remains alive. Also calls FT_Done_Face() when the hb_face_t
|
||||
/// face object is destroyed.
|
||||
///
|
||||
/// Use this version unless you know you have good reasons not to.
|
||||
pub fn createFace(face: freetype.c.FT_Face) Error!Face {
|
||||
const handle = hb_ft_face_create_referenced(face) orelse return Error.HarfbuzzFailed;
|
||||
return Face{ .handle = handle };
|
||||
}
|
||||
|
||||
/// Creates an hb_font_t font object from the specified FT_Face.
|
||||
pub fn createFont(face: freetype.c.FT_Face) Error!Font {
|
||||
const handle = hb_ft_font_create_referenced(face) orelse return Error.HarfbuzzFailed;
|
||||
return Font{ .handle = handle };
|
||||
}
|
||||
|
||||
/// Configures the font-functions structure of the specified hb_font_t font
|
||||
/// object to use FreeType font functions.
|
||||
///
|
||||
/// In particular, you can use this function to configure an existing
|
||||
/// hb_face_t face object for use with FreeType font functions even if that
|
||||
/// hb_face_t face object was initially created with hb_face_create(), and
|
||||
/// therefore was not initially configured to use FreeType font functions.
|
||||
///
|
||||
/// An hb_face_t face object created with hb_ft_face_create() is preconfigured
|
||||
/// for FreeType font functions and does not require this function to be used.
|
||||
pub fn setFontFuncs(font: Font) void {
|
||||
c.hb_ft_font_set_funcs(font.handle);
|
||||
}
|
||||
|
||||
test {
|
||||
if (!@hasDecl(c, "hb_ft_font_create_referenced")) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
const testFont = freetype.testing.font_regular;
|
||||
const ftc = freetype.c;
|
||||
const ftok = ftc.FT_Err_Ok;
|
||||
|
||||
var ft_lib: ftc.FT_Library = undefined;
|
||||
if (ftc.FT_Init_FreeType(&ft_lib) != ftok)
|
||||
return error.FreeTypeInitFailed;
|
||||
defer _ = ftc.FT_Done_FreeType(ft_lib);
|
||||
|
||||
var ft_face: ftc.FT_Face = undefined;
|
||||
try testing.expect(ftc.FT_New_Memory_Face(
|
||||
ft_lib,
|
||||
testFont,
|
||||
@intCast(testFont.len),
|
||||
0,
|
||||
&ft_face,
|
||||
) == ftok);
|
||||
defer _ = ftc.FT_Done_Face(ft_face);
|
||||
|
||||
var face = try createFace(ft_face);
|
||||
defer face.destroy();
|
||||
|
||||
var font = try createFont(ft_face);
|
||||
defer font.destroy();
|
||||
setFontFuncs(font);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
const blob = @import("blob.zig");
|
||||
const buffer = @import("buffer.zig");
|
||||
const common = @import("common.zig");
|
||||
const errors = @import("errors.zig");
|
||||
const face = @import("face.zig");
|
||||
const font = @import("font.zig");
|
||||
const shapepkg = @import("shape.zig");
|
||||
const versionpkg = @import("version.zig");
|
||||
|
||||
pub const c = @import("c.zig").c;
|
||||
pub const freetype = @import("freetype.zig");
|
||||
pub const coretext = @import("coretext.zig");
|
||||
pub const MemoryMode = blob.MemoryMode;
|
||||
pub const Blob = blob.Blob;
|
||||
pub const Buffer = buffer.Buffer;
|
||||
pub const GlyphPosition = buffer.GlyphPosition;
|
||||
pub const Direction = common.Direction;
|
||||
pub const Script = common.Script;
|
||||
pub const Language = common.Language;
|
||||
pub const Feature = common.Feature;
|
||||
pub const Face = face.Face;
|
||||
pub const Font = font.Font;
|
||||
pub const shape = shapepkg.shape;
|
||||
pub const Version = versionpkg.Version;
|
||||
pub const version = versionpkg.version;
|
||||
pub const versionAtLeast = versionpkg.versionAtLeast;
|
||||
pub const versionString = versionpkg.versionString;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const Font = @import("font.zig").Font;
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const Feature = @import("common.zig").Feature;
|
||||
|
||||
/// Shapes buffer using font turning its Unicode characters content to
|
||||
/// positioned glyphs. If features is not NULL, it will be used to control
|
||||
/// the features applied during shaping. If two features have the same tag
|
||||
/// but overlapping ranges the value of the feature with the higher index
|
||||
/// takes precedence.
|
||||
pub fn shape(font: Font, buf: Buffer, features: ?[]const Feature) void {
|
||||
const hb_feats: [*c]const c.hb_feature_t = feats: {
|
||||
if (features) |fs| {
|
||||
if (fs.len > 0) break :feats @ptrCast(fs.ptr);
|
||||
}
|
||||
|
||||
break :feats null;
|
||||
};
|
||||
|
||||
c.hb_shape(
|
||||
font.handle,
|
||||
buf.handle,
|
||||
hb_feats,
|
||||
if (features) |f| @intCast(f.len) else 0,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
pub const Version = struct {
|
||||
major: u32,
|
||||
minor: u32,
|
||||
micro: u32,
|
||||
};
|
||||
|
||||
/// Returns library version as three integer components.
|
||||
pub fn version() Version {
|
||||
var major: c_uint = 0;
|
||||
var minor: c_uint = 0;
|
||||
var micro: c_uint = 0;
|
||||
c.hb_version(&major, &minor, µ);
|
||||
return .{ .major = major, .minor = minor, .micro = micro };
|
||||
}
|
||||
|
||||
/// Tests the library version against a minimum value, as three integer components.
|
||||
pub fn versionAtLeast(vsn: Version) bool {
|
||||
return c.hb_version_atleast(
|
||||
vsn.major,
|
||||
vsn.minor,
|
||||
vsn.micro,
|
||||
) > 0;
|
||||
}
|
||||
|
||||
/// Returns library version as a string with three components.
|
||||
pub fn versionString() [:0]const u8 {
|
||||
const res = c.hb_version_string();
|
||||
return std.mem.sliceTo(res, 0);
|
||||
}
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
|
||||
// Should be able to get the version
|
||||
const vsn = version();
|
||||
try testing.expect(vsn.major > 0);
|
||||
|
||||
// Should be at least version 1
|
||||
try testing.expect(versionAtLeast(.{
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
.micro = 0,
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user