@simonklee/opentui-tex-native-source 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@
3
3
  #include "wrapper/cwrapper.h"
4
4
 
5
5
  #include "microtex.h"
6
+ #include "core/parse_limits.h"
6
7
  #include "utils/log.h"
7
8
  #include "wrapper/graphic_wrapper.h"
8
9
 
@@ -136,16 +137,22 @@ MICROTEX_CAPI RenderPtr microtex_parseRender(
136
137
  #ifdef HAVE_LOG
137
138
  logv("parse: %s\n", tex);
138
139
  #endif
139
- auto r = MicroTeX::parse(
140
- tex,
141
- width,
142
- textSize,
143
- lineSpace,
144
- color,
145
- fillWidth,
146
- {enableOverrideTeXStyle, static_cast<TexStyle>(texStyle)}
147
- );
148
- return reinterpret_cast<RenderPtr>(r);
140
+ try {
141
+ parse_limits = {};
142
+ auto r = MicroTeX::parse(
143
+ tex,
144
+ width,
145
+ textSize,
146
+ lineSpace,
147
+ color,
148
+ fillWidth,
149
+ {enableOverrideTeXStyle, static_cast<TexStyle>(texStyle)}
150
+ );
151
+ return reinterpret_cast<RenderPtr>(r);
152
+ } catch (...) {
153
+ // C++ exceptions cannot unwind through the Zig/C ABI.
154
+ return nullptr;
155
+ }
149
156
  }
150
157
 
151
158
  MICROTEX_CAPI void microtex_deleteRender(RenderPtr render) {
@@ -154,10 +161,14 @@ MICROTEX_CAPI void microtex_deleteRender(RenderPtr render) {
154
161
  }
155
162
 
156
163
  MICROTEX_CAPI DrawingData microtex_getDrawingData(RenderPtr render, int x, int y) {
157
- auto r = reinterpret_cast<Render*>(render);
158
- Graphics2D_wrapper g2;
159
- r->draw(g2, x, y);
160
- return g2.getDrawingData();
164
+ try {
165
+ auto r = reinterpret_cast<Render*>(render);
166
+ Graphics2D_wrapper g2;
167
+ r->draw(g2, x, y);
168
+ return g2.getDrawingData();
169
+ } catch (...) {
170
+ return nullptr;
171
+ }
161
172
  }
162
173
 
163
174
  MICROTEX_CAPI void microtex_freeDrawingData(DrawingData data) {
@@ -9,7 +9,7 @@ pub const TexSvgRender = struct {
9
9
  font_data: []const u8 = DEFAULT_FONT,
10
10
  };
11
11
 
12
- context: microtex.FontContext,
12
+ context: *microtex.FontContext,
13
13
  mtex: microtex.MicroTeX,
14
14
 
15
15
  pub fn init(
@@ -17,13 +17,15 @@ pub const TexSvgRender = struct {
17
17
  opts: Options,
18
18
  ) !TexSvgRender {
19
19
  microtex.setRenderGlyphUsePath(true);
20
- var context = microtex.FontContext.init(allocator);
21
- errdefer context.deinit();
20
+ // MicroTeX retains this address in its C callbacks.
21
+ const context = try allocator.create(microtex.FontContext);
22
+ errdefer allocator.destroy(context);
23
+ context.* = .{};
22
24
 
23
25
  const mtex = try microtex.MicroTeX.init(
24
26
  allocator,
25
27
  opts.font_data,
26
- &context,
28
+ context,
27
29
  );
28
30
  errdefer mtex.deinit();
29
31
  return .{
@@ -33,8 +35,9 @@ pub const TexSvgRender = struct {
33
35
  }
34
36
 
35
37
  pub fn deinit(self: *TexSvgRender) void {
38
+ const allocator = self.mtex.allocator;
36
39
  self.mtex.deinit();
37
- self.context.deinit();
40
+ allocator.destroy(self.context);
38
41
  self.* = undefined;
39
42
  }
40
43
 
@@ -62,23 +65,25 @@ pub const TexSvgRender = struct {
62
65
  const style: u32 = b: {
63
66
  if (opts.style) |s| {
64
67
  break :b switch (s) {
65
- .display => 1,
66
- .in_line => 0,
68
+ .display => 0,
69
+ .in_line => 2,
67
70
  };
68
71
  }
69
72
  break :b 0;
70
73
  };
71
74
 
75
+ self.context.unsupported_text = false;
72
76
  var r = try self.mtex.parseRender(null_term, .{
73
77
  .text_size = @floatFromInt(opts.size),
74
78
  .line_space = @floatFromInt(opts.spacing),
75
- .override_syle = opts.style == null,
79
+ .override_syle = opts.style != null,
76
80
  .style = style,
77
81
  .color = 0,
78
82
  });
79
83
  defer r.deinit();
84
+ if (self.context.unsupported_text) return error.UnsupportedGlyph;
80
85
 
81
- var data = r.getDrawingData(opts.x_pad, opts.y_pad);
86
+ var data = try r.getDrawingData(opts.x_pad, opts.y_pad);
82
87
 
83
88
  var s = svg.Svg.init(allocator);
84
89
  defer s.deinit();
@@ -107,9 +112,7 @@ pub const TexSvgRender = struct {
107
112
  .fill_path => try s.fillPath(),
108
113
  .draw_line => |i| try s.drawLine(i.x1, i.y1, i.x2, i.y2),
109
114
  .set_stroke => |i| try s.setStroke(i.width),
110
- else => {
111
- std.log.default.warn("Unhandled opcopde: {any}", .{cmd});
112
- },
115
+ else => return error.UnsupportedDrawingCommand,
113
116
  }
114
117
  }
115
118
  var buf_writer: std.Io.Writer.Allocating = .init(allocator);
@@ -125,6 +125,7 @@ pub const MicroTeX = struct {
125
125
  }
126
126
 
127
127
  pub fn deinit(self: *Self) void {
128
+ c.microtex_releaseFontMeta(self.font_meta);
128
129
  c.microtex_release();
129
130
  self.allocator.free(self.clm_data);
130
131
  self.* = undefined;
@@ -309,13 +310,14 @@ pub const Render = struct {
309
310
  self.* = undefined;
310
311
  }
311
312
 
312
- pub fn getDrawingData(self: *Render, x: u32, y: u32) DrawingData {
313
+ pub fn getDrawingData(self: *Render, x: u32, y: u32) !DrawingData {
313
314
  return self.drawing_data orelse {
314
315
  const ptr = c.microtex_getDrawingData(
315
316
  self.ptr,
316
317
  @intCast(x),
317
318
  @intCast(y),
318
319
  );
320
+ if (ptr == null) return error.FailedToRender;
319
321
  self.drawing_data = DrawingData.init(ptr);
320
322
  return self.drawing_data.?;
321
323
  };
@@ -323,25 +325,18 @@ pub const Render = struct {
323
325
  };
324
326
 
325
327
  pub const FontContext = struct {
326
- const FontText = struct {
327
- font: FontDescription,
328
- text: []const u8,
329
- };
330
- const FontMap = std.AutoHashMap(usize, FontText);
331
-
332
- id: usize = 0,
333
- fontmap: FontMap,
328
+ unsupported_text: bool = false,
334
329
 
335
330
  fn createTextLayout(
336
331
  self: *FontContext,
337
332
  text: []const u8,
338
333
  font: FontDescription,
339
334
  ) !usize {
340
- const id = self.id;
341
- self.id += 1;
342
-
343
- try self.fontmap.put(id, .{ .text = text, .font = font });
344
- return id;
335
+ _ = text;
336
+ _ = font;
337
+ // SVG output only supports glyph paths, not platform text layouts.
338
+ self.unsupported_text = true;
339
+ return 0;
345
340
  }
346
341
 
347
342
  fn getTextLayoutBounds(
@@ -349,16 +344,14 @@ pub const FontContext = struct {
349
344
  id: usize,
350
345
  bounds: TextLayoutBounds,
351
346
  ) !void {
352
- const value = self.fontmap.get(id).?;
353
- _ = value;
354
- // TODO: would need a way of measuring how big this is going to be
355
- bounds.setBounds(200, 100, 10);
347
+ _ = self;
348
+ _ = id;
349
+ bounds.setBounds(0, 0, 0);
356
350
  }
357
351
 
358
352
  fn releaseTextLayout(self: *FontContext, id: usize) void {
359
- if (!self.fontmap.remove(id)) {
360
- std.log.default.debug("Failed to remove text layout: {d}", .{id});
361
- }
353
+ _ = self;
354
+ _ = id;
362
355
  }
363
356
 
364
357
  fn isPathExists(self: *FontContext, id: usize) bool {
@@ -366,16 +359,6 @@ pub const FontContext = struct {
366
359
  _ = id;
367
360
  return false;
368
361
  }
369
-
370
- pub fn init(allocator: std.mem.Allocator) FontContext {
371
- const map = FontMap.init(allocator);
372
- return .{ .fontmap = map };
373
- }
374
-
375
- pub fn deinit(self: *FontContext) void {
376
- self.fontmap.deinit();
377
- self.* = undefined;
378
- }
379
362
  };
380
363
 
381
364
  pub fn setRenderGlyphUsePath(use: bool) void {
@@ -1,21 +1,94 @@
1
- diff --git a/src/svg.zig b/src/svg.zig
2
- --- a/src/svg.zig
3
- +++ b/src/svg.zig
4
- @@ -45,6 +45,7 @@ pub const Svg = struct {
5
- try self.path.append(allocator, ' ');
6
- }
7
- - var path_writer = self.path.writer(allocator).adaptToNewApi(&.{});
8
- - try path_writer.new_interface.print(fmt, args);
9
- + var path_writer = std.Io.Writer.Allocating.fromArrayList(allocator, &self.path);
10
- + defer self.path = path_writer.toArrayList();
11
- + try path_writer.writer.print(fmt, args);
12
- }
13
- };
14
- diff --git a/src/root.zig b/src/root.zig
1
+ --- a/build.zig.zon
2
+ +++ b/build.zig.zon
3
+ @@ -4,8 +4,7 @@
4
+ .version = "0.1.0",
5
+ .dependencies = .{
6
+ .microtex = .{
7
+ - .url = "https://github.com/fjebaker/MicroTeX/archive/openmath.tar.gz",
8
+ - .hash = "N-V-__8AADt0yQCpAsFGWBTOCyfdUkjnlLJF_V-FBo08cjK0",
9
+ + .path = "../microtex",
10
+ },
11
+ },
12
+ .paths = .{
15
13
  --- a/src/root.zig
16
14
  +++ b/src/root.zig
17
- @@ -112,14 +112,13 @@ pub const TexSvgRender = struct {
18
- },
15
+ @@ -9,7 +9,7 @@
16
+ font_data: []const u8 = DEFAULT_FONT,
17
+ };
18
+
19
+ - context: microtex.FontContext,
20
+ + context: *microtex.FontContext,
21
+ mtex: microtex.MicroTeX,
22
+
23
+ pub fn init(
24
+ @@ -17,13 +17,15 @@
25
+ opts: Options,
26
+ ) !TexSvgRender {
27
+ microtex.setRenderGlyphUsePath(true);
28
+ - var context = microtex.FontContext.init(allocator);
29
+ - errdefer context.deinit();
30
+ + // MicroTeX retains this address in its C callbacks.
31
+ + const context = try allocator.create(microtex.FontContext);
32
+ + errdefer allocator.destroy(context);
33
+ + context.* = .{};
34
+
35
+ const mtex = try microtex.MicroTeX.init(
36
+ allocator,
37
+ opts.font_data,
38
+ - &context,
39
+ + context,
40
+ );
41
+ errdefer mtex.deinit();
42
+ return .{
43
+ @@ -33,8 +35,9 @@
44
+ }
45
+
46
+ pub fn deinit(self: *TexSvgRender) void {
47
+ + const allocator = self.mtex.allocator;
48
+ self.mtex.deinit();
49
+ - self.context.deinit();
50
+ + allocator.destroy(self.context);
51
+ self.* = undefined;
52
+ }
53
+
54
+ @@ -62,23 +65,25 @@
55
+ const style: u32 = b: {
56
+ if (opts.style) |s| {
57
+ break :b switch (s) {
58
+ - .display => 1,
59
+ - .in_line => 0,
60
+ + .display => 0,
61
+ + .in_line => 2,
62
+ };
63
+ }
64
+ break :b 0;
65
+ };
66
+
67
+ + self.context.unsupported_text = false;
68
+ var r = try self.mtex.parseRender(null_term, .{
69
+ .text_size = @floatFromInt(opts.size),
70
+ .line_space = @floatFromInt(opts.spacing),
71
+ - .override_syle = opts.style == null,
72
+ + .override_syle = opts.style != null,
73
+ .style = style,
74
+ .color = 0,
75
+ });
76
+ defer r.deinit();
77
+ + if (self.context.unsupported_text) return error.UnsupportedGlyph;
78
+
79
+ - var data = r.getDrawingData(opts.x_pad, opts.y_pad);
80
+ + var data = try r.getDrawingData(opts.x_pad, opts.y_pad);
81
+
82
+ var s = svg.Svg.init(allocator);
83
+ defer s.deinit();
84
+ @@ -107,19 +112,16 @@
85
+ .fill_path => try s.fillPath(),
86
+ .draw_line => |i| try s.drawLine(i.x1, i.y1, i.x2, i.y2),
87
+ .set_stroke => |i| try s.setStroke(i.width),
88
+ - else => {
89
+ - std.log.default.warn("Unhandled opcopde: {any}", .{cmd});
90
+ - },
91
+ + else => return error.UnsupportedDrawingCommand,
19
92
  }
20
93
  }
21
94
  - var buf = std.ArrayList(u8).empty;
@@ -33,3 +106,123 @@ diff --git a/src/root.zig b/src/root.zig
33
106
  + return buf_writer.toOwnedSlice();
34
107
  }
35
108
  };
109
+ --- a/src/svg.zig
110
+ +++ b/src/svg.zig
111
+ @@ -45,8 +45,9 @@
112
+ if (self.path.items.len > 0) {
113
+ try self.path.append(allocator, ' ');
114
+ }
115
+ - var path_writer = self.path.writer(allocator).adaptToNewApi(&.{});
116
+ - try path_writer.new_interface.print(fmt, args);
117
+ + var path_writer = std.Io.Writer.Allocating.fromArrayList(allocator, &self.path);
118
+ + defer self.path = path_writer.toArrayList();
119
+ + try path_writer.writer.print(fmt, args);
120
+ }
121
+ };
122
+
123
+ --- a/src/tex.zig
124
+ +++ b/src/tex.zig
125
+ @@ -111,7 +111,7 @@
126
+ );
127
+
128
+ const content = try allocator.dupe(u8, clm_data);
129
+ - const font_meta = c.microtex_init(content.len, content.ptr);
130
+ + const font_meta = c.microtex_init(@intCast(content.len), content.ptr);
131
+
132
+ const name = c.microtex_getFontName(font_meta);
133
+ c.microtex_setDefaultMainFont(name);
134
+ @@ -125,6 +125,7 @@
135
+ }
136
+
137
+ pub fn deinit(self: *Self) void {
138
+ + c.microtex_releaseFontMeta(self.font_meta);
139
+ c.microtex_release();
140
+ self.allocator.free(self.clm_data);
141
+ self.* = undefined;
142
+ @@ -309,13 +310,14 @@
143
+ self.* = undefined;
144
+ }
145
+
146
+ - pub fn getDrawingData(self: *Render, x: u32, y: u32) DrawingData {
147
+ + pub fn getDrawingData(self: *Render, x: u32, y: u32) !DrawingData {
148
+ return self.drawing_data orelse {
149
+ const ptr = c.microtex_getDrawingData(
150
+ self.ptr,
151
+ @intCast(x),
152
+ @intCast(y),
153
+ );
154
+ + if (ptr == null) return error.FailedToRender;
155
+ self.drawing_data = DrawingData.init(ptr);
156
+ return self.drawing_data.?;
157
+ };
158
+ @@ -323,25 +325,18 @@
159
+ };
160
+
161
+ pub const FontContext = struct {
162
+ - const FontText = struct {
163
+ - font: FontDescription,
164
+ - text: []const u8,
165
+ - };
166
+ - const FontMap = std.AutoHashMap(usize, FontText);
167
+ -
168
+ - id: usize = 0,
169
+ - fontmap: FontMap,
170
+ + unsupported_text: bool = false,
171
+
172
+ fn createTextLayout(
173
+ self: *FontContext,
174
+ text: []const u8,
175
+ font: FontDescription,
176
+ ) !usize {
177
+ - const id = self.id;
178
+ - self.id += 1;
179
+ -
180
+ - try self.fontmap.put(id, .{ .text = text, .font = font });
181
+ - return id;
182
+ + _ = text;
183
+ + _ = font;
184
+ + // SVG output only supports glyph paths, not platform text layouts.
185
+ + self.unsupported_text = true;
186
+ + return 0;
187
+ }
188
+
189
+ fn getTextLayoutBounds(
190
+ @@ -349,16 +344,14 @@
191
+ id: usize,
192
+ bounds: TextLayoutBounds,
193
+ ) !void {
194
+ - const value = self.fontmap.get(id).?;
195
+ - _ = value;
196
+ - // TODO: would need a way of measuring how big this is going to be
197
+ - bounds.setBounds(200, 100, 10);
198
+ + _ = self;
199
+ + _ = id;
200
+ + bounds.setBounds(0, 0, 0);
201
+ }
202
+
203
+ fn releaseTextLayout(self: *FontContext, id: usize) void {
204
+ - if (!self.fontmap.remove(id)) {
205
+ - std.log.default.debug("Failed to remove text layout: {d}", .{id});
206
+ - }
207
+ + _ = self;
208
+ + _ = id;
209
+ }
210
+
211
+ fn isPathExists(self: *FontContext, id: usize) bool {
212
+ @@ -366,16 +359,6 @@
213
+ _ = id;
214
+ return false;
215
+ }
216
+ -
217
+ - pub fn init(allocator: std.mem.Allocator) FontContext {
218
+ - const map = FontMap.init(allocator);
219
+ - return .{ .fontmap = map };
220
+ - }
221
+ -
222
+ - pub fn deinit(self: *FontContext) void {
223
+ - self.fontmap.deinit();
224
+ - self.* = undefined;
225
+ - }
226
+ };
227
+
228
+ pub fn setRenderGlyphUsePath(use: bool) void {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonklee/opentui-tex-native-source",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/simonklee/opentui-tex.git"
@@ -4,8 +4,8 @@ set -euo pipefail
4
4
  cd "$(dirname "$0")/.."
5
5
 
6
6
  readonly vendor=native/vendor
7
- readonly stamp="$vendor/.bootstrap-v5"
8
- readonly stamp_value='zigtex=fda2819e45f57ca2071810c47249a46bfc782cdf microtex=74e8fee9e47edc3a777e64fb2eff9a85a01defac nanosvg=239e102ec2c691f2902e20ace2ed36ee4a35cfe6 zig=0.16-v5'
7
+ readonly stamp="$vendor/.bootstrap-v8"
8
+ readonly stamp_value='zigtex=fda2819e45f57ca2071810c47249a46bfc782cdf microtex=74e8fee9e47edc3a777e64fb2eff9a85a01defac nanosvg=239e102ec2c691f2902e20ace2ed36ee4a35cfe6 zig=0.16-v8'
9
9
  mkdir -p "$vendor"
10
10
 
11
11
  download() {
@@ -14,15 +14,25 @@ download() {
14
14
  --connect-timeout 20 --max-time 180 "$url" --output "$output"
15
15
  }
16
16
 
17
+ verify_checksum() {
18
+ local expected=$1 path=$2 actual
19
+ if command -v sha256sum >/dev/null 2>&1; then
20
+ actual=$(sha256sum "$path") || return
21
+ else
22
+ actual=$(shasum -a 256 "$path") || return
23
+ fi
24
+ [[ "${actual%% *}" == "$expected" ]]
25
+ }
26
+
17
27
  fetch_file() {
18
28
  local name=$1 url=$2 expected=$3 path="$vendor/$1" temporary
19
- if [[ -f "$path" ]] && echo "$expected $path" | sha256sum --check --status; then
29
+ if [[ -f "$path" ]] && verify_checksum "$expected" "$path"; then
20
30
  return
21
31
  fi
22
32
  temporary=$(mktemp "$vendor/.download.XXXXXX")
23
33
  trap 'rm -f "$temporary"' RETURN
24
34
  download "$url" "$temporary"
25
- echo "$expected $temporary" | sha256sum --check --status
35
+ verify_checksum "$expected" "$temporary"
26
36
  mv "$temporary" "$path"
27
37
  trap - RETURN
28
38
  }
@@ -33,7 +43,7 @@ fetch_archive() {
33
43
  temporary=$(mktemp -d "$vendor/.extract.XXXXXX")
34
44
  trap 'rm -rf "$archive" "$temporary"' RETURN
35
45
  download "$url" "$archive"
36
- echo "$expected $archive" | sha256sum --check --status
46
+ verify_checksum "$expected" "$archive"
37
47
  tar --extract --gzip --file "$archive" --directory "$temporary" --strip-components=1
38
48
  rm -rf "$vendor/$name"
39
49
  mv "$temporary" "$vendor/$name"
@@ -59,25 +69,8 @@ if [[ ! -f "$stamp" ]] || [[ "$(<"$stamp")" != "$stamp_value" ]]; then
59
69
  https://github.com/fjebaker/MicroTeX/archive/74e8fee9e47edc3a777e64fb2eff9a85a01defac.tar.gz \
60
70
  8da1600f75a674d465715b1d3e0c2bfd84adab128b45e91253ecfa46f856a963
61
71
 
62
- sed -i \
63
- -e 's# .url = .*# .path = "../microtex",#' \
64
- -e '/ .hash = /d' \
65
- "$vendor/zigtex/build.zig.zon"
66
- sed -i \
67
- -e 's/conf_header.getOutput()/conf_header.getOutputFile()/' \
68
- -e 's/lib.linkLibCpp();/lib.root_module.link_libcpp = true;/' \
69
- -e 's/lib.addConfigHeader/lib.root_module.addConfigHeader/g' \
70
- -e 's/lib.addIncludePath/lib.root_module.addIncludePath/g' \
71
- -e 's/lib.addCSourceFiles/lib.root_module.addCSourceFiles/g' \
72
- -e 's/exe.addIncludePath/exe.root_module.addIncludePath/g' \
73
- -e 's/exe.addCSourceFiles/exe.root_module.addCSourceFiles/g' \
74
- -e 's/exe.linkLibrary/exe.root_module.linkLibrary/g' \
75
- -e '/ .optimize = optimize,/a\ .pic = true,' \
76
- "$vendor/microtex/build.zig"
77
- patch --directory "$vendor/zigtex" --strip 1 < native/zigtex-0.16.patch
78
- sed -i \
79
- 's/c.microtex_init(content.len, content.ptr)/c.microtex_init(@intCast(content.len), content.ptr)/' \
80
- "$vendor/zigtex/src/tex.zig"
72
+ patch -d "$vendor/zigtex" -p1 < native/zigtex-0.16.patch
73
+ patch -d "$vendor/microtex" -p1 < native/microtex.patch
81
74
  printf '%s\n' "$stamp_value" > "$stamp"
82
75
  fi
83
76