font/coretext: Use positions to fix x/y offsets

pull/9883/head
Jacob Sandlund 2025-12-04 10:09:41 -05:00
parent cf06417b7d
commit 572c06f67d
2 changed files with 85 additions and 8 deletions

View File

@ -106,6 +106,19 @@ pub const Run = opaque {
pub fn getStatus(self: *Run) Status {
return @bitCast(c.CTRunGetStatus(@ptrCast(self)));
}
pub fn getAttributes(self: *Run) *foundation.Dictionary {
return @ptrCast(@constCast(c.CTRunGetAttributes(@ptrCast(self))));
}
pub fn getFont(self: *Run) ?*text.Font {
const attrs = self.getAttributes();
const font_ptr = attrs.getValue(*const anyopaque, c.kCTFontAttributeName);
if (font_ptr) |ptr| {
return @ptrCast(@constCast(ptr));
}
return null;
}
};
/// https://developer.apple.com/documentation/coretext/ctrunstatus?language=objc

View File

@ -377,11 +377,21 @@ pub const Shaper = struct {
const line = typesetter.createLine(.{ .location = 0, .length = 0 });
self.cf_release_pool.appendAssumeCapacity(line);
// This keeps track of the current offsets within a single cell.
// This keeps track of the current offsets within a run.
var run_offset: struct {
x: f64 = 0,
y: f64 = 0,
} = .{};
// This keeps track of the current offsets within a cell.
var cell_offset: struct {
cluster: u32 = 0,
x: f64 = 0,
y: f64 = 0,
// For debugging positions, turn this on:
start_index: usize = 0,
end_index: usize = 0,
} = .{};
// Clear our cell buf and make sure we have enough room for the whole
@ -411,15 +421,18 @@ pub const Shaper = struct {
// Get our glyphs and positions
const glyphs = ctrun.getGlyphsPtr() orelse try ctrun.getGlyphs(alloc);
const advances = ctrun.getAdvancesPtr() orelse try ctrun.getAdvances(alloc);
const positions = ctrun.getPositionsPtr() orelse try ctrun.getPositions(alloc);
const indices = ctrun.getStringIndicesPtr() orelse try ctrun.getStringIndices(alloc);
assert(glyphs.len == advances.len);
assert(glyphs.len == positions.len);
assert(glyphs.len == indices.len);
for (
glyphs,
advances,
positions,
indices,
) |glyph, advance, index| {
) |glyph, advance, position, index| {
// Our cluster is also our cell X position. If the cluster changes
// then we need to reset our current cell offsets.
const cluster = state.codepoints.items[index].cluster;
@ -431,20 +444,71 @@ pub const Shaper = struct {
// wait for that.
if (cell_offset.cluster > cluster) break :pad;
cell_offset = .{ .cluster = cluster };
cell_offset = .{
.cluster = cluster,
.x = run_offset.x,
.y = run_offset.y,
// For debugging positions, turn this on:
.start_index = index,
.end_index = index,
};
} else {
if (index < cell_offset.start_index) {
cell_offset.start_index = index;
}
if (index > cell_offset.end_index) {
cell_offset.end_index = index;
}
}
const x_offset = position.x - cell_offset.x;
const y_offset = position.y - cell_offset.y;
const advance_x_offset = run_offset.x - cell_offset.x;
const advance_y_offset = run_offset.y - cell_offset.y;
const x_offset_diff = x_offset - advance_x_offset;
const y_offset_diff = y_offset - advance_y_offset;
if (@abs(x_offset_diff) > 0.0001 or @abs(y_offset_diff) > 0.0001) {
var allocating = std.Io.Writer.Allocating.init(alloc);
const writer = &allocating.writer;
const codepoints = state.codepoints.items[cell_offset.start_index .. cell_offset.end_index + 1];
for (codepoints) |cp| {
if (cp.codepoint == 0) continue; // Skip surrogate pair padding
try writer.print("\\u{{{x}}}", .{cp.codepoint});
}
try writer.writeAll("");
for (codepoints) |cp| {
if (cp.codepoint == 0) continue; // Skip surrogate pair padding
try writer.print("{u}", .{@as(u21, @intCast(cp.codepoint))});
}
const formatted_cps = try allocating.toOwnedSlice();
log.warn("position differs from advance: cluster={d} pos=({d:.2},{d:.2}) adv=({d:.2},{d:.2}) diff=({d:.2},{d:.2}) current cp={x}, cps={s}", .{
cluster,
x_offset,
y_offset,
advance_x_offset,
advance_y_offset,
x_offset_diff,
y_offset_diff,
state.codepoints.items[index].codepoint,
formatted_cps,
});
}
self.cell_buf.appendAssumeCapacity(.{
.x = @intCast(cluster),
.x_offset = @intFromFloat(@round(cell_offset.x)),
.y_offset = @intFromFloat(@round(cell_offset.y)),
.x_offset = @intFromFloat(@round(x_offset)),
.y_offset = @intFromFloat(@round(y_offset)),
.glyph_index = glyph,
});
// Add our advances to keep track of our current cell offsets.
// Add our advances to keep track of our run offsets.
// Advances apply to the NEXT cell.
cell_offset.x += advance.width;
cell_offset.y += advance.height;
run_offset.x += advance.width;
run_offset.y += advance.height;
}
}