ranger-compiler 3.5.1 → 3.5.2

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 (338) hide show
  1. package/CHANGELOG.md +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,281 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; EVGHostMeasurerTest — does a platform's one function reach every layout?
5
+ ; ==============================================================================
6
+ ;
7
+ ; `EVGHostTextMeasurer` is the seam PLAN_NATIVE_HOSTS.md S0 adds: a platform
8
+ ; hands over one function and every layout in the process measures with it.
9
+ ; This drives the Ranger half with a made-up platform — widths and vertical
10
+ ; metrics nothing like the table's, so a layout that still used the table
11
+ ; is caught by the numbers — and asks:
12
+ ;
13
+ ; * does a layout built after `EVGDefaultMeasurer.install` measure with it,
14
+ ; with no call on the layout itself, and one built after `clear` not;
15
+ ; * does the `-Bold` suffix `effectiveFontFamily` writes reach the host as
16
+ ; a flag, with the family stripped;
17
+ ; * is the platform asked for a face's vertical metrics ONCE per face and
18
+ ; never per run, and for a width once per distinct run through the
19
+ ; engine's cache;
20
+ ; * does `line-height: normal` become the face's own line box;
21
+ ; * does an unattached measurer fall back to the table, and does attaching
22
+ ; later move `measureKey` so an engine re-measures;
23
+ ; * does a text node in a real layout wrap where the host's widths say.
24
+ ;
25
+ ; npm run evg:hostmeasurer:test
26
+ ; ==============================================================================
27
+
28
+ Import "EVGHostTextMeasurer.rgr"
29
+ Import "EVGTextEngine.rgr"
30
+ Import "EVGLayout.rgr"
31
+ Import "EVGElement.rgr"
32
+ Import "EVGUnit.rgr"
33
+
34
+ class EVGHostMeasurerTest {
35
+ def passed:int 0
36
+ def failed:int 0
37
+ ; What the fake platform was asked, for the assertions on call counts
38
+ ; and on what reached it.
39
+ def lastFamily:string ""
40
+ def lastBold:boolean false
41
+ def lastText:string ""
42
+
43
+ sfn m@(main):void () {
44
+ def t (new EVGHostMeasurerTest())
45
+ t.run()
46
+ }
47
+
48
+ fn ok:void (what:string cond:boolean) {
49
+ if cond {
50
+ passed = (passed + 1)
51
+ print (" PASS " + what)
52
+ } {
53
+ failed = (failed + 1)
54
+ print (" FAIL " + what)
55
+ }
56
+ }
57
+
58
+ fn near:void (what:string a:double b:double) {
59
+ def d:double (a - b)
60
+ if (d < 0.0) {
61
+ d = (0.0 - d)
62
+ }
63
+ this.ok((what + " (" + (to_string a) + " vs " + (to_string b) + ")") (d < 0.001))
64
+ }
65
+
66
+ ; The made-up platform: every glyph is 0.7em wide (0.9em bold), the
67
+ ; ascent 1.0em, the descent 0.3em, the gap 0.2em — so the normal line box
68
+ ; is 1.5em where the table says 1.15, and no width agrees with the table.
69
+ fn fakePlatform:EVGHostTextMeasurer () {
70
+ def hm (new EVGHostTextMeasurer())
71
+ hm.attach((fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean) {
72
+ lastFamily = family
73
+ lastBold = bold
74
+ lastText = text
75
+ if (kind == 0) {
76
+ def per:double 0.7
77
+ if bold {
78
+ per = 0.9
79
+ }
80
+ return ((to_double (strlen text)) * size * per)
81
+ }
82
+ if (kind == 1) {
83
+ return (size * 1.0)
84
+ }
85
+ if (kind == 2) {
86
+ return (size * 0.3)
87
+ }
88
+ return (size * 0.2)
89
+ }) "fake")
90
+ return hm
91
+ }
92
+
93
+ fn run:void () {
94
+ print "=== EVGHostTextMeasurer ==="
95
+ this.testDefaultReachesFreshLayouts()
96
+ this.testBoldSuffix()
97
+ this.testFaceCacheAndRunCache()
98
+ this.testLineHeight()
99
+ this.testUnattachedFallsBackThenAttaches()
100
+ this.testWrapInRealLayout()
101
+ this.testHasFace()
102
+ EVGDefaultMeasurer.uninstall()
103
+ print ""
104
+ print ("passed = " + (to_string passed) + " failed = " + (to_string failed))
105
+ if (failed > 0) {
106
+ print "FAILURES"
107
+ } {
108
+ print "ALL PASS"
109
+ }
110
+ }
111
+
112
+ fn testDefaultReachesFreshLayouts:void () {
113
+ print "-- the default reaches a layout that never heard of it"
114
+ EVGDefaultMeasurer.uninstall()
115
+ def before (new EVGLayout())
116
+ def te0:EVGTextEngine (before.getTextEngine())
117
+ def m0:EVGTextMetrics (te0.measureRun("abcd" "sans-serif" 10.0))
118
+ this.ok("without an install a layout measures with the table" (m0.width < 28.0))
119
+
120
+ def hm:EVGHostTextMeasurer (this.fakePlatform())
121
+ EVGDefaultMeasurer.install(hm)
122
+ this.ok("isInstalled" (EVGDefaultMeasurer.isInstalled()))
123
+ def lay (new EVGLayout())
124
+ def te:EVGTextEngine (lay.getTextEngine())
125
+ def m:EVGTextMetrics (te.measureRun("abcd" "sans-serif" 10.0))
126
+ this.near("a fresh layout measures with the host" m.width 28.0)
127
+ this.ok("and reports itself font-accurate" (te.measurer.isFontAccurate()))
128
+
129
+ def eng (new EVGTextEngine())
130
+ def m2:EVGTextMetrics (eng.measureRun("ab" "sans-serif" 10.0))
131
+ this.near("a fresh engine too" m2.width 14.0)
132
+
133
+ EVGDefaultMeasurer.uninstall()
134
+ def after (new EVGLayout())
135
+ def te2:EVGTextEngine (after.getTextEngine())
136
+ def m3:EVGTextMetrics (te2.measureRun("abcd" "sans-serif" 10.0))
137
+ this.ok("after clear a new layout is back on the table" (m3.width < 28.0))
138
+ this.near("and the old one keeps the host it was built with" ((te.measureRun("abcd" "sans-serif" 10.0)).width) 28.0)
139
+ }
140
+
141
+ fn testBoldSuffix:void () {
142
+ print "-- the -Bold suffix reaches the host as a flag"
143
+ def hm:EVGHostTextMeasurer (this.fakePlatform())
144
+ def m:EVGTextMetrics (hm.measureText("ab" "Inter-Bold" 10.0))
145
+ this.ok("family stripped" (lastFamily == "Inter"))
146
+ this.ok("bold set" lastBold)
147
+ this.near("bold width" m.width 18.0)
148
+ def m2:EVGTextMetrics (hm.measureText("ab" "Inter" 10.0))
149
+ this.ok("plain family unchanged" (lastFamily == "Inter"))
150
+ this.ok("bold clear" (lastBold == false))
151
+ this.near("plain width" m2.width 14.0)
152
+ this.ok("a family that merely ends in Bold is not stripped twice" ((EVGHostTextMeasurer.baseFamily("Bold")) == "Bold"))
153
+ def el (EVGElement.createSpan())
154
+ el.setAttribute("font-family" "Inter")
155
+ el.setAttribute("font-weight" "bold")
156
+ this.ok("effectiveFontFamily is Family-Bold" ((el.effectiveFontFamily()) == "Inter-Bold"))
157
+ }
158
+
159
+ fn testFaceCacheAndRunCache:void () {
160
+ print "-- the platform is asked per face and per distinct run, not per call"
161
+ def hm:EVGHostTextMeasurer (this.fakePlatform())
162
+ def eng (new EVGTextEngine())
163
+ eng.setMeasurer(hm)
164
+ def i:int 0
165
+ while (i < 50) {
166
+ eng.measureRun("hello" "sans-serif" 12.0)
167
+ eng.measureRun("hello world" "sans-serif" 12.0)
168
+ i = (i + 1)
169
+ }
170
+ this.ok("three vertical calls for one face" (hm.faceCalls == 3))
171
+ this.ok("two width calls for two runs through the engine" (hm.widthCalls == 2))
172
+ eng.measureRun("hello" "sans-serif-Bold" 12.0)
173
+ this.ok("a bold face is a second face" (hm.faceCalls == 6))
174
+ eng.measureRun("hello" "sans-serif" 13.0)
175
+ this.ok("a size is a third" (hm.faceCalls == 9))
176
+ this.ok("the empty run costs no width call" (((hm.measureText("" "sans-serif" 12.0)).width) == 0.0))
177
+ this.ok("and asked the host for nothing" (hm.widthCalls == 4))
178
+ }
179
+
180
+ fn testLineHeight:void () {
181
+ print "-- line-height: normal is the face's line box"
182
+ def hm:EVGHostTextMeasurer (this.fakePlatform())
183
+ this.near("ascent + descent + gap" (hm.getLineHeight("sans-serif" 10.0)) 15.0)
184
+ def m:EVGTextMetrics (hm.measureText("x" "sans-serif" 10.0))
185
+ this.near("ascent" m.ascent 10.0)
186
+ this.near("descent" m.descent 3.0)
187
+ this.near("the run carries the same line height" m.lineHeight 15.0)
188
+ ; A host that does not know the gap says so with a negative number.
189
+ def noGap (new EVGHostTextMeasurer())
190
+ noGap.attach((fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean) {
191
+ if (kind == 3) {
192
+ return (0.0 - 1.0)
193
+ }
194
+ if (kind == 0) {
195
+ return (size * (to_double (strlen text)))
196
+ }
197
+ return (size * 0.5)
198
+ }) "nogap")
199
+ this.near("an unknown gap is zero, not negative" (noGap.getLineHeight("sans-serif" 10.0)) 10.0)
200
+ }
201
+
202
+ fn testUnattachedFallsBackThenAttaches:void () {
203
+ print "-- unattached measures like the table; attaching moves the key"
204
+ def hm (new EVGHostTextMeasurer())
205
+ def table (new SimpleTextMeasurer())
206
+ def a:EVGTextMetrics (hm.measureText("Päiväkirja" "sans-serif" 14.0))
207
+ def b:EVGTextMetrics (table.measureText("Päiväkirja" "sans-serif" 14.0))
208
+ this.near("width as the table" a.width b.width)
209
+ this.near("line height as the table" (hm.getLineHeight("sans-serif" 14.0)) (table.getLineHeight("sans-serif" 14.0)))
210
+ this.ok("not font-accurate before attach" ((hm.isFontAccurate()) == false))
211
+ this.ok("no face before attach" ((hm.hasFace("sans-serif")) == false))
212
+ def k0:string (hm.measureKey())
213
+ def eng (new EVGTextEngine())
214
+ eng.setMeasurer(hm)
215
+ def w0:double ((eng.measureRun("hello" "sans-serif" 10.0)).width)
216
+ hm.attach((fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean) {
217
+ if (kind == 0) {
218
+ return (size * 2.0 * (to_double (strlen text)))
219
+ }
220
+ return (size * 0.5)
221
+ }) "late")
222
+ def k1:string (hm.measureKey())
223
+ this.ok("the key moved" (k0 != k1))
224
+ this.ok("the key names the host" ((indexOf k1 "late") >= 0))
225
+ def w1:double ((eng.measureRun("hello" "sans-serif" 10.0)).width)
226
+ this.ok("the engine measured again without clearCache" (w1 != w0))
227
+ this.near("with the host's width" w1 100.0)
228
+ hm.invalidate()
229
+ this.ok("invalidate moves the key again" ((hm.measureKey()) != k1))
230
+ }
231
+
232
+ fn testWrapInRealLayout:void () {
233
+ print "-- a text node wraps where the host's widths say"
234
+ ; 0.7em a glyph at 10px is 7px; "aaaa bbbb cccc" is 14 glyphs = 98px.
235
+ ; In a 60px box the host wraps it into three lines; the table
236
+ ; (0.556em) fits "aaaa bbbb" in 50px and makes two.
237
+ def hm:EVGHostTextMeasurer (this.fakePlatform())
238
+ EVGDefaultMeasurer.install(hm)
239
+ def root (EVGElement.createDiv())
240
+ root.setAttribute("width" "60px")
241
+ root.setAttribute("height" "200px")
242
+ def span (EVGElement.createSpan())
243
+ span.setAttribute("width" "60px")
244
+ span.setAttribute("font-size" "10px")
245
+ span.setAttribute("font-family" "sans-serif")
246
+ span.textContent = "aaaa bbbb cccc"
247
+ root.addChild(span)
248
+ def lay (new EVGLayout())
249
+ lay.setPageSize(60.0 200.0)
250
+ lay.layout(root)
251
+ ; Three lines of the face's 15px line box.
252
+ this.near("three lines at the host's line height" span.calculatedHeight 45.0)
253
+ def te:EVGTextEngine (lay.getTextEngine())
254
+ this.ok("the engine counts three" ((te.lineCount("aaaa bbbb cccc" "sans-serif" 10.0 60.0)) == 3))
255
+ EVGDefaultMeasurer.uninstall()
256
+ def lay2 (new EVGLayout())
257
+ def root2 (EVGElement.createDiv())
258
+ root2.setAttribute("width" "60px")
259
+ root2.setAttribute("height" "200px")
260
+ def span2 (EVGElement.createSpan())
261
+ span2.setAttribute("width" "60px")
262
+ span2.setAttribute("font-size" "10px")
263
+ span2.setAttribute("font-family" "sans-serif")
264
+ span2.textContent = "aaaa bbbb cccc"
265
+ root2.addChild(span2)
266
+ lay2.setPageSize(60.0 200.0)
267
+ lay2.layout(root2)
268
+ this.ok("the table makes fewer" (span2.calculatedHeight < 45.0))
269
+ }
270
+
271
+ fn testHasFace:void () {
272
+ print "-- hasFace: a substituting host has every face, a strict one names them"
273
+ def hm:EVGHostTextMeasurer (this.fakePlatform())
274
+ this.ok("resolvesAll by default" (hm.hasFace("Whatever")))
275
+ hm.setResolvesAll(false)
276
+ this.ok("strict: unknown family has no face" ((hm.hasFace("Whatever")) == false))
277
+ hm.addKnownFamily("Inter")
278
+ this.ok("strict: a known family has one" (hm.hasFace("Inter")))
279
+ this.ok("strict: and its bold" (hm.hasFace("Inter-Bold")))
280
+ }
281
+ }
@@ -0,0 +1,331 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; EVGHostTextMeasurer — the platform measures, EVG breaks the lines
5
+ ; ==============================================================================
6
+ ;
7
+ ; PLAN_NATIVE_HOSTS.md, decision D3 and stage S0. Every painter below the
8
+ ; display list draws text with a face the PLATFORM chose — a browser's font
9
+ ; stack, CoreText on Apple, a `Typeface` on Android — while the layout above
10
+ ; it measured the run with `SimpleTextMeasurer`'s advance table, a snapshot of
11
+ ; one browser's sans fallback taken once. Where the two faces differ, a run
12
+ ; is drawn wider or narrower than the box that was sized for it: a label
13
+ ; clips, a caret lands beside its glyphs, a line wraps that paint would not
14
+ ; have wrapped.
15
+ ;
16
+ ; This measurer closes that gap the way `EVGContextMeasurer` closes it for
17
+ ; the software canvas: the host that will paint is the host that measures.
18
+ ; It is GENERIC on purpose — one measurer, every platform, every app — and
19
+ ; the whole of what a platform has to provide is ONE FUNCTION:
20
+ ;
21
+ ; metric(kind text family size bold italic) -> double
22
+ ;
23
+ ; kind 0 the advance width of `text` set in that face at that size
24
+ ; kind 1 the face's ascent, in pixels, at that size (text ignored)
25
+ ; kind 2 the face's descent (text ignored)
26
+ ; kind 3 the face's line gap, or a negative number for "unknown"
27
+ ;
28
+ ; The host writes it in its own language against its own text API — canvas
29
+ ; `measureText`, `CTLineGetTypographicBounds`, `Paint.measureText` — and
30
+ ; hands it over with `attach`. Everything else is here, written once:
31
+ ; the per-face cache of the vertical metrics (three host calls per face,
32
+ ; not three per run), the `-Bold` convention `EVGElement.effectiveFontFamily`
33
+ ; encodes the weight in, the cache key the text engine needs, and the
34
+ ; fallback to the table when no host has attached yet, so an app can install
35
+ ; this at start-up and attach the platform later — a browser after
36
+ ; `document.fonts.ready` — without ever measuring nothing.
37
+ ;
38
+ ; WHY A FUNCTION AND NOT A SUBCLASS. A host could extend this class in its own
39
+ ; language and override `measureText`; JavaScript, Swift and Kotlin all
40
+ ; allow it. But then each of the three hosts owns a copy of the face cache,
41
+ ; the weight convention and the key — the three things that were wrong in
42
+ ; three different ways when five painters each walked the tree. A closure
43
+ ; keeps the platform's contribution to the one thing only the platform
44
+ ; knows, and Ranger's lambda fields compile to a settable property of
45
+ ; function type on every target (`(Int, String, …) -> Double` in Kotlin, an
46
+ ; `@escaping` closure in Swift, a plain property in ES6).
47
+ ;
48
+ ; WHAT IT MEANS FOR PRINT PARITY. Nothing. The lines are still broken here,
49
+ ; by `EVGTextEngine`, one run per line; a PDF laid out with the TTF measurer
50
+ ; and a screen laid out with this one agree wherever they share a face, and
51
+ ; where they do not, the screen is now honest about its own face rather
52
+ ; than pretending to be the table.
53
+ ;
54
+ ; See `EVGHostMeasurerTest.rgr` (`npm run evg:hostmeasurer:test`) and the
55
+ ; three hosts: `gl/evg-measure.js`, `apple/Sources/CoreTextMeasurer.swift`,
56
+ ; `android/src/…/AndroidTextMeasurer.kt` (with its Java2D twin).
57
+ ; ==============================================================================
58
+
59
+ Import "EVGTextMeasurer.rgr"
60
+
61
+ class EVGHostTextMeasurer {
62
+ Extends(EVGTextMeasurer)
63
+
64
+ ; The one function. Unset until the host attaches; every question is then
65
+ ; answered by `fallback` below, so a measurer that was installed before
66
+ ; its platform arrived measures like the table did, not like nothing.
67
+ def metricFn@(optional):(fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean))
68
+ def attached:boolean false
69
+ ; Which platform is on the other end — "canvas", "coretext", "android",
70
+ ; "awt", or whatever the host calls itself. It goes into `measureKey`, so
71
+ ; a text engine that outlives a host swap cannot hand back the old host's
72
+ ; widths.
73
+ def hostName:string ""
74
+ ; Bumped by `attach`, `detach` and `invalidate`. Same reason: the engine
75
+ ; caches by `measureKey`, and a face that finished loading is a different
76
+ ; answer to the same question.
77
+ def generation:int 0
78
+
79
+ ; Does the platform draw SOMETHING for every family it is asked for? A
80
+ ; browser's font stack and CoreText both substitute, so every family
81
+ ; "has a face" — the face the painter will use — and warning about it
82
+ ; would warn about every label on the page. A host that bundles exact
83
+ ; TTFs and wants a missing one reported sets this false and names what
84
+ ; it has through `addKnownFamily`.
85
+ def resolvesAll:boolean true
86
+ def known:[string]
87
+
88
+ ; The face cache: key -> ascent, descent, line gap, in pixels. A face is
89
+ ; a family, a size, a weight and a slant; a page asks for a handful of
90
+ ; them and thousands of runs in each, so the three vertical questions
91
+ ; are asked once per face and never per run.
92
+ def faceKeys:[string]
93
+ def faceAsc:[double]
94
+ def faceDesc:[double]
95
+ def faceGap:[double]
96
+ ; The last face looked up, because a wrap asks the same face for every
97
+ ; candidate line and a linear search over a dozen keys per call adds up.
98
+ def lastKey:string ""
99
+ def lastIdx:int (0 - 1)
100
+
101
+ ; How many times the host has been called, by kind. For the test and for
102
+ ; a bench: a measurer that calls the platform per character instead of
103
+ ; per run would pass every width check and cost a frame.
104
+ def widthCalls:int 0
105
+ def faceCalls:int 0
106
+
107
+ ; Answers everything while no host is attached.
108
+ def fallback:SimpleTextMeasurer (new SimpleTextMeasurer())
109
+
110
+ Constructor () {
111
+ }
112
+
113
+ ; --- the kinds, by name -------------------------------------------------
114
+
115
+ sfn KIND_WIDTH:int () {
116
+ return 0
117
+ }
118
+
119
+ sfn KIND_ASCENT:int () {
120
+ return 1
121
+ }
122
+
123
+ sfn KIND_DESCENT:int () {
124
+ return 2
125
+ }
126
+
127
+ sfn KIND_GAP:int () {
128
+ return 3
129
+ }
130
+
131
+ ; --- attaching the platform ---------------------------------------------
132
+
133
+ fn attach:void (f:(fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean)) name:string) {
134
+ metricFn = f
135
+ hostName = name
136
+ attached = true
137
+ this.invalidate()
138
+ }
139
+
140
+ fn detach:void () {
141
+ attached = false
142
+ this.invalidate()
143
+ }
144
+
145
+ ; The host's answers changed — a face finished loading, the device's
146
+ ; font settings moved. The cache is dropped and the key moves on, so
147
+ ; every engine holding this measurer measures again.
148
+ fn invalidate:void () {
149
+ generation = (generation + 1)
150
+ def fk:[string]
151
+ faceKeys = fk
152
+ def fa:[double]
153
+ faceAsc = fa
154
+ def fd:[double]
155
+ faceDesc = fd
156
+ def fg:[double]
157
+ faceGap = fg
158
+ lastKey = ""
159
+ lastIdx = (0 - 1)
160
+ }
161
+
162
+ fn setResolvesAll:void (b:boolean) {
163
+ resolvesAll = b
164
+ }
165
+
166
+ fn addKnownFamily:void (name:string) {
167
+ if ((strlen name) == 0) {
168
+ return
169
+ }
170
+ def i:int 0
171
+ while (i < (array_length known)) {
172
+ if ((itemAt known i) == name) {
173
+ return
174
+ }
175
+ i = (i + 1)
176
+ }
177
+ push known name
178
+ }
179
+
180
+ fn isAttached:boolean () {
181
+ return attached
182
+ }
183
+
184
+ ; --- the EVGTextMeasurer contract ---------------------------------------
185
+
186
+ fn isFontAccurate:boolean () {
187
+ return attached
188
+ }
189
+
190
+ fn hasFace:boolean (fontFamily:string) {
191
+ if (attached == false) {
192
+ return false
193
+ }
194
+ if resolvesAll {
195
+ return true
196
+ }
197
+ def base:string (EVGHostTextMeasurer.baseFamily(fontFamily))
198
+ def i:int 0
199
+ while (i < (array_length known)) {
200
+ if ((itemAt known i) == base) {
201
+ return true
202
+ }
203
+ i = (i + 1)
204
+ }
205
+ return false
206
+ }
207
+
208
+ fn measureKey:string () {
209
+ def out@(mutable):string ("host:" + hostName + ":" + (to_string generation))
210
+ if (attached == false) {
211
+ out = (out + ":table")
212
+ }
213
+ return out
214
+ }
215
+
216
+ ; --- the family convention ------------------------------------------------
217
+ ;
218
+ ; `EVGElement.effectiveFontFamily` hands the layout `Family-Bold` for a
219
+ ; bold element and `Family` otherwise; that suffix is the only way the
220
+ ; weight reaches a measurer, and `FontManager.getFont` reads the same
221
+ ; convention for print. Italic has no such suffix today, so a slanted run
222
+ ; is measured upright — the same thing the table did, recorded here
223
+ ; rather than hidden.
224
+
225
+ sfn isBoldFace:boolean (face:string) {
226
+ def n:int (strlen face)
227
+ if (n < 6) {
228
+ return false
229
+ }
230
+ def tail:string (substring face (n - 5) n)
231
+ return (tail == "-Bold")
232
+ }
233
+
234
+ sfn baseFamily:string (face:string) {
235
+ if (EVGHostTextMeasurer.isBoldFace(face)) {
236
+ def n:int (strlen face)
237
+ return (substring face 0 (n - 5))
238
+ }
239
+ return face
240
+ }
241
+
242
+ ; --- the face cache -------------------------------------------------------
243
+
244
+ fn faceIndex:int (family:string size:double bold:boolean) {
245
+ def key:string (family + "|" + (to_string size) + "|" + (to_string bold))
246
+ if (key == lastKey) {
247
+ return lastIdx
248
+ }
249
+ def i:int 0
250
+ while (i < (array_length faceKeys)) {
251
+ if ((itemAt faceKeys i) == key) {
252
+ lastKey = key
253
+ lastIdx = i
254
+ return i
255
+ }
256
+ i = (i + 1)
257
+ }
258
+ def f:(fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean)) (unwrap metricFn)
259
+ def asc:double (f(1 "" family size bold false))
260
+ def desc:double (f(2 "" family size bold false))
261
+ def gap:double (f(3 "" family size bold false))
262
+ faceCalls = (faceCalls + 3)
263
+ ; A host that cannot say what the gap is says so with a negative
264
+ ; number; the line box is then the face's ascent and descent alone.
265
+ if (gap < 0.0) {
266
+ gap = 0.0
267
+ }
268
+ ; A host that answers nothing for the vertical — a canvas without
269
+ ; `fontBoundingBox*`, say — gets the table's proportions, which are
270
+ ; measured off a real face rather than made up.
271
+ if ((asc + desc) <= 0.0) {
272
+ asc = (size * (EVGTextMeasurer.fallbackAscentEm()))
273
+ desc = (size * (EVGTextMeasurer.fallbackDescentEm()))
274
+ }
275
+ push faceKeys key
276
+ push faceAsc asc
277
+ push faceDesc desc
278
+ push faceGap gap
279
+ lastKey = key
280
+ lastIdx = ((array_length faceKeys) - 1)
281
+ return lastIdx
282
+ }
283
+
284
+ ; --- measurement ------------------------------------------------------------
285
+
286
+ fn measureText:EVGTextMetrics (text:string fontFamily:string fontSize:double) {
287
+ if (attached == false) {
288
+ return (fallback.measureText(text fontFamily fontSize))
289
+ }
290
+ def bold:boolean (EVGHostTextMeasurer.isBoldFace(fontFamily))
291
+ def family:string (EVGHostTextMeasurer.baseFamily(fontFamily))
292
+ def fi:int (this.faceIndex(family fontSize bold))
293
+ def w:double 0.0
294
+ if ((strlen text) > 0) {
295
+ def f:(fn:double (kind:int text:string family:string size:double bold:boolean italic:boolean)) (unwrap metricFn)
296
+ w = (f(0 text family fontSize bold false))
297
+ widthCalls = (widthCalls + 1)
298
+ }
299
+ def asc:double (itemAt faceAsc fi)
300
+ def desc:double (itemAt faceDesc fi)
301
+ def gap:double (itemAt faceGap fi)
302
+ def m (new EVGTextMetrics())
303
+ m.width = w
304
+ m.ascent = asc
305
+ m.descent = desc
306
+ m.lineHeight = (asc + desc + gap)
307
+ m.height = m.lineHeight
308
+ return m
309
+ }
310
+
311
+ fn measureTextWidth:double (text:string fontFamily:string fontSize:double) {
312
+ def m:EVGTextMetrics (this.measureText(text fontFamily fontSize))
313
+ return m.width
314
+ }
315
+
316
+ ; `line-height: normal` is the face's own line box — ascent, descent and
317
+ ; the gap the face asks for between lines. The table says 1.15em for its
318
+ ; sans; a host says what its face says.
319
+ fn getLineHeight:double (fontFamily:string fontSize:double) {
320
+ if (attached == false) {
321
+ return (fallback.getLineHeight(fontFamily fontSize))
322
+ }
323
+ def bold:boolean (EVGHostTextMeasurer.isBoldFace(fontFamily))
324
+ def family:string (EVGHostTextMeasurer.baseFamily(fontFamily))
325
+ def fi:int (this.faceIndex(family fontSize bold))
326
+ def asc:double (itemAt faceAsc fi)
327
+ def desc:double (itemAt faceDesc fi)
328
+ def gap:double (itemAt faceGap fi)
329
+ return (asc + desc + gap)
330
+ }
331
+ }