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,447 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; ==============================================================================
|
|
4
|
+
; EVGHostTreeTest — does the fifth list say only what changed?
|
|
5
|
+
; ==============================================================================
|
|
6
|
+
;
|
|
7
|
+
; `EVGHostTree` describes a laid-out tree to a host that keeps nodes, as ops
|
|
8
|
+
; against what it said last time. What has to be true of it, and is asked here:
|
|
9
|
+
;
|
|
10
|
+
; * the first build CREATES every node, parents before children, at the
|
|
11
|
+
; inspector's paths, and every rectangle it reports is a rectangle the
|
|
12
|
+
; display list drew for the same element;
|
|
13
|
+
; * a build over a tree that did not change emits NOTHING;
|
|
14
|
+
; * a colour changes: one UPDATE, PAINT alone; a height changes: the rows
|
|
15
|
+
; below it get GEOMETRY alone;
|
|
16
|
+
; * a keyed reorder through `EVGReconcile` MOVES nodes and remakes none;
|
|
17
|
+
; * a row taken out is one REMOVE; a row added is one CREATE;
|
|
18
|
+
; * a scroll is one SCROLL bit on the container and no op on its children,
|
|
19
|
+
; because their geometry is parent-relative at scroll 0;
|
|
20
|
+
; * text that changes is a TEXT bit, and the lines are the engine's lines;
|
|
21
|
+
; * the JSON carries what a DOM host reads.
|
|
22
|
+
;
|
|
23
|
+
; npm run evg:hosttree:test
|
|
24
|
+
; ==============================================================================
|
|
25
|
+
|
|
26
|
+
Import "EVGElement.rgr"
|
|
27
|
+
Import "EVGUnit.rgr"
|
|
28
|
+
Import "EVGStyleSheet.rgr"
|
|
29
|
+
Import "EVGLayout.rgr"
|
|
30
|
+
Import "EVGDisplayList.rgr"
|
|
31
|
+
Import "EVGReconcile.rgr"
|
|
32
|
+
Import "EVGHostTree.rgr"
|
|
33
|
+
|
|
34
|
+
class EVGHostTreeTest {
|
|
35
|
+
def passed:int 0
|
|
36
|
+
def failed:int 0
|
|
37
|
+
def sheet:EVGStyleSheet (new EVGStyleSheet())
|
|
38
|
+
|
|
39
|
+
sfn m@(main):void () {
|
|
40
|
+
def t (new EVGHostTreeTest())
|
|
41
|
+
t.run()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn ok:void (what:string cond:boolean) {
|
|
45
|
+
if cond {
|
|
46
|
+
passed = (passed + 1)
|
|
47
|
+
print (" PASS " + what)
|
|
48
|
+
} {
|
|
49
|
+
failed = (failed + 1)
|
|
50
|
+
print (" FAIL " + what)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fn eq:void (what:string got:int want:int) {
|
|
55
|
+
this.ok((what + " (" + (to_string got) + " vs " + (to_string want) + ")") (got == want))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
; --- the fixture --------------------------------------------------------------
|
|
59
|
+
; A column of keyed rows with text, and a scroll box with tall content.
|
|
60
|
+
|
|
61
|
+
sfn css:string () {
|
|
62
|
+
return (""
|
|
63
|
+
+ ".page { display: flex; flex-direction: column; width: 300px; height: 400px; gap: 4px; padding: 10px; background-color: #ffffff; }"
|
|
64
|
+
+ ".row { width: 200px; height: 24px; font-size: 12px; color: #111111; border-radius: 4px; }"
|
|
65
|
+
+ ".row-a { background-color: #ff0000; }"
|
|
66
|
+
+ ".row-b { background-color: #00ff00; }"
|
|
67
|
+
+ ".row-c { background-color: #0000ff; }"
|
|
68
|
+
+ ".row-d { background-color: #ffff00; }"
|
|
69
|
+
+ ".scroller { width: 200px; height: 60px; overflow: hidden; background-color: #eeeeee; }"
|
|
70
|
+
+ ".tall { width: 180px; height: 300px; background-color: #cccccc; }")
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
sfn row:EVGElement (name:string) {
|
|
74
|
+
def el (EVGElement.createDiv())
|
|
75
|
+
el.className = ("row row-" + name)
|
|
76
|
+
el.id = ("row-" + name)
|
|
77
|
+
el.key = name
|
|
78
|
+
el.role = "listitem"
|
|
79
|
+
el.textContent = ("Row " + name)
|
|
80
|
+
return el
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
sfn page:EVGElement (names:string) {
|
|
84
|
+
def el (EVGElement.createDiv())
|
|
85
|
+
el.className = "page"
|
|
86
|
+
el.id = "page"
|
|
87
|
+
def i:int 0
|
|
88
|
+
while (i < (strlen names)) {
|
|
89
|
+
el.addChild((EVGHostTreeTest.row((substring names i (i + 1)))))
|
|
90
|
+
i = (i + 1)
|
|
91
|
+
}
|
|
92
|
+
def sc (EVGElement.createDiv())
|
|
93
|
+
sc.className = "scroller"
|
|
94
|
+
sc.id = "scroller"
|
|
95
|
+
sc.key = "scroller"
|
|
96
|
+
def tall (EVGElement.createDiv())
|
|
97
|
+
tall.className = "tall"
|
|
98
|
+
tall.id = "tall"
|
|
99
|
+
sc.addChild(tall)
|
|
100
|
+
el.addChild(sc)
|
|
101
|
+
return el
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn layout:EVGLayout (root:EVGElement) {
|
|
105
|
+
sheet.setViewport(300.0 400.0 false)
|
|
106
|
+
sheet.applyTree(root "")
|
|
107
|
+
def lay (new EVGLayout())
|
|
108
|
+
lay.setPageSize(300.0 400.0)
|
|
109
|
+
lay.layout(root)
|
|
110
|
+
return lay
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fn opsOfKind:int (t:EVGHostTree kind:int) {
|
|
114
|
+
def n:int 0
|
|
115
|
+
def i:int 0
|
|
116
|
+
while (i < (t.opCount())) {
|
|
117
|
+
if (((t.opAt(i)).kind) == kind) {
|
|
118
|
+
n = (n + 1)
|
|
119
|
+
}
|
|
120
|
+
i = (i + 1)
|
|
121
|
+
}
|
|
122
|
+
return n
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fn opFor@(optional):EVGHostOp (t:EVGHostTree path:string) {
|
|
126
|
+
def i:int 0
|
|
127
|
+
while (i < (t.opCount())) {
|
|
128
|
+
def op:EVGHostOp (t.opAt(i))
|
|
129
|
+
if (op.path == path) {
|
|
130
|
+
def found@(optional):EVGHostOp op
|
|
131
|
+
return found
|
|
132
|
+
}
|
|
133
|
+
i = (i + 1)
|
|
134
|
+
}
|
|
135
|
+
def none@(optional):EVGHostOp
|
|
136
|
+
return none
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fn run:void () {
|
|
140
|
+
print "=== EVGHostTree ==="
|
|
141
|
+
sheet.parse((EVGHostTreeTest.css()))
|
|
142
|
+
this.testFirstBuild()
|
|
143
|
+
this.testNothingChanged()
|
|
144
|
+
this.testPaintAlone()
|
|
145
|
+
this.testGeometryBelow()
|
|
146
|
+
this.testKeyedReorder()
|
|
147
|
+
this.testRemoveAndAdd()
|
|
148
|
+
this.testAnotherElementAtTheAddress()
|
|
149
|
+
this.testScrollIsOneBit()
|
|
150
|
+
this.testText()
|
|
151
|
+
this.testJson()
|
|
152
|
+
print ""
|
|
153
|
+
print ("passed = " + (to_string passed) + " failed = " + (to_string failed))
|
|
154
|
+
if (failed > 0) {
|
|
155
|
+
print "FAILURES"
|
|
156
|
+
} {
|
|
157
|
+
print "ALL PASS"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn testFirstBuild:void () {
|
|
162
|
+
print "-- the first build creates everything, at the inspector's paths"
|
|
163
|
+
def root:EVGElement (EVGHostTreeTest.page("abc"))
|
|
164
|
+
def lay:EVGLayout (this.layout(root))
|
|
165
|
+
def t (new EVGHostTree())
|
|
166
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
167
|
+
t.build(root)
|
|
168
|
+
; page + 3 rows + scroller + tall = 6
|
|
169
|
+
this.eq("six nodes" t.count 6)
|
|
170
|
+
this.eq("six creates" t.created 6)
|
|
171
|
+
this.eq("and nothing else" (t.opCount()) 6)
|
|
172
|
+
this.ok("the root is 0" (((t.opAt(0)).path) == "0"))
|
|
173
|
+
this.ok("a keyed row is k:" (((t.opAt(1)).path) == "0/k:a"))
|
|
174
|
+
this.ok("its parent is the root" (((t.opAt(1)).parentPath) == "0"))
|
|
175
|
+
this.ok("the tall box is under the scroller" (((t.opAt(5)).path) == "0/k:scroller/0"))
|
|
176
|
+
this.ok("a row is a listitem" (((t.opAt(1)).role) == "listitem"))
|
|
177
|
+
; Every rectangle is one the display list drew.
|
|
178
|
+
def dl (new EVGDisplayList())
|
|
179
|
+
dl.setTextEngine((lay.getTextEngine()))
|
|
180
|
+
dl.build(root)
|
|
181
|
+
def matched:int 0
|
|
182
|
+
def i:int 0
|
|
183
|
+
while (i < (t.opCount())) {
|
|
184
|
+
def op:EVGHostOp (t.opAt(i))
|
|
185
|
+
def j:int 0
|
|
186
|
+
while (j < (array_length dl.cmds)) {
|
|
187
|
+
def c:EVGDrawCmd (itemAt dl.cmds j)
|
|
188
|
+
if (c.kind == 0) {
|
|
189
|
+
if ((c.x == op.pageX) && (c.y == op.pageY)) {
|
|
190
|
+
if ((c.w == op.w) && (c.h == op.h)) {
|
|
191
|
+
matched = (matched + 1)
|
|
192
|
+
j = (array_length dl.cmds)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
j = (j + 1)
|
|
197
|
+
}
|
|
198
|
+
i = (i + 1)
|
|
199
|
+
}
|
|
200
|
+
this.eq("every node's page rectangle is a rectangle the list drew" matched 6)
|
|
201
|
+
; Parent-relative: the first row sits at the page's padding.
|
|
202
|
+
def a:EVGHostOp (t.opAt(1))
|
|
203
|
+
this.ok("the first row is at the padding, inside its parent" ((a.x == 10.0) && (a.y == 10.0)))
|
|
204
|
+
this.ok("and on the page too, because the page is at 0" ((a.pageX == 10.0) && (a.pageY == 10.0)))
|
|
205
|
+
this.ok("the second row is a row and a gap below" (((t.opAt(2)).y) == 38.0))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fn testNothingChanged:void () {
|
|
209
|
+
print "-- a tree that did not change says nothing"
|
|
210
|
+
def root:EVGElement (EVGHostTreeTest.page("abc"))
|
|
211
|
+
def lay:EVGLayout (this.layout(root))
|
|
212
|
+
def t (new EVGHostTree())
|
|
213
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
214
|
+
t.build(root)
|
|
215
|
+
this.layout(root)
|
|
216
|
+
t.build(root)
|
|
217
|
+
this.eq("no ops" (t.opCount()) 0)
|
|
218
|
+
this.eq("still six nodes" t.count 6)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fn testPaintAlone:void () {
|
|
222
|
+
print "-- a colour is PAINT and nothing else"
|
|
223
|
+
def root:EVGElement (EVGHostTreeTest.page("abc"))
|
|
224
|
+
def lay:EVGLayout (this.layout(root))
|
|
225
|
+
def t (new EVGHostTree())
|
|
226
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
227
|
+
t.build(root)
|
|
228
|
+
def b:EVGElement (root.getChild(1))
|
|
229
|
+
b.setAttribute("background-color" "#123456")
|
|
230
|
+
this.layout(root)
|
|
231
|
+
t.build(root)
|
|
232
|
+
this.eq("one op" (t.opCount()) 1)
|
|
233
|
+
def op:EVGHostOp (t.opAt(0))
|
|
234
|
+
this.ok("an update" (op.kind == 1))
|
|
235
|
+
this.ok("on row b" (op.path == "0/k:b"))
|
|
236
|
+
this.eq("PAINT alone" op.bits 2)
|
|
237
|
+
this.ok("with the new colour" ((op.bgR == 18) && (op.bgG == 52)))
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
fn testGeometryBelow:void () {
|
|
241
|
+
print "-- a height moves the rows below it: GEOMETRY alone"
|
|
242
|
+
def root:EVGElement (EVGHostTreeTest.page("abc"))
|
|
243
|
+
def lay:EVGLayout (this.layout(root))
|
|
244
|
+
def t (new EVGHostTree())
|
|
245
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
246
|
+
t.build(root)
|
|
247
|
+
def a:EVGElement (root.getChild(0))
|
|
248
|
+
a.setAttribute("height" "40px")
|
|
249
|
+
this.layout(root)
|
|
250
|
+
t.build(root)
|
|
251
|
+
; a grew; b, c and the scroller moved down; the tall box did not move
|
|
252
|
+
; inside the scroller. Four ops.
|
|
253
|
+
this.eq("four ops" (t.opCount()) 4)
|
|
254
|
+
def opA@(optional):EVGHostOp (this.opFor(t "0/k:a"))
|
|
255
|
+
def opB@(optional):EVGHostOp (this.opFor(t "0/k:b"))
|
|
256
|
+
def opTall@(optional):EVGHostOp (this.opFor(t "0/k:scroller/0"))
|
|
257
|
+
this.ok("a has an op" (!null? opA))
|
|
258
|
+
this.ok("b has an op" (!null? opB))
|
|
259
|
+
this.ok("the tall box, inside its parent, has none" (null? opTall))
|
|
260
|
+
if opB {
|
|
261
|
+
def ob:EVGHostOp (unwrap opB)
|
|
262
|
+
this.eq("b: GEOMETRY alone" ob.bits 1)
|
|
263
|
+
this.ok("b moved down by 16" (ob.y == 54.0))
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
fn testKeyedReorder:void () {
|
|
268
|
+
print "-- a keyed reorder moves nodes and remakes none"
|
|
269
|
+
def live:EVGElement (EVGHostTreeTest.page("abc"))
|
|
270
|
+
def lay:EVGLayout (this.layout(live))
|
|
271
|
+
def t (new EVGHostTree())
|
|
272
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
273
|
+
t.build(live)
|
|
274
|
+
def next:EVGElement (EVGHostTreeTest.page("cab"))
|
|
275
|
+
def rec (new EVGReconcile())
|
|
276
|
+
rec.reconcile(live next)
|
|
277
|
+
this.layout(live)
|
|
278
|
+
t.build(live)
|
|
279
|
+
this.eq("nothing created" t.created 0)
|
|
280
|
+
this.eq("nothing removed" t.removed 0)
|
|
281
|
+
this.ok("some moved" (t.moved > 0))
|
|
282
|
+
def opC@(optional):EVGHostOp (this.opFor(t "0/k:c"))
|
|
283
|
+
this.ok("c has ops" (!null? opC))
|
|
284
|
+
; The row that moved to the top also moved on the page: a MOVE and a
|
|
285
|
+
; GEOMETRY update, both on the same path.
|
|
286
|
+
def moves:int (this.opsOfKind(t 2))
|
|
287
|
+
this.ok("moves are MOVE ops" (moves == t.moved))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
fn testRemoveAndAdd:void () {
|
|
291
|
+
print "-- a row taken out is one REMOVE; one put in is one CREATE"
|
|
292
|
+
def live:EVGElement (EVGHostTreeTest.page("abc"))
|
|
293
|
+
def lay:EVGLayout (this.layout(live))
|
|
294
|
+
def t (new EVGHostTree())
|
|
295
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
296
|
+
t.build(live)
|
|
297
|
+
def rec (new EVGReconcile())
|
|
298
|
+
rec.reconcile(live (EVGHostTreeTest.page("ac")))
|
|
299
|
+
this.layout(live)
|
|
300
|
+
t.build(live)
|
|
301
|
+
this.eq("one removed" t.removed 1)
|
|
302
|
+
this.eq("none created" t.created 0)
|
|
303
|
+
def gone@(optional):EVGHostOp (this.opFor(t "0/k:b"))
|
|
304
|
+
this.ok("and it is b" (!null? gone))
|
|
305
|
+
if gone {
|
|
306
|
+
this.ok("as a REMOVE" (((unwrap gone).kind) == 3))
|
|
307
|
+
}
|
|
308
|
+
def rec2 (new EVGReconcile())
|
|
309
|
+
rec2.reconcile(live (EVGHostTreeTest.page("acd")))
|
|
310
|
+
this.layout(live)
|
|
311
|
+
t.build(live)
|
|
312
|
+
this.eq("one created" t.created 1)
|
|
313
|
+
this.eq("none removed" t.removed 0)
|
|
314
|
+
def made@(optional):EVGHostOp (this.opFor(t "0/k:d"))
|
|
315
|
+
this.ok("and it is d" (!null? made))
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
fn testAnotherElementAtTheAddress:void () {
|
|
319
|
+
print "-- another element at an address is a REMOVE and a CREATE, its children made with it"
|
|
320
|
+
; Two pages whose first child sits at the same unkeyed address, `0/0`,
|
|
321
|
+
; and is a different thing on each: a row on one, a scroller with a
|
|
322
|
+
; child on the other. The way an app's scenes share a page.
|
|
323
|
+
def one:EVGElement (EVGElement.createDiv())
|
|
324
|
+
one.className = "page"
|
|
325
|
+
one.id = "page"
|
|
326
|
+
def r:EVGElement (EVGHostTreeTest.row("a"))
|
|
327
|
+
r.key = ""
|
|
328
|
+
one.addChild(r)
|
|
329
|
+
def lay:EVGLayout (this.layout(one))
|
|
330
|
+
def t (new EVGHostTree())
|
|
331
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
332
|
+
t.build(one)
|
|
333
|
+
this.eq("the row page: two created" t.created 2)
|
|
334
|
+
|
|
335
|
+
def two:EVGElement (EVGElement.createDiv())
|
|
336
|
+
two.className = "page"
|
|
337
|
+
two.id = "page"
|
|
338
|
+
def sc (EVGElement.createDiv())
|
|
339
|
+
sc.className = "scroller"
|
|
340
|
+
sc.id = "scroller"
|
|
341
|
+
def tall (EVGElement.createDiv())
|
|
342
|
+
tall.className = "tall"
|
|
343
|
+
tall.id = "tall"
|
|
344
|
+
sc.addChild(tall)
|
|
345
|
+
two.addChild(sc)
|
|
346
|
+
this.layout(two)
|
|
347
|
+
t.build(two)
|
|
348
|
+
this.eq("the scroller page: one removed" t.removed 1)
|
|
349
|
+
this.eq("two created" t.created 2)
|
|
350
|
+
this.eq("none updated" t.updated 0)
|
|
351
|
+
; The order a host needs: the old node gone before the new one is put
|
|
352
|
+
; at its address, and the child after its parent.
|
|
353
|
+
def kinds:string ""
|
|
354
|
+
def i:int 0
|
|
355
|
+
while (i < (t.opCount())) {
|
|
356
|
+
def op:EVGHostOp (t.opAt(i))
|
|
357
|
+
if (op.path == "0/0") { kinds = (kinds + (to_string op.kind)) }
|
|
358
|
+
if (op.path == "0/0/0") { kinds = (kinds + "k" + (to_string op.kind)) }
|
|
359
|
+
i = (i + 1)
|
|
360
|
+
}
|
|
361
|
+
this.ok(("REMOVE 0/0, CREATE 0/0, CREATE 0/0/0, in that order (" + kinds + ")") (kinds == "30k0"))
|
|
362
|
+
def made@(optional):EVGHostOp (this.opFor(t "0/0/0"))
|
|
363
|
+
if made {
|
|
364
|
+
this.ok("and the child is the tall one" (((unwrap made).id) == "tall"))
|
|
365
|
+
}
|
|
366
|
+
; And the same tree again says nothing.
|
|
367
|
+
t.build(two)
|
|
368
|
+
this.eq("built again: nothing" (t.opCount()) 0)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
fn testScrollIsOneBit:void () {
|
|
372
|
+
print "-- a scroll is one SCROLL bit on the container, nothing on its children"
|
|
373
|
+
def root:EVGElement (EVGHostTreeTest.page("abc"))
|
|
374
|
+
def lay:EVGLayout (this.layout(root))
|
|
375
|
+
def t (new EVGHostTree())
|
|
376
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
377
|
+
t.build(root)
|
|
378
|
+
def sc:EVGElement (root.getChild(3))
|
|
379
|
+
this.ok("the scroller clips" (sc.clipsContent()))
|
|
380
|
+
sc.scrollTop = 40.0
|
|
381
|
+
this.layout(root)
|
|
382
|
+
t.build(root)
|
|
383
|
+
this.eq("one op" (t.opCount()) 1)
|
|
384
|
+
def op:EVGHostOp (t.opAt(0))
|
|
385
|
+
this.ok("on the scroller" (op.path == "0/k:scroller"))
|
|
386
|
+
this.eq("SCROLL alone" op.bits 16)
|
|
387
|
+
this.ok("carrying the offset" (op.scrollTop == 40.0))
|
|
388
|
+
; The tall box did move on the page — by -40 — and the host tree said
|
|
389
|
+
; nothing about it, because inside its parent it did not.
|
|
390
|
+
def tall:EVGElement (sc.getChild(0))
|
|
391
|
+
this.ok("the child moved on the page" (tall.calculatedY == (sc.calculatedY - 40.0)))
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
fn testText:void () {
|
|
395
|
+
print "-- text is the engine's lines, and a change is TEXT"
|
|
396
|
+
def root:EVGElement (EVGHostTreeTest.page("abc"))
|
|
397
|
+
def lay:EVGLayout (this.layout(root))
|
|
398
|
+
def t (new EVGHostTree())
|
|
399
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
400
|
+
t.build(root)
|
|
401
|
+
def a:EVGHostOp (t.opAt(1))
|
|
402
|
+
this.eq("one line" (array_length a.lines) 1)
|
|
403
|
+
this.ok("the line is the text" (((itemAt a.lines 0)) == "Row a"))
|
|
404
|
+
this.ok("at the padding" (((itemAt a.lineX 0)) == 0.0))
|
|
405
|
+
this.ok("a font and a size" (((strlen a.fontFamily) > 0) && (a.fontSize == 12.0)))
|
|
406
|
+
def b:EVGElement (root.getChild(1))
|
|
407
|
+
b.textContent = "Row b, with a good deal more to say than fits on one line of a two hundred pixel row"
|
|
408
|
+
this.layout(root)
|
|
409
|
+
t.build(root)
|
|
410
|
+
def opB@(optional):EVGHostOp (this.opFor(t "0/k:b"))
|
|
411
|
+
this.ok("b has an op" (!null? opB))
|
|
412
|
+
if opB {
|
|
413
|
+
def ob:EVGHostOp (unwrap opB)
|
|
414
|
+
this.eq("TEXT alone" ob.bits 4)
|
|
415
|
+
def te:EVGTextEngine (lay.getTextEngine())
|
|
416
|
+
def want:int (te.lineCount(b.textContent (b.effectiveFontFamily()) 12.0 200.0))
|
|
417
|
+
this.ok("as many lines as the engine breaks" (((array_length ob.lines) == want) && (want > 1)))
|
|
418
|
+
this.ok("each a line height apart" (((itemAt ob.lineY 1)) == ob.lineH))
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
fn testJson:void () {
|
|
423
|
+
print "-- the JSON carries what a DOM host reads"
|
|
424
|
+
def root:EVGElement (EVGHostTreeTest.page("ab"))
|
|
425
|
+
def lay:EVGLayout (this.layout(root))
|
|
426
|
+
def t (new EVGHostTree())
|
|
427
|
+
t.setTextEngine((lay.getTextEngine()))
|
|
428
|
+
t.build(root)
|
|
429
|
+
def j:string (t.toJson())
|
|
430
|
+
this.ok("a create" ((indexOf j "\"op\":\"create\"") >= 0))
|
|
431
|
+
this.ok("a keyed path" ((indexOf j "\"path\":\"0/k:a\"") >= 0))
|
|
432
|
+
this.ok("a background" ((indexOf j "\"bg\":[255,0,0,1.00]") >= 0))
|
|
433
|
+
this.ok("text lines" ((indexOf j "\"text\":{\"lines\":[[\"Row a\",0.00,0.00]]") >= 0))
|
|
434
|
+
this.ok("a role" ((indexOf j "\"role\":\"listitem\"") >= 0))
|
|
435
|
+
this.ok("a scroll box" ((indexOf j "\"clip\":true") >= 0))
|
|
436
|
+
this.ok("its offset" ((indexOf j "\"scroll\":[0.00,0.00]") >= 0))
|
|
437
|
+
this.ok("the tally" ((indexOf j "\"created\":5") >= 0))
|
|
438
|
+
root.getChild(0).scrollTop = 0.0
|
|
439
|
+
def sc:EVGElement (root.getChild(2))
|
|
440
|
+
sc.scrollTop = 12.0
|
|
441
|
+
this.layout(root)
|
|
442
|
+
t.build(root)
|
|
443
|
+
def j2:string (t.toJson())
|
|
444
|
+
this.ok("an update" ((indexOf j2 "\"op\":\"update\"") >= 0))
|
|
445
|
+
this.ok("with the SCROLL bit" ((indexOf j2 "\"bits\":16") >= 0))
|
|
446
|
+
}
|
|
447
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; ==============================================================================
|
|
4
|
+
; EVGImageDecode - PNG/JPEG bytes to a SoftCanvas ImageBuffer
|
|
5
|
+
; ==============================================================================
|
|
6
|
+
;
|
|
7
|
+
; This began as PptxImageDecode, next to the deck viewer, with a note on it
|
|
8
|
+
; saying it was not PPTX-specific and anything with image bytes could reuse it.
|
|
9
|
+
; The book editor is the second thing that needed it, which is the moment a
|
|
10
|
+
; note like that has to become a move: a picture on a page and a picture on a
|
|
11
|
+
; slide are the same JPEG, and two copies of the sniff-the-first-two-bytes
|
|
12
|
+
; decision would drift.
|
|
13
|
+
;
|
|
14
|
+
; Sniffing rather than trusting a file extension is the point of it - an asset
|
|
15
|
+
; named .jpg is regularly a PNG, and SoftCanvas blits the same pdf_writer
|
|
16
|
+
; ImageBuffer either way.
|
|
17
|
+
; ==============================================================================
|
|
18
|
+
|
|
19
|
+
Import "pkg:image/ImageBuffer.rgr"
|
|
20
|
+
Import "pkg:image/JPEGDecoder.rgr"
|
|
21
|
+
Import "pkg:image/ProgressiveJPEGDecoder.rgr"
|
|
22
|
+
Import "pkg:image/PNGDecoder.rgr"
|
|
23
|
+
|
|
24
|
+
class EVGImageDecode {
|
|
25
|
+
Constructor () {
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
sfn decode@(optional):ImageBuffer (bytes:buffer) {
|
|
29
|
+
def n:int (buffer_length bytes)
|
|
30
|
+
if (n < 8) {
|
|
31
|
+
def __missing@(optional):ImageBuffer
|
|
32
|
+
return __missing
|
|
33
|
+
}
|
|
34
|
+
def b0:int (buffer_get bytes 0)
|
|
35
|
+
def b1:int (buffer_get bytes 1)
|
|
36
|
+
; JPEG SOI
|
|
37
|
+
if ((b0 == 255) && (b1 == 216)) {
|
|
38
|
+
; Baseline and progressive are different decoders, and which one a
|
|
39
|
+
; file needs is in its SOF marker rather than in its name. Sending
|
|
40
|
+
; a progressive JPEG to the baseline decoder is not an error, it is
|
|
41
|
+
; a picture that does not arrive — "some of the photos are simply
|
|
42
|
+
; missing" is a poor way to find out which kind you have.
|
|
43
|
+
if (EVGImageDecode.isProgressive(bytes)) {
|
|
44
|
+
def prog:ProgressiveJPEGDecoder (new ProgressiveJPEGDecoder)
|
|
45
|
+
prog.quiet = true
|
|
46
|
+
def pjim:ImageBuffer (prog.decodeBytes(bytes))
|
|
47
|
+
if ((pjim.width > 0) && (pjim.height > 0)) {
|
|
48
|
+
def hitP@(optional):ImageBuffer pjim
|
|
49
|
+
return hitP
|
|
50
|
+
}
|
|
51
|
+
def __failPJ@(optional):ImageBuffer
|
|
52
|
+
return __failPJ
|
|
53
|
+
}
|
|
54
|
+
def jdec:JPEGDecoder (new JPEGDecoder)
|
|
55
|
+
; A picture is decoded here because something is being DRAWN, not
|
|
56
|
+
; because anybody asked about the JPEG, so the decoder's own
|
|
57
|
+
; narration is noise - and on a native host it is noise on
|
|
58
|
+
; somebody's terminal, twenty-five lines per photograph.
|
|
59
|
+
jdec.quiet = true
|
|
60
|
+
def jim:ImageBuffer (jdec.decodeBytes(bytes))
|
|
61
|
+
if ((jim.width > 0) && (jim.height > 0)) {
|
|
62
|
+
def hit@(optional):ImageBuffer jim
|
|
63
|
+
return hit
|
|
64
|
+
}
|
|
65
|
+
def __failJ@(optional):ImageBuffer
|
|
66
|
+
return __failJ
|
|
67
|
+
}
|
|
68
|
+
; PNG signature 89 50 4E 47
|
|
69
|
+
if ((b0 == 137) && (b1 == 80)) {
|
|
70
|
+
def pdec:PNGDecoder (new PNGDecoder)
|
|
71
|
+
def pim:ImageBuffer (pdec.decodeBytes(bytes))
|
|
72
|
+
; Ask the DECODER what it read, not the buffer it handed back.
|
|
73
|
+
; `decodeBytes` returns a 1x1 image when it gives up, so judging by
|
|
74
|
+
; the result made a genuine 1x1 PNG — a spacer, a placeholder, the
|
|
75
|
+
; thumbnail in a `.fig` — indistinguishable from a failure, and it
|
|
76
|
+
; was rejected. The header's own dimensions stay 0 when nothing
|
|
77
|
+
; parsed, and say 1x1 when the file really is that.
|
|
78
|
+
if ((pdec.width > 0) && (pdec.height > 0)) {
|
|
79
|
+
def hit2@(optional):ImageBuffer pim
|
|
80
|
+
return hit2
|
|
81
|
+
}
|
|
82
|
+
def __failP@(optional):ImageBuffer
|
|
83
|
+
return __failP
|
|
84
|
+
}
|
|
85
|
+
def __missing2@(optional):ImageBuffer
|
|
86
|
+
return __missing2
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
; SOF2 says progressive, SOF0 says baseline, and whichever comes first
|
|
90
|
+
; decides. The scan stops at that marker rather than walking the file:
|
|
91
|
+
; a frame header is in the first few hundred bytes, and past it every
|
|
92
|
+
; 0xFF byte is entropy-coded data that means nothing here.
|
|
93
|
+
sfn isProgressive:boolean (bytes:buffer) {
|
|
94
|
+
def n:int (buffer_length bytes)
|
|
95
|
+
def limit:int n
|
|
96
|
+
if (limit > 65536) {
|
|
97
|
+
limit = 65536
|
|
98
|
+
}
|
|
99
|
+
def i:int 2
|
|
100
|
+
while (i < (limit - 1)) {
|
|
101
|
+
if ((buffer_get bytes i) == 255) {
|
|
102
|
+
def marker:int (buffer_get bytes (i + 1))
|
|
103
|
+
if (marker == 194) {
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
if (marker == 192) {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
if (marker == 193) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
i = (i + 1)
|
|
114
|
+
}
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; EVGImageMeasurer.rgr - Abstract image measurement interface for EVG layout
|
|
4
|
+
|
|
5
|
+
class EVGImageDimensions {
|
|
6
|
+
def width:int 0
|
|
7
|
+
def height:int 0
|
|
8
|
+
def aspectRatio:double 1.0
|
|
9
|
+
def isValid:boolean false
|
|
10
|
+
|
|
11
|
+
Constructor () {
|
|
12
|
+
width = 0
|
|
13
|
+
height = 0
|
|
14
|
+
aspectRatio = 1.0
|
|
15
|
+
isValid = false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
sfn create:EVGImageDimensions (w:int h:int) {
|
|
19
|
+
def d (new EVGImageDimensions())
|
|
20
|
+
d.width = w
|
|
21
|
+
d.height = h
|
|
22
|
+
if (h > 0) {
|
|
23
|
+
d.aspectRatio = ((to_double w) / (to_double h))
|
|
24
|
+
}
|
|
25
|
+
d.isValid = true
|
|
26
|
+
return d
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
; Abstract interface - implementations must override
|
|
31
|
+
class EVGImageMeasurer {
|
|
32
|
+
|
|
33
|
+
fn getImageDimensions:EVGImageDimensions (src:string) {
|
|
34
|
+
; Default implementation - returns invalid dimensions
|
|
35
|
+
; Subclasses should override to provide actual image dimensions
|
|
36
|
+
def dims (new EVGImageDimensions())
|
|
37
|
+
return dims
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn calculateHeightForWidth:double (src:string targetWidth:double) {
|
|
41
|
+
; Calculate height to maintain aspect ratio for given width
|
|
42
|
+
def dims:EVGImageDimensions (this.getImageDimensions(src))
|
|
43
|
+
if dims.isValid {
|
|
44
|
+
return (targetWidth / dims.aspectRatio)
|
|
45
|
+
}
|
|
46
|
+
; Default to square if we can't determine aspect ratio
|
|
47
|
+
return targetWidth
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fn calculateWidthForHeight:double (src:string targetHeight:double) {
|
|
51
|
+
; Calculate width to maintain aspect ratio for given height
|
|
52
|
+
def dims:EVGImageDimensions (this.getImageDimensions(src))
|
|
53
|
+
if dims.isValid {
|
|
54
|
+
return (targetHeight * dims.aspectRatio)
|
|
55
|
+
}
|
|
56
|
+
; Default to square if we can't determine aspect ratio
|
|
57
|
+
return targetHeight
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn calculateFitDimensions:EVGImageDimensions (src:string maxWidth:double maxHeight:double) {
|
|
61
|
+
; Calculate dimensions to fit within box while maintaining aspect ratio
|
|
62
|
+
def dims:EVGImageDimensions (this.getImageDimensions(src))
|
|
63
|
+
if (dims.isValid == false) {
|
|
64
|
+
; Default to max size if we can't determine aspect ratio
|
|
65
|
+
return (EVGImageDimensions.create((to_int maxWidth) (to_int maxHeight)))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def scaleW:double (maxWidth / (to_double dims.width))
|
|
69
|
+
def scaleH:double (maxHeight / (to_double dims.height))
|
|
70
|
+
def scale:double scaleW
|
|
71
|
+
if (scaleH < scaleW) {
|
|
72
|
+
scale = scaleH
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
def newW:int (to_int ((to_double dims.width) * scale))
|
|
76
|
+
def newH:int (to_int ((to_double dims.height) * scale))
|
|
77
|
+
|
|
78
|
+
return (EVGImageDimensions.create(newW newH))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
; Simple implementation that always returns invalid (for testing without images)
|
|
83
|
+
class SimpleImageMeasurer {
|
|
84
|
+
Extends(EVGImageMeasurer)
|
|
85
|
+
; Uses default implementation - returns invalid dimensions
|
|
86
|
+
}
|