fontisan 0.4.46 → 0.4.48

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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/BENCHMARKS.md +56 -0
  3. data/CHANGELOG.md +12 -0
  4. data/Cargo.lock +550 -0
  5. data/Cargo.toml +12 -0
  6. data/Gemfile +10 -0
  7. data/README.adoc +52 -0
  8. data/Rakefile +5 -1
  9. data/benchmark/baseline.json +5 -0
  10. data/benchmark/ci_gate.rb +217 -0
  11. data/benchmark/corpus/harness.rb +175 -0
  12. data/benchmark/corpus/ops.rb +150 -0
  13. data/benchmark/corpus/run.rb +182 -0
  14. data/benchmark/corpus/scan_child.rb +8 -0
  15. data/benchmark/corpus/tiers.rb +176 -0
  16. data/benchmark/tier_baseline.json +12 -0
  17. data/docs/.vitepress/config.ts +2 -1
  18. data/docs/guide/engines.md +117 -0
  19. data/docs/reference-facade.md +130 -0
  20. data/ext/fontisan_cdylib/Cargo.lock +313 -0
  21. data/ext/fontisan_cdylib/Cargo.toml +18 -0
  22. data/ext/fontisan_cdylib/build.rs +22 -0
  23. data/ext/fontisan_cdylib/extconf.rb +36 -0
  24. data/ext/fontisan_cdylib/src/lib.rs +13 -0
  25. data/ext/fontisan_native/Cargo.toml +21 -0
  26. data/ext/fontisan_native/build.rs +22 -0
  27. data/ext/fontisan_native/clippy.toml +10 -0
  28. data/ext/fontisan_native/extconf.rb +59 -0
  29. data/ext/fontisan_native/src/lib.rs +463 -0
  30. data/lib/fontisan/audit/checks/cmap_check.rb +7 -0
  31. data/lib/fontisan/audit/checks/hinting_check.rb +1 -1
  32. data/lib/fontisan/audit/checks/maxp_check.rb +1 -1
  33. data/lib/fontisan/audit/checks/opentype_conformance_check.rb +5 -2
  34. data/lib/fontisan/base_collection.rb +30 -0
  35. data/lib/fontisan/byte_ops.rb +219 -0
  36. data/lib/fontisan/cli.rb +5 -5
  37. data/lib/fontisan/commands/audit_command.rb +38 -43
  38. data/lib/fontisan/commands/convert_command.rb +53 -0
  39. data/lib/fontisan/commands/instance_command.rb +75 -0
  40. data/lib/fontisan/commands/subset_command.rb +43 -0
  41. data/lib/fontisan/commands/unpack_command.rb +56 -0
  42. data/lib/fontisan/converters/cff_table_builder.rb +1 -1
  43. data/lib/fontisan/converters/collection_converter.rb +13 -5
  44. data/lib/fontisan/converters/format_converter.rb +1 -1
  45. data/lib/fontisan/converters/woff2_encoder.rb +8 -6
  46. data/lib/fontisan/converters/woff_writer.rb +6 -5
  47. data/lib/fontisan/engine.rb +350 -0
  48. data/lib/fontisan/ffi/client.rb +121 -0
  49. data/lib/fontisan/ffi.rb +343 -0
  50. data/lib/fontisan/ffi_platform.rb +24 -0
  51. data/lib/fontisan/font_loader.rb +5 -1
  52. data/lib/fontisan/font_writer.rb +13 -1
  53. data/lib/fontisan/native.rb +231 -0
  54. data/lib/fontisan/pipeline/transformation_pipeline.rb +10 -8
  55. data/lib/fontisan/pipeline.rb +1 -1
  56. data/lib/fontisan/reference/table_digests.rb +251 -0
  57. data/lib/fontisan/reference.rb +619 -0
  58. data/lib/fontisan/scan.rb +372 -0
  59. data/lib/fontisan/sfnt_directory.rb +41 -0
  60. data/lib/fontisan/sfnt_font.rb +16 -6
  61. data/lib/fontisan/subset/builder.rb +2 -1
  62. data/lib/fontisan/tables/cff/charstring.rb +16 -0
  63. data/lib/fontisan/tables/cff.rb +15 -5
  64. data/lib/fontisan/tables/cmap.rb +11 -1
  65. data/lib/fontisan/tables/glyf.rb +4 -6
  66. data/lib/fontisan/tables/gpos.rb +7 -0
  67. data/lib/fontisan/tables/gsub.rb +7 -0
  68. data/lib/fontisan/tables/layout_common.rb +172 -0
  69. data/lib/fontisan/tables/name.rb +10 -4
  70. data/lib/fontisan/utilities/brotli_wrapper.rb +39 -4
  71. data/lib/fontisan/validators/opentype_validator.rb +15 -3
  72. data/lib/fontisan/variable/instancer.rb +23 -192
  73. data/lib/fontisan/variable/static_font_builder.rb +4 -1
  74. data/lib/fontisan/variable/table_updater.rb +4 -2
  75. data/lib/fontisan/variation/instance_generator.rb +3 -149
  76. data/lib/fontisan/variation/instancing/axis_space.rb +136 -0
  77. data/lib/fontisan/variation/instancing/glyph_outline.rb +204 -0
  78. data/lib/fontisan/variation/instancing/gvar_reader.rb +146 -0
  79. data/lib/fontisan/variation/instancing/item_variation_store.rb +92 -0
  80. data/lib/fontisan/variation/instancing/name_rewriter.rb +90 -0
  81. data/lib/fontisan/variation/instancing/true_type_instancer.rb +326 -0
  82. data/lib/fontisan/variation/instancing.rb +20 -0
  83. data/lib/fontisan/variation/variable_svg_generator.rb +19 -1
  84. data/lib/fontisan/variation.rb +1 -0
  85. data/lib/fontisan/version.rb +1 -1
  86. data/lib/fontisan/woff2/collection_decoder.rb +15 -1
  87. data/lib/fontisan/woff2/collection_encoder.rb +29 -6
  88. data/lib/fontisan/woff2/encoder_rules.rb +12 -4
  89. data/lib/fontisan/woff2/glyf_loca_reconstruct.rb +1 -1
  90. data/lib/fontisan/woff2_collection.rb +7 -5
  91. data/lib/fontisan/woff2_font.rb +12 -3
  92. data/lib/fontisan/woff_container.rb +83 -0
  93. data/lib/fontisan/woff_font.rb +24 -20
  94. data/lib/fontisan.rb +24 -1
  95. data/rakelib/bench.rake +9 -0
  96. data/rakelib/cdylib.rake +39 -0
  97. data/rakelib/corpus.rake +272 -0
  98. data/rakelib/corpus_convert.rake +43 -0
  99. data/rakelib/corpus_goldens.rake +22 -0
  100. data/rakelib/corpus_malformed.rake +20 -0
  101. data/rakelib/corpus_scan.rake +24 -0
  102. data/rakelib/java.rake +61 -0
  103. data/rakelib/native.rake +43 -0
  104. data/scripts/build-woff2.sh +57 -0
  105. data/scripts/corpus/generate_convert_goldens.rb +478 -0
  106. data/scripts/corpus/generate_goldens.rb +282 -0
  107. data/scripts/corpus/generate_malformed_goldens.rb +254 -0
  108. data/scripts/corpus/generate_scan_goldens.rb +98 -0
  109. data/scripts/tier_fallback.rb +219 -0
  110. data/scripts/tier_smoke.rb +64 -0
  111. metadata +75 -3
@@ -0,0 +1,313 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "adler2"
7
+ version = "2.0.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
10
+
11
+ [[package]]
12
+ name = "alloc-no-stdlib"
13
+ version = "2.0.4"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
16
+
17
+ [[package]]
18
+ name = "alloc-stdlib"
19
+ version = "0.2.4"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
22
+ dependencies = [
23
+ "alloc-no-stdlib",
24
+ ]
25
+
26
+ [[package]]
27
+ name = "block-buffer"
28
+ version = "0.10.4"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
31
+ dependencies = [
32
+ "generic-array",
33
+ ]
34
+
35
+ [[package]]
36
+ name = "brotli"
37
+ version = "8.0.4"
38
+ source = "registry+https://github.com/rust-lang/crates.io-index"
39
+ checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
40
+ dependencies = [
41
+ "alloc-no-stdlib",
42
+ "alloc-stdlib",
43
+ "brotli-decompressor",
44
+ ]
45
+
46
+ [[package]]
47
+ name = "brotli-decompressor"
48
+ version = "5.0.3"
49
+ source = "registry+https://github.com/rust-lang/crates.io-index"
50
+ checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
51
+ dependencies = [
52
+ "alloc-no-stdlib",
53
+ "alloc-stdlib",
54
+ ]
55
+
56
+ [[package]]
57
+ name = "bytemuck"
58
+ version = "1.25.2"
59
+ source = "registry+https://github.com/rust-lang/crates.io-index"
60
+ checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
61
+ dependencies = [
62
+ "bytemuck_derive",
63
+ ]
64
+
65
+ [[package]]
66
+ name = "bytemuck_derive"
67
+ version = "1.12.1"
68
+ source = "registry+https://github.com/rust-lang/crates.io-index"
69
+ checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c"
70
+ dependencies = [
71
+ "proc-macro2",
72
+ "quote",
73
+ "syn",
74
+ ]
75
+
76
+ [[package]]
77
+ name = "cfg-if"
78
+ version = "1.0.5"
79
+ source = "registry+https://github.com/rust-lang/crates.io-index"
80
+ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
81
+
82
+ [[package]]
83
+ name = "cpufeatures"
84
+ version = "0.2.17"
85
+ source = "registry+https://github.com/rust-lang/crates.io-index"
86
+ checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
87
+ dependencies = [
88
+ "libc",
89
+ ]
90
+
91
+ [[package]]
92
+ name = "crypto-common"
93
+ version = "0.1.7"
94
+ source = "registry+https://github.com/rust-lang/crates.io-index"
95
+ checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
96
+ dependencies = [
97
+ "generic-array",
98
+ "typenum",
99
+ ]
100
+
101
+ [[package]]
102
+ name = "digest"
103
+ version = "0.10.7"
104
+ source = "registry+https://github.com/rust-lang/crates.io-index"
105
+ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
106
+ dependencies = [
107
+ "block-buffer",
108
+ "crypto-common",
109
+ ]
110
+
111
+ [[package]]
112
+ name = "foldhash"
113
+ version = "0.2.0"
114
+ source = "registry+https://github.com/rust-lang/crates.io-index"
115
+ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
116
+
117
+ [[package]]
118
+ name = "font-types"
119
+ version = "0.12.7"
120
+ source = "registry+https://github.com/rust-lang/crates.io-index"
121
+ checksum = "835ae9d781464a79026cde387f9cbc650c9a4909d7cf7e658b127e3c35d2f35d"
122
+ dependencies = [
123
+ "bytemuck",
124
+ ]
125
+
126
+ [[package]]
127
+ name = "fontisan"
128
+ version = "0.0.0"
129
+ source = "git+https://github.com/fontist/fontisan-rs?rev=daffb3f45b67517e924a3e5d0e113a2ecbde9959#daffb3f45b67517e924a3e5d0e113a2ecbde9959"
130
+ dependencies = [
131
+ "brotli",
132
+ "brotli-decompressor",
133
+ "miniz_oxide",
134
+ "read-fonts",
135
+ "sha2",
136
+ ]
137
+
138
+ [[package]]
139
+ name = "fontisan_cdylib"
140
+ version = "0.0.0"
141
+ dependencies = [
142
+ "fontisan",
143
+ ]
144
+
145
+ [[package]]
146
+ name = "generic-array"
147
+ version = "0.14.7"
148
+ source = "registry+https://github.com/rust-lang/crates.io-index"
149
+ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
150
+ dependencies = [
151
+ "typenum",
152
+ "version_check",
153
+ ]
154
+
155
+ [[package]]
156
+ name = "libc"
157
+ version = "0.2.191"
158
+ source = "registry+https://github.com/rust-lang/crates.io-index"
159
+ checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
160
+
161
+ [[package]]
162
+ name = "miniz_oxide"
163
+ version = "0.8.9"
164
+ source = "registry+https://github.com/rust-lang/crates.io-index"
165
+ checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
166
+ dependencies = [
167
+ "adler2",
168
+ ]
169
+
170
+ [[package]]
171
+ name = "once_cell"
172
+ version = "1.21.4"
173
+ source = "registry+https://github.com/rust-lang/crates.io-index"
174
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
175
+
176
+ [[package]]
177
+ name = "proc-macro2"
178
+ version = "1.0.107"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
181
+ dependencies = [
182
+ "unicode-ident",
183
+ ]
184
+
185
+ [[package]]
186
+ name = "quote"
187
+ version = "1.0.47"
188
+ source = "registry+https://github.com/rust-lang/crates.io-index"
189
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
190
+ dependencies = [
191
+ "proc-macro2",
192
+ ]
193
+
194
+ [[package]]
195
+ name = "read-fonts"
196
+ version = "0.46.0"
197
+ source = "registry+https://github.com/rust-lang/crates.io-index"
198
+ checksum = "bdaa4fffe9ff3ca2c353fa9729792805b9b3e324695ff5a081f860612e81b330"
199
+ dependencies = [
200
+ "bytemuck",
201
+ "foldhash",
202
+ "font-types",
203
+ "once_cell",
204
+ "smallvec",
205
+ "yoke",
206
+ ]
207
+
208
+ [[package]]
209
+ name = "sha2"
210
+ version = "0.10.9"
211
+ source = "registry+https://github.com/rust-lang/crates.io-index"
212
+ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
213
+ dependencies = [
214
+ "cfg-if",
215
+ "cpufeatures",
216
+ "digest",
217
+ ]
218
+
219
+ [[package]]
220
+ name = "smallvec"
221
+ version = "1.16.4"
222
+ source = "registry+https://github.com/rust-lang/crates.io-index"
223
+ checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8"
224
+
225
+ [[package]]
226
+ name = "stable_deref_trait"
227
+ version = "1.2.1"
228
+ source = "registry+https://github.com/rust-lang/crates.io-index"
229
+ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
230
+
231
+ [[package]]
232
+ name = "syn"
233
+ version = "3.0.9"
234
+ source = "registry+https://github.com/rust-lang/crates.io-index"
235
+ checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
236
+ dependencies = [
237
+ "proc-macro2",
238
+ "quote",
239
+ "unicode-ident",
240
+ ]
241
+
242
+ [[package]]
243
+ name = "synstructure"
244
+ version = "0.14.0"
245
+ source = "registry+https://github.com/rust-lang/crates.io-index"
246
+ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02"
247
+ dependencies = [
248
+ "proc-macro2",
249
+ "quote",
250
+ "syn",
251
+ ]
252
+
253
+ [[package]]
254
+ name = "typenum"
255
+ version = "1.20.1"
256
+ source = "registry+https://github.com/rust-lang/crates.io-index"
257
+ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
258
+
259
+ [[package]]
260
+ name = "unicode-ident"
261
+ version = "1.0.27"
262
+ source = "registry+https://github.com/rust-lang/crates.io-index"
263
+ checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
264
+
265
+ [[package]]
266
+ name = "version_check"
267
+ version = "0.9.5"
268
+ source = "registry+https://github.com/rust-lang/crates.io-index"
269
+ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
270
+
271
+ [[package]]
272
+ name = "yoke"
273
+ version = "0.8.4"
274
+ source = "registry+https://github.com/rust-lang/crates.io-index"
275
+ checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3"
276
+ dependencies = [
277
+ "stable_deref_trait",
278
+ "yoke-derive",
279
+ "zerofrom",
280
+ ]
281
+
282
+ [[package]]
283
+ name = "yoke-derive"
284
+ version = "0.8.4"
285
+ source = "registry+https://github.com/rust-lang/crates.io-index"
286
+ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71"
287
+ dependencies = [
288
+ "proc-macro2",
289
+ "quote",
290
+ "syn",
291
+ "synstructure",
292
+ ]
293
+
294
+ [[package]]
295
+ name = "zerofrom"
296
+ version = "0.1.8"
297
+ source = "registry+https://github.com/rust-lang/crates.io-index"
298
+ checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
299
+ dependencies = [
300
+ "zerofrom-derive",
301
+ ]
302
+
303
+ [[package]]
304
+ name = "zerofrom-derive"
305
+ version = "0.1.8"
306
+ source = "registry+https://github.com/rust-lang/crates.io-index"
307
+ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a"
308
+ dependencies = [
309
+ "proc-macro2",
310
+ "quote",
311
+ "syn",
312
+ "synstructure",
313
+ ]
@@ -0,0 +1,18 @@
1
+ [package]
2
+ name = "fontisan_cdylib"
3
+ version = "0.0.0"
4
+ edition = "2021"
5
+ publish = false
6
+ description = "C ABI of the fontisan engine as a shared library for the Ruby `ffi` tier (no magnus)"
7
+
8
+ [lib]
9
+ name = "fontisan_cdylib"
10
+ crate-type = ["cdylib"]
11
+
12
+ # Standalone: not a member of any enclosing workspace.
13
+ [workspace]
14
+
15
+ [dependencies]
16
+ # TEMPORARY (decision D3: the crate is not on crates.io yet). Replace with
17
+ # `fontisan = { version = "=<release>", features = ["ffi"] }` once published.
18
+ fontisan = { git = "https://github.com/fontist/fontisan-rs", rev = "daffb3f45b67517e924a3e5d0e113a2ecbde9959", features = ["ffi"] }
@@ -0,0 +1,22 @@
1
+ //! Embeds the engine rev this binary is compiled against, read from the
2
+ //! `fontisan` dependency's `rev = "..."` in this crate's own Cargo.toml (the
3
+ //! one place a repin edits), as `FONTISAN_ENGINE_REV`. The Ruby side compares
4
+ //! it with `Fontisan::Native::ENGINE_REV`, so a binary built from an older pin
5
+ //! counts as unavailable instead of serving old behaviour.
6
+ use std::{env, fs, path::PathBuf};
7
+
8
+ fn main() {
9
+ let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"))
10
+ .join("Cargo.toml");
11
+ println!("cargo:rerun-if-changed={}", manifest.display());
12
+ let text = fs::read_to_string(&manifest).expect("read Cargo.toml");
13
+ let rev = text
14
+ .lines()
15
+ .filter(|line| line.trim_start().starts_with("fontisan") && line.contains("git"))
16
+ .find_map(|line| {
17
+ let tail = line.split("rev").nth(1)?;
18
+ tail.split('"').nth(1).map(str::to_owned)
19
+ })
20
+ .expect("the fontisan dependency in Cargo.toml must carry `rev = \"<sha>\"`");
21
+ println!("cargo:rustc-env=FONTISAN_ENGINE_REV={rev}");
22
+ }
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Builds the :ffi tier's shared library with cargo and installs it next to
4
+ # lib/fontisan/ffi.rb. With no cargo, or FONTISAN_SKIP_CDYLIB=1, it writes a
5
+ # Makefile that does nothing, so the gem installs and the :ruby tier serves.
6
+ require "mkmf"
7
+ require "rbconfig"
8
+
9
+ here = __dir__
10
+ dest = File.expand_path("../../lib/fontisan/ffi", here)
11
+ cargo = ENV["FONTISAN_SKIP_CDYLIB"] == "1" ? nil : find_executable("cargo")
12
+ extension = case RbConfig::CONFIG["host_os"]
13
+ when /darwin/ then "dylib"
14
+ when /mswin|mingw|cygwin/ then "dll"
15
+ else "so"
16
+ end
17
+ prefix = extension == "dll" ? "" : "lib"
18
+ built = "target/release/#{prefix}fontisan_cdylib.#{extension}"
19
+
20
+ makefile =
21
+ if cargo
22
+ <<~MAKE
23
+ all:
24
+ \tcd "#{here}" && "#{cargo}" build --release
25
+ \tmkdir -p "#{dest}"
26
+ \tcp "#{File.join(here, built)}" "#{dest}/"
27
+ install:
28
+ \t@true
29
+ clean:
30
+ \t@true
31
+ MAKE
32
+ else
33
+ warn "fontisan: cargo not found; skipping the :ffi cdylib (the :ruby tier serves)"
34
+ "all:\n\t@true\ninstall:\n\t@true\nclean:\n\t@true\n"
35
+ end
36
+ File.write("Makefile", makefile)
@@ -0,0 +1,13 @@
1
+ //! The engine's C ABI (`fontisan_call`, `fontisan_result_free`,
2
+ //! `fontisan_abi_version`) re-exported so this cdylib exports the symbols.
3
+ //! Without the glob re-export the library links empty; CI checks the exports
4
+ //! with `nm -gU` / `dumpbin /exports`.
5
+ pub use fontisan::ffi::*;
6
+
7
+ /// The engine rev this library was compiled against (see `build.rs`), as a
8
+ /// NUL-terminated static string. The Ruby `:ffi` tier refuses a library that
9
+ /// lacks this symbol or reports another rev than `Fontisan::Native::ENGINE_REV`.
10
+ #[no_mangle]
11
+ pub extern "C" fn fontisan_cdylib_engine_rev() -> *const std::ffi::c_char {
12
+ concat!(env!("FONTISAN_ENGINE_REV"), "\0").as_ptr().cast()
13
+ }
@@ -0,0 +1,21 @@
1
+ [package]
2
+ name = "fontisan_native"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ rust-version = "1.85"
6
+ publish = false
7
+ description = "The :native tier of the fontisan gem: the fontisan-rs engine through magnus."
8
+
9
+ [lib]
10
+ crate-type = ["cdylib"]
11
+
12
+ [dependencies]
13
+ # TEMPORARY (decision D3: the crate is not on crates.io yet). Replace with
14
+ # `fontisan = "=<version>"` once published. The rev must be a commit on
15
+ # fontist/fontisan-rs; keep it equal to the rev in ext/fontisan_cdylib/Cargo.toml.
16
+ fontisan = { git = "https://github.com/fontist/fontisan-rs", rev = "daffb3f45b67517e924a3e5d0e113a2ecbde9959" }
17
+ magnus = "=0.8.4"
18
+ rb-sys = { version = "=0.9.130", default-features = false }
19
+
20
+ [lints.rust]
21
+ unsafe_op_in_unsafe_fn = "deny"
@@ -0,0 +1,22 @@
1
+ //! Embeds the engine rev this binary is compiled against, read from the
2
+ //! `fontisan` dependency's `rev = "..."` in this crate's own Cargo.toml (the
3
+ //! one place a repin edits), as `FONTISAN_ENGINE_REV`. The Ruby side compares
4
+ //! it with `Fontisan::Native::ENGINE_REV`, so a binary built from an older pin
5
+ //! counts as unavailable instead of serving old behaviour.
6
+ use std::{env, fs, path::PathBuf};
7
+
8
+ fn main() {
9
+ let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"))
10
+ .join("Cargo.toml");
11
+ println!("cargo:rerun-if-changed={}", manifest.display());
12
+ let text = fs::read_to_string(&manifest).expect("read Cargo.toml");
13
+ let rev = text
14
+ .lines()
15
+ .filter(|line| line.trim_start().starts_with("fontisan") && line.contains("git"))
16
+ .find_map(|line| {
17
+ let tail = line.split("rev").nth(1)?;
18
+ tail.split('"').nth(1).map(str::to_owned)
19
+ })
20
+ .expect("the fontisan dependency in Cargo.toml must carry `rev = \"<sha>\"`");
21
+ println!("cargo:rustc-env=FONTISAN_ENGINE_REV={rev}");
22
+ }
@@ -0,0 +1,10 @@
1
+ # The engine never spawns processes (index rule "No C, no forks"). xtask is its
2
+ # own workspace and is exempt.
3
+ disallowed-methods = [
4
+ { path = "std::process::Command::new", reason = "the engine must not spawn processes" },
5
+ { path = "libc::fork", reason = "the engine must not fork" },
6
+ ]
7
+ disallowed-types = [
8
+ { path = "std::process::Command", reason = "the engine must not spawn processes" },
9
+ ]
10
+ msrv = "1.85"
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Builds the :native tier. Degrades cleanly: without a Rust toolchain (or the
4
+ # rb_sys gem) it writes a no-op Makefile, the gem installs without the
5
+ # extension, and Fontisan::Native.available? is false, so only the :ruby tier
6
+ # serves. Set FONTISAN_NATIVE_REQUIRED=1 to turn that degradation into a failure.
7
+ #
8
+ # A failure at `make` time (a Cargo/Ruby target mismatch on Windows, a missing
9
+ # linker, an old rustc) degrades the same way: rb_sys's Makefile is kept as
10
+ # Makefile.rb_sys and a wrapper Makefile runs it, turning its failure into a
11
+ # warning. Only POSIX-sh-compatible recipe syntax is used (`cmd || echo`), so it
12
+ # works under GNU make on Linux/macOS and the RubyInstaller devkit (msys sh).
13
+ require "mkmf"
14
+
15
+ RB_SYS_MAKEFILE = "Makefile.rb_sys"
16
+
17
+ def cargo_present?
18
+ system("cargo", "--version", out: File::NULL, err: File::NULL)
19
+ rescue SystemCallError
20
+ false
21
+ end
22
+
23
+ def skip_native(reason)
24
+ abort "fontisan: cannot build the native extension: #{reason}" if ENV["FONTISAN_NATIVE_REQUIRED"] == "1"
25
+
26
+ warn "fontisan: skipping the native extension (#{reason}); the :ruby engine tier serves."
27
+ File.write("Makefile", <<~MAKEFILE)
28
+ all:
29
+ install:
30
+ clean:
31
+ distclean:
32
+ .PHONY: all install clean distclean
33
+ MAKEFILE
34
+ end
35
+
36
+ # Wraps the rb_sys Makefile so that a failing build/install/clean leaves the gem
37
+ # installed without the extension (unless FONTISAN_NATIVE_REQUIRED=1).
38
+ def wrap_rust_makefile
39
+ return if ENV["FONTISAN_NATIVE_REQUIRED"] == "1"
40
+
41
+ File.rename("Makefile", RB_SYS_MAKEFILE)
42
+ warning = %(echo "fontisan: the native extension did not build; the :ruby engine tier serves.")
43
+ targets = %w[all install clean distclean].map do |target|
44
+ "#{target}:\n\t$(MAKE) -f #{RB_SYS_MAKEFILE} #{target} || #{warning}\n"
45
+ end
46
+ File.write("Makefile", "#{targets.join}\n.PHONY: all install clean distclean\n")
47
+ end
48
+
49
+ if cargo_present?
50
+ begin
51
+ require "rb_sys/mkmf"
52
+ create_rust_makefile("fontisan/native/fontisan_native")
53
+ wrap_rust_makefile
54
+ rescue LoadError
55
+ skip_native("the rb_sys gem is not installed")
56
+ end
57
+ else
58
+ skip_native("cargo not found")
59
+ end