diff --git a/src/font/opentype/glyf.zig b/src/font/opentype/glyf.zig index 912b5fe84..1719b027e 100644 --- a/src/font/opentype/glyf.zig +++ b/src/font/opentype/glyf.zig @@ -50,6 +50,23 @@ pub const Glyf = struct { return self.points[start..end]; } + /// Deep copy this outline using `alloc`. + /// + /// The returned outline owns its contour and point storage and must be + /// released with `deinit`. + pub fn clone(self: *const Outline, alloc: Allocator) Allocator.Error!Outline { + const contours = try alloc.dupe(sfnt.uint16, self.contours); + errdefer alloc.free(contours); + + const points = try alloc.dupe(Point, self.points); + errdefer alloc.free(points); + + return .{ + .contours = contours, + .points = points, + }; + } + /// Free all memory owned by this outline. Pass in the same /// allocator used for decoding. pub fn deinit(self: *Outline, alloc: Allocator) void { diff --git a/src/terminal/apc/glyph/Glossary.zig b/src/terminal/apc/glyph/Glossary.zig index 28458dbb2..e82d9e8e8 100644 --- a/src/terminal/apc/glyph/Glossary.zig +++ b/src/terminal/apc/glyph/Glossary.zig @@ -193,6 +193,23 @@ pub const Entry = struct { self.* = undefined; } + /// Deep copy this glossary entry using `alloc`. + /// + /// The returned entry owns decoded glyph memory and must be released with + /// `deinit`. + pub fn clone(self: *const Entry, alloc: Allocator) Allocator.Error!Entry { + const glyph: Glyph = switch (self.glyph) { + .glyf => |*outline| .{ .glyf = try outline.clone(alloc) }, + }; + + return .{ + .glyph = glyph, + .design = self.design, + .width = self.width, + .constraint = self.constraint, + }; + } + /// Return the renderer constraint for a register request. /// /// Glyph Protocol ยง8.5 defines sizing, alignment, and padding in terms of @@ -320,6 +337,26 @@ test "Entry init decodes glyf payload and applies register fields" { try testing.expectEqual(@as(usize, 1), entry.glyph.glyf.contours.len); } +test "Entry clone deep copies decoded glyf payload" { + const testing = std.testing; + const alloc = testing.allocator; + + var entry = try testRegisterEntry(alloc, 0xE000); + defer entry.deinit(alloc); + + var cloned = try entry.clone(alloc); + defer cloned.deinit(alloc); + + try testing.expectEqual(entry.design, cloned.design); + try testing.expectEqual(entry.width, cloned.width); + try testing.expectEqual(entry.constraint, cloned.constraint); + + try testing.expectEqualSlices(u16, entry.glyph.glyf.contours, cloned.glyph.glyf.contours); + try testing.expectEqualSlices(Glyf.Outline.Point, entry.glyph.glyf.points, cloned.glyph.glyf.points); + try testing.expect(entry.glyph.glyf.contours.ptr != cloned.glyph.glyf.contours.ptr); + try testing.expect(entry.glyph.glyf.points.ptr != cloned.glyph.glyf.points.ptr); +} + test "Entry init rejects invalid register payload" { const testing = std.testing; const alloc = testing.allocator;