ranger-compiler 3.5.1 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,1252 @@
1
+ # EVG Layout Format Specification
2
+
3
+ **Version:** 1.0
4
+ **Status:** Draft
5
+ **Date:** December 2024
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Introduction](#1-introduction)
10
+ 2. [Document Structure](#2-document-structure)
11
+ 3. [Element Types](#3-element-types)
12
+ 4. [Attributes Reference](#4-attributes-reference)
13
+ 5. [Unit System](#5-unit-system)
14
+ 6. [Layout Algorithm](#6-layout-algorithm)
15
+ 7. [Font Integration](#7-font-integration)
16
+ 8. [Rendering Targets](#8-rendering-targets)
17
+ 9. [Components](#9-components)
18
+ 10. [Examples](#10-examples)
19
+
20
+ ---
21
+
22
+ ## 1. Introduction
23
+
24
+ ### 1.1 Overview
25
+
26
+ EVG (Extended Vector Graphics) is an XML-based declarative layout format designed for creating visual layouts that can be rendered to multiple output formats including PDF, images (PNG, JPEG), video frames, SVG, and HTML/CSS.
27
+
28
+ ### 1.2 Design Goals
29
+
30
+ - **Declarative**: Describe *what* should be rendered, not *how*
31
+ - **Cross-platform**: XML input can be rendered by implementations in any language
32
+ - **Multi-target**: Single source renders to PDF, images, video, HTML, or SVG
33
+ - **CSS-like**: Familiar box model with margins, padding, and borders
34
+ - **Flexible layout**: Flow-based layout with wrapping, alignment, and absolute positioning
35
+ - **Typography-aware**: First-class font support with text measurement
36
+
37
+ ### 1.3 Basic Example
38
+
39
+ ```xml
40
+ <div width="400" height="300" background-color="#ffffff">
41
+ <div padding="20" background-color="#3498db" border-radius="10">
42
+ <span font-size="24" font-family="Helvetica" color="#ffffff">
43
+ Hello, EVG!
44
+ </span>
45
+ </div>
46
+ </div>
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 2. Document Structure
52
+
53
+ ### 2.1 Root Element
54
+
55
+ An EVG document is a well-formed XML document. The root element is typically a `<div>` or `<View>` element that defines the canvas dimensions.
56
+
57
+ ```xml
58
+ <div width="800" height="600" background-color="#ffffff">
59
+ <!-- Child elements -->
60
+ </div>
61
+ ```
62
+
63
+ ### 2.2 Element Hierarchy
64
+
65
+ EVG uses a hierarchical tree structure where:
66
+ - Container elements (`<div>`, `<View>`) can have child elements
67
+ - Leaf elements (`<span>`, `<img>`, `<path>`) typically do not have children
68
+ - Elements inherit certain properties from their parent (e.g., font settings)
69
+
70
+ ### 2.3 Coordinate System
71
+
72
+ - Origin (0, 0) is at the **top-left** corner
73
+ - X-axis increases to the **right**
74
+ - Y-axis increases **downward**
75
+ - All position and dimension values are in logical pixels (can be scaled)
76
+
77
+ ---
78
+
79
+ ## 3. Element Types
80
+
81
+ ### 3.1 Container Elements
82
+
83
+ #### `<div>` / `<View>`
84
+
85
+ A rectangular container that can hold child elements. Supports all layout and styling attributes.
86
+
87
+ ```xml
88
+ <div width="200" height="100" background-color="#f0f0f0" padding="10">
89
+ <!-- children -->
90
+ </div>
91
+ ```
92
+
93
+ **Aliases:** `div`, `View`
94
+
95
+ ---
96
+
97
+ ### 3.2 Text Elements
98
+
99
+ #### `<span>` / `<Label>`
100
+
101
+ Renders text content. Text can be provided as element content or via the `text` attribute.
102
+
103
+ ```xml
104
+ <span font-size="16" font-family="Arial" color="#333333">
105
+ This is text content
106
+ </span>
107
+
108
+ <!-- Or using attribute -->
109
+ <span font-size="16" text="This is text content" />
110
+ ```
111
+
112
+ **Key behaviors:**
113
+ - Text wraps within the parent's available width
114
+ - Height is auto-calculated based on text measurement
115
+ - Supports `inline="true"` for inline-block behavior
116
+ - Preserves whitespace (pre-line behavior)
117
+
118
+ **Aliases:** `span`, `Label`
119
+
120
+ ---
121
+
122
+ ### 3.3 Image Elements
123
+
124
+ #### `<img>` / `<Image>`
125
+
126
+ Displays an image from a URL or embedded source.
127
+
128
+ ```xml
129
+ <img src="https://example.com/image.png" width="200" height="150" />
130
+ <img src="./local/image.jpg" border-radius="10" />
131
+ ```
132
+
133
+ **Attributes:**
134
+ - `src` or `imageUrl`: Image source (URL or file path)
135
+ - Standard dimension and styling attributes apply
136
+
137
+ **Aliases:** `img`, `Image`
138
+
139
+ ---
140
+
141
+ ### 3.4 SVG Path Elements
142
+
143
+ #### `<path>` / `<Path>`
144
+
145
+ Renders an SVG path definition. Used for vector graphics, icons, and shapes.
146
+
147
+ ```xml
148
+ <path
149
+ d="M10,6.5c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S12.2,6.5,10,6.5"
150
+ width="20"
151
+ height="20"
152
+ background-color="#333333"
153
+ viewBox="0 0 20 20"
154
+ />
155
+ ```
156
+
157
+ **Attributes:**
158
+ - `d` or `svgPath`: SVG path data string (M, L, C, Z commands, etc.)
159
+ - `viewBox`: SVG viewBox for scaling ("minX minY width height")
160
+ - `background-color`: Fill color
161
+ - `border-color`: Stroke color
162
+ - `opacity`: Fill and stroke opacity
163
+
164
+ **Aliases:** `path`, `Path`
165
+
166
+ ---
167
+
168
+ ### 3.5 Input Elements (Interactive)
169
+
170
+ For interactive applications (browser rendering):
171
+
172
+ #### `<input>` / `<Input>`
173
+ Single-line text input.
174
+
175
+ #### `<textarea>` / `<TextField>`
176
+ Multi-line text input.
177
+
178
+ ---
179
+
180
+ ## 4. Attributes Reference
181
+
182
+ ### 4.1 Dimension Attributes
183
+
184
+ | Attribute | Type | Description | Default |
185
+ |-----------|------|-------------|---------|
186
+ | `width` | unit | Element width | 100% of parent |
187
+ | `height` | unit | Element height | Auto (content-based) |
188
+ | `innerWidth` | read-only | Computed inner width (after padding/border) | - |
189
+ | `innerHeight` | read-only | Computed inner height (after padding/border) | - |
190
+
191
+ ### 4.2 Position Attributes
192
+
193
+ | Attribute | Type | Description | Effect |
194
+ |-----------|------|-------------|--------|
195
+ | `x` | unit | X position | For absolute positioning |
196
+ | `y` | unit | Y position | For absolute positioning |
197
+ | `left` | unit | Distance from parent's left edge | Enables absolute positioning |
198
+ | `top` | unit | Distance from parent's top edge | Enables absolute positioning |
199
+ | `right` | unit | Distance from parent's right edge | Enables absolute positioning |
200
+ | `bottom` | unit | Distance from parent's bottom edge | Enables absolute positioning |
201
+
202
+ | `position` | string | `relative` (default), `absolute` or `fixed` | See below |
203
+
204
+ **Note:** When `left`, `top`, `right`, or `bottom` is set, the element uses **absolute positioning** and is removed from the flow layout.
205
+
206
+ **`position: fixed`** is out of flow the same way, and differs in two things:
207
+ it is positioned against the **viewport** rather than against its parent — its
208
+ `left`/`top`/`right`/`bottom` and every percentage inside it resolve against the
209
+ page — and **a scroll does not move it**. That is the mode: the content slides
210
+ under the furniture. Both scroll paths honour it, the full layout's
211
+ `applyScroll` and the incremental `scrollOnlyFrom`. It is checked by
212
+ `npm run evg:fixed:test`.
213
+
214
+ `left` and `right` (or `top` and `bottom`) BOTH set with no `width` (or
215
+ `height`) stretches the box between them, CSS 2.1 10.3.7 — that is how a bar
216
+ inset by 16 on each side is written without knowing the page's width. It
217
+ applies to `absolute` as well as `fixed`.
218
+
219
+ One limit: a fixed box inside a container that clips still clips against that
220
+ container. In CSS a fixed box escapes an ancestor's `overflow`; here the clip
221
+ stack is built from the tree, so it does not. Put the bar beside the scroller
222
+ rather than inside it.
223
+
224
+ ### 4.3 Spacing Attributes
225
+
226
+ #### Margin (outer spacing)
227
+
228
+ | Attribute | Type | Description |
229
+ |-----------|------|-------------|
230
+ | `margin` | unit | All sides margin |
231
+ | `margin-left` | unit | Left margin |
232
+ | `margin-right` | unit | Right margin |
233
+ | `margin-top` | unit | Top margin |
234
+ | `margin-bottom` | unit | Bottom margin |
235
+
236
+ #### Padding (inner spacing)
237
+
238
+ | Attribute | Type | Description |
239
+ |-----------|------|-------------|
240
+ | `padding` | unit | All sides padding |
241
+ | `padding-left` | unit | Left padding |
242
+ | `padding-right` | unit | Right padding |
243
+ | `padding-top` | unit | Top padding |
244
+ | `padding-bottom` | unit | Bottom padding |
245
+
246
+ ### 4.4 Layout Attributes
247
+
248
+ | Attribute | Type | Values | Description |
249
+ |-----------|------|--------|-------------|
250
+ | `direction` | string | `row` (default), `column`, `bottom-to-top` | Child layout direction |
251
+ | `align` | string | `left` (default), `center`, `right` | Horizontal alignment |
252
+ | `vertical-align` | string | `top` (default), `center`, `bottom` | Vertical alignment |
253
+ | `inline` | boolean | `true`, `false` | Inline-block behavior |
254
+ | `line-break` | boolean | `true`, `false` | Force line break after element |
255
+ | `overflow` | string | `visible`, `hidden`, `clip`, `page-break` | Overflow behavior |
256
+
257
+ ### 4.5 Visual Attributes
258
+
259
+ #### Colors
260
+
261
+ | Attribute | Type | Description |
262
+ |-----------|------|-------------|
263
+ | `color` | color | Text/foreground color |
264
+ | `background-color` | color | Background fill color |
265
+
266
+ #### Gradients
267
+
268
+ | Attribute | Type | Description |
269
+ |-----------|------|-------------|
270
+ | `linear-gradient` | string | Linear gradient fill (see format below) |
271
+ | `radial-gradient` | string | Radial gradient fill (see format below) |
272
+
273
+ **Linear Gradient Format:**
274
+ ```
275
+ linear-gradient="[angle,] color1 [stop1], color2 [stop2], ..."
276
+ ```
277
+
278
+ - **angle** (optional): Direction in degrees (e.g., `90deg`, `135deg`, `-20deg`)
279
+ - **color**: Any valid color value
280
+ - **stop** (optional): Position as percentage (e.g., `20%`, `80%`)
281
+
282
+ **Examples:**
283
+ ```xml
284
+ <!-- Simple two-color gradient (top to bottom) -->
285
+ <View linear-gradient="#31a6ED, #51C6ED" />
286
+
287
+ <!-- With angle (135 degrees) -->
288
+ <View linear-gradient="135deg, red, yellow 20%, black, blue" />
289
+
290
+ <!-- Multiple colors with stops -->
291
+ <View linear-gradient="90deg, green, blue, red 20%, yellow, black, blue" />
292
+
293
+ <!-- With rgba colors -->
294
+ <View linear-gradient="-20deg, #f2a2f2, #121232 80%, rgba(242, 162, 242, 0.7)" />
295
+ ```
296
+
297
+ **Radial Gradient Format:**
298
+ ```
299
+ radial-gradient="color1 [stop1], color2 [stop2], ..."
300
+ ```
301
+
302
+ Radial gradients radiate from the center outward.
303
+
304
+ **Examples:**
305
+ ```xml
306
+ <!-- Simple two-color radial -->
307
+ <View radial-gradient="red, blue" />
308
+
309
+ <!-- Multiple colors -->
310
+ <View radial-gradient="red, green, blue" />
311
+
312
+ <!-- With color stops -->
313
+ <View radial-gradient="red, green 50%, blue" />
314
+
315
+ <!-- With rgba and multiple stops (halo effect) -->
316
+ <View radial-gradient="white, rgba(255,255,255,0.1) 10%, rgba(255,255,255,0.5) 30%, rgba(0,0,255,0.1)"
317
+ border-radius="50%" />
318
+ ```
319
+
320
+ #### Border
321
+
322
+ | Attribute | Type | Description |
323
+ |-----------|------|-------------|
324
+ | `border` | shorthand | Width, style and colour in any order (`1px solid #cbd5e1`); `none` clears it. The style keyword is accepted and ignored — EVG strokes one way |
325
+ | `border-width` | unit | Border thickness |
326
+ | `border-color` | color | Border color |
327
+ | `border-radius` | unit | Corner radius (supports percentage, e.g., `50%` for circle) |
328
+
329
+ #### Shadow Effects
330
+
331
+ | Attribute | Type | Description |
332
+ |-----------|------|-------------|
333
+ | `shadow-radius` | unit | Box shadow blur radius |
334
+ | `shadow-color` | color | Box shadow color (default: `rgba(0,0,0,0.75)`) |
335
+ | `shadow-offset-x` | unit | Shadow horizontal offset |
336
+ | `shadow-offset-y` | unit | Shadow vertical offset |
337
+ | `shadow-opacity` | float | Shadow opacity (0.0 to 1.0) |
338
+
339
+ **Shadow Examples:**
340
+ ```xml
341
+ <!-- Simple shadow -->
342
+ <View shadow-radius="10px" background-color="#222222" />
343
+
344
+ <!-- Shadow with custom color -->
345
+ <View shadow-radius="2px" shadow-color="rgba(0,0,0,0.2)" />
346
+
347
+ <!-- Shadow on a card-like panel -->
348
+ <View padding="20" background-color="white" border-radius="10"
349
+ shadow-radius="5" shadow-color="rgba(0,0,0,0.1)" />
350
+ ```
351
+
352
+ #### Opacity
353
+
354
+ | Attribute | Type | Description |
355
+ |-----------|------|-------------|
356
+ | `opacity` | float | Element transparency (0.0 = fully transparent, 1.0 = fully opaque) |
357
+
358
+ ### 4.6 Typography Attributes
359
+
360
+ | Attribute | Type | Description |
361
+ |-----------|------|-------------|
362
+ | `font-size` | unit | Text size |
363
+ | `font-family` | string | Font family name |
364
+ | `text` | string | Text content (alternative to element content) |
365
+ | `align` | string | Text alignment (left, center, right) |
366
+
367
+ ### 4.7 Transform Attributes
368
+
369
+ | Attribute | Type | Description |
370
+ |-----------|------|-------------|
371
+ | `rotate` | number | Rotation in degrees |
372
+ | `scale` | number | Scale factor (1.0 = 100%) |
373
+
374
+ ### 4.8 Identification Attributes
375
+
376
+ | Attribute | Type | Description |
377
+ |-----------|------|-------------|
378
+ | `id` | string | Unique identifier for element lookup |
379
+ | `key` | string | Reconciliation identity, scoped to the parent (see below) |
380
+ | `cname` | string | Component name for registration |
381
+
382
+ **`id` and `key` are not the same thing.** `id` is global and outward-facing:
383
+ the hit test reports it, the accessibility tree carries it, and an application
384
+ addresses a control by it, so no two elements may share one. `key` is
385
+ sibling-scoped and inward-facing: it tells `EVGReconcile` that a newly built
386
+ child is the same *logical* child as one already in the live tree, so a rebuild
387
+ can keep the element — and with it, anything the element remembers, a
388
+ transition in flight most of all. Two lists on one page may both key a row
389
+ `"video"`.
390
+
391
+ A child with no key is matched by **position among its unkeyed siblings**, and
392
+ a match also requires the same element type. Reordering an unkeyed list
393
+ therefore keeps the elements but moves the data under them, which is exactly
394
+ what makes a reordered unkeyed list animate wrongly.
395
+
396
+ ---
397
+
398
+ ## 5. Unit System
399
+
400
+ ### 5.1 Supported Units
401
+
402
+ EVG supports multiple unit types for dimension and position values:
403
+
404
+ | Unit | Suffix | Description | Example |
405
+ |------|--------|-------------|---------|
406
+ | Pixels | `px` or none | Absolute pixel values | `100`, `100px` |
407
+ | Percentage | `%` | Percentage of parent's dimension | `50%` |
408
+ | Em | `em` | Relative to font size | `1.5em` |
409
+ | Height-relative Percentage | `hp` | Percentage of parent's height | `50hp` |
410
+ | Fill | `fill` | Flexible fill (like CSS flex: 1) | `fill` |
411
+
412
+ ### 5.2 Unit Resolution
413
+
414
+ Units are resolved during the layout phase in this order:
415
+
416
+ 1. **Pixels (px):** Used directly
417
+ 2. **Percentage (%):** Calculated against parent's `innerWidth`
418
+ 3. **Em (em):** Calculated as `fontSize × value`
419
+ 4. **Height Percentage (hp):** Calculated against parent's `innerHeight`
420
+ 5. **Fill:** Expands to fill available space
421
+
422
+ ### 5.3 Parsing Logic
423
+
424
+ ```
425
+ Function parseUnit(value: string) -> {value: number, type: UnitType}:
426
+ If value ends with "%":
427
+ return {parseFloat(value), PERCENTAGE}
428
+ If value ends with "em":
429
+ return {parseFloat(value), EM}
430
+ If value ends with "px":
431
+ return {parseFloat(value), PIXELS}
432
+ If value ends with "hp":
433
+ return {parseFloat(value), HEIGHT_PERCENTAGE}
434
+ If value equals "fill":
435
+ return {100, FILL}
436
+ Else:
437
+ return {parseFloat(value), PIXELS} // Default to pixels
438
+ ```
439
+
440
+ ---
441
+
442
+ ## 6. Layout Algorithm
443
+
444
+ ### 6.1 Overview
445
+
446
+ EVG uses a **flow-based layout algorithm** similar to CSS block/inline layout:
447
+
448
+ 1. **Parse:** Convert XML to element tree with property values
449
+ 2. **Adjust:** Resolve units to pixel values based on parent dimensions
450
+ 3. **Layout:** Calculate positions using flow algorithm
451
+ 4. **Render:** Draw elements in tree order
452
+
453
+ ### 6.2 Box Model
454
+
455
+ Each element follows the CSS box model:
456
+
457
+ ```
458
+ +------------------------------------------+
459
+ | MARGIN |
460
+ | +------------------------------------+ |
461
+ | | BORDER | |
462
+ | | +------------------------------+ | |
463
+ | | | PADDING | | |
464
+ | | | +------------------------+ | | |
465
+ | | | | | | | |
466
+ | | | | CONTENT | | | |
467
+ | | | | (innerWidth x | | | |
468
+ | | | | innerHeight) | | | |
469
+ | | | +------------------------+ | | |
470
+ | | +------------------------------+ | |
471
+ | +------------------------------------+ |
472
+ +------------------------------------------+
473
+ ```
474
+
475
+ **Dimension calculations:**
476
+
477
+ ```
478
+ innerWidth = width - paddingLeft - paddingRight - (borderWidth × 2)
479
+ innerHeight = height - paddingTop - paddingBottom - (borderWidth × 2)
480
+ totalWidth = width + marginLeft + marginRight
481
+ totalHeight = height + marginTop + marginBottom
482
+ ```
483
+
484
+ ### 6.3 Flow Layout Algorithm
485
+
486
+ ```
487
+ Function layoutChildren(parent, children):
488
+ currentX = parent.paddingLeft
489
+ currentY = parent.paddingTop
490
+ rowHeight = 0
491
+ currentRow = []
492
+
493
+ For each child in children:
494
+ // Resolve child dimensions
495
+ adjustLayoutParams(child, parent)
496
+
497
+ // Check if child uses absolute positioning
498
+ If child.left.is_set OR child.top.is_set OR child.bottom.is_set:
499
+ child.calculated.absolute = true
500
+ positionAbsoluteChild(child, parent)
501
+ Continue
502
+
503
+ // Calculate child's total width
504
+ childTotalWidth = child.width.pixels + child.marginLeft + child.marginRight
505
+
506
+ // Check if we need to wrap to next row
507
+ If currentX + childTotalWidth > parent.innerWidth AND currentRow.length > 0:
508
+ // Apply row alignment
509
+ alignRow(currentRow, parent, rowHeight)
510
+
511
+ // Move to next row
512
+ currentY += rowHeight
513
+ currentX = parent.paddingLeft
514
+ rowHeight = 0
515
+ currentRow = []
516
+
517
+ // Position child
518
+ child.calculated.x = currentX + child.marginLeft
519
+ child.calculated.y = currentY + child.marginTop
520
+
521
+ // Update row tracking
522
+ currentX += childTotalWidth
523
+ rowHeight = max(rowHeight, child.calculated.height)
524
+ currentRow.push(child)
525
+
526
+ // Handle explicit line breaks
527
+ If child.lineBreak.b_value:
528
+ alignRow(currentRow, parent, rowHeight)
529
+ currentY += rowHeight
530
+ currentX = parent.paddingLeft
531
+ rowHeight = 0
532
+ currentRow = []
533
+
534
+ // Handle last row
535
+ alignRow(currentRow, parent, rowHeight)
536
+
537
+ Return currentY + rowHeight + parent.paddingBottom
538
+ ```
539
+
540
+ ### 6.4 Alignment
541
+
542
+ #### Horizontal Alignment (`align`)
543
+
544
+ ```
545
+ Function alignRow(row, parent, rowHeight):
546
+ If parent.align.s_value == "center":
547
+ rowWidth = sum of all children widths
548
+ offset = (parent.innerWidth - rowWidth) / 2
549
+ For each child in row:
550
+ child.calculated.x += offset
551
+
552
+ Else If parent.align.s_value == "right":
553
+ rowWidth = sum of all children widths
554
+ offset = parent.innerWidth - rowWidth
555
+ For each child in row:
556
+ child.calculated.x += offset
557
+ ```
558
+
559
+ #### Vertical Alignment (`vertical-align`)
560
+
561
+ ```
562
+ Function alignRowVertically(row, rowHeight):
563
+ For each child in row:
564
+ If parent.verticalAlign.s_value == "center":
565
+ offset = (rowHeight - child.calculated.height) / 2
566
+ child.calculated.y += offset
567
+
568
+ Else If parent.verticalAlign.s_value == "bottom":
569
+ offset = rowHeight - child.calculated.height
570
+ child.calculated.y += offset
571
+ ```
572
+
573
+ ### 6.5 Absolute Positioning
574
+
575
+ When `left`, `top`, `right`, or `bottom` is set:
576
+
577
+ ```
578
+ Function positionAbsoluteChild(child, parent):
579
+ If child.left.is_set:
580
+ child.calculated.x = child.marginLeft + child.left.pixels
581
+ Else If child.right.is_set:
582
+ child.calculated.x = parent.innerWidth - child.right.pixels - child.width.pixels
583
+
584
+ If child.top.is_set:
585
+ child.calculated.y = child.marginTop + child.top.pixels
586
+ Else If child.bottom.is_set:
587
+ child.calculated.y = parent.innerHeight - child.bottom.pixels - child.height.pixels
588
+ ```
589
+
590
+ ### 6.6 Text Measurement
591
+
592
+ For text elements (`<span>`, `<Label>`), the layout algorithm must measure text to determine height:
593
+
594
+ ```
595
+ Function measureText(text, fontFamily, fontSize, maxWidth) -> {width, height}:
596
+ // Implementation depends on rendering target
597
+ // Must handle:
598
+ // - Multi-line text wrapping at maxWidth
599
+ // - Font metrics (ascent, descent, line height)
600
+ // - Unicode and special characters
601
+ ```
602
+
603
+ ---
604
+
605
+ ## 7. Font Integration
606
+
607
+ ### 7.1 Font Provider Interface
608
+
609
+ Implementations must provide a font provider that supports:
610
+
611
+ ```
612
+ Interface IFontProvider:
613
+ // Register a font with a name
614
+ registerFont(name: string, source: FontSource): void
615
+
616
+ // Get font source by name
617
+ getFont(name: string): FontSource | null
618
+
619
+ // Check if font is available
620
+ hasFont(name: string): boolean
621
+
622
+ // List all registered fonts
623
+ listFonts(): string[]
624
+ ```
625
+
626
+ ### 7.2 Font Sources
627
+
628
+ Fonts can be provided as:
629
+ - **File path:** `"/fonts/MyFont.ttf"` (for file system access)
630
+ - **URL:** `"https://example.com/fonts/MyFont.woff2"`
631
+ - **ArrayBuffer:** Binary font data loaded in memory
632
+ - **Uint8Array:** Binary font data
633
+
634
+ ### 7.3 Font Registration
635
+
636
+ Fonts must be registered before use:
637
+
638
+ ```javascript
639
+ // Node.js example
640
+ EVG.registerFont("CustomFont", "/path/to/font.ttf");
641
+
642
+ // Browser example
643
+ const fontData = await fetch('/fonts/custom.woff2').then(r => r.arrayBuffer());
644
+ EVG.registerFont("CustomFont", fontData);
645
+ ```
646
+
647
+ ### 7.4 Font Fallback
648
+
649
+ When a specified font is not available:
650
+ 1. Check registered fonts
651
+ 2. Fall back to system fonts
652
+ 3. Use default font (implementation-specific, typically "Helvetica" or "Arial")
653
+
654
+ ### 7.5 Font Metrics for Text Measurement
655
+
656
+ Text measurement requires access to font metrics:
657
+
658
+ ```
659
+ Interface ITextMeasurer:
660
+ measureText(text: string, fontFamily: string, fontSize: number): {
661
+ width: number,
662
+ height: number
663
+ }
664
+ ```
665
+
666
+ Different renderers provide their own measurement:
667
+ - **PDF:** Use PDFKit's text measurement
668
+ - **Canvas:** Use CanvasRenderingContext2D.measureText()
669
+ - **Browser:** Use DOM-based measurement with actual fonts
670
+
671
+ ---
672
+
673
+ ## 8. Rendering Targets
674
+
675
+ ### 8.1 Renderer Interface
676
+
677
+ All renderers must implement:
678
+
679
+ ```
680
+ Interface IRenderer:
681
+ // Document lifecycle
682
+ beginDocument(width: number, height: number): void
683
+ endDocument(): void
684
+ addPage(width?: number, height?: number): void
685
+
686
+ // Drawing primitives
687
+ drawRect(x, y, width, height, options): void
688
+ drawRoundedRect(x, y, width, height, radius, options): void
689
+ drawText(text, x, y, options): void
690
+ drawImage(source, x, y, width, height): void
691
+ drawPath(pathData, x, y, width, height, options): void
692
+
693
+ // Gradient rendering
694
+ drawLinearGradient(x, y, width, height, gradient): void
695
+ drawRadialGradient(x, y, width, height, gradient): void
696
+
697
+ // Shadow rendering
698
+ drawShadow(x, y, width, height, shadowOptions): void
699
+
700
+ // State management
701
+ save(): void
702
+ restore(): void
703
+ translate(x, y): void
704
+ rotate(angle): void
705
+ scale(sx, sy): void
706
+ setClipRect(x, y, width, height): void
707
+ setOpacity(opacity): void
708
+
709
+ // Text measurement
710
+ measureText(text, fontFamily, fontSize): {width, height}
711
+ ```
712
+
713
+ ### 8.2 Gradient Rendering
714
+
715
+ #### Linear Gradient Data Structure
716
+
717
+ ```
718
+ Interface LinearGradient:
719
+ angle: number // Rotation angle in degrees (0 = left-to-right)
720
+ stops: GradientStop[] // Color stops
721
+
722
+ Interface GradientStop:
723
+ offset: number // Position from 0.0 to 1.0
724
+ color: string // Color value (hex, rgb, rgba)
725
+ ```
726
+
727
+ **Rendering Algorithm:**
728
+ 1. Parse gradient string to extract angle and color stops
729
+ 2. Calculate gradient start and end points based on angle and bounding box
730
+ 3. Create gradient fill with color stops
731
+ 4. Apply to element background
732
+
733
+ **Example Implementation (Canvas):**
734
+ ```javascript
735
+ function drawLinearGradient(ctx, x, y, width, height, gradient) {
736
+ // Calculate gradient line based on angle
737
+ const angleRad = (gradient.angle - 90) * Math.PI / 180;
738
+ const centerX = x + width / 2;
739
+ const centerY = y + height / 2;
740
+ const length = Math.sqrt(width * width + height * height) / 2;
741
+
742
+ const x1 = centerX - Math.cos(angleRad) * length;
743
+ const y1 = centerY - Math.sin(angleRad) * length;
744
+ const x2 = centerX + Math.cos(angleRad) * length;
745
+ const y2 = centerY + Math.sin(angleRad) * length;
746
+
747
+ const grad = ctx.createLinearGradient(x1, y1, x2, y2);
748
+ gradient.stops.forEach(stop => {
749
+ grad.addColorStop(stop.offset, stop.color);
750
+ });
751
+
752
+ ctx.fillStyle = grad;
753
+ ctx.fillRect(x, y, width, height);
754
+ }
755
+ ```
756
+
757
+ #### Radial Gradient Data Structure
758
+
759
+ ```
760
+ Interface RadialGradient:
761
+ centerX: number // Center X (default: 50% of width)
762
+ centerY: number // Center Y (default: 50% of height)
763
+ radius: number // Radius (default: max dimension / 2)
764
+ stops: GradientStop[] // Color stops
765
+ ```
766
+
767
+ **Rendering Algorithm:**
768
+ 1. Parse gradient string to extract color stops
769
+ 2. Center gradient at element center
770
+ 3. Set radius to cover the element (typically half the diagonal)
771
+ 4. Create radial gradient fill
772
+
773
+ **Example Implementation (Canvas):**
774
+ ```javascript
775
+ function drawRadialGradient(ctx, x, y, width, height, gradient) {
776
+ const centerX = x + width / 2;
777
+ const centerY = y + height / 2;
778
+ const radius = Math.max(width, height) / 2;
779
+
780
+ const grad = ctx.createRadialGradient(
781
+ centerX, centerY, 0, // Inner circle (center point)
782
+ centerX, centerY, radius // Outer circle
783
+ );
784
+
785
+ gradient.stops.forEach(stop => {
786
+ grad.addColorStop(stop.offset, stop.color);
787
+ });
788
+
789
+ ctx.fillStyle = grad;
790
+ ctx.fillRect(x, y, width, height);
791
+ }
792
+ ```
793
+
794
+ ### 8.3 Shadow Rendering
795
+
796
+ #### Shadow Data Structure
797
+
798
+ ```
799
+ Interface ShadowOptions:
800
+ radius: number // Blur radius in pixels
801
+ color: string // Shadow color (default: rgba(0,0,0,0.75))
802
+ offsetX: number // Horizontal offset (default: 0)
803
+ offsetY: number // Vertical offset (default: 0)
804
+ opacity: number // Shadow opacity (default: 1.0)
805
+ ```
806
+
807
+ **Rendering Algorithm:**
808
+ 1. Save current rendering state
809
+ 2. Set shadow properties before drawing the element
810
+ 3. Draw the element (shadow is rendered automatically by the graphics context)
811
+ 4. Restore state
812
+
813
+ **Example Implementation (Canvas):**
814
+ ```javascript
815
+ function drawWithShadow(ctx, drawFn, shadowOptions) {
816
+ ctx.save();
817
+
818
+ ctx.shadowColor = shadowOptions.color || 'rgba(0,0,0,0.75)';
819
+ ctx.shadowBlur = shadowOptions.radius || 0;
820
+ ctx.shadowOffsetX = shadowOptions.offsetX || 0;
821
+ ctx.shadowOffsetY = shadowOptions.offsetY || 0;
822
+
823
+ drawFn(); // Execute the actual drawing
824
+
825
+ ctx.restore();
826
+ }
827
+ ```
828
+
829
+ **Example Implementation (SVG):**
830
+ ```xml
831
+ <defs>
832
+ <filter id="shadow" x="-50%" y="-50%" width="200%" height="200%">
833
+ <feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="rgba(0,0,0,0.75)"/>
834
+ </filter>
835
+ </defs>
836
+ <rect filter="url(#shadow)" ... />
837
+ ```
838
+
839
+ **Example Implementation (CSS):**
840
+ ```css
841
+ box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.75);
842
+ ```
843
+
844
+ ### 8.4 PDF Rendering
845
+
846
+ PDF output uses libraries like PDFKit (Node.js) or jsPDF (browser).
847
+
848
+ **Considerations:**
849
+ - Fonts must be embedded for custom fonts
850
+ - Use actual PDF units (72 DPI by default)
851
+ - Support multi-page documents via `overflow="page-break"`
852
+ - Gradients require native PDF gradient support or rasterization
853
+ - Shadows may need to be simulated with multiple offset shapes
854
+
855
+ ### 8.5 Image Rendering (PNG, JPEG)
856
+
857
+ Image output uses canvas APIs (node-canvas, browser Canvas).
858
+
859
+ **Considerations:**
860
+ - Set pixel ratio for high-DPI output
861
+ - Support transparency (PNG) or no transparency (JPEG)
862
+ - Flatten layers to single raster output
863
+ - Native shadow and gradient support via Canvas API
864
+
865
+ ### 8.6 SVG Rendering
866
+
867
+ SVG output generates an XML SVG document.
868
+
869
+ **Considerations:**
870
+ - Embed fonts as base64 or reference external fonts
871
+ - Convert all elements to SVG equivalents
872
+ - Preserve vector quality (no rasterization)
873
+ - Use `<linearGradient>` and `<radialGradient>` elements in `<defs>`
874
+ - Use `<filter>` with `<feDropShadow>` for shadows
875
+
876
+ ### 8.7 HTML/CSS Rendering
877
+
878
+ HTML output generates semantic HTML with CSS styling.
879
+
880
+ **Considerations:**
881
+ - Choose CSS strategy: inline styles, CSS classes, or CSS-in-JS
882
+ - Use flexbox or absolute positioning for layout
883
+ - Support responsive output with percentage-based dimensions
884
+ - Use CSS `linear-gradient()`, `radial-gradient()` for gradients
885
+ - Use CSS `box-shadow` for shadow effects
886
+
887
+ ---
888
+
889
+ ## 9. Components
890
+
891
+ ### 9.1 Component Definition
892
+
893
+ Components are reusable EVG fragments registered by name:
894
+
895
+ ```xml
896
+ <!-- Define a component -->
897
+ <div cname="Button">
898
+ <div padding="10" background-color="#3498db" border-radius="5">
899
+ <span id="content" color="white" font-size="14" />
900
+ </div>
901
+ </div>
902
+ ```
903
+
904
+ ### 9.2 Component Registration
905
+
906
+ ```javascript
907
+ EVG.registerComponent("Button", buttonXML);
908
+ ```
909
+
910
+ ### 9.3 Component Usage
911
+
912
+ ```xml
913
+ <Button>Click Me</Button>
914
+ ```
915
+
916
+ When parsed, the component's XML replaces the custom element, with content inserted at the `id="content"` slot.
917
+
918
+ ### 9.4 Content Slot
919
+
920
+ Components can define content slots using `id="content"` or `<content>` element:
921
+
922
+ ```xml
923
+ <div cname="Card">
924
+ <div padding="20" background-color="white" shadow-radius="10">
925
+ <div id="content" />
926
+ </div>
927
+ </div>
928
+ ```
929
+
930
+ ---
931
+
932
+ ## 10. Examples
933
+
934
+ ### 10.1 Simple Card
935
+
936
+ ```xml
937
+ <div width="400" height="300" background-color="#ecf0f1">
938
+ <div margin="20" padding="20" background-color="white"
939
+ border-radius="10" shadow-radius="5" shadow-color="rgba(0,0,0,0.1)">
940
+ <span font-size="24" font-family="Helvetica" color="#2c3e50">
941
+ Card Title
942
+ </span>
943
+ <div margin-top="10">
944
+ <span font-size="14" color="#7f8c8d">
945
+ This is the card description text that can wrap to multiple lines.
946
+ </span>
947
+ </div>
948
+ </div>
949
+ </div>
950
+ ```
951
+
952
+ ### 10.2 Horizontal Layout with Alignment
953
+
954
+ ```xml
955
+ <div width="600" height="100" background-color="#34495e"
956
+ align="center" vertical-align="center">
957
+ <div width="80" height="80" background-color="#e74c3c"
958
+ border-radius="10" margin="10" />
959
+ <div width="80" height="80" background-color="#e67e22"
960
+ border-radius="10" margin="10" />
961
+ <div width="80" height="80" background-color="#f1c40f"
962
+ border-radius="10" margin="10" />
963
+ </div>
964
+ ```
965
+
966
+ ### 10.3 Icon with SVG Path
967
+
968
+ ```xml
969
+ <div width="200" height="200" background-color="#ffffff"
970
+ align="center" vertical-align="center">
971
+ <path
972
+ d="M10,6.5c-2.2,0-4,1.8-4,4s1.8,4,4,4s4-1.8,4-4S12.2,6.5,10,6.5M10,13.9c-1.8,0-3.2-1.5-3.2-3.2S8.2,7.4,10,7.4c1.8,0,3.2,1.5,3.2,3.2S11.8,13.9,10,13.9M17.1,5.7l-3.2,0L12.5,3.7c-0.1-0.1-0.2-0.2-0.3-0.2H7.8c-0.1,0-0.3,0.1-0.3,0.2L6.1,5.7H2.9c-1,0-1.7,0.7-1.7,1.7v7.4c0,1,0.8,1.7,1.7,1.7h14.2c1,0,1.7-0.8,1.7-1.7V7.2C18.8,6.2,18.1,5.7,17.1,5.7"
973
+ width="100"
974
+ height="100"
975
+ viewBox="0 0 20 20"
976
+ background-color="#3498db"
977
+ />
978
+ </div>
979
+ ```
980
+
981
+ ### 10.4 Absolute Positioning
982
+
983
+ ```xml
984
+ <div width="400" height="300" background-color="#2c3e50">
985
+ <!-- Centered content -->
986
+ <div width="200" height="100" background-color="white"
987
+ left="100" top="100" border-radius="10">
988
+ <span>Centered Box</span>
989
+ </div>
990
+
991
+ <!-- Bottom-right badge -->
992
+ <div width="50" height="50" background-color="#e74c3c"
993
+ right="20" bottom="20" border-radius="25"
994
+ align="center" vertical-align="center">
995
+ <span font-size="20" color="white">!</span>
996
+ </div>
997
+ </div>
998
+ ```
999
+
1000
+ ### 10.5 Container Component Example
1001
+
1002
+ From `components/container.xml`:
1003
+
1004
+ ```xml
1005
+ <div margin="10" border-radius="5" background-color="#337799"
1006
+ color="white" padding="10">
1007
+ <div font-size="16" color="#ffee77">
1008
+ This is a container component from components/container.xml
1009
+ </div>
1010
+ <div id="content" font-size="12" margin-top="10" />
1011
+ </div>
1012
+ ```
1013
+
1014
+ ---
1015
+
1016
+ ## Appendix A: Color Format
1017
+
1018
+ EVG supports the following color formats:
1019
+
1020
+ | Format | Example | Description |
1021
+ |--------|---------|-------------|
1022
+ | Hex 6-digit | `#FF5733` | RGB in hexadecimal |
1023
+ | Hex 3-digit | `#F53` | Shorthand RGB |
1024
+ | Hex 8-digit | `#FF573388` | RGBA with alpha |
1025
+ | RGB | `rgb(255, 87, 51)` | RGB functional notation |
1026
+ | RGBA | `rgba(255, 87, 51, 0.5)` | RGB with alpha channel |
1027
+ | Named | `red`, `blue`, `white` | CSS named colors |
1028
+
1029
+ ---
1030
+
1031
+ ## Appendix B: SVG Path Commands
1032
+
1033
+ The `d` attribute in `<path>` elements supports standard SVG path commands:
1034
+
1035
+ | Command | Parameters | Description |
1036
+ |---------|------------|-------------|
1037
+ | `M` | x,y | Move to absolute position |
1038
+ | `m` | dx,dy | Move relative |
1039
+ | `L` | x,y | Line to absolute position |
1040
+ | `l` | dx,dy | Line relative |
1041
+ | `H` | x | Horizontal line to absolute X |
1042
+ | `h` | dx | Horizontal line relative |
1043
+ | `V` | y | Vertical line to absolute Y |
1044
+ | `v` | dy | Vertical line relative |
1045
+ | `C` | x1,y1 x2,y2 x,y | Cubic Bézier curve |
1046
+ | `c` | dx1,dy1 dx2,dy2 dx,dy | Cubic Bézier relative |
1047
+ | `S` | x2,y2 x,y | Smooth cubic Bézier |
1048
+ | `Q` | x1,y1 x,y | Quadratic Bézier curve |
1049
+ | `A` | rx ry rotation large-arc sweep x y | Arc |
1050
+ | `Z` | - | Close path |
1051
+
1052
+ ---
1053
+
1054
+ ## Appendix C: Implementation Checklist
1055
+
1056
+ When implementing an EVG renderer in a new language, ensure support for:
1057
+
1058
+ ### Required
1059
+ - [ ] XML parsing with attribute extraction
1060
+ - [ ] Unit value parsing (px, %, em, rem, vw, vh, hp, fill)
1061
+ - [ ] Box model calculations (margin, padding, border)
1062
+ - [ ] Flow layout algorithm with wrapping
1063
+ - [ ] Absolute positioning
1064
+ - [ ] Horizontal and vertical alignment
1065
+ - [ ] Rectangle rendering (with rounded corners)
1066
+ - [ ] Text rendering with font support
1067
+ - [ ] Text measurement for layout calculations
1068
+ - [ ] Basic color parsing (hex, rgb, rgba)
1069
+
1070
+ ### Recommended
1071
+ - [ ] SVG path rendering
1072
+ - [ ] Image rendering
1073
+ - [ ] Opacity and transparency
1074
+ - [ ] Box shadows
1075
+ - [ ] Linear gradients
1076
+ - [ ] Transforms (rotate, scale)
1077
+ - [ ] Component registration and expansion
1078
+
1079
+ ### Optional
1080
+ - [ ] Page breaks for multi-page documents
1081
+ - [ ] Interactive elements (input, textarea)
1082
+ - [ ] Event handling
1083
+ - [ ] Animation support
1084
+
1085
+ ---
1086
+
1087
+ ## Appendix D: Attribute Name Aliases
1088
+
1089
+ EVG supports both camelCase and kebab-case for attribute names:
1090
+
1091
+ | Canonical | Alias |
1092
+ |-----------|-------|
1093
+ | `background-color` | `backgroundColor` |
1094
+ | `border-radius` | `borderRadius` |
1095
+ | `border-width` | `borderWidth` |
1096
+ | `border-color` | `borderColor` |
1097
+ | `font-size` | `fontSize` |
1098
+ | `font-family` | `fontFamily` |
1099
+ | `margin-left` | `marginLeft` |
1100
+ | `margin-right` | `marginRight` |
1101
+ | `margin-top` | `marginTop` |
1102
+ | `margin-bottom` | `marginBottom` |
1103
+ | `padding-left` | `paddingLeft` |
1104
+ | `padding-right` | `paddingRight` |
1105
+ | `padding-top` | `paddingTop` |
1106
+ | `padding-bottom` | `paddingBottom` |
1107
+ | `vertical-align` | `verticalAlign` |
1108
+ | `line-break` | `lineBreak` |
1109
+ | `shadow-radius` | `shadowRadius` |
1110
+ | `shadow-color` | `shadowColor` |
1111
+ | `shadow-offset-x` | `shadowOffsetX` |
1112
+ | `shadow-offset-y` | `shadowOffsetY` |
1113
+ | `linear-gradient` | `linearGradient` |
1114
+ | `radial-gradient` | `radialGradient` |
1115
+
1116
+ ---
1117
+
1118
+ ## Appendix E: Default Values
1119
+
1120
+ | Property | Default Value |
1121
+ |----------|---------------|
1122
+ | `width` | 100% of parent |
1123
+ | `height` | Auto (content-based) |
1124
+ | `font-size` | 14px |
1125
+ | `font-family` | Implementation-specific (typically "Arial" or "Helvetica") |
1126
+ | `color` | #000000 (black) |
1127
+ | `background-color` | transparent |
1128
+ | `opacity` | 1.0 |
1129
+ | `margin` | 0 |
1130
+ | `padding` | 0 |
1131
+ | `border-width` | 0 |
1132
+ | `border-radius` | 0 |
1133
+ | `align` | left |
1134
+ | `vertical-align` | top |
1135
+ | `direction` | row |
1136
+ | `overflow` | visible |
1137
+ | `shadow-color` | rgba(0,0,0,0.75) |
1138
+ | `shadow-radius` | 0 |
1139
+ | `shadow-offset-x` | 0 |
1140
+ | `shadow-offset-y` | 0 |
1141
+
1142
+ ---
1143
+
1144
+ ## Appendix F: Gradient String Parsing
1145
+
1146
+ ### Linear Gradient Parsing Algorithm
1147
+
1148
+ ```
1149
+ Function parseLinearGradient(value: string) -> LinearGradient:
1150
+ tokens = tokenize(value) // Split by comma, respecting parentheses
1151
+ angle = 180 // Default: top to bottom
1152
+ stops = []
1153
+
1154
+ For each token in tokens:
1155
+ token = trim(token)
1156
+
1157
+ // Check if first token is an angle
1158
+ If isAngle(token): // ends with "deg"
1159
+ angle = parseFloat(token)
1160
+ Continue
1161
+
1162
+ // Parse color stop: "color [position]"
1163
+ parts = splitByWhitespace(token)
1164
+ color = parts[0]
1165
+ position = null
1166
+
1167
+ If parts.length > 1 AND isPercentage(parts[1]):
1168
+ position = parseFloat(parts[1]) / 100
1169
+
1170
+ stops.push({color, position})
1171
+
1172
+ // Fill in missing positions (evenly distributed)
1173
+ fillMissingPositions(stops)
1174
+
1175
+ Return {angle, stops}
1176
+
1177
+ Function fillMissingPositions(stops):
1178
+ // First stop defaults to 0, last to 1
1179
+ If stops[0].position is null:
1180
+ stops[0].position = 0
1181
+ If stops[last].position is null:
1182
+ stops[last].position = 1
1183
+
1184
+ // Interpolate missing positions between defined ones
1185
+ For i from 1 to stops.length - 1:
1186
+ If stops[i].position is null:
1187
+ // Find next defined position
1188
+ nextDefined = findNextDefinedPosition(stops, i)
1189
+ prevDefined = findPrevDefinedPosition(stops, i)
1190
+
1191
+ // Linear interpolation
1192
+ range = nextDefined.position - prevDefined.position
1193
+ count = nextDefined.index - prevDefined.index
1194
+ stops[i].position = prevDefined.position + range * (i - prevDefined.index) / count
1195
+ ```
1196
+
1197
+ ### Radial Gradient Parsing Algorithm
1198
+
1199
+ ```
1200
+ Function parseRadialGradient(value: string) -> RadialGradient:
1201
+ tokens = tokenize(value) // Split by comma, respecting parentheses
1202
+ stops = []
1203
+
1204
+ For each token in tokens:
1205
+ token = trim(token)
1206
+
1207
+ // Parse color stop: "color [position]"
1208
+ parts = splitByWhitespace(token)
1209
+ color = parts[0]
1210
+ position = null
1211
+
1212
+ If parts.length > 1 AND isPercentage(parts[1]):
1213
+ position = parseFloat(parts[1]) / 100
1214
+
1215
+ stops.push({color, position})
1216
+
1217
+ // Fill in missing positions
1218
+ fillMissingPositions(stops)
1219
+
1220
+ Return {stops}
1221
+ ```
1222
+
1223
+ ### Example Parsing Results
1224
+
1225
+ **Input:** `"135deg, red, yellow 20%, black, blue"`
1226
+ ```json
1227
+ {
1228
+ "angle": 135,
1229
+ "stops": [
1230
+ {"color": "red", "position": 0},
1231
+ {"color": "yellow", "position": 0.2},
1232
+ {"color": "black", "position": 0.6},
1233
+ {"color": "blue", "position": 1.0}
1234
+ ]
1235
+ }
1236
+ ```
1237
+
1238
+ **Input:** `"red, green 50%, blue"` (radial)
1239
+ ```json
1240
+ {
1241
+ "stops": [
1242
+ {"color": "red", "position": 0},
1243
+ {"color": "green", "position": 0.5},
1244
+ {"color": "blue", "position": 1.0}
1245
+ ]
1246
+ }
1247
+ ```
1248
+
1249
+ ---
1250
+
1251
+ *This specification is maintained as part of the EVG project.*
1252
+