diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg/harfbuzz/buffer.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg/harfbuzz/buffer.zig')
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/buffer.zig | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig b/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig new file mode 100644 index 00000000..b97c1bef --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig @@ -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")); +} |
