Merge pull request #1740 from mitchellh/ct-rtl

coretext: force LTR font shaping
This commit is contained in:
Mitchell Hashimoto
2024-05-08 10:19:57 -07:00
committed by GitHub
5 changed files with 94 additions and 4 deletions

View File

@ -1,6 +1,7 @@
.{ .{
.name = "macos", .name = "macos",
.version = "0.1.0", .version = "0.1.0",
.paths = .{""},
.dependencies = .{ .dependencies = .{
.apple_sdk = .{ .path = "../apple-sdk" }, .apple_sdk = .{ .path = "../apple-sdk" },
}, },

View File

@ -6,6 +6,7 @@ pub usingnamespace @import("text/font_manager.zig");
pub usingnamespace @import("text/frame.zig"); pub usingnamespace @import("text/frame.zig");
pub usingnamespace @import("text/framesetter.zig"); pub usingnamespace @import("text/framesetter.zig");
pub usingnamespace @import("text/line.zig"); pub usingnamespace @import("text/line.zig");
pub usingnamespace @import("text/paragraph_style.zig");
pub usingnamespace @import("text/run.zig"); pub usingnamespace @import("text/run.zig");
pub usingnamespace @import("text/stylized_strings.zig"); pub usingnamespace @import("text/stylized_strings.zig");

View File

@ -0,0 +1,49 @@
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const foundation = @import("../foundation.zig");
const graphics = @import("../graphics.zig");
const text = @import("../text.zig");
const c = @import("c.zig");
// https://developer.apple.com/documentation/coretext/ctparagraphstyle?language=objc
pub const ParagraphStyle = opaque {
pub fn create(
settings: []const ParagraphStyleSetting,
) Allocator.Error!*ParagraphStyle {
return @ptrCast(@constCast(c.CTParagraphStyleCreate(
@ptrCast(settings.ptr),
settings.len,
)));
}
pub fn release(self: *ParagraphStyle) void {
foundation.CFRelease(self);
}
};
/// https://developer.apple.com/documentation/coretext/ctparagraphstylesetting?language=objc
pub const ParagraphStyleSetting = extern struct {
spec: ParagraphStyleSpecifier,
value_size: usize,
value: *const anyopaque,
};
/// https://developer.apple.com/documentation/coretext/ctparagraphstylespecifier?language=objc
pub const ParagraphStyleSpecifier = enum(c_uint) {
base_writing_direction = 13,
};
/// https://developer.apple.com/documentation/uikit/nswritingdirectionattributename?language=objc
pub const WritingDirection = enum(c_int) {
natural = -1,
ltr = 0,
rtl = 1,
lro = 2,
rlo = 3,
};
test ParagraphStyle {
const p = try ParagraphStyle.create(&.{});
defer p.release();
}

View File

@ -3,10 +3,14 @@ const c = @import("c.zig");
pub const StringAttribute = enum { pub const StringAttribute = enum {
font, font,
paragraph_style,
writing_direction,
pub fn key(self: StringAttribute) *foundation.String { pub fn key(self: StringAttribute) *foundation.String {
return @ptrFromInt(@intFromPtr(switch (self) { return @ptrFromInt(@intFromPtr(switch (self) {
.font => c.kCTFontAttributeName, .font => c.kCTFontAttributeName,
.paragraph_style => c.kCTParagraphStyleAttributeName,
.writing_direction => c.kCTWritingDirectionAttributeName,
})); }));
} }
}; };

View File

@ -44,6 +44,11 @@ pub const Shaper = struct {
/// The shared memory used for shaping results. /// The shared memory used for shaping results.
cell_buf: CellBuf, cell_buf: CellBuf,
/// The cached writing direction value for shaping. This isn't
/// configurable we just use this as a cache to avoid creating
/// and releasing many objects when shaping.
writing_direction: *macos.foundation.Array,
const CellBuf = std.ArrayListUnmanaged(font.shape.Cell); const CellBuf = std.ArrayListUnmanaged(font.shape.Cell);
const CodepointList = std.ArrayListUnmanaged(Codepoint); const CodepointList = std.ArrayListUnmanaged(Codepoint);
const Codepoint = struct { const Codepoint = struct {
@ -172,14 +177,37 @@ pub const Shaper = struct {
for (hardcoded_features) |name| try feats.append(name); for (hardcoded_features) |name| try feats.append(name);
for (opts.features) |name| try feats.append(name); for (opts.features) |name| try feats.append(name);
const run_state = try RunState.init(); var run_state = try RunState.init();
errdefer run_state.deinit(); errdefer run_state.deinit(alloc);
// For now we only support LTR text. If we shape RTL text then
// rendering will be very wrong so we need to explicitly force
// LTR no matter what.
//
// See: https://github.com/mitchellh/ghostty/issues/1737
// See: https://github.com/mitchellh/ghostty/issues/1442
const writing_direction = array: {
const dir: macos.text.WritingDirection = .lro;
const num = try macos.foundation.Number.create(
.int,
&@intFromEnum(dir),
);
defer num.release();
var arr_init = [_]*const macos.foundation.Number{num};
break :array try macos.foundation.Array.create(
macos.foundation.Number,
&arr_init,
);
};
errdefer writing_direction.release();
return Shaper{ return Shaper{
.alloc = alloc, .alloc = alloc,
.cell_buf = .{}, .cell_buf = .{},
.run_state = run_state, .run_state = run_state,
.features = feats, .features = feats,
.writing_direction = writing_direction,
}; };
} }
@ -187,6 +215,7 @@ pub const Shaper = struct {
self.cell_buf.deinit(self.alloc); self.cell_buf.deinit(self.alloc);
self.run_state.deinit(self.alloc); self.run_state.deinit(self.alloc);
self.features.deinit(); self.features.deinit();
self.writing_direction.release();
} }
pub fn runIterator( pub fn runIterator(
@ -276,8 +305,14 @@ pub const Shaper = struct {
// Get our font and use that get the attributes to set for the // Get our font and use that get the attributes to set for the
// attributed string so the whole string uses the same font. // attributed string so the whole string uses the same font.
const attr_dict = dict: { const attr_dict = dict: {
var keys = [_]?*const anyopaque{macos.text.StringAttribute.font.key()}; var keys = [_]?*const anyopaque{
var values = [_]?*const anyopaque{run_font}; macos.text.StringAttribute.font.key(),
macos.text.StringAttribute.writing_direction.key(),
};
var values = [_]?*const anyopaque{
run_font,
self.writing_direction,
};
break :dict try macos.foundation.Dictionary.create(&keys, &values); break :dict try macos.foundation.Dictionary.create(&keys, &values);
}; };
defer attr_dict.release(); defer attr_dict.release();