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,1674 @@
1
+ # EVG Rendering System - Development Plan
2
+
3
+ ## Implementation Status Summary
4
+
5
+ This section summarizes what is currently implemented in the PDF renderer vs. what's defined in the EVG SPEC (`lib/evg/SPEC.md`).
6
+
7
+ ### ✅ IMPLEMENTED (Working in PDF Renderer)
8
+
9
+ | Feature | Status | Notes |
10
+ |---------|--------|-------|
11
+ | **Elements** | | |
12
+ | `<View>` / `<div>` | ✅ Full | Container element |
13
+ | `<Label>` / `<span>` | ✅ Full | Text element |
14
+ | `<Image>` / `<img>` | ✅ Full | JPEG with EXIF orientation, resize |
15
+ | `<Path>` / `<path>` | ✅ Full | SVG path rendering |
16
+ | `<Rect>` | ✅ Full | Rectangle element |
17
+ | `<Print>`, `<Section>`, `<Page>` | ✅ Full | Multi-page document structure |
18
+ | **Dimensions** | | |
19
+ | `width`, `height` | ✅ Full | px, %, em, hp, fill units |
20
+ | `minWidth`, `minHeight` | ✅ Full | |
21
+ | `maxWidth`, `maxHeight` | ✅ Full | |
22
+ | **Spacing** | | |
23
+ | `margin` (all sides) | ✅ Full | margin-top/right/bottom/left |
24
+ | `padding` (all sides) | ✅ Full | padding-top/right/bottom/left |
25
+ | **Position** | | |
26
+ | `left`, `top`, `right`, `bottom` | ✅ Full | Absolute positioning |
27
+ | `position: absolute/relative` | ✅ Full | |
28
+ | **Layout** | | |
29
+ | `display: flex/block` | ✅ Full | |
30
+ | `flexDirection` | ✅ Full | row, column |
31
+ | `justifyContent` | ✅ Full | flex-start, center, flex-end, space-between |
32
+ | `alignItems` | ✅ Full | flex-start, center, flex-end |
33
+ | `gap` | ✅ Full | Flexbox gap |
34
+ | `align` | ✅ Full | Horizontal alignment |
35
+ | `verticalAlign` | ✅ Full | Vertical alignment |
36
+ | `overflow: page-break` | ✅ Full | Multi-page content flow |
37
+ | **Visual** | | |
38
+ | `backgroundColor` | ✅ Full | Solid colors |
39
+ | `color` | ✅ Full | Text color |
40
+ | `borderWidth` | ✅ Full | All sides |
41
+ | `borderColor` | ✅ Full | |
42
+ | `borderRadius` | ✅ Full | Rounded corners |
43
+ | `clipPath` | ✅ Full | `circle`, `ellipse` clipping |
44
+ | **Typography** | | |
45
+ | `fontSize` | ✅ Full | px, em units |
46
+ | `fontFamily` | ✅ Full | TrueType fonts with Unicode support |
47
+ | `fontWeight` | ✅ Partial | Bold via font name (e.g., "Noto Sans Bold") |
48
+ | `textAlign` | ✅ Full | left, center, right |
49
+ | `lineHeight` | ✅ Full | Line spacing multiplier |
50
+ | **Images** | | |
51
+ | `src` / `imageUrl` | ✅ Full | Local file paths |
52
+ | EXIF Orientation | ✅ Full | Auto-rotate based on camera orientation |
53
+ | Image Resize | ✅ Full | `maxImageSize`, `imageQuality` on Print |
54
+ | **SVG Paths** | | |
55
+ | `d` / `svgPath` | ✅ Full | M, L, C, Q, Z commands |
56
+ | `viewBox` | ✅ Full | SVG viewBox scaling |
57
+ | `fill` / `fillColor` | ✅ Full | |
58
+ | `stroke` / `strokeColor` | ✅ Partial | |
59
+
60
+ ### ⚠️ PARSED BUT NOT RENDERED (In EVGElement but not used by PDF renderer)
61
+
62
+ | Feature | Status | Notes |
63
+ |---------|--------|-------|
64
+ | `opacity` | ⚠️ Parsed | Parsed in JSXToEVG.rgr, **NOT rendered in PDF** |
65
+ | `rotate` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
66
+ | `scale` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
67
+ | `shadowRadius` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
68
+ | `shadowColor` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
69
+ | `shadowOffsetX` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
70
+ | `shadowOffsetY` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
71
+ | `overflow` | ⚠️ Defined | Defined in EVGElement.rgr, **NOT parsed or rendered** |
72
+
73
+ ### ❌ NOT IMPLEMENTED (In SPEC but missing from implementation)
74
+
75
+ | Feature | Priority | Notes |
76
+ |---------|----------|-------|
77
+ | **Transforms** | | |
78
+ | `rotate` | HIGH | Rotation in degrees - needs PDF transformation matrix |
79
+ | `scale` | HIGH | Scale factor - needs PDF transformation matrix |
80
+ | `transform` | MEDIUM | CSS-style transform string |
81
+ | **Gradients** | | |
82
+ | `linear-gradient` | HIGH | PDF Type 2 (Axial) shading pattern |
83
+ | `radial-gradient` | MEDIUM | PDF Type 3 (Radial) shading pattern |
84
+ | **Shadows** | | |
85
+ | `shadow-radius` | MEDIUM | Box shadow blur - complex in PDF |
86
+ | `shadow-color` | MEDIUM | Shadow color |
87
+ | `shadow-offset-x/y` | MEDIUM | Shadow position offset |
88
+ | **Visual** | | |
89
+ | `opacity` | HIGH | PDF ExtGState transparency |
90
+ | `overflow: hidden` | HIGH | Clip children to View bounds - needs PDF clipping rect |
91
+ | `overflow: scroll` | LOW | Not applicable to PDF |
92
+ | **Image Features** | | |
93
+ | `viewBox` on Image | HIGH | Crop/zoom into image region (NEW) |
94
+ | `backgroundImage` | HIGH | View with background image (NEW) |
95
+ | `backgroundViewBox` | MEDIUM | Crop background image (NEW) |
96
+ | `backgroundSize` | MEDIUM | cover, contain, stretch (NEW) |
97
+ | **New Elements** | | |
98
+ | `<Layer>` | HIGH | Stacking layer for overlays (NEW) |
99
+ | **New Renderers** | | |
100
+ | HTML Renderer | **HIGHEST** | Unit testing, fast preview, development iteration |
101
+ | **Interactive** | | |
102
+ | `<input>` | LOW | For web renderer only |
103
+ | `<textarea>` | LOW | For web renderer only |
104
+
105
+ ### Implementation Complexity
106
+
107
+ | Feature | PDF Implementation Complexity | Notes |
108
+ |---------|-------------------------------|-------|
109
+ | `opacity` | Low | ExtGState with `ca` and `CA` operators |
110
+ | `rotate` | Medium | PDF `cm` transformation matrix |
111
+ | `scale` | Medium | PDF `cm` transformation matrix |
112
+ | `overflow: hidden` | Low | PDF `re ... W n` clipping rectangle |
113
+ | `linear-gradient` | Medium | PDF Shading Pattern Type 2 |
114
+ | `radial-gradient` | Medium-High | PDF Shading Pattern Type 3 |
115
+ | `shadow` | High | No native PDF shadow - must fake with blurred shapes |
116
+ | `viewBox` on Image | Medium | Calculate source rect, use PDF image matrix |
117
+ | `backgroundImage` | Medium | Draw image before content, use clipping |
118
+ | `Layer` | Low | Just render order + optional blend mode |
119
+
120
+ ---
121
+
122
+ ## Overview
123
+
124
+ This document outlines the plan for enhancing the EVG (Element Visual Graph) rendering system with:
125
+ 1. **HTML Renderer** - Fast HTML/CSS output for unit testing and live preview (HIGH PRIORITY)
126
+ 2. **Web-based Visual Debugger** - Preview and debug layouts in browser before PDF/JPEG export
127
+ 3. **New Visual Features** - Gradients, layers, enhanced image handling
128
+ 4. **Metadata System** - Image EXIF data integration, GPS coordinates
129
+ 5. **Map Components** - GPS-based image positioning
130
+
131
+ ---
132
+
133
+ ## Architecture
134
+
135
+ ```
136
+ ┌─────────────────────────────────────────────────────────────────────┐
137
+ │ TSX/JSX Source │
138
+ │ <Print><Page><View>...<Image src="photo.jpg"/>...</View></Page> │
139
+ └─────────────────────────────────────────────────────────────────────┘
140
+ │
141
+ ▼
142
+ ┌─────────────────────────────────────────────────────────────────────┐
143
+ │ Component Engine │
144
+ │ (gallery/pdf_writer/src/jsx/) │
145
+ │ Parses TSX → EVG Element Tree with metadata │
146
+ └─────────────────────────────────────────────────────────────────────┘
147
+ │
148
+ ▼
149
+ ┌─────────────────────────────────────────────────────────────────────┐
150
+ │ EVG Element Tree │
151
+ │ (lib/evg/EVGElement.rgr) │
152
+ │ - Auto-generated IDs / User-defined IDs │
153
+ │ - Image metadata (EXIF, GPS) attached to elements │
154
+ │ - Gradient definitions, Layer stacking │
155
+ └─────────────────────────────────────────────────────────────────────┘
156
+ │
157
+ ┌─────────────┼─────────────┐
158
+ ▼ ▼ ▼
159
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
160
+ │ Web Renderer │ │ PDF Renderer │ │ JPEG Renderer│
161
+ │ (HTML5) │ │ (existing) │ │ (existing) │
162
+ │ Debug UI │ │ │ │ │
163
+ └──────────────┘ └──────────────┘ └──────────────┘
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Phase 0: HTML Renderer (HIGH PRIORITY)
169
+
170
+ ### 0.1 Why HTML First?
171
+
172
+ The HTML renderer should be implemented **before** enhancing the PDF renderer because:
173
+
174
+ 1. **Unit Testing** - HTML output can be parsed and validated programmatically
175
+ 2. **Fast Preview** - No PDF generation overhead, instant browser preview
176
+ 3. **Debugging** - Browser DevTools for inspecting layout issues
177
+ 4. **Reference Implementation** - Verify layout algorithm correctness before PDF
178
+ 5. **Development Speed** - Iterate quickly on new features
179
+
180
+ ### 0.2 HTML/CSS vs SVG vs Canvas
181
+
182
+ | Approach | Pros | Cons | Best For |
183
+ |----------|------|------|----------|
184
+ | **HTML/CSS (DIV)** | Native flexbox, easy debugging, testable | Font metrics differ from PDF | Unit tests, preview |
185
+ | **SVG** | Vector, precise positioning, clipPath support | No text wrapping, complex layout | Icons, paths |
186
+ | **Canvas** | Pixel-perfect, closest to PDF | Not inspectable, no DOM | Final preview |
187
+
188
+ **Recommendation:** Start with **HTML/CSS** for testing, add **Canvas** later for pixel-accurate preview.
189
+
190
+ ### 0.3 Feature Comparison: HTML vs PDF
191
+
192
+ | Feature | HTML/CSS Support | Notes |
193
+ |---------|------------------|-------|
194
+ | **Layout** | | |
195
+ | Flexbox | ✅ Native | `display: flex` |
196
+ | Absolute positioning | ✅ Native | `position: absolute` |
197
+ | Margin/Padding | ✅ Native | CSS box model |
198
+ | `overflow: hidden` | ✅ Native | CSS `overflow: hidden` |
199
+ | `overflow: page-break` | ✅ Supported | Use layout data to split into page divs |
200
+ | **Text & Fonts** | | |
201
+ | Font metrics | ✅ Supported | Use same TTF parser data as PDF renderer |
202
+ | Text measurement | ✅ Supported | Pre-computed from TTF or Canvas API |
203
+ | TrueType fonts | ✅ Supported | `@font-face` with same TTF files |
204
+ | **Visual** | | |
205
+ | `backgroundColor` | ✅ Native | CSS `background-color` |
206
+ | `borderRadius` | ✅ Native | CSS `border-radius` |
207
+ | `opacity` | ✅ Native | CSS `opacity` |
208
+ | `linear-gradient` | ✅ Native | CSS `linear-gradient()` |
209
+ | `radial-gradient` | ✅ Native | CSS `radial-gradient()` |
210
+ | `shadow` | ✅ Native | CSS `box-shadow` |
211
+ | CMYK colors | ✅ Converted | CMYK→RGB math conversion |
212
+ | **Transforms** | | |
213
+ | `rotate` | ✅ Native | CSS `transform: rotate()` |
214
+ | `scale` | ✅ Native | CSS `transform: scale()` |
215
+ | **Images** | | |
216
+ | `<Image>` | ✅ Native | `<img>` tag |
217
+ | `viewBox` (crop) | ✅ CSS | `object-fit` + `object-position` |
218
+ | `backgroundImage` | ✅ Native | CSS `background-image` |
219
+ | EXIF orientation | ⚠️ Browser | Modern browsers auto-rotate |
220
+ | **Paths** | | |
221
+ | SVG `<Path>` | ✅ Inline SVG | Embed `<svg>` element |
222
+ | `clipPath` | ✅ CSS/SVG | `clip-path: circle()` or SVG `<clipPath>` |
223
+ | Custom SVG paths | ✅ SVG | Inline SVG with `<clipPath>` element |
224
+ | **Multi-page** | | |
225
+ | Multiple pages | ✅ Supported | Each page as `<div class="evg-page">` |
226
+ | Page breaks | ✅ Supported | Layout engine provides page assignments |
227
+
228
+ ### 0.4 Font Rendering: CSS vs PDF Attributes
229
+
230
+ To ensure identical text rendering between HTML and PDF, we must understand which attributes affect text measurement and rendering in each system.
231
+
232
+ #### 0.4.1 Text Measurement Attributes Comparison
233
+
234
+ | Attribute | CSS Property | PDF Operator/Property | Affects Measurement? | Notes |
235
+ |-----------|--------------|----------------------|---------------------|-------|
236
+ | **Font Selection** | | | | |
237
+ | Font family | `font-family` | `/FontName Tf` | ✅ Yes | Must use same TTF file |
238
+ | Font size | `font-size` | `fontSize Tf` | ✅ Yes | Direct scaling factor |
239
+ | Font weight | `font-weight` | Different font file | ✅ Yes | Bold = different glyphs |
240
+ | Font style | `font-style` | Different font file | ✅ Yes | Italic = different glyphs |
241
+ | **Spacing** | | | | |
242
+ | Letter spacing | `letter-spacing` | `Tc` (char spacing) | ✅ Yes | Added to each glyph advance |
243
+ | Word spacing | `word-spacing` | `Tw` (word spacing) | ✅ Yes | Added to space character |
244
+ | Line height | `line-height` | `TL` (leading) | ✅ Yes | Vertical spacing between lines |
245
+ | **Rendering** | | | | |
246
+ | Text color | `color` | `rg` / `RG` (fill) | ❌ No | Visual only |
247
+ | Text shadow | `text-shadow` | N/A (simulate) | ❌ No | Visual only |
248
+ | Text decoration | `text-decoration` | Manual lines | ❌ No | Visual only |
249
+ | **Transform** | | | | |
250
+ | Text scale X | `transform: scaleX()` | `Tm` matrix | ⚠️ Complex | Horizontal scaling |
251
+ | Text rotation | `transform: rotate()` | `Tm` matrix | ⚠️ Complex | Rotation matrix |
252
+ | **Alignment** | | | | |
253
+ | Text align | `text-align` | Manual positioning | ❌ No | Layout, not measurement |
254
+ | Vertical align | `vertical-align` | Manual positioning | ❌ No | Layout, not measurement |
255
+
256
+ #### 0.4.2 TrueType Font Tables for Measurement
257
+
258
+ | Table | Purpose | Used For |
259
+ |-------|---------|----------|
260
+ | `head` | Font header | `unitsPerEm` - scaling factor |
261
+ | `hhea` | Horizontal header | `ascender`, `descender`, `lineGap` |
262
+ | `hmtx` | Horizontal metrics | Glyph advance widths |
263
+ | `cmap` | Character map | Unicode → Glyph ID mapping |
264
+ | `kern` | Kerning (optional) | Pair-wise spacing adjustments |
265
+ | `GPOS` | Glyph positioning (optional) | Advanced kerning, ligatures |
266
+ | `OS/2` | OS/2 metrics | `sTypoAscender`, `sTypoDescender`, `sxHeight` |
267
+
268
+ #### 0.4.3 Text Width Calculation Formula
269
+
270
+ ```
271
+ textWidth = Σ (glyphAdvance[char] + letterSpacing) × (fontSize / unitsPerEm)
272
+ + (wordCount - 1) × wordSpacing
273
+
274
+ Where:
275
+ - glyphAdvance[char] = from hmtx table
276
+ - letterSpacing = CSS letter-spacing / PDF Tc
277
+ - fontSize = CSS font-size / PDF Tf size
278
+ - unitsPerEm = from head table (typically 1000 or 2048)
279
+ - wordSpacing = CSS word-spacing / PDF Tw
280
+ ```
281
+
282
+ #### 0.4.4 Line Height Calculation
283
+
284
+ ```
285
+ lineHeight = (ascender - descender + lineGap) × (fontSize / unitsPerEm) × lineHeightMultiplier
286
+
287
+ CSS:
288
+ line-height: 1.5; /* multiplier */
289
+ line-height: 24px; /* absolute */
290
+
291
+ PDF:
292
+ TL operator sets leading (line spacing)
293
+ T* moves to next line using TL value
294
+ ```
295
+
296
+ #### 0.4.5 Critical CSS Properties for Matching PDF
297
+
298
+ ```css
299
+ /* Reset browser defaults that affect text measurement */
300
+ .evg-text {
301
+ /* Font selection - MUST match PDF font exactly */
302
+ font-family: 'Noto Sans', sans-serif;
303
+ font-size: 16px;
304
+ font-weight: 400; /* normal */
305
+ font-style: normal;
306
+
307
+ /* Spacing - affects measurement */
308
+ letter-spacing: 0; /* or specific value */
309
+ word-spacing: 0; /* or specific value */
310
+ line-height: 1.2; /* must match PDF TL calculation */
311
+
312
+ /* Text rendering hints */
313
+ text-rendering: geometricPrecision; /* prioritize accurate positioning */
314
+ -webkit-font-smoothing: antialiased;
315
+
316
+ /* Disable browser adjustments */
317
+ font-kerning: normal; /* or 'none' to disable */
318
+ font-feature-settings: normal;
319
+ text-size-adjust: 100%; /* prevent mobile scaling */
320
+
321
+ /* Whitespace handling */
322
+ white-space: pre-wrap; /* preserve spaces, wrap lines */
323
+ word-break: break-word;
324
+
325
+ /* Box model for text containers */
326
+ margin: 0;
327
+ padding: 0;
328
+ border: 0;
329
+ }
330
+ ```
331
+
332
+ #### 0.4.6 PDF Text Operators Reference
333
+
334
+ | Operator | Syntax | Description | CSS Equivalent |
335
+ |----------|--------|-------------|----------------|
336
+ | `Tf` | `/FontName size Tf` | Select font and size | `font-family`, `font-size` |
337
+ | `Tc` | `spacing Tc` | Character spacing | `letter-spacing` |
338
+ | `Tw` | `spacing Tw` | Word spacing | `word-spacing` |
339
+ | `TL` | `leading TL` | Line leading (height) | `line-height` |
340
+ | `Tr` | `mode Tr` | Render mode (fill/stroke) | `color`, `-webkit-text-stroke` |
341
+ | `Ts` | `rise Ts` | Text rise (baseline shift) | `vertical-align` |
342
+ | `Tm` | `a b c d e f Tm` | Text matrix (transform) | `transform` |
343
+ | `Tj` | `(text) Tj` | Show text | N/A |
344
+ | `TJ` | `[(text) kern (text)] TJ` | Show text with positioning | `letter-spacing` per char |
345
+ | `T*` | `T*` | Move to next line | N/A (implicit in HTML) |
346
+
347
+ #### 0.4.7 Verification Test Cases
348
+
349
+ ```ranger
350
+ class FontMetricsTest {
351
+ fn testSingleCharWidth:boolean () {
352
+ ; Measure single character in both systems
353
+ def font "Noto Sans"
354
+ def size 16.0
355
+ def char "A"
356
+
357
+ def pdfWidth (pdfMeasureText char font size)
358
+ def htmlWidth (htmlMeasureText char font size)
359
+
360
+ ; Allow 0.1px tolerance for rounding
361
+ assert (abs(pdfWidth - htmlWidth) < 0.1)
362
+ return true
363
+ }
364
+
365
+ fn testMultilineHeight:boolean () {
366
+ ; Verify line height matches
367
+ def text "Line 1\nLine 2\nLine 3"
368
+ def font "Noto Sans"
369
+ def size 16.0
370
+ def lineHeight 1.5
371
+
372
+ def pdfHeight (pdfMeasureTextHeight text font size lineHeight)
373
+ def htmlHeight (htmlMeasureTextHeight text font size lineHeight)
374
+
375
+ assert (abs(pdfHeight - htmlHeight) < 0.5)
376
+ return true
377
+ }
378
+
379
+ fn testKerning:boolean () {
380
+ ; Test kerning pairs like "AV", "To", "WA"
381
+ def pairs ["AV" "To" "WA" "VA" "Ty"]
382
+ ; ... verify kerning affects width consistently
383
+ }
384
+
385
+ fn testLetterSpacing:boolean () {
386
+ ; Verify letter-spacing / Tc behaves identically
387
+ def text "Hello"
388
+ def spacing 2.0 ; 2px letter spacing
389
+
390
+ def pdfWidth (pdfMeasureText text font size spacing)
391
+ def htmlWidth (htmlMeasureText text font size spacing)
392
+
393
+ ; spacing adds (strlen - 1) * spacing to width
394
+ assert (abs(pdfWidth - htmlWidth) < 0.1)
395
+ return true
396
+ }
397
+ }
398
+ ```
399
+
400
+ #### 0.4.8 Known Differences to Handle
401
+
402
+ | Issue | Cause | Solution |
403
+ |-------|-------|----------|
404
+ | **Sub-pixel rendering** | Browsers round to device pixels | Use `text-rendering: geometricPrecision` |
405
+ | **Font hinting** | TTF hints adjust at small sizes | May cause ±1px differences |
406
+ | **Kerning tables** | GPOS vs kern table priority | Explicitly enable/disable kerning |
407
+ | **Default line-height** | Browsers use ~1.2, varies by font | Always set explicit `line-height` |
408
+ | **Whitespace collapse** | HTML collapses multiple spaces | Use `white-space: pre-wrap` |
409
+ | **Word breaking** | Different break algorithms | May cause line wrapping differences |
410
+
411
+ ### 0.5 Font Metrics & Text Measurement
412
+
413
+ The HTML renderer can achieve **pixel-perfect text layout** by using the same font metrics system as the PDF renderer:
414
+
415
+ **Approach 1: TrueType Font Parsing (Server-side / Pre-computed)**
416
+ ```
417
+ ┌─────────────────────────────────────────────────────────────────────┐
418
+ │ TrueType Font File (.ttf) │
419
+ └─────────────────────────────────────────────────────────────────────┘
420
+ │
421
+ ▼
422
+ ┌─────────────────────────────────────────────────────────────────────┐
423
+ │ TrueTypeParser.rgr (existing) │
424
+ │ - Parse cmap table (character to glyph mapping) │
425
+ │ - Parse hmtx table (horizontal metrics - advance widths) │
426
+ │ - Parse head table (unitsPerEm) │
427
+ │ - Calculate: charWidth = (advanceWidth / unitsPerEm) * fontSize │
428
+ └─────────────────────────────────────────────────────────────────────┘
429
+ │
430
+ ▼
431
+ ┌─────────────────────────────────────────────────────────────────────┐
432
+ │ Font Metrics Lookup Table │
433
+ │ { │
434
+ │ "Noto Sans": { │
435
+ │ "unitsPerEm": 1000, │
436
+ │ "ascender": 1069, │
437
+ │ "descender": -293, │
438
+ │ "glyphWidths": { "A": 633, "B": 653, "C": 698, ... } │
439
+ │ } │
440
+ │ } │
441
+ └─────────────────────────────────────────────────────────────────────┘
442
+ │
443
+ ┌─────────────┴─────────────┐
444
+ ▼ ▼
445
+ ┌──────────────────┐ ┌──────────────────┐
446
+ │ PDF Renderer │ │ HTML Renderer │
447
+ │ (same metrics) │ │ (same metrics) │
448
+ └──────────────────┘ └──────────────────┘
449
+ ```
450
+
451
+ **Approach 2: Canvas API (Browser runtime)**
452
+ ```javascript
453
+ // Browser-side font measurement using Canvas API
454
+ function measureText(text, fontFamily, fontSize) {
455
+ const canvas = document.createElement('canvas');
456
+ const ctx = canvas.getContext('2d');
457
+ ctx.font = `${fontSize}px "${fontFamily}"`;
458
+ const metrics = ctx.measureText(text);
459
+ return {
460
+ width: metrics.width,
461
+ actualBoundingBoxAscent: metrics.actualBoundingBoxAscent,
462
+ actualBoundingBoxDescent: metrics.actualBoundingBoxDescent
463
+ };
464
+ }
465
+ ```
466
+
467
+ **Recommended Strategy:**
468
+ 1. **Pre-compute** font metrics from TTF files using existing `TrueTypeParser.rgr`
469
+ 2. **Embed metrics** in HTML output as JSON or generate CSS with exact measurements
470
+ 3. **Fallback** to Canvas API for dynamic/unknown fonts in browser
471
+
472
+ This ensures **identical text layout** between PDF and HTML renderers.
473
+
474
+ ### 0.6 Page Breaks in HTML
475
+
476
+ Page breaks ARE possible in HTML because we control the layout engine:
477
+
478
+ ```
479
+ ┌─────────────────────────────────────────────────────────────────────┐
480
+ │ EVG Layout Engine │
481
+ │ - Already calculates element positions │
482
+ │ - Already tracks page assignments (calculatedPage) │
483
+ │ - Knows exact page dimensions and margins │
484
+ └─────────────────────────────────────────────────────────────────────┘
485
+ │
486
+ ▼
487
+ ┌─────────────────────────────────────────────────────────────────────┐
488
+ │ HTML Page Rendering │
489
+ │ │
490
+ │ Page 1: Page 2: │
491
+ │ ┌─────────────────────┐ ┌─────────────────────┐ │
492
+ │ │ <div class="page-1">│ │ <div class="page-2">│ │
493
+ │ │ Element A │ │ Element D │ │
494
+ │ │ Element B │ │ Element E │ │
495
+ │ │ Element C │ │ (continued...) │ │
496
+ │ │ (page ends here) │ │ │ │
497
+ │ └─────────────────────┘ └─────────────────────┘ │
498
+ └─────────────────────────────────────────────────────────────────────┘
499
+ ```
500
+
501
+ **Implementation:**
502
+ ```html
503
+ <div class="evg-document">
504
+ <!-- Page 1 -->
505
+ <div class="evg-page" style="width: 595px; height: 842px;">
506
+ <!-- Elements with calculatedPage == 0 -->
507
+ </div>
508
+
509
+ <!-- Page 2 -->
510
+ <div class="evg-page" style="width: 595px; height: 842px;">
511
+ <!-- Elements with calculatedPage == 1 -->
512
+ </div>
513
+ </div>
514
+ ```
515
+
516
+ ### 0.7 CMYK Color Conversion
517
+
518
+ CMYK to RGB conversion is straightforward mathematics:
519
+
520
+ ```ranger
521
+ class ColorConverter {
522
+ ; CMYK to RGB (simple conversion)
523
+ sfn cmykToRgb:EVGColor (c:double m:double y:double k:double) {
524
+ ; Formula: R = 255 × (1-C) × (1-K)
525
+ def r:int (to_int (255.0 * (1.0 - c) * (1.0 - k)))
526
+ def g:int (to_int (255.0 * (1.0 - m) * (1.0 - k)))
527
+ def b:int (to_int (255.0 * (1.0 - y) * (1.0 - k)))
528
+ return (EVGColor.create(r g b 255))
529
+ }
530
+
531
+ ; RGB to CMYK
532
+ sfn rgbToCmyk:[double] (r:int g:int b:int) {
533
+ def rn:double ((to_double r) / 255.0)
534
+ def gn:double ((to_double g) / 255.0)
535
+ def bn:double ((to_double b) / 255.0)
536
+
537
+ def k:double (1.0 - (max3 rn gn bn))
538
+ if (k == 1.0) {
539
+ return [0.0 0.0 0.0 1.0] ; Pure black
540
+ }
541
+
542
+ def c:double ((1.0 - rn - k) / (1.0 - k))
543
+ def m:double ((1.0 - gn - k) / (1.0 - k))
544
+ def y:double ((1.0 - bn - k) / (1.0 - k))
545
+
546
+ return [c m y k]
547
+ }
548
+ }
549
+ ```
550
+
551
+ **For accurate CMYK preview:**
552
+ - Convert CMYK to RGB for browser display
553
+ - Show warning indicator for CMYK colors (since RGB gamut differs)
554
+ - Optional: Use ICC color profiles for more accurate conversion
555
+
556
+ ### 0.8 What CAN'T Be Done in HTML?
557
+
558
+ | Feature | Issue | Solution |
559
+ |---------|-------|----------|
560
+ | **PDF-specific metadata** | Title, author, keywords | N/A for preview (PDF only) |
561
+ | **Embedded fonts** | PDF embeds font subsets | Use `@font-face` with TTF/WOFF |
562
+ | **Exact CMYK gamut** | Browsers use RGB | Convert + show indicator |
563
+ | **PDF encryption** | Security features | N/A for preview |
564
+
565
+ **Everything else CAN be done** - font metrics, page breaks, and color conversion are all solvable.
566
+
567
+ ### 0.9 Implementation Plan
568
+
569
+ **Files to create:**
570
+ ```
571
+ lib/evg/web/
572
+ ├── EVGHTMLRenderer.rgr # Main HTML renderer
573
+ ├── EVGCSSGenerator.rgr # CSS style generation
574
+ ├── EVGHTMLDocument.rgr # HTML document wrapper
575
+ └── tests/
576
+ ├── html_render_test.rgr # Unit tests
577
+ └── expected/ # Expected HTML output files
578
+ ```
579
+
580
+ **EVGHTMLRenderer Interface:**
581
+ ```ranger
582
+ class EVGHTMLRenderer {
583
+ def indentLevel:int 0
584
+ def cssStyles:[string] ; Collected CSS rules
585
+ def usedFonts:[string] ; Fonts to include as web fonts
586
+
587
+ fn render:string (root:EVGElement)
588
+ fn renderElement:string (el:EVGElement)
589
+ fn renderView:string (el:EVGElement)
590
+ fn renderLabel:string (el:EVGElement)
591
+ fn renderImage:string (el:EVGElement)
592
+ fn renderPath:string (el:EVGElement)
593
+
594
+ fn generateCSS:string (el:EVGElement)
595
+ fn generateInlineStyle:string (el:EVGElement)
596
+
597
+ ; Output options
598
+ def outputMode:string "inline" ; "inline" | "stylesheet" | "both"
599
+ def prettyPrint:boolean true
600
+ }
601
+ ```
602
+
603
+ ### 0.10 HTML Output Example
604
+
605
+ **Input TSX:**
606
+ ```tsx
607
+ <Page width="595px" height="842px" padding="40px">
608
+ <View backgroundColor="#f5f5f5" padding="20px" borderRadius="10px">
609
+ <Label fontSize="24px" color="#2c3e50">Hello World</Label>
610
+ <Image src="photo.jpg" width="200px" height="150px" />
611
+ </View>
612
+ </Page>
613
+ ```
614
+
615
+ **Output HTML:**
616
+ ```html
617
+ <!DOCTYPE html>
618
+ <html>
619
+ <head>
620
+ <meta charset="UTF-8">
621
+ <style>
622
+ @font-face {
623
+ font-family: 'Noto Sans';
624
+ src: url('./fonts/NotoSans-Regular.ttf');
625
+ }
626
+ .evg-page {
627
+ width: 595px;
628
+ height: 842px;
629
+ padding: 40px;
630
+ box-sizing: border-box;
631
+ background: white;
632
+ position: relative;
633
+ overflow: hidden;
634
+ }
635
+ .evg-view-1 {
636
+ background-color: #f5f5f5;
637
+ padding: 20px;
638
+ border-radius: 10px;
639
+ display: flex;
640
+ flex-direction: column;
641
+ }
642
+ .evg-label-1 {
643
+ font-size: 24px;
644
+ color: #2c3e50;
645
+ font-family: 'Noto Sans', sans-serif;
646
+ }
647
+ .evg-image-1 {
648
+ width: 200px;
649
+ height: 150px;
650
+ object-fit: cover;
651
+ }
652
+ </style>
653
+ </head>
654
+ <body>
655
+ <div class="evg-page">
656
+ <div class="evg-view-1">
657
+ <span class="evg-label-1">Hello World</span>
658
+ <img class="evg-image-1" src="photo.jpg" alt="">
659
+ </div>
660
+ </div>
661
+ </body>
662
+ </html>
663
+ ```
664
+
665
+ ### 0.11 Unit Testing with HTML
666
+
667
+ **Test approach:**
668
+ ```ranger
669
+ Import "EVGHTMLRenderer.rgr"
670
+ Import "JSXToEVG.rgr"
671
+
672
+ class HTMLRenderTest {
673
+ fn testSimpleView:boolean () {
674
+ def tsx "<View width=\"100px\" height=\"50px\" backgroundColor=\"#ff0000\"></View>"
675
+ def parser (new JSXToEVG())
676
+ def root:EVGElement (parser.parse(tsx))
677
+
678
+ def renderer (new EVGHTMLRenderer())
679
+ def html:string (renderer.render(root))
680
+
681
+ ; Verify output contains expected CSS
682
+ assert (contains html "width: 100px")
683
+ assert (contains html "height: 50px")
684
+ assert (contains html "background-color: #ff0000")
685
+
686
+ return true
687
+ }
688
+
689
+ fn testFlexLayout:boolean () {
690
+ def tsx "<View display=\"flex\" justifyContent=\"center\" alignItems=\"center\">...</View>"
691
+ ; ... verify flex CSS output
692
+ }
693
+
694
+ fn testGradient:boolean () {
695
+ def tsx "<View background=\"linear-gradient(45deg, red, blue)\">...</View>"
696
+ ; ... verify gradient CSS output
697
+ }
698
+ }
699
+ ```
700
+
701
+ ### 0.12 Live Preview Server
702
+
703
+ ```ranger
704
+ class EVGPreviewServer {
705
+ def port:int 8080
706
+ def watchPath:string ""
707
+ def renderer:EVGHTMLRenderer
708
+
709
+ fn start:void () {
710
+ ; Start HTTP server
711
+ ; Watch TSX files for changes
712
+ ; WebSocket for hot reload
713
+ }
714
+
715
+ fn handleRequest:string (path:string) {
716
+ if (path == "/") {
717
+ return this.renderPreview()
718
+ }
719
+ if (startsWith path "/fonts/") {
720
+ return this.serveFont(path)
721
+ }
722
+ if (startsWith path "/images/") {
723
+ return this.serveImage(path)
724
+ }
725
+ }
726
+ }
727
+ ```
728
+
729
+ **Usage:**
730
+ ```bash
731
+ evg preview example.tsx --port=8080
732
+ # Opens browser at http://localhost:8080
733
+ # Auto-refreshes on file changes
734
+ ```
735
+
736
+ ### 0.13 Implementation Priority
737
+
738
+ 1. **Phase 0.1:** Basic HTML structure (View, Label) - 1 day
739
+ 2. **Phase 0.2:** CSS generation (colors, dimensions, spacing) - 1 day
740
+ 3. **Phase 0.3:** Flexbox layout mapping - 1 day
741
+ 4. **Phase 0.4:** Image rendering with viewBox - 1 day
742
+ 5. **Phase 0.5:** SVG Path embedding - 1 day
743
+ 6. **Phase 0.6:** Gradients and shadows - 1 day
744
+ 7. **Phase 0.7:** Unit test framework - 1 day
745
+ 8. **Phase 0.8:** Preview server with hot reload - 2 days
746
+
747
+ **Total: ~10 days**
748
+
749
+ ---
750
+
751
+ ## Phase 1: Web-based Visual Debugger
752
+
753
+ ### 1.1 HTML5/Canvas Renderer
754
+
755
+ Create a new renderer that outputs the EVG tree as HTML/CSS or Canvas drawings for browser preview.
756
+
757
+ **Files to create:**
758
+ - `lib/evg/web/EVGWebRenderer.rgr` - Renders EVG to HTML/CSS
759
+ - `lib/evg/web/EVGCanvasRenderer.rgr` - Renders EVG to Canvas commands
760
+ - `lib/evg/web/evg_preview_server.rgr` - Simple HTTP server for preview
761
+
762
+ **Features:**
763
+ - Real-time preview of layout
764
+ - Element inspector (click to see properties)
765
+ - Highlight element boundaries and padding/margins
766
+ - Show calculated sizes vs. specified sizes
767
+ - Export to PDF/JPEG from preview
768
+
769
+ ### 1.2 Debug UI Components
770
+
771
+ ```
772
+ ┌─────────────────────────────────────────────────────────────────────┐
773
+ │ EVG Visual Debugger [Export ▼] │
774
+ ├─────────────────────────────────────────────────────────────────────┤
775
+ │ ┌───────────────────────────────────┐ ┌───────────────────────────┐ │
776
+ │ │ │ │ Element Inspector │ │
777
+ │ │ │ ├───────────────────────────┤ │
778
+ │ │ [Page Preview] │ │ ID: header-title │ │
779
+ │ │ │ │ Type: Label │ │
780
+ │ │ │ │ Position: (40, 40) │ │
781
+ │ │ │ │ Size: 515 x 32 │ │
782
+ │ │ │ │ Font: Noto Sans 24px │ │
783
+ │ │ │ │ Color: #2c3e50 │ │
784
+ │ │ │ │ │ │
785
+ │ │ │ │ ─── Computed ─── │ │
786
+ │ │ │ │ Padding: 0 │ │
787
+ │ │ │ │ Margin: 0 0 20 0 │ │
788
+ │ └───────────────────────────────────┘ └───────────────────────────┘ │
789
+ │ Page: [1] [2] [3] [4] Zoom: [100%] │
790
+ └─────────────────────────────────────────────────────────────────────┘
791
+ ```
792
+
793
+ ---
794
+
795
+ ## Phase 1.5: Incremental Rendering & Live Preview
796
+
797
+ ### 1.5.1 Problem Statement
798
+
799
+ Current PDF rendering:
800
+ - Regenerates entire PDF on every change
801
+ - Slow for large documents (100+ pages)
802
+ - No live preview during development
803
+ - Must export and open externally to see results
804
+
805
+ Goal: **Sub-second preview updates** during development
806
+
807
+ ### 1.5.2 Incremental PDF Rendering Architecture
808
+
809
+ ```
810
+ ┌─────────────────────────────────────────────────────────────────────┐
811
+ │ TSX Source (File Watch) │
812
+ └─────────────────────────────────────────────────────────────────────┘
813
+ │
814
+ ▼
815
+ ┌─────────────────────────────────────────────────────────────────────┐
816
+ │ Change Detection Engine │
817
+ │ - Parse TSX to AST │
818
+ │ - Diff against previous AST │
819
+ │ - Identify changed pages/elements │
820
+ │ - Track dependency graph (shared styles, images) │
821
+ └─────────────────────────────────────────────────────────────────────┘
822
+ │
823
+ ┌─────────────┼─────────────┐
824
+ ▼ ▼ ▼
825
+ No Changes Page Changed Global Change
826
+ │ │ │
827
+ │ ▼ ▼
828
+ │ Re-render Full Re-render
829
+ │ affected all pages
830
+ │ pages only
831
+ │ │ │
832
+ └─────────────┴─────────────┘
833
+ │
834
+ ▼
835
+ ┌─────────────────────────────────────────────────────────────────────┐
836
+ │ PDF Update Strategy │
837
+ │ Option A: Page-level PDF replacement │
838
+ │ Option B: Render to PNG per page + composite │
839
+ │ Option C: Hybrid preview (web) + full export (PDF) │
840
+ └─────────────────────────────────────────────────────────────────────┘
841
+ ```
842
+
843
+ ### 1.5.3 Change Detection Strategy
844
+
845
+ **Page-level hashing:**
846
+ ```
847
+ Page 1: hash("content...") = "a1b2c3" → unchanged
848
+ Page 2: hash("content...") = "d4e5f6" → CHANGED (was "x7y8z9")
849
+ Page 3: hash("content...") = "g7h8i9" → unchanged
850
+ ```
851
+
852
+ **What triggers full re-render:**
853
+ - Shared style changes (affects all pages using that style)
854
+ - Font changes
855
+ - Global variable changes
856
+ - Page count changes (PDF cross-reference table must update)
857
+
858
+ **What triggers partial re-render:**
859
+ - Single page content change
860
+ - Image replacement (same dimensions)
861
+ - Text edits within a page
862
+
863
+ ### 1.5.4 Live Preview Implementation Options
864
+
865
+ #### Option A: Native PDF Viewer Wrapper (macOS)
866
+
867
+ Wrap `evg` binary with a native macOS app that:
868
+ 1. Watches TSX files for changes (FSEvents)
869
+ 2. Calls `evg` to render changed pages to PNG
870
+ 3. Displays PNGs in scrollable preview
871
+ 4. Full PDF export on demand
872
+
873
+ **Pros:**
874
+ - Native performance
875
+ - True WYSIWYG (same renderer)
876
+ - No browser needed
877
+
878
+ **Cons:**
879
+ - macOS only initially
880
+ - Requires Swift/Objective-C wrapper
881
+
882
+ ```
883
+ ┌─────────────────────────────────────────────────────────────────────┐
884
+ │ EVG Live Preview (macOS) [Export] │
885
+ ├─────────────────────────────────────────────────────────────────────┤
886
+ │ ┌──────────────────────────────────────────────────────────────┐ │
887
+ │ │ │ │
888
+ │ │ ┌────────────────────────────────────────────────────┐ │ │
889
+ │ │ │ │ │ │
890
+ │ │ │ Page 1 (PNG preview) │ │ │
891
+ │ │ │ │ │ │
892
+ │ │ └────────────────────────────────────────────────────┘ │ │
893
+ │ │ │ │
894
+ │ │ ┌────────────────────────────────────────────────────┐ │ │
895
+ │ │ │ │ │ │
896
+ │ │ │ Page 2 (PNG preview) │ │ │
897
+ │ │ │ *** UPDATED *** │ │ │
898
+ │ │ └────────────────────────────────────────────────────┘ │ │
899
+ │ │ │ │
900
+ │ └──────────────────────────────────────────────────────────────┘ │
901
+ │ Page 2 of 10 | Last update: 0.3s | Watching: example.tsx │
902
+ └─────────────────────────────────────────────────────────────────────┘
903
+ ```
904
+
905
+ **Implementation (Swift):**
906
+ ```swift
907
+ // EVGPreviewApp.swift
908
+ class EVGPreviewController {
909
+ var pageCache: [Int: NSImage] = [:]
910
+ var pageHashes: [Int: String] = [:]
911
+
912
+ func onFileChanged(path: String) {
913
+ let changedPages = detectChangedPages(path)
914
+ for pageNum in changedPages {
915
+ // Render only changed page to PNG
916
+ let png = renderPageToPNG(pageNum)
917
+ pageCache[pageNum] = png
918
+ updatePreview(pageNum)
919
+ }
920
+ }
921
+
922
+ func renderPageToPNG(_ page: Int) -> NSImage {
923
+ // Call evg binary with --page=N --format=png
924
+ let task = Process()
925
+ task.executableURL = URL(fileURLWithPath: "/usr/local/bin/evg")
926
+ task.arguments = [sourcePath, "--page=\(page)", "--format=png", "-o", "-"]
927
+ // ... capture stdout as PNG data
928
+ }
929
+ }
930
+ ```
931
+
932
+ #### Option B: Electron/Tauri Wrapper (Cross-platform)
933
+
934
+ **Pros:**
935
+ - Cross-platform (macOS, Windows, Linux)
936
+ - Web technologies for UI
937
+ - Hot-reload built-in
938
+
939
+ **Cons:**
940
+ - Larger bundle size (Electron)
941
+ - Tauri requires Rust knowledge
942
+
943
+ #### Option C: VS Code Extension
944
+
945
+ Integrate preview directly into VS Code:
946
+
947
+ **Pros:**
948
+ - No separate app needed
949
+ - Side-by-side with code
950
+ - Familiar environment
951
+
952
+ **Cons:**
953
+ - Limited to VS Code users
954
+ - WebView has some limitations
955
+
956
+ ```
957
+ ┌─────────────────────────────────────────────────────────────────────┐
958
+ │ VS Code │
959
+ ├─────────────────────────────────────────────────────────────────────┤
960
+ │ example.tsx │ EVG Preview │
961
+ │ ──────────────────────│─────────────────────────────────────────────│
962
+ │ <Print> │ ┌────────────────────────────────────────┐ │
963
+ │ <Page> │ │ │ │
964
+ │ <View> │ │ [Live Page Preview] │ │
965
+ │ <Label>Hello │ │ │ │
966
+ │ ... │ │ │ │
967
+ │ │ └────────────────────────────────────────┘ │
968
+ │ │ Page: [1] [2] [3] [Export PDF] │
969
+ └─────────────────────────────────────────────────────────────────────┘
970
+ ```
971
+
972
+ #### Option D: Browser-based with WebSocket Hot Reload
973
+
974
+ **Pros:**
975
+ - No installation (just open browser)
976
+ - Works on any OS
977
+ - Easy to share previews remotely
978
+
979
+ **Cons:**
980
+ - Requires running server
981
+ - Slight rendering differences from PDF
982
+
983
+ ### 1.5.5 Recommended Approach: Hybrid System
984
+
985
+ **Development workflow:**
986
+ 1. **Primary: Web preview** (fast, cross-platform, hot-reload)
987
+ 2. **Verification: PNG per-page** (pixel-accurate to PDF)
988
+ 3. **Final: Full PDF export** (production output)
989
+
990
+ **Implementation phases:**
991
+
992
+ **Phase 1.5a: Single-page PNG render mode**
993
+ ```bash
994
+ # New CLI option: render single page to PNG
995
+ evg example.tsx --page=2 --format=png -o page2.png
996
+
997
+ # Render all pages as PNGs to directory
998
+ evg example.tsx --format=png --pages-dir=./preview/
999
+ ```
1000
+
1001
+ **Phase 1.5b: File watcher with incremental render**
1002
+ ```bash
1003
+ # Watch mode with live preview server
1004
+ evg example.tsx --watch --preview-port=8080
1005
+
1006
+ # Opens browser at http://localhost:8080
1007
+ # WebSocket pushes updates on file change
1008
+ ```
1009
+
1010
+ **Phase 1.5c: Native preview app (optional)**
1011
+ ```bash
1012
+ # macOS app that uses evg internally
1013
+ EVGPreview.app
1014
+ ```
1015
+
1016
+ ### 1.5.6 PDF Incremental Update (Advanced)
1017
+
1018
+ PDF format allows incremental updates (appending changes without rewriting):
1019
+
1020
+ ```
1021
+ Original PDF:
1022
+ ┌─────────────────────────────────────────┐
1023
+ │ Header │
1024
+ │ Object 1 (Page 1 content) │
1025
+ │ Object 2 (Page 2 content) │
1026
+ │ Object 3 (Page 3 content) │
1027
+ │ XRef Table │
1028
+ │ Trailer │
1029
+ └─────────────────────────────────────────┘
1030
+
1031
+ After incremental update (Page 2 changed):
1032
+ ┌─────────────────────────────────────────┐
1033
+ │ [Original PDF bytes unchanged] │
1034
+ │ ... │
1035
+ │ ─────── Incremental Update ─────── │
1036
+ │ Object 2' (New Page 2 content) │
1037
+ │ XRef Table (updated) │
1038
+ │ Trailer (updated) │
1039
+ └─────────────────────────────────────────┘
1040
+ ```
1041
+
1042
+ **Challenges:**
1043
+ - PDF viewers may not support incremental updates well
1044
+ - File size grows with each update
1045
+ - Complex implementation
1046
+
1047
+ **Recommendation:** Use PNG preview for development, full PDF for export.
1048
+
1049
+ ### 1.5.7 Performance Targets
1050
+
1051
+ | Operation | Target Time | Notes |
1052
+ |-----------|-------------|-------|
1053
+ | Detect changes | < 50ms | AST diff |
1054
+ | Render single page to PNG | < 200ms | Typical page |
1055
+ | WebSocket push to browser | < 10ms | PNG base64 |
1056
+ | **Total update latency** | **< 300ms** | From save to preview |
1057
+
1058
+ ---
1059
+
1060
+ ## Phase 2: New Visual Features
1061
+
1062
+ ### 2.1 Linear Gradients
1063
+
1064
+ **TSX Syntax:**
1065
+ ```tsx
1066
+ <View
1067
+ width="100%"
1068
+ height="200px"
1069
+ background="linear-gradient(45deg, #ff6b6b, #4ecdc4)"
1070
+ />
1071
+
1072
+ // Or with explicit stops
1073
+ <View
1074
+ width="100%"
1075
+ height="200px"
1076
+ background="linear-gradient(to right, #ff6b6b 0%, #feca57 50%, #4ecdc4 100%)"
1077
+ />
1078
+ ```
1079
+
1080
+ **Implementation:**
1081
+ - `EVGGradient.rgr` - Gradient definition class
1082
+ - PDF: Use shading patterns (Type 2 - Axial)
1083
+ - Web: CSS `linear-gradient()`
1084
+
1085
+ ### 2.2 Radial Gradients
1086
+
1087
+ **TSX Syntax:**
1088
+ ```tsx
1089
+ <View
1090
+ width="200px"
1091
+ height="200px"
1092
+ background="radial-gradient(circle at center, #ffffff, #000000)"
1093
+ />
1094
+
1095
+ // Elliptical
1096
+ <View
1097
+ width="300px"
1098
+ height="200px"
1099
+ background="radial-gradient(ellipse at 30% 50%, #ff6b6b 0%, transparent 70%)"
1100
+ />
1101
+ ```
1102
+
1103
+ **Implementation:**
1104
+ - PDF: Use shading patterns (Type 3 - Radial)
1105
+ - Web: CSS `radial-gradient()`
1106
+
1107
+ ### 2.3 Layer Element
1108
+
1109
+ A `Layer` element renders on top of its parent with the same dimensions, enabling stacking of content.
1110
+
1111
+ **TSX Syntax:**
1112
+ ```tsx
1113
+ <View width="400px" height="300px">
1114
+ {/* Base layer - image */}
1115
+ <Layer>
1116
+ <Image src="photo.jpg" width="100%" height="100%" />
1117
+ </Layer>
1118
+
1119
+ {/* Overlay layer - gradient */}
1120
+ <Layer>
1121
+ <View
1122
+ width="100%"
1123
+ height="100%"
1124
+ background="linear-gradient(to top, rgba(0,0,0,0.7), transparent)"
1125
+ />
1126
+ </Layer>
1127
+
1128
+ {/* Text layer */}
1129
+ <Layer verticalAlign="bottom" padding="20px">
1130
+ <Label fontSize="24px" color="white">Photo Title</Label>
1131
+ <Label fontSize="14px" color="white" opacity="0.8">Subtitle text</Label>
1132
+ </Layer>
1133
+ </View>
1134
+ ```
1135
+
1136
+ **Properties:**
1137
+ - `verticalAlign`: "top" | "center" | "bottom"
1138
+ - `align`: "left" | "center" | "right"
1139
+ - `padding`: Inset from parent edges
1140
+ - `opacity`: Layer transparency
1141
+
1142
+ **Implementation:**
1143
+ - Layer inherits parent's calculated width/height
1144
+ - Layers are rendered in order (first = bottom, last = top)
1145
+ - PDF: Save/restore graphics state for each layer
1146
+
1147
+ ### 2.4 Image viewBox (Crop/Zoom)
1148
+
1149
+ The `viewBox` attribute allows cropping/zooming into a specific region of an image using relative percentages.
1150
+
1151
+ **TSX Syntax:**
1152
+ ```tsx
1153
+ // Full image (default)
1154
+ <Image src="photo.jpg" width="400px" height="300px" />
1155
+
1156
+ // Zoom into center 80% of image (10% crop from each edge)
1157
+ <Image
1158
+ src="photo.jpg"
1159
+ width="400px"
1160
+ height="300px"
1161
+ viewBox="10% 10% 80% 80%"
1162
+ />
1163
+
1164
+ // Zoom into top-left quadrant
1165
+ <Image
1166
+ src="photo.jpg"
1167
+ width="400px"
1168
+ height="300px"
1169
+ viewBox="0% 0% 50% 50%"
1170
+ />
1171
+
1172
+ // Zoom into bottom-right area
1173
+ <Image
1174
+ src="photo.jpg"
1175
+ width="400px"
1176
+ height="300px"
1177
+ viewBox="40% 40% 60% 60%"
1178
+ />
1179
+
1180
+ // Also supports pixel values
1181
+ <Image
1182
+ src="photo.jpg"
1183
+ width="400px"
1184
+ height="300px"
1185
+ viewBox="100px 100px 800px 600px"
1186
+ />
1187
+ ```
1188
+
1189
+ **viewBox Format:** `"x y width height"`
1190
+ - `x` - Horizontal offset (% or px from left edge)
1191
+ - `y` - Vertical offset (% or px from top edge)
1192
+ - `width` - Width of visible area (% or px)
1193
+ - `height` - Height of visible area (% or px)
1194
+
1195
+ **Implementation:**
1196
+ - Calculate source rectangle in original image pixels
1197
+ - Scale/stretch source rectangle to fill element dimensions
1198
+ - PDF: Use image clipping and transformation matrix
1199
+ - Web: Use CSS `object-position` and `object-fit`, or Canvas `drawImage()` with source rect
1200
+
1201
+ **Use Cases:**
1202
+ - Portrait crop from landscape photo
1203
+ - Focus on face/subject
1204
+ - Create zoom effects
1205
+ - Ken Burns style animations (future)
1206
+
1207
+ ### 2.5 Image Element as Container
1208
+
1209
+ `Image` element children render inside a virtual layer over the image.
1210
+
1211
+ **TSX Syntax:**
1212
+ ```tsx
1213
+ <Image src="photo.jpg" width="400px" height="300px">
1214
+ <View
1215
+ position="absolute"
1216
+ bottom="0"
1217
+ width="100%"
1218
+ padding="10px"
1219
+ background="rgba(0,0,0,0.5)"
1220
+ >
1221
+ <Label color="white">{image.date}</Label>
1222
+ </View>
1223
+ </Image>
1224
+ ```
1225
+
1226
+ ### 2.6 Background Images
1227
+
1228
+ Views can have background images with optional viewBox cropping.
1229
+
1230
+ **TSX Syntax:**
1231
+ ```tsx
1232
+ // Simple background image
1233
+ <View
1234
+ width="100%"
1235
+ height="400px"
1236
+ backgroundImage="photo.jpg"
1237
+ backgroundSize="cover"
1238
+ >
1239
+ <Label fontSize="32px" color="white">Title Text</Label>
1240
+ </View>
1241
+
1242
+ // Background with viewBox zoom
1243
+ <View
1244
+ width="100%"
1245
+ height="400px"
1246
+ backgroundImage="photo.jpg"
1247
+ backgroundViewBox="20% 10% 60% 80%"
1248
+ backgroundSize="cover"
1249
+ >
1250
+ <Label fontSize="32px" color="white">Zoomed Background</Label>
1251
+ </View>
1252
+
1253
+ // Combined with gradient overlay
1254
+ <View
1255
+ width="100%"
1256
+ height="400px"
1257
+ backgroundImage="photo.jpg"
1258
+ backgroundSize="cover"
1259
+ >
1260
+ <Layer background="linear-gradient(to top, rgba(0,0,0,0.8), transparent)">
1261
+ <View padding="20px" verticalAlign="bottom">
1262
+ <Label fontSize="32px" color="white">Hero Title</Label>
1263
+ <Label fontSize="16px" color="white" opacity="0.8">Subtitle</Label>
1264
+ </View>
1265
+ </Layer>
1266
+ </View>
1267
+ ```
1268
+
1269
+ **backgroundSize options:**
1270
+ - `"cover"` - Scale to cover entire element (may crop)
1271
+ - `"contain"` - Scale to fit within element (may have gaps)
1272
+ - `"stretch"` - Stretch to fill exactly (may distort)
1273
+ - `"100% 100%"` - Explicit size
1274
+
1275
+ ---
1276
+
1277
+ ## Phase 3: Element ID System
1278
+
1279
+ ### 3.1 Automatic ID Generation
1280
+
1281
+ Every element gets a unique ID automatically.
1282
+
1283
+ **Format:** `{elementType}_{index}` or `{elementType}_{parentId}_{index}`
1284
+
1285
+ ```tsx
1286
+ // Auto-generated IDs
1287
+ <View> {/* id: "view_0" */}
1288
+ <Label>Text</Label> {/* id: "label_view_0_0" */}
1289
+ <Image src="..." /> {/* id: "image_view_0_1" */}
1290
+ </View>
1291
+ ```
1292
+
1293
+ ### 3.2 User-defined IDs
1294
+
1295
+ Users can set custom IDs for elements they need to reference.
1296
+
1297
+ **TSX Syntax:**
1298
+ ```tsx
1299
+ <View id="main-container">
1300
+ <Label id="title">Hello World</Label>
1301
+ <Image id="hero-image" src="photo.jpg" />
1302
+ </View>
1303
+ ```
1304
+
1305
+ ### 3.3 ID Registry API
1306
+
1307
+ ```typescript
1308
+ // Get element by ID
1309
+ const element = evg.getElementById("hero-image");
1310
+
1311
+ // Get all elements of type
1312
+ const allImages = evg.getElementsByType("image");
1313
+
1314
+ // Get element metadata
1315
+ const metadata = evg.getElementMetadata("hero-image");
1316
+ ```
1317
+
1318
+ ---
1319
+
1320
+ ## Phase 4: Image Metadata System
1321
+
1322
+ ### 4.1 EXIF Data Extraction
1323
+
1324
+ Extend existing `JPEGMetadata.rgr` to provide structured metadata.
1325
+
1326
+ **Available metadata:**
1327
+ ```typescript
1328
+ interface ImageMetadata {
1329
+ // Basic
1330
+ width: number;
1331
+ height: number;
1332
+ orientation: number;
1333
+
1334
+ // Camera
1335
+ make?: string;
1336
+ model?: string;
1337
+ exposureTime?: string;
1338
+ fNumber?: number;
1339
+ iso?: number;
1340
+ focalLength?: number;
1341
+
1342
+ // Date/Time
1343
+ dateTime?: string; // "2024:06:15 14:32:45"
1344
+ dateTimeOriginal?: string;
1345
+
1346
+ // GPS
1347
+ gps?: {
1348
+ latitude: number; // Decimal degrees (positive = N)
1349
+ longitude: number; // Decimal degrees (positive = E)
1350
+ altitude?: number; // Meters
1351
+ timestamp?: string;
1352
+ };
1353
+ }
1354
+ ```
1355
+
1356
+ ### 4.2 Context Access in TSX
1357
+
1358
+ **Option A: Context Provider**
1359
+ ```tsx
1360
+ <Image src="photo.jpg" width="400px" height="300px">
1361
+ <ImageContext>
1362
+ {(meta) => (
1363
+ <View position="absolute" bottom="0" padding="10px">
1364
+ <Label color="white">{meta.dateTime}</Label>
1365
+ {meta.gps && (
1366
+ <Label color="white" fontSize="12px">
1367
+ 📍 {meta.gps.latitude.toFixed(4)}, {meta.gps.longitude.toFixed(4)}
1368
+ </Label>
1369
+ )}
1370
+ </View>
1371
+ )}
1372
+ </ImageContext>
1373
+ </Image>
1374
+ ```
1375
+
1376
+ **Option B: Special Variables**
1377
+ ```tsx
1378
+ <Image src="photo.jpg" width="400px" height="300px">
1379
+ <Label color="white">${image.dateTime}</Label>
1380
+ <Label color="white">${image.gps.latitude}</Label>
1381
+ </Image>
1382
+ ```
1383
+
1384
+ ### 4.3 Metadata API by Element ID
1385
+
1386
+ ```typescript
1387
+ // Get metadata for specific image element
1388
+ const meta = evg.getImageMetadata("hero-image");
1389
+ console.log(meta.gps.latitude, meta.gps.longitude);
1390
+
1391
+ // Get all images with GPS data
1392
+ const geoImages = evg.getImagesWithGPS();
1393
+ ```
1394
+
1395
+ ---
1396
+
1397
+ ## Phase 5: MapBox Component
1398
+
1399
+ ### 5.1 Concept
1400
+
1401
+ A `MapBox` component that:
1402
+ 1. Takes a list of images with GPS coordinates
1403
+ 2. Calculates bounding box from coordinate extremes
1404
+ 3. Positions images relative to their GPS location within the box
1405
+ 4. Optionally draws route/path between images
1406
+
1407
+ ### 5.2 TSX Syntax
1408
+
1409
+ **Basic Usage:**
1410
+ ```tsx
1411
+ <MapBox
1412
+ width="600px"
1413
+ height="400px"
1414
+ images={[
1415
+ "IMG_001.jpg",
1416
+ "IMG_002.jpg",
1417
+ "IMG_003.jpg"
1418
+ ]}
1419
+ markerSize="60px"
1420
+ showRoute={true}
1421
+ routeColor="#3498db"
1422
+ />
1423
+ ```
1424
+
1425
+ **Advanced Usage:**
1426
+ ```tsx
1427
+ <MapBox
1428
+ width="600px"
1429
+ height="400px"
1430
+ padding="20px"
1431
+ backgroundColor="#f0f0f0"
1432
+ borderRadius="10px"
1433
+ >
1434
+ {/* Images auto-positioned by GPS */}
1435
+ <MapImage src="IMG_001.jpg" markerStyle="circle" />
1436
+ <MapImage src="IMG_002.jpg" markerStyle="square" />
1437
+ <MapImage src="IMG_003.jpg" markerStyle="circle" />
1438
+
1439
+ {/* Route line connecting images by time order */}
1440
+ <MapRoute color="#3498db" width="2px" dashed={true} />
1441
+
1442
+ {/* Custom marker at specific coordinates */}
1443
+ <MapMarker lat={60.1699} lon={24.9384}>
1444
+ <View padding="5px" backgroundColor="red" borderRadius="50%">
1445
+ <Label color="white" fontSize="12px">Start</Label>
1446
+ </View>
1447
+ </MapMarker>
1448
+ </MapBox>
1449
+ ```
1450
+
1451
+ ### 5.3 Implementation Details
1452
+
1453
+ **Coordinate Transformation:**
1454
+ ```
1455
+ GPS Bounds:
1456
+ minLat = min(all latitudes)
1457
+ maxLat = max(all latitudes)
1458
+ minLon = min(all longitudes)
1459
+ maxLon = max(all longitudes)
1460
+
1461
+ Mapping function:
1462
+ x = (lon - minLon) / (maxLon - minLon) * boxWidth
1463
+ y = (maxLat - lat) / (maxLat - minLat) * boxHeight // Inverted Y
1464
+ ```
1465
+
1466
+ **Features:**
1467
+ - Auto-calculate bounds from image GPS or manual bounds
1468
+ - Padding to prevent markers at edges
1469
+ - Scale bar showing distance
1470
+ - North arrow indicator
1471
+ - Optional background map tile integration (future)
1472
+
1473
+ ### 5.4 MapBox Props
1474
+
1475
+ ```typescript
1476
+ interface MapBoxProps {
1477
+ // Dimensions
1478
+ width: string | number;
1479
+ height: string | number;
1480
+
1481
+ // Content
1482
+ images?: string[]; // Array of image paths
1483
+
1484
+ // Bounds (optional - auto-calculated if not specified)
1485
+ bounds?: {
1486
+ north: number; // Max latitude
1487
+ south: number; // Min latitude
1488
+ east: number; // Max longitude
1489
+ west: number; // Min longitude
1490
+ };
1491
+
1492
+ // Markers
1493
+ markerSize?: string | number;
1494
+ markerStyle?: "circle" | "square" | "thumbnail";
1495
+
1496
+ // Route
1497
+ showRoute?: boolean;
1498
+ routeColor?: string;
1499
+ routeWidth?: string | number;
1500
+ routeDashed?: boolean;
1501
+ sortBy?: "time" | "none"; // How to order route points
1502
+
1503
+ // Visual
1504
+ backgroundColor?: string;
1505
+ padding?: string | number;
1506
+ showScale?: boolean;
1507
+ showNorthArrow?: boolean;
1508
+ }
1509
+ ```
1510
+
1511
+ ---
1512
+
1513
+ ## Phase 6: Implementation Priority
1514
+
1515
+ ### Sprint 1: Foundation (Week 1-2)
1516
+ - [ ] Element ID system (auto + manual)
1517
+ - [ ] Image metadata extraction enhancement
1518
+ - [ ] Metadata API by element ID
1519
+
1520
+ ### Sprint 2: Visual Features (Week 3-4)
1521
+ - [ ] Linear gradient support (PDF + Web)
1522
+ - [ ] Radial gradient support (PDF + Web)
1523
+ - [ ] Layer element implementation
1524
+
1525
+ ### Sprint 3: Web Debugger (Week 5-6)
1526
+ - [ ] HTML/CSS renderer for EVG
1527
+ - [ ] Debug UI with element inspector
1528
+ - [ ] Hot-reload preview
1529
+
1530
+ ### Sprint 4: MapBox (Week 7-8)
1531
+ - [ ] GPS coordinate extraction from images
1532
+ - [ ] Coordinate transformation
1533
+ - [ ] MapBox component with route drawing
1534
+ - [ ] MapImage and MapMarker sub-components
1535
+
1536
+ ---
1537
+
1538
+ ## File Structure
1539
+
1540
+ ```
1541
+ gallery/
1542
+ ├── evg/
1543
+ │ ├── EVGElement.rgr # Core element (add ID, metadata)
1544
+ │ ├── EVGLayout.rgr # Layout engine
1545
+ │ ├── EVGGradient.rgr # NEW: Gradient definitions
1546
+ │ ├── EVGLayer.rgr # NEW: Layer element
1547
+ │ ├── EVGMapBox.rgr # NEW: Map component
1548
+ │ └── web/
1549
+ │ ├── EVGWebRenderer.rgr # NEW: HTML/CSS output
1550
+ │ ├── EVGCanvasRenderer.rgr # NEW: Canvas output
1551
+ │ └── evg_debug_server.rgr # NEW: Preview server
1552
+ │
1553
+ ├── pdf_writer/
1554
+ │ ├── src/
1555
+ │ │ ├── core/
1556
+ │ │ │ ├── EVGPDFRenderer.rgr # Update for gradients, layers
1557
+ │ │ │ └── PDFGradient.rgr # NEW: PDF gradient patterns
1558
+ │ │ ├── jpeg/
1559
+ │ │ │ └── JPEGMetadata.rgr # Enhance EXIF parsing
1560
+ │ │ └── jsx/
1561
+ │ │ └── ComponentEngine.rgr # Add Layer, MapBox components
1562
+ │ └── examples/
1563
+ │ ├── test_gradients.tsx # NEW: Gradient examples
1564
+ │ ├── test_layers.tsx # NEW: Layer examples
1565
+ │ └── test_mapbox.tsx # NEW: MapBox examples
1566
+ ```
1567
+
1568
+ ---
1569
+
1570
+ ## Notes
1571
+
1572
+ - **Backward Compatibility**: All existing TSX files must continue to work
1573
+ - **Performance**: Lazy-load image metadata only when accessed
1574
+ - **Memory**: Cache parsed metadata to avoid re-reading files
1575
+ - **Web Debugger**: Should work without requiring Node.js (pure Ranger HTTP server)
1576
+
1577
+ ---
1578
+
1579
+ ## Priority Implementation Order
1580
+
1581
+ Based on critical use cases (text overlays, image cropping, backgrounds), here is the recommended implementation order:
1582
+
1583
+ ### HIGH PRIORITY (Implement First)
1584
+
1585
+ 1. **Layer Element** (Phase 2.3)
1586
+ - Essential for text overlays on images
1587
+ - Background image + gradient overlay patterns
1588
+ - Stacking multiple visual elements
1589
+ - *Implementation:* Add Layer to component parser, update all renderers
1590
+
1591
+ 2. **Image viewBox** (Phase 2.4)
1592
+ - Crop/zoom into specific image regions
1593
+ - Portrait crop from landscape photos
1594
+ - Focus on subjects without pre-processing images
1595
+ - *Implementation:* Add viewBox parsing, calculate source rectangles
1596
+
1597
+ 3. **Background Images** (Phase 2.6)
1598
+ - Views with background images
1599
+ - Combined with viewBox for background cropping
1600
+ - Essential for hero sections, cards, etc.
1601
+ - *Implementation:* Add backgroundImage/backgroundViewBox attributes
1602
+
1603
+ 4. **Linear Gradients** (Phase 2.1)
1604
+ - Gradient overlays for readability
1605
+ - Visual effects and transitions
1606
+ - *Implementation:* Parse gradient syntax, implement in PDF/Canvas
1607
+
1608
+ ### MEDIUM PRIORITY (Implement Second)
1609
+
1610
+ 5. **Image as Container** (Phase 2.5)
1611
+ - Child elements overlay on images
1612
+ - Alternative to Layer for simple cases
1613
+ - *Implementation:* Handle Image children as overlay layer
1614
+
1615
+ 6. **Radial Gradients** (Phase 2.2)
1616
+ - Spotlight/vignette effects
1617
+ - *Implementation:* Similar to linear but with radial math
1618
+
1619
+ 7. **Web Debugger** (Phase 1)
1620
+ - Interactive preview without PDF export
1621
+ - Layout debugging tools
1622
+ - *Implementation:* HTML/CSS renderer, debug server
1623
+
1624
+ ### LOWER PRIORITY (Implement Later)
1625
+
1626
+ 8. **Element IDs** (Phase 3)
1627
+ - Debugging and referencing
1628
+ - *Implementation:* Auto-generate or parse id attribute
1629
+
1630
+ 9. **Image Metadata** (Phase 4)
1631
+ - EXIF, GPS extraction
1632
+ - Context for templates
1633
+ - *Implementation:* Enhance JPEG parser
1634
+
1635
+ 10. **MapBox Component** (Phase 5)
1636
+ - GPS-based image positioning
1637
+ - *Implementation:* MapBox API integration
1638
+
1639
+ ---
1640
+
1641
+ ## Quick Start Examples
1642
+
1643
+ ### Text on Image (Layer + Background)
1644
+ ```tsx
1645
+ <View width="600px" height="400px" backgroundImage="sunset.jpg" backgroundSize="cover">
1646
+ <Layer background="linear-gradient(to top, rgba(0,0,0,0.7), transparent)">
1647
+ <View verticalAlign="bottom" padding="20px">
1648
+ <Label fontSize="28px" color="white" fontWeight="bold">Beautiful Sunset</Label>
1649
+ <Label fontSize="14px" color="white" opacity="0.8">Location: Finland</Label>
1650
+ </View>
1651
+ </Layer>
1652
+ </View>
1653
+ ```
1654
+
1655
+ ### Cropped Portrait Image
1656
+ ```tsx
1657
+ <Image
1658
+ src="landscape_photo.jpg"
1659
+ width="200px"
1660
+ height="300px"
1661
+ viewBox="25% 10% 50% 80%"
1662
+ />
1663
+ ```
1664
+
1665
+ ### Image Gallery Card
1666
+ ```tsx
1667
+ <View width="300px" height="200px" borderRadius="10px" overflow="hidden">
1668
+ <Image src="photo.jpg" width="100%" height="100%" viewBox="10% 0% 80% 100%">
1669
+ <View position="absolute" bottom="0" width="100%" background="rgba(0,0,0,0.6)" padding="10px">
1670
+ <Label color="white">Photo Title</Label>
1671
+ </View>
1672
+ </Image>
1673
+ </View>
1674
+ ```