diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-29 19:55:57 +0200 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-29 19:55:57 +0200 |
| commit | afd538d6af0baf8a1861ff2cdef881edbfb57000 (patch) | |
| tree | db41d566c318163cb41916defd7cd274b92a9a43 /src/eval/math.zig | |
| parent | 0535fa152ae990a28d0b7b9a59e96911074118b8 (diff) | |
feat(): support math content
Diffstat (limited to 'src/eval/math.zig')
| -rw-r--r-- | src/eval/math.zig | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/eval/math.zig b/src/eval/math.zig new file mode 100644 index 0000000..20d3fec --- /dev/null +++ b/src/eval/math.zig @@ -0,0 +1,108 @@ +const std = @import("std"); +const typst = @cImport(@cInclude("typdown_typst.h")); +const Allocator = std.mem.Allocator; +const HTML = Element.HTML; +const Element = @import("Element.zig"); +const Node = Element.Node; + +const content_template = @embedFile("template_math_content.typ"); +const block_template = @embedFile("template_math_block.typ"); + +pub const Error = error{InvalidTypstTemplate} || Allocator.Error; + +fn typstInterop(alloc: Allocator, comptime f: fn ([*c]const u8) callconv(.c) [*c]const u8, content: []const u8) ![]const u8 { + const source = try alloc.dupeZ(u8, content); + defer alloc.free(source); + const raw_res = f(source); + const res = try alloc.dupe(u8, std.mem.span(raw_res)); + defer typst.typst_freeString(raw_res); + return res; +} + +fn generateSVG(alloc: Allocator, content: []const u8) ![]const u8 { + return try typstInterop(alloc, typst.typst_generateSVG, content); +} + +fn escape(alloc: Allocator, content: []const u8) ![]const u8 { + return try typstInterop(alloc, typst.typst_escapeMath, content); +} + +fn generateFile(alloc: Allocator, template: []const u8, content: []const u8) Error![]const u8 { + var iter = std.mem.splitSequence(u8, template, "!!"); + const beg = iter.next() orelse return Error.InvalidTypstTemplate; + const end = iter.next() orelse return Error.InvalidTypstTemplate; + if (iter.next() != null) return Error.InvalidTypstTemplate; + + var acc = try std.ArrayList(u8).initCapacity(alloc, beg.len + end.len + content.len); + try acc.appendSlice(alloc, beg); + try acc.appendSlice(alloc, content); + try acc.appendSlice(alloc, end); + return try acc.toOwnedSlice(alloc); +} + +fn Math(comptime template: []const u8) type { + return struct { + content: ?[]const u8 = null, + node: Node, + + const Self = @This(); + + pub fn init(alloc: Allocator) !*Self { + const v = try alloc.create(Self); + v.node = .{ .ptr = v, .vtable = .{ .element = fromNode } }; + return v; + } + + pub fn element(self: *Self) Element { + return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; + } + + fn getNode(context: *anyopaque) *Node { + const self: *Self = @ptrCast(@alignCast(context)); + return &self.node; + } + + fn fromNode(context: *anyopaque) Element { + const self: *Self = @ptrCast(@alignCast(context)); + return self.element(); + } + + fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { + const self: *Self = @ptrCast(@alignCast(context)); + const content = self.content orelse return (try HTML.Literal.init(alloc, "")).element(); + + var arena = std.heap.ArenaAllocator.init(alloc); + defer arena.deinit(); + const escaped = try escape(arena.allocator(), content); + const file = generateFile(arena.allocator(), template, escaped) catch |err| switch (err) { + Error.InvalidTypstTemplate => @panic("invalid template"), + Error.OutOfMemory => return Error.OutOfMemory, + }; + const svg = try generateSVG(arena.allocator(), file); + return (try HTML.Literal.initNoEscape(alloc, svg)).element(); + } + }; +} + +pub const Content = Math(content_template); +pub const Block = Math(block_template); + +fn doTest(alloc: Allocator, v: []const u8, r: []const u8) !void { + const escaped = try escape(alloc, v); + defer alloc.free(escaped); + std.testing.expect(std.mem.eql(u8, escaped, r)) catch |err| { + std.debug.print("{s}\n", .{escaped}); + return err; + }; +} + +test "escape math" { + const alloc = std.testing.allocator; + + try doTest(alloc, "hello", "hello"); + try doTest(alloc, "hello $ world", "hello \\$ world"); + try doTest(alloc, + \\hello + \\world + , "hello\\ world"); +} |
