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.
- package/CHANGELOG.md +1064 -20
- package/LICENSE +3 -1
- package/README.md +123 -28
- package/dist/Lang.rgr +1188 -290
- package/dist/api.d.ts +2343 -879
- package/dist/api.js +79923 -56921
- package/dist/lib/JSON.rgr +102 -91
- package/dist/lib/Shell.rgr +4 -4
- package/dist/lib/apple/AppleToolchain.rgr +4 -4
- package/dist/lib/apple/README.md +5 -4
- package/dist/lib/apple/apple_test.rgr +1 -1
- package/dist/lib/core/README.md +1 -1
- package/dist/lib/evg/EVG.rgr +12 -0
- package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
- package/dist/lib/evg/EVGA11yTree.rgr +894 -0
- package/dist/lib/evg/EVGBox.rgr +267 -0
- package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
- package/dist/lib/evg/EVGCodepoint.rgr +316 -0
- package/dist/lib/evg/EVGColor.rgr +700 -0
- package/dist/lib/evg/EVGCommands.rgr +177 -0
- package/dist/lib/evg/EVGComponent.rgr +331 -0
- package/dist/lib/evg/EVGComponentTest.rgr +387 -0
- package/dist/lib/evg/EVGConnector.rgr +541 -0
- package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
- package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
- package/dist/lib/evg/EVGEasing.rgr +370 -0
- package/dist/lib/evg/EVGEffectTest.rgr +253 -0
- package/dist/lib/evg/EVGElement.rgr +4914 -0
- package/dist/lib/evg/EVGFixedTest.rgr +289 -0
- package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
- package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
- package/dist/lib/evg/EVGFling.rgr +244 -0
- package/dist/lib/evg/EVGFocus.rgr +400 -0
- package/dist/lib/evg/EVGFocusTest.rgr +399 -0
- package/dist/lib/evg/EVGGradient.rgr +322 -0
- package/dist/lib/evg/EVGGrapheme.rgr +190 -0
- package/dist/lib/evg/EVGGrid.rgr +975 -0
- package/dist/lib/evg/EVGHitTest.rgr +208 -0
- package/dist/lib/evg/EVGHoles.rgr +294 -0
- package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
- package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
- package/dist/lib/evg/EVGHostTree.rgr +834 -0
- package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
- package/dist/lib/evg/EVGImageDecode.rgr +117 -0
- package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
- package/dist/lib/evg/EVGInspect.rgr +883 -0
- package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
- package/dist/lib/evg/EVGJsonTest.rgr +535 -0
- package/dist/lib/evg/EVGLayout.rgr +4220 -0
- package/dist/lib/evg/EVGMeasure.rgr +1267 -0
- package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
- package/dist/lib/evg/EVGPatch.rgr +1721 -0
- package/dist/lib/evg/EVGPatchTest.rgr +692 -0
- package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
- package/dist/lib/evg/EVGReconcile.rgr +226 -0
- package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
- package/dist/lib/evg/EVGReject.rgr +126 -0
- package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
- package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
- package/dist/lib/evg/EVGRuler.rgr +232 -0
- package/dist/lib/evg/EVGRulerTest.rgr +172 -0
- package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
- package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
- package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
- package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
- package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
- package/dist/lib/evg/EVGText.rgr +105 -0
- package/dist/lib/evg/EVGTextEngine.rgr +536 -0
- package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
- package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
- package/dist/lib/evg/EVGToolbar.rgr +1369 -0
- package/dist/lib/evg/EVGTransition.rgr +728 -0
- package/dist/lib/evg/EVGTreeJson.rgr +721 -0
- package/dist/lib/evg/EVGUnit.rgr +554 -0
- package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
- package/dist/lib/evg/EvgApp.rgr +189 -0
- package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
- package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
- package/dist/lib/evg/EvgHost.rgr +451 -0
- package/dist/lib/evg/EvgTest.rgr +91 -0
- package/dist/lib/evg/EvgTraceColor.rgr +236 -0
- package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
- package/dist/lib/evg/EvgTraceFit.rgr +767 -0
- package/dist/lib/evg/EvgTracePath.rgr +435 -0
- package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
- package/dist/lib/evg/EvgViewport.rgr +313 -0
- package/dist/lib/evg/FxDemoDoc.rgr +185 -0
- package/dist/lib/evg/HOSTS.md +227 -0
- package/dist/lib/evg/ISSUES.md +835 -0
- package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
- package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
- package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
- package/dist/lib/evg/PLAN_EVG.md +518 -0
- package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
- package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
- package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
- package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
- package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
- package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
- package/dist/lib/evg/PathBuilder.rgr +333 -0
- package/dist/lib/evg/README.md +1236 -0
- package/dist/lib/evg/SPEC.md +1252 -0
- package/dist/lib/evg/SVGPathParser.rgr +1199 -0
- package/dist/lib/evg/SvgParser.rgr +1736 -0
- package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
- package/dist/lib/evg/VectorShapes.rgr +331 -0
- package/dist/lib/evg/VectorStroke.rgr +107 -0
- package/dist/lib/evg/VectorViewBox.rgr +380 -0
- package/dist/lib/evg/agent/README.md +290 -0
- package/dist/lib/evg/agent/evg_agent.rgr +899 -0
- package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
- package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
- package/dist/lib/evg/agent/fixtures/connector.css +63 -0
- package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
- package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
- package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
- package/dist/lib/evg/agent/fixtures/popover.css +99 -0
- package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
- package/dist/lib/evg/agent/roundtrip.sh +74 -0
- package/dist/lib/evg/agent/smoke.sh +230 -0
- package/dist/lib/evg/android/README.md +97 -0
- package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
- package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
- package/dist/lib/evg/android/androidstubs/App.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
- package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
- package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
- package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
- package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
- package/dist/lib/evg/android/androidstubs/View.kt +113 -0
- package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
- package/dist/lib/evg/apple/README.md +128 -0
- package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
- package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
- package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
- package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
- package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
- package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
- package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
- package/dist/lib/evg/bench/README.md +64 -0
- package/dist/lib/evg/bench/layout-bench.mjs +316 -0
- package/dist/lib/evg/bench/layout-cases.mjs +510 -0
- package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
- package/dist/lib/evg/bin/.gitignore +17 -0
- package/dist/lib/evg/evg_test.rgr +313 -0
- package/dist/lib/evg/gl/README.md +198 -0
- package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
- package/dist/lib/evg/gl/blur-check.mjs +399 -0
- package/dist/lib/evg/gl/boxmodel.json +1 -0
- package/dist/lib/evg/gl/demo.html +46 -0
- package/dist/lib/evg/gl/effect-presets.css +285 -0
- package/dist/lib/evg/gl/effect-presets.js +63 -0
- package/dist/lib/evg/gl/effect-shots.mjs +135 -0
- package/dist/lib/evg/gl/evg-a11y.js +566 -0
- package/dist/lib/evg/gl/evg-binary.js +160 -0
- package/dist/lib/evg/gl/evg-engine.js +296 -0
- package/dist/lib/evg/gl/evg-fx.js +237 -0
- package/dist/lib/evg/gl/evg-gestures.js +209 -0
- package/dist/lib/evg/gl/evg-list.js +167 -0
- package/dist/lib/evg/gl/evg-measure.js +186 -0
- package/dist/lib/evg/gl/evg-textinput.js +303 -0
- package/dist/lib/evg/gl/evg-view.js +144 -0
- package/dist/lib/evg/gl/evg-webgl.js +3942 -0
- package/dist/lib/evg/gl/fx-check.mjs +998 -0
- package/dist/lib/evg/gl/fx-demo.html +166 -0
- package/dist/lib/evg/gl/fx-demo.js +2 -0
- package/dist/lib/evg/gl/fx-serve.mjs +47 -0
- package/dist/lib/evg/gl/gestures-check.mjs +189 -0
- package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
- package/dist/lib/evg/gl/measure-check.mjs +135 -0
- package/dist/lib/evg/gl/rotation-check.mjs +207 -0
- package/dist/lib/evg/gl/shift-check.mjs +113 -0
- package/dist/lib/evg/gl/stroke-check.mjs +168 -0
- package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
- package/dist/lib/evg/gl/view-check.mjs +343 -0
- package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
- package/dist/lib/evg/html/evg-dom.js +318 -0
- package/dist/lib/evg/html/evg-html.js +600 -0
- package/dist/lib/evg/inspect/README.md +415 -0
- package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
- package/dist/lib/evg/inspect/evg-inspect.js +947 -0
- package/dist/lib/evg/inspect/shots/css.png +0 -0
- package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
- package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
- package/dist/lib/evg/inspect/shots/state.png +0 -0
- package/dist/lib/evg/inspect/shots.mjs +226 -0
- package/dist/lib/evg/oracle/css-blur.json +576 -0
- package/dist/lib/evg/oracle/css-box.json +157 -0
- package/dist/lib/evg/oracle/css-timing.json +709 -0
- package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
- package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
- package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
- package/dist/lib/evg/original/EVGColor.clj +310 -0
- package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
- package/dist/lib/evg/original/SVGPath.rgr +627 -0
- package/dist/lib/evg/original/Vec2.crgr +103 -0
- package/dist/lib/evg/ranger.json +9 -0
- package/dist/lib/evg/showcase/README.md +246 -0
- package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
- package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
- package/dist/lib/evg/showcase/build.mjs +651 -0
- package/dist/lib/evg/showcase/pages/album.tsx +30 -0
- package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
- package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
- package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
- package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
- package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
- package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
- package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
- package/dist/lib/evg/showcase/pages/more.tsx +332 -0
- package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
- package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
- package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
- package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
- package/dist/lib/evg/showcase/pages/units.tsx +35 -0
- package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
- package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
- package/dist/lib/evg/showcase/pages/views.tsx +223 -0
- package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
- package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
- package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
- package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
- package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
- package/dist/lib/evg/showcase/themes/more-default.css +128 -0
- package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
- package/dist/lib/evg/showcase/themes/showcase.css +508 -0
- package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
- package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
- package/dist/lib/evg/showcase/themes/views-default.css +15 -0
- package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
- package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
- package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
- package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
- package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
- package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
- package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
- package/dist/lib/evg/web/responsive/README.md +97 -0
- package/dist/lib/evg/web/responsive/build.mjs +90 -0
- package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
- package/dist/lib/evg/web/responsive/index.html +177 -0
- package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
- package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
- package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
- package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
- package/dist/lib/evg/web/tools/minify.mjs +60 -0
- package/dist/lib/evg/web/tracer/build.mjs +87 -0
- package/dist/lib/evg/web/tracer/index.html +2805 -0
- package/dist/lib/evg/web/tracer/sample.png +0 -0
- package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
- package/dist/lib/evgr/Cargo.lock +21 -0
- package/dist/lib/evgr/Cargo.toml +19 -0
- package/dist/lib/evgr/README.md +140 -0
- package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
- package/dist/lib/evgr/bench/compare.mjs +252 -0
- package/dist/lib/evgr/bench/speed.mjs +277 -0
- package/dist/lib/evgr/bin/.gitignore +3 -0
- package/dist/lib/evgr/src/bin/bench.rs +106 -0
- package/dist/lib/evgr/src/bin/smoke.rs +13 -0
- package/dist/lib/evgr/src/grid.rs +730 -0
- package/dist/lib/evgr/src/lib.rs +1003 -0
- package/dist/lib/evgr/src/style.rs +468 -0
- package/dist/lib/evgr/src/text.rs +83 -0
- package/dist/lib/image/BitReader.rgr +171 -0
- package/dist/lib/image/Buffer.rgr +173 -0
- package/dist/lib/image/DCT.rgr +283 -0
- package/dist/lib/image/Deflate.rgr +339 -0
- package/dist/lib/image/HuffmanDecoder.rgr +181 -0
- package/dist/lib/image/ImageBuffer.rgr +706 -0
- package/dist/lib/image/JPEGDecoder.rgr +808 -0
- package/dist/lib/image/PNGDecoder.rgr +544 -0
- package/dist/lib/image/PNGEncoder.rgr +388 -0
- package/dist/lib/image/PPMImage.rgr +224 -0
- package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
- package/dist/lib/image/README.md +33 -0
- package/dist/lib/image/RasterBuffer.rgr +294 -0
- package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
- package/dist/lib/image/WebPDecoder.rgr +433 -0
- package/dist/lib/image/WebPLossless.rgr +1035 -0
- package/dist/lib/image/WebPLossy.rgr +1929 -0
- package/dist/lib/image/ranger.json +9 -0
- package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
- package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
- package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
- package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
- package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
- package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
- package/dist/lib/image/tests/run_webp_tests.sh +39 -0
- package/dist/lib/rust/RsJson.rgr +468 -0
- package/dist/lib/rust/RsPrelude.rgr +1663 -0
- package/dist/lib/shell_test.rgr +7 -7
- package/dist/lib/stdlib.rgr +21 -5
- package/dist/lib/zip/ranger.json +6 -0
- package/dist/rgrc.js +87318 -67610
- package/package.json +1010 -1040
- package/dist/README.md +0 -117
- 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
|
+
}
|