textSizeRaw
Doesn't scale the font or max_width, always stops at newlines
Assumes the text is valid utf8. Will exit early with non-full size on invalid utf8
Parameters
- self:*Entry
- *Entry
- gpa:std.mem.Allocator
- std.mem.Allocator
- text:[]const u8
- []const u8
- opts:Font.TextSizeOptions
- Font.TextSizeOptions
Source
Implementation
pub fn textSizeRaw(
self: *Entry,
gpa: std.mem.Allocator,
text: []const u8,
opts: Font.TextSizeOptions,
) std.mem.Allocator.Error!Size {
const mwidth = opts.max_width orelse dvui.max_float_safe;
const snap = dvui.snapToPixels();
var x: f32 = 0;
var minx: f32 = 0;
var maxx: f32 = 0;
var miny: f32 = 0;
var maxy: f32 = self.height;
var tw: f32 = 0;
var th: f32 = self.height;
var ei: usize = 0;
var nearest_break: bool = false;
const kerning: bool = opts.kerning orelse true;
var last_codepoint: u32 = 0;
var next_kern_idx: u32 = 0;
var next_kern_byte: u32 = 0;
if (opts.kern_in) |ki| {
next_kern_byte = ki[next_kern_idx];
next_kern_idx += 1;
}
var i: usize = 0;
while (i < text.len) {
const cplen = std.unicode.utf8ByteSequenceLength(text[i]) catch unreachable;
const codepoint = std.unicode.utf8Decode(text[i..][0..cplen]) catch unreachable;
if (codepoint == '\n') {
// newlines always terminate, and don't use any space
ei += 1;
break;
}
const gi = try self.glyphInfoGetOrReplacement(gpa, codepoint);
if (kerning and last_codepoint != 0 and i >= next_kern_byte) {
const kk = self.kern(last_codepoint, codepoint);
x += if (snap) @round(kk) else kk;
if (opts.kern_in) |ki| {
if (next_kern_idx < ki.len) {
next_kern_byte = ki[next_kern_idx];
next_kern_idx += 1;
}
}
if (kk != 0) {
if (opts.kern_out) |ko| {
// fill in first 0
for (ko) |*k| {
if (k.* == 0) {
k.* = @intCast(i);
break;
}
}
}
}
}
i += cplen;
last_codepoint = codepoint;
const adv = if (snap) @round(gi.advance) else gi.advance;
minx = @min(minx, x + gi.leftBearing);
maxx = @max(maxx, x + gi.leftBearing + gi.w);
maxx = @max(maxx, x + adv);
miny = @min(miny, self.ascent - gi.topBearing);
maxy = @max(maxy, self.ascent - gi.topBearing + gi.h);
if ((maxx - minx) > mwidth) {
switch (opts.end_metric) {
.before => break, // went too far
.nearest => {
if ((maxx - minx) - mwidth >= mwidth - tw) {
break; // current one is closest
} else {
// get the next glyph and then break
nearest_break = true;
}
},
}
}
// record that we processed this codepoint
ei += cplen;
// update space taken by glyph
tw = maxx - minx;
th = maxy - miny;
x += adv;
if (nearest_break) break;
}
// TODO: xstart and ystart
if (opts.end_idx) |endout| {
endout.* = ei;
}
if (opts.kern_out) |ko| {
// fill in first 0
for (ko) |*k| {
if (k.* == 0) {
k.* = @intCast(i);
break;
}
}
}
//std.debug.print("textSizeRaw size {d} for \"{s}\" {d}x{d} {d}\n", .{ self.size, text, tw, th, ei });
return Size{ .w = tw, .h = th };
}