ranger-compiler 3.5.0 → 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 +1160 -19
  2. package/LICENSE +3 -1
  3. package/README.md +124 -29
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79924 -56922
  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,536 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; EVGTextEngine.rgr - The one text measurement + line breaking implementation
4
+ ;
5
+ ; PLAN_CSS_LAYOUT_AND_FONTS.md §8. Layout, PDF paint and raster paint must agree
6
+ ; on where text breaks and how wide it is, or a page that looks right in preview
7
+ ; prints wrong. Before this module there were four separate wrap implementations:
8
+ ;
9
+ ; EVGLayout.estimateLineCount word-by-word, space width guessed as
10
+ ; fontSize * 0.3, family hardcoded "Helvetica"
11
+ ; EVGPDFRenderer.wrapText whole-candidate-line measurement, real family
12
+ ; EVGTextMeasurer.wrapText character-by-character accumulation
13
+ ; FontManager.measureText strlen * fontSize * 0.5 when no face loaded
14
+ ;
15
+ ; They break lines in different places, so layout computed one line count and
16
+ ; paint drew another. This module keeps the PDF algorithm — measuring the whole
17
+ ; candidate line so the real space advance is included rather than approximated
18
+ ; — and everything else calls into it.
19
+ ;
20
+ ; The engine does not itself know about fonts: it delegates to an
21
+ ; EVGTextMeasurer. What it adds is (a) one algorithm, (b) the actual font family
22
+ ; threaded through instead of a hardcoded one, and (c) honesty about whether the
23
+ ; measurer can back the families a document asks for.
24
+
25
+ Import "EVGGrapheme.rgr"
26
+ Import "EVGTextMeasurer.rgr"
27
+ ; So that every compiled module a host can attach to has the host measurer
28
+ ; and the default registry, without the program importing them itself.
29
+ Import "EVGHostTextMeasurer.rgr"
30
+
31
+ ; One laid-out line. ascent/descent are carried from day one so baseline
32
+ ; alignment (§4.1 rule 4) can be added without re-measuring.
33
+ class EVGTextLine {
34
+ def text:string ""
35
+ def width:double 0.0
36
+ def ascent:double 0.0
37
+ def descent:double 0.0
38
+ ; True on the first line of every paragraph after the first — the line
39
+ ; that a `paragraph-spacing` gap goes ABOVE. A wrapped continuation is
40
+ ; not one: the gap belongs to the break the author typed, not to the
41
+ ; ones the box made.
42
+ def startsParagraph:boolean false
43
+
44
+ Constructor () {
45
+ text = ""
46
+ width = 0.0
47
+ ascent = 0.0
48
+ descent = 0.0
49
+ }
50
+ }
51
+
52
+ ; One cached wrap. A map cannot hold a list directly, so the lines live in a
53
+ ; store beside the index and this is what the store holds.
54
+ class EVGWrapEntry {
55
+ def lines:[EVGTextLine]
56
+
57
+ Constructor () {
58
+ }
59
+ }
60
+
61
+ class EVGTextEngine {
62
+ def measurer:EVGTextMeasurer
63
+ ; When strict, a family the measurer cannot back is a hard error rather
64
+ ; than a silent fall back to guessed widths. Print pipelines should set it.
65
+ def strict:boolean false
66
+ ; Families already reported, so one missing face does not produce one
67
+ ; warning per text node.
68
+ def reported:[string]
69
+ def warnings:[string]
70
+ ; Set once a strict violation has been seen, so callers can fail the run.
71
+ def hadFatal:boolean false
72
+
73
+ ; --- THE WRAP CACHE -------------------------------------------------------
74
+ ;
75
+ ; A SCROLL LAYS THE WHOLE TREE OUT AGAIN. The offset moved, so every box is
76
+ ; measured and every paragraph is broken into lines again — and not one
77
+ ; character of the text changed. In a profile of `gallery/realtrainer`
78
+ ; scrolling a real training diary, `breakLines` was over half of the frame
79
+ ; and every wrap in it produced the answer it had produced on the frame
80
+ ; before.
81
+ ;
82
+ ; A wrap is a pure function of five things: the text, the family, the size,
83
+ ; the width it is wrapped into, and what the measurer would say. The first
84
+ ; four are the arguments; the fifth is `measurer.measureKey()`, which is
85
+ ; how a measurer that loads a face or is switched to bold mid-run says that
86
+ ; its answers have changed — see `EVGTextMeasurer.measureKey`.
87
+ ;
88
+ ; It is a CACHE and not a memo table: it is dropped whole when it grows
89
+ ; past a bound, because a document that is edited produces a new key for
90
+ ; every keystroke and nothing would ever evict them. Dropping everything is
91
+ ; the cheapest eviction there is and the next frame refills what it uses.
92
+ ;
93
+ ; What comes back is SHARED. Two callers asking for the same wrap get the
94
+ ; same `EVGTextLine` objects, so the lines are to be read and not written
95
+ ; to; every caller in the tree derives a count, a width or the strings and
96
+ ; leaves them alone.
97
+ def wrapIndex:[string:int]
98
+ def wrapStore:[EVGWrapEntry]
99
+ ; The same for whole-string metrics, which `EVGLayout` asks for once per
100
+ ; text node per frame and `InputCtl` asks for once per character of the
101
+ ; string in front of the caret.
102
+ def runIndex:[string:int]
103
+ def runStore:[EVGTextMetrics]
104
+
105
+ ; The last key's PREFIX, and the fields it was built from.
106
+ ;
107
+ ; Everything in a cache key except the text is the same from one call to
108
+ ; the next almost every time — a paragraph is measured word by word in one
109
+ ; face at one size — and building it was costing more than the lookup it
110
+ ; is for: two doubles turned into strings and six joins, per word. Five
111
+ ; comparisons and one join instead.
112
+ def keyPrefix:string ""
113
+ def keyKind:string ""
114
+ def keyFamily:string ""
115
+ def keySize:double -1.0
116
+ def keyMax:double -1.0
117
+ def keyMeasure:string ""
118
+
119
+ ; Starts on the heuristic measurer so the engine is always usable; callers
120
+ ; that care about print correctness install a real one via setMeasurer and
121
+ ; turn on strict mode.
122
+ Constructor () {
123
+ def m:EVGTextMeasurer (EVGDefaultMeasurer.measurer())
124
+ measurer = m
125
+ strict = false
126
+ hadFatal = false
127
+ }
128
+
129
+ ; A different measurer measures differently, so nothing measured by the
130
+ ; old one may survive it.
131
+ fn setMeasurer:void (m:EVGTextMeasurer) {
132
+ measurer = m
133
+ this.clearCache()
134
+ }
135
+
136
+ ; Drop everything measured so far. Called when the measurer is replaced,
137
+ ; and when the cache has grown past the bound below; a caller that knows
138
+ ; its measurer has changed underneath it in a way `measureKey` does not
139
+ ; report may also call it.
140
+ fn clearCache:void () {
141
+ def wi:[string:int]
142
+ wrapIndex = wi
143
+ def ws:[EVGWrapEntry]
144
+ wrapStore = ws
145
+ def ri:[string:int]
146
+ runIndex = ri
147
+ def rs:[EVGTextMetrics]
148
+ runStore = rs
149
+ }
150
+
151
+ ; The slack a line is allowed over the width it is being broken into: a
152
+ ; millionth of a pixel, which is a hundred million times the error a
153
+ ; shrink-wrap round trip introduces and a millionth of the smallest thing
154
+ ; anyone can see.
155
+ sfn fitEpsilon:double () {
156
+ return 0.000001
157
+ }
158
+
159
+ ; How many wraps and how many metrics are kept before the lot is dropped.
160
+ ; A page of text is a few hundred distinct strings; this is room for a
161
+ ; document's worth of them at several widths without ever growing without
162
+ ; bound.
163
+ sfn cacheLimit:int () {
164
+ return 4096
165
+ }
166
+
167
+ ; Everything about a measurement that is not the text: the size, the width
168
+ ; it is wrapped into, what the measurer would say, and the family. The TEXT
169
+ ; GOES LAST because it is the one field that can contain the separator.
170
+ fn cacheKey:string (kind:string fontFamily:string fontSize:double maxWidth:double text:string) {
171
+ ; `measureKey` is asked every time on purpose: it is what a face
172
+ ; loaded after something was measured changes, and a prefix that
173
+ ; remembered it would hand back keys the new face has made stale.
174
+ def mk:string (measurer.measureKey())
175
+ def same:boolean true
176
+ if (kind != keyKind) { same = false }
177
+ if (fontFamily != keyFamily) { same = false }
178
+ if (fontSize != keySize) { same = false }
179
+ if (maxWidth != keyMax) { same = false }
180
+ if (mk != keyMeasure) { same = false }
181
+ if (! same) {
182
+ keyKind = kind
183
+ keyFamily = fontFamily
184
+ keySize = fontSize
185
+ keyMax = maxWidth
186
+ keyMeasure = mk
187
+ keyPrefix = (kind + "\n" + (to_string fontSize) + "\n" + (to_string maxWidth)
188
+ + "\n" + mk + "\n" + fontFamily + "\n")
189
+ }
190
+ return (keyPrefix + text)
191
+ }
192
+
193
+ fn setStrict:void (s:boolean) {
194
+ strict = s
195
+ }
196
+
197
+ fn warningCount:int () {
198
+ return (array_length warnings)
199
+ }
200
+
201
+ fn warningAt:string (i:int) {
202
+ return (itemAt warnings i)
203
+ }
204
+
205
+ ; -----------------------------------------------------------------------
206
+ ; Font honesty
207
+ ; -----------------------------------------------------------------------
208
+
209
+ ; Record (once per family) that this family cannot be measured from a real
210
+ ; face. In strict mode this also marks the run as failed.
211
+ fn noteFamily:void (fontFamily:string) {
212
+ def i:int 0
213
+ while (i < (array_length reported)) {
214
+ if ((itemAt reported i) == fontFamily) {
215
+ return
216
+ }
217
+ i = (i + 1)
218
+ }
219
+ push reported fontFamily
220
+
221
+ if ((measurer.isFontAccurate()) == false) {
222
+ push warnings ("No font metrics available for \"" + fontFamily
223
+ + "\" - measuring with heuristic widths. Print layout will not match paint.")
224
+ if strict {
225
+ hadFatal = true
226
+ }
227
+ return
228
+ }
229
+ if ((measurer.hasFace(fontFamily)) == false) {
230
+ push warnings ("Font face not loaded: \"" + fontFamily
231
+ + "\" - falling back to another face. Widths will not match paint.")
232
+ if strict {
233
+ hadFatal = true
234
+ }
235
+ }
236
+ }
237
+
238
+ fn checkFamily:void (fontFamily:string) {
239
+ if ((measurer.isFontAccurate()) == false) {
240
+ this.noteFamily(fontFamily)
241
+ return
242
+ }
243
+ if ((measurer.hasFace(fontFamily)) == false) {
244
+ this.noteFamily(fontFamily)
245
+ }
246
+ }
247
+
248
+ ; -----------------------------------------------------------------------
249
+ ; §8 contract
250
+ ; -----------------------------------------------------------------------
251
+
252
+ fn measureRunSpaced:EVGTextMetrics (text:string fontFamily:string fontSize:double spacing:double) {
253
+ def m:EVGTextMetrics (this.measureRun(text fontFamily fontSize))
254
+ if (spacing == 0.0) {
255
+ return m
256
+ }
257
+ def wider (new EVGTextMetrics())
258
+ wider.width = (m.width + (EVGTextEngine.trackingWidth(text spacing)))
259
+ wider.height = m.height
260
+ wider.ascent = m.ascent
261
+ wider.descent = m.descent
262
+ wider.lineHeight = m.lineHeight
263
+ return wider
264
+ }
265
+
266
+ fn measureRun:EVGTextMetrics (text:string fontFamily:string fontSize:double) {
267
+ ; Before the cache, and not inside it: the font warnings are counted
268
+ ; per family and reported once, and a second frame must not be quieter
269
+ ; than the first because the first one filled a cache.
270
+ this.checkFamily(fontFamily)
271
+ def key:string (this.cacheKey("m" fontFamily fontSize 0.0 text))
272
+ def hit@(optional):int (get runIndex key)
273
+ if (null? hit) {
274
+ def m:EVGTextMetrics (measurer.measureText(text fontFamily fontSize))
275
+ if ((array_length runStore) >= (EVGTextEngine.cacheLimit())) {
276
+ this.clearCache()
277
+ }
278
+ push runStore m
279
+ set runIndex key ((array_length runStore) - 1)
280
+ return m
281
+ }
282
+ return (itemAt runStore (unwrap hit))
283
+ }
284
+
285
+ fn lineHeightFor:double (fontFamily:string fontSize:double) {
286
+ return (measurer.getLineHeight(fontFamily fontSize))
287
+ }
288
+
289
+ ; Greedy word wrap. Honours explicit newlines, and measures each CANDIDATE
290
+ ; LINE whole rather than summing word widths plus a guessed space — that is
291
+ ; what makes the result identical to what the paint side draws.
292
+ ;
293
+ ; maxWidth <= 0 means "do not wrap": each newline-separated paragraph
294
+ ; becomes exactly one line.
295
+ fn breakLines:[EVGTextLine] (text:string fontFamily:string fontSize:double maxWidth:double) {
296
+ return (this.breakLinesSpaced(text fontFamily fontSize maxWidth 0.0))
297
+ }
298
+
299
+ ; The same wrap with `letter-spacing` in it. Zero is the call above and
300
+ ; the same cache entry, so a caller that has no tracking pays nothing
301
+ ; and cannot be moved by this.
302
+ fn breakLinesSpaced:[EVGTextLine] (text:string fontFamily:string fontSize:double maxWidth:double spacing:double) {
303
+ this.checkFamily(fontFamily)
304
+ def key:string (this.cacheKey("w" fontFamily fontSize maxWidth text))
305
+ if (spacing != 0.0) {
306
+ key = ((key + "|ls") + (to_string spacing))
307
+ }
308
+ def hit@(optional):int (get wrapIndex key)
309
+ if (null? hit) {
310
+ def fresh:[EVGTextLine] (this.wrapUncached(text fontFamily fontSize maxWidth spacing))
311
+ if ((array_length wrapStore) >= (EVGTextEngine.cacheLimit())) {
312
+ this.clearCache()
313
+ }
314
+ def entry (new EVGWrapEntry())
315
+ entry.lines = fresh
316
+ push wrapStore entry
317
+ set wrapIndex key ((array_length wrapStore) - 1)
318
+ return fresh
319
+ }
320
+ def kept:EVGWrapEntry (itemAt wrapStore (unwrap hit))
321
+ return kept.lines
322
+ }
323
+
324
+ ; The wrap itself, with no cache in front of it.
325
+ fn wrapUncached:[EVGTextLine] (text:string fontFamily:string fontSize:double maxWidth:double spacing:double) {
326
+ def out:[EVGTextLine]
327
+
328
+ def paragraphs:[string] (strsplit text "\n")
329
+ def p:int 0
330
+ while (p < (array_length paragraphs)) {
331
+ def para:string (itemAt paragraphs p)
332
+ def firstOfPara:int (array_length out)
333
+ if (maxWidth <= 0.0) {
334
+ push out (this.measuredLineSpaced(para fontFamily fontSize spacing))
335
+ } {
336
+ def words:[string] (strsplit para " ")
337
+ ; How many TOKENS are on the line, not how many characters.
338
+ ; `strsplit " / " " "` is ["", "/", ""], and an empty token is a
339
+ ; token: it is the space that was there. Asking `strlen
340
+ ; currentLine == 0` cannot tell "nothing on this line yet" from
341
+ ; "one empty token on this line", so it took the `testLine =
342
+ ; word` branch a second time and dropped the space in front —
343
+ ; the breadcrumb separator went in as " / " and came out "/ ",
344
+ ; drawn hard against the crumb before it with the gap on the
345
+ ; wrong side. The no-wrap path (maxWidth <= 0) pushed the
346
+ ; paragraph verbatim and kept its spaces, so the same string
347
+ ; drew two different ways depending on whether it had a width.
348
+ def currentLine:string ""
349
+ def tokensOnLine:int 0
350
+ def w:int 0
351
+ while (w < (array_length words)) {
352
+ def word:string (itemAt words w)
353
+ def testLine:string ""
354
+ if (tokensOnLine == 0) {
355
+ testLine = word
356
+ } {
357
+ testLine = currentLine + " " + word
358
+ }
359
+ def testWidth:double ((measurer.measureTextWidth(testLine fontFamily fontSize)) + (EVGTextEngine.trackingWidth(testLine spacing)))
360
+ ; A HAIR OF TOLERANCE, and it is not a fudge — it is what
361
+ ; stops a box from breaking its own content.
362
+ ;
363
+ ; A text element with no stated width shrink-wraps: the
364
+ ; layout measures the run, adds the padding and the border,
365
+ ; and that sum is the box. The breaker then works with the
366
+ ; box MINUS the same padding and border, and
367
+ ; `(w + chrome) - chrome` is not always `w` in binary
368
+ ; floating point. For some strings it lands a few parts in
369
+ ; a quadrillion low, and the line that was measured to fit
370
+ ; exactly is then one part in a quadrillion too wide and
371
+ ; goes onto a second line. `lib/evg/web/responsive`
372
+ ; had six labels in a column, five on one line and
373
+ ; `Display list` on two, for exactly this reason — see
374
+ ; ISSUES.md #7.
375
+ ;
376
+ ; A micron of a pixel cannot change any wrap a person
377
+ ; asked for; it can only undo a round trip that should
378
+ ; have been the identity.
379
+ if (((testWidth - maxWidth) > (EVGTextEngine.fitEpsilon())) && (tokensOnLine > 0)) {
380
+ push out (this.measuredLineSpaced(currentLine fontFamily fontSize spacing))
381
+ currentLine = word
382
+ tokensOnLine = 1
383
+ } {
384
+ currentLine = testLine
385
+ tokensOnLine = (tokensOnLine + 1)
386
+ }
387
+ w = (w + 1)
388
+ }
389
+ ; An empty paragraph is still a line — blank lines take vertical
390
+ ; space, and dropping them would shorten the block.
391
+ push out (this.measuredLineSpaced(currentLine fontFamily fontSize spacing))
392
+ }
393
+ if (p > 0) {
394
+ if (firstOfPara < (array_length out)) {
395
+ def opener:EVGTextLine (itemAt out firstOfPara)
396
+ opener.startsParagraph = true
397
+ }
398
+ }
399
+ p = (p + 1)
400
+ }
401
+
402
+ if ((array_length out) == 0) {
403
+ push out (this.measuredLineSpaced("" fontFamily fontSize spacing))
404
+ }
405
+ return out
406
+ }
407
+
408
+ ; One measured line, RETURNED rather than pushed into a list the caller
409
+ ; passed in.
410
+ ;
411
+ ; It used to take the list. That works on JavaScript, where an array
412
+ ; parameter is the caller's array, and does nothing on Go, where it is a
413
+ ; slice and the append lands on a copy — so `breakLines` returned NO lines
414
+ ; on Go at any width, and every display list built from an element tree
415
+ ; drew no text there at all. Nothing warns in between: the JavaScript
416
+ ; suites pass and the Go ones draw an empty page.
417
+ ;
418
+ ; Found from `gallery/markdown`, whose own layout had hit the same trap
419
+ ; a week earlier and left a note about it.
420
+ fn measuredLine:EVGTextLine (text:string fontFamily:string fontSize:double) {
421
+ return (this.measuredLineSpaced(text fontFamily fontSize 0.0))
422
+ }
423
+
424
+ fn measuredLineSpaced:EVGTextLine (text:string fontFamily:string fontSize:double spacing:double) {
425
+ def m:EVGTextMetrics (measurer.measureText(text fontFamily fontSize))
426
+ def line (new EVGTextLine())
427
+ line.text = text
428
+ line.width = (m.width + (EVGTextEngine.trackingWidth(text spacing)))
429
+ line.ascent = m.ascent
430
+ line.descent = m.descent
431
+ return line
432
+ }
433
+
434
+ ; What `letter-spacing` adds to a run: the extra advance AFTER every
435
+ ; character, the last one included, which is what CSS says and what a
436
+ ; canvas with `letterSpacing` set measures back. Counting the gaps
437
+ ; BETWEEN characters instead would make every measurement here one
438
+ ; space narrower than the ink the painter lays down, and a box sized
439
+ ; from it would clip its own last letter.
440
+ ;
441
+ ; Counted in graphemes, so an emoji or a combining accent is one
442
+ ; character and not the two or four units it is stored in.
443
+ sfn trackingWidth:double (text:string spacing:double) {
444
+ if (spacing == 0.0) {
445
+ return 0.0
446
+ }
447
+ return ((to_double (EVGGrapheme.clusterCount(text))) * spacing)
448
+ }
449
+
450
+ ; Number of lines `text` occupies at `maxWidth` — the wrap and the height
451
+ ; calculation therefore cannot disagree, because it is the same wrap.
452
+ fn lineCount:int (text:string fontFamily:string fontSize:double maxWidth:double) {
453
+ return (this.lineCountSpaced(text fontFamily fontSize maxWidth 0.0))
454
+ }
455
+
456
+ fn lineCountSpaced:int (text:string fontFamily:string fontSize:double maxWidth:double spacing:double) {
457
+ def lines:[EVGTextLine] (this.breakLinesSpaced(text fontFamily fontSize maxWidth spacing))
458
+ return (array_length lines)
459
+ }
460
+
461
+ ; Widest line, for shrink-wrapping a text node to its content.
462
+ fn maxLineWidth:double (text:string fontFamily:string fontSize:double) {
463
+ return (this.maxLineWidthSpaced(text fontFamily fontSize 0.0))
464
+ }
465
+
466
+ fn maxLineWidthSpaced:double (text:string fontFamily:string fontSize:double spacing:double) {
467
+ def lines:[EVGTextLine] (this.breakLinesSpaced(text fontFamily fontSize 0.0 spacing))
468
+ def maxW:double 0.0
469
+ def i:int 0
470
+ while (i < (array_length lines)) {
471
+ def ln:EVGTextLine (itemAt lines i)
472
+ if (ln.width > maxW) {
473
+ maxW = ln.width
474
+ }
475
+ i = (i + 1)
476
+ }
477
+ return maxW
478
+ }
479
+
480
+ ; The narrowest this text can be made without breaking a word — CSS's
481
+ ; min-content. Breaking at every opportunity puts one word per line, so the
482
+ ; widest of those lines is the answer.
483
+ fn minLineWidth:double (text:string fontFamily:string fontSize:double) {
484
+ return (this.minLineWidthSpaced(text fontFamily fontSize 0.0))
485
+ }
486
+
487
+ fn minLineWidthSpaced:double (text:string fontFamily:string fontSize:double spacing:double) {
488
+ def lines:[EVGTextLine] (this.breakLinesSpaced(text fontFamily fontSize 0.001 spacing))
489
+ def maxW:double 0.0
490
+ def i:int 0
491
+ while (i < (array_length lines)) {
492
+ def ln:EVGTextLine (itemAt lines i)
493
+ if (ln.width > maxW) {
494
+ maxW = ln.width
495
+ }
496
+ i = (i + 1)
497
+ }
498
+ return maxW
499
+ }
500
+
501
+ ; How much taller a block is for the gaps between its paragraphs: one
502
+ ; `paragraph-spacing` per break the author typed, and none for the
503
+ ; breaks the box made by wrapping.
504
+ fn paragraphLeading:double (text:string fontFamily:string fontSize:double maxWidth:double spacing:double paragraphSpacing:double) {
505
+ if (paragraphSpacing == 0.0) {
506
+ return 0.0
507
+ }
508
+ def lines:[EVGTextLine] (this.breakLinesSpaced(text fontFamily fontSize maxWidth spacing))
509
+ def breaks:int 0
510
+ def i:int 0
511
+ while (i < (array_length lines)) {
512
+ if ((itemAt lines i).startsParagraph) {
513
+ breaks = (breaks + 1)
514
+ }
515
+ i = (i + 1)
516
+ }
517
+ return ((to_double breaks) * paragraphSpacing)
518
+ }
519
+
520
+ ; Convenience for callers that only want the strings (PDF paint).
521
+ fn breakToStrings:[string] (text:string fontFamily:string fontSize:double maxWidth:double) {
522
+ return (this.breakToStringsSpaced(text fontFamily fontSize maxWidth 0.0))
523
+ }
524
+
525
+ fn breakToStringsSpaced:[string] (text:string fontFamily:string fontSize:double maxWidth:double spacing:double) {
526
+ def out:[string]
527
+ def lines:[EVGTextLine] (this.breakLinesSpaced(text fontFamily fontSize maxWidth spacing))
528
+ def i:int 0
529
+ while (i < (array_length lines)) {
530
+ def ln:EVGTextLine (itemAt lines i)
531
+ push out ln.text
532
+ i = (i + 1)
533
+ }
534
+ return out
535
+ }
536
+ }