ghostty/pkg/harfbuzz/buffer.zig
Mitchell Hashimoto ea73edaf7f pkg/harfbuzz: fix c.zig
2024-08-16 15:38:18 -07:00

334 lines
13 KiB
Zig

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);
}
};
/// 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,
};
/// 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"));
}