Skip to content

Commit d8d60af

Browse files
committed
Add SVG element and SVG output renderer documentation + ImageSharp
1 parent ced1a10 commit d8d60af

252 files changed

Lines changed: 21135 additions & 947 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ packages/
4242
# Private templates (local only)
4343
examples/private/
4444

45+
# Snapshot test output (actual/diff files from failed comparisons)
46+
**/Snapshots/output/
47+
4548
# Private golden snapshot images
4649
**/golden/wb_receipt.png
4750

.scratch/brainstorm-imagesharp-qr-svg.md

Lines changed: 515 additions & 0 deletions
Large diffs are not rendered by default.

.scratch/brainstorm-text-wrapping.md

Lines changed: 593 additions & 0 deletions
Large diffs are not rendered by default.

AGENTS.md

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ A modular .NET library for rendering images from YAML templates with flexbox lay
44

55
## LLM Documentation
66

7-
- `llms.txt` -- concise project overview for LLM context windows (~200 lines)
8-
- `llms-full.txt` -- comprehensive reference with all YAML properties, API details, and conventions (~580 lines)
7+
- `llms.txt` -- concise project overview for LLM context windows (~450 lines)
8+
- `llms-full.txt` -- comprehensive reference with all YAML properties, API details, and conventions (~1250 lines)
99

1010
## Build & Test
1111

@@ -34,35 +34,55 @@ src/FlexRender.Core/ # Core library (0 external dependencies)
3434
src/FlexRender.Yaml/ # YAML template parser (-> Core + YamlDotNet)
3535
Parsing/ # TemplateParser
3636
src/FlexRender.Http/ # HTTP resource loader (-> Core)
37-
src/FlexRender.Skia/ # SkiaSharp renderer (-> Core + SkiaSharp)
37+
src/FlexRender.Skia.Render/ # SkiaSharp renderer (-> Core + SkiaSharp)
3838
Abstractions/ # ISkiaRenderer, IFontLoader, IImageLoader, IFontManager
3939
Rendering/ # SkiaRenderer, TextRenderer, FontManager, ColorParser, RotationHelper, BmpEncoder, BoxShadowParser, GradientParser
4040
Loaders/ # FontLoader, ImageLoader
4141
Providers/ # IContentProvider<T,O>, ImageProvider
42-
src/FlexRender.QrCode/ # QR code provider (-> Skia + QRCoder)
43-
src/FlexRender.Barcode/ # Barcode provider (-> Skia)
42+
src/FlexRender.Skia/ # Skia backend meta-package (renderer + providers)
43+
src/FlexRender.QrCode.Skia.Render/ # QR provider for Skia (-> Skia + QRCoder)
44+
src/FlexRender.QrCode.Svg.Render/ # QR provider for SVG (-> Svg)
45+
src/FlexRender.QrCode.ImageSharp.Render/ # QR provider for ImageSharp (-> ImageSharp + QRCoder)
46+
src/FlexRender.QrCode/ # QR meta-package (references all renderers)
47+
src/FlexRender.Barcode.Skia.Render/ # Barcode provider for Skia
48+
src/FlexRender.Barcode.Svg.Render/ # Barcode provider for SVG
49+
src/FlexRender.Barcode.ImageSharp.Render/ # Barcode provider for ImageSharp
50+
src/FlexRender.Barcode/ # Barcode meta-package (references all renderers)
51+
src/FlexRender.SvgElement.Skia.Render/ # SvgElement provider for Skia (-> Svg.Skia)
52+
src/FlexRender.SvgElement.Svg.Render/ # SvgElement provider for SVG (native)
53+
src/FlexRender.SvgElement/ # SvgElement meta-package (references all renderers)
54+
src/FlexRender.ImageSharp.Render/ # ImageSharp renderer (-> Core + SixLabors.ImageSharp)
55+
Rendering/ # ImageSharpRenderingEngine, ImageSharpTextRenderer, ImageSharpFontManager
56+
src/FlexRender.ImageSharp/ # ImageSharp backend meta-package (renderer + providers)
57+
src/FlexRender.Svg.Render/ # SVG output renderer (-> Core)
58+
src/FlexRender.Svg/ # SVG backend meta-package (renderer + providers)
4459
src/FlexRender.DependencyInjection/ # Microsoft.Extensions.DI integration
45-
src/FlexRender.MetaPackage/ # Meta-package (references all sub-packages)
60+
src/FlexRender.MetaPackage/ # Meta-package (core + all backends + DI)
4661
4762
src/FlexRender.Cli/ # CLI tool (System.CommandLine, uses all packages)
4863
Commands/ # render, validate, info, watch, debug-layout
4964
5065
tests/FlexRender.Tests/ # Unit + snapshot tests
5166
tests/FlexRender.Cli.Tests/ # CLI integration tests
67+
tests/FlexRender.ImageSharp.Tests/ # ImageSharp visual snapshot tests
5268
examples/ # Example YAML templates
5369
```
5470

5571
## NuGet Package Structure
5672

5773
```
5874
FlexRender.Core (0 external deps)
59-
^ ^ ^
60-
| | |
61-
FlexRender.Yaml FlexRender.Http FlexRender.Skia (YamlDotNet) (SkiaSharp)
62-
^ ^
63-
| |
64-
FlexRender.QrCode FlexRender.Barcode (QRCoder)
65-
| |
75+
^ ^ ^ ^
76+
| | | |
77+
FlexRender.Yaml FlexRender.Http FlexRender.Skia.Render FlexRender.ImageSharp.Render FlexRender.Svg.Render
78+
^ ^ ^ ^
79+
| | | |
80+
Qr/Bar/SvgElement providers per renderer (Skia/Svg/ImageSharp)
81+
| | |
82+
FlexRender.QrCode / FlexRender.Barcode / FlexRender.SvgElement (meta)
83+
| |
84+
FlexRender.Skia / FlexRender.ImageSharp / FlexRender.Svg (backend meta)
85+
|
6686
FlexRender.DependencyInjection (Microsoft.Extensions.DI)
6787
|
6888
FlexRender.MetaPackage (references all)
@@ -79,7 +99,7 @@ SkiaSharp is a managed C# binding over the native Skia engine. On Linux (includi
7999
| `SkiaSharp.NativeAssets.Linux` | Standard Linux environments with system libs (fontconfig, freetype) |
80100
| `SkiaSharp.NativeAssets.Linux.NoDependencies` | Minimal/Docker containers without system libs |
81101

82-
The native assets package is added only to **executable projects** (CLI, tests, examples) -- not to library projects like `FlexRender.Skia`. Library consumers bring their own native assets for their target platform.
102+
The native assets package is added only to **executable projects** (CLI, tests, examples) -- not to library projects like `FlexRender.Skia.Render`. Library consumers bring their own native assets for their target platform.
83103

84104
Similarly, `FlexRender.HarfBuzz` requires `HarfBuzzSharp.NativeAssets.Linux` on Linux. Add it to executable projects that use HarfBuzz text shaping.
85105

@@ -96,6 +116,7 @@ YAML Template
96116
-> TemplateProcessor (resolve {{variable}} expressions in element properties)
97117
-> LayoutEngine (two-pass: MeasureAllIntrinsics -> ComputeLayout -> LayoutNode tree)
98118
-> SkiaRenderer (traverse LayoutNode tree -> draw to SKBitmap via SkiaSharp)
119+
OR ImageSharpRenderer (traverse LayoutNode tree -> draw via SixLabors.ImageSharp)
99120
```
100121

101122
### FlexRenderBuilder API
@@ -149,7 +170,8 @@ byte[] png = await render.Render(_templates["receipt"], data);
149170
| Parsing | `TemplateParser`, `Template`, `CanvasSettings`, `TextElement`, `FlexElement`, `QrElement`, `BarcodeElement`, `ImageElement`, `SeparatorElement`, `TableElement`, `TableColumn`, `TableRow`, `EachElement`, `IfElement` |
150171
| Template Engine | `TemplateExpander`, `TemplateProcessor`, `ExpressionLexer`, `ExpressionEvaluator`, `TemplateContext`, `InlineExpressionParser`, `InlineExpressionEvaluator`, `FilterRegistry`, `ITemplateFilter` |
151172
| Layout | `LayoutEngine`, `LayoutNode`, `LayoutContext`, `LayoutSize`, `IntrinsicSize`, `Unit`, `UnitParser`, `MarginValue`, `MarginValues`, `PaddingParser.ParseMargin` |
152-
| Rendering | `SkiaRender` (IFlexRender impl), `SkiaRenderer`, `TextRenderer`, `FontManager`, `ColorParser`, `RotationHelper`, `BmpEncoder`, `BoxShadowParser`, `GradientParser` |
173+
| Rendering (Skia) | `SkiaRender` (IFlexRender impl), `SkiaRenderer`, `TextRenderer`, `FontManager`, `ColorParser`, `RotationHelper`, `BmpEncoder`, `BoxShadowParser`, `GradientParser` |
174+
| Rendering (ImageSharp) | `ImageSharpRender` (IFlexRender impl), `ImageSharpRenderingEngine`, `ImageSharpTextRenderer`, `ImageSharpFontManager` |
153175
| Providers | `IContentProvider<T,O>`, `QrProvider`, `BarcodeProvider`, `ImageProvider` |
154176
| Loaders | `FileResourceLoader`, `Base64ResourceLoader`, `EmbeddedResourceLoader`, `HttpResourceLoader` |
155177
| DI | `ServiceCollectionExtensions.AddFlexRender()` |
@@ -205,7 +227,7 @@ These limits exist to prevent abuse and resource exhaustion. Never remove or wea
205227

206228
## Test Conventions
207229

208-
- **Total tests**: 1204 (unit + snapshot + integration)
230+
- **Total tests**: 1264+ (unit + snapshot + integration, including ImageSharp snapshot tests)
209231
- **Framework**: xUnit with `[Fact]` and `[Theory]`/`[InlineData]`
210232
- **Assertions**: `Assert.*` from xUnit; `FluentAssertions` in some tests
211233
- **Naming**: `MethodUnderTest_Scenario_ExpectedResult` (e.g., `Parse_SimpleTextElement_ParsesCorrectly`)
@@ -290,7 +312,9 @@ dotnet run --project src/FlexRender.Cli -- watch template.yaml -d data.json -o p
290312
dotnet run --project src/FlexRender.Cli -- debug-layout template.yaml -d data.json
291313
```
292314

293-
Global options: `-v`/`--verbose`, `--fonts <dir>`, `--scale <float>`
315+
Global options: `-v`/`--verbose`, `--fonts <dir>`, `--scale <float>`, `-b`/`--backend <skia|imagesharp>`
316+
317+
The `--backend` option selects the rendering backend: `skia` (default, full features) or `imagesharp` (pure .NET, no native deps, no QR/barcode/SVG element support).
294318

295319
**Working directory matters:** CLI resolves all relative paths (template, data, fonts, image `src`) from the current working directory. When running examples, `cd` into `examples/` first, otherwise assets like `assets/fonts/Inter-Regular.ttf` won't be found.
296320

@@ -365,10 +389,7 @@ var sb = new StringBuilder(estimatedCapacity);
365389

366390
1. Add property to `TemplateElement` in `Parsing/Ast/TemplateElement.cs`
367391
2. Parse the property in `ElementParsers.cs`
368-
3. **CRITICAL**: Copy the property in ALL THREE `CopyBaseProperties()` methods:
369-
- `TemplateExpander.CopyBaseProperties()` (FlexRender.Core)
370-
- `TemplatePreprocessor.CopyBaseProperties()` (FlexRender.Skia)
371-
- `SvgPreprocessor.CopyBaseProperties()` (FlexRender.Svg)
392+
3. Add the property to `TemplateElement.CopyBaseProperties()` (single source of truth in `Parsing/Ast/TemplateElement.cs`)
372393
4. If the property contains expressions (e.g., `{{variable}}`), also add `ProcessExpression()` calls in the preprocessors
373394
5. Write tests to verify the property survives the full rendering pipeline
374395

Directory.Packages.props

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@
44
</PropertyGroup>
55
<ItemGroup>
66
<!-- Production dependencies -->
7+
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
8+
<PackageVersion Include="xunit.v3" Version="3.2.2" />
79
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
8-
<PackageVersion Include="SkiaSharp" Version="3.119.1" />
9-
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.1" />
10-
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="3.119.1" />
11-
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="3.119.1" />
12-
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.2" />
13-
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="3.119.1" />
10+
<PackageVersion Include="SkiaSharp" Version="3.119.2" />
11+
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.2" />
12+
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="3.119.2" />
13+
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="3.119.2" />
14+
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.3" />
15+
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="3.119.2" />
1416
<PackageVersion Include="QRCoder" Version="1.7.0" />
17+
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
18+
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
19+
<PackageVersion Include="SixLabors.Fonts" Version="2.1.3" />
1520
<PackageVersion Include="Svg.Skia" Version="3.4.1" />
1621
<!-- Microsoft.Extensions: version 10.x for net10.0, version 8.x for net8.0 -->
1722
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.2" Condition="'$(TargetFramework)' == 'net10.0'" />
@@ -25,9 +30,7 @@
2530
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.102" />
2631
<!-- Test dependencies -->
2732
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
28-
<PackageVersion Include="xunit" Version="2.9.3" />
2933
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
30-
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
3134
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
3235
</ItemGroup>
3336
</Project>

FlexRender.slnx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,52 @@
11
<Solution>
22
<Folder Name="/src/">
33
<Project Path="src/FlexRender.Core/FlexRender.Core.csproj" />
4-
<Project Path="src/FlexRender.Yaml/FlexRender.Yaml.csproj" />
5-
<Project Path="src/FlexRender.Http/FlexRender.Http.csproj" />
6-
<Project Path="src/FlexRender.Skia/FlexRender.Skia.csproj" />
7-
<Project Path="src/FlexRender.Svg/FlexRender.Svg.csproj" />
8-
<Project Path="src/FlexRender.QrCode/FlexRender.QrCode.csproj" />
9-
<Project Path="src/FlexRender.Barcode/FlexRender.Barcode.csproj" />
10-
<Project Path="src/FlexRender.HarfBuzz/FlexRender.HarfBuzz.csproj" />
114
<Project Path="src/FlexRender.DependencyInjection/FlexRender.DependencyInjection.csproj" />
125
<Project Path="src/FlexRender.MetaPackage/FlexRender.MetaPackage.csproj" />
136
<Project Path="src/FlexRender.Cli/FlexRender.Cli.csproj" />
147
</Folder>
8+
<Folder Name="/src/Barcode/">
9+
<Project Path="src/FlexRender.Barcode.Svg.Render/FlexRender.Barcode.Svg.Render.csproj" />
10+
<Project Path="src/FlexRender.Barcode.Skia.Render/FlexRender.Barcode.Skia.Render.csproj" />
11+
<Project Path="src/FlexRender.Barcode.ImageSharp.Render/FlexRender.Barcode.ImageSharp.Render.csproj" />
12+
<Project Path="src/FlexRender.Barcode/FlexRender.Barcode.csproj" />
13+
</Folder>
14+
<Folder Name="/src/Parsers/">
15+
<Project Path="src/FlexRender.Yaml/FlexRender.Yaml.csproj" />
16+
</Folder>
17+
<Folder Name="/src/QR/">
18+
<Project Path="src/FlexRender.QrCode.Svg.Render/FlexRender.QrCode.Svg.Render.csproj" />
19+
<Project Path="src/FlexRender.QrCode.Skia.Render/FlexRender.QrCode.Skia.Render.csproj" />
20+
<Project Path="src/FlexRender.QrCode.ImageSharp.Render/FlexRender.QrCode.ImageSharp.Render.csproj" />
21+
<Project Path="src/FlexRender.QrCode/FlexRender.QrCode.csproj" />
22+
</Folder>
23+
<Folder Name="/src/Renderers/">
24+
<Project Path="src/FlexRender.HarfBuzz/FlexRender.HarfBuzz.csproj" />
25+
<Project Path="src/FlexRender.ImageSharp/FlexRender.ImageSharp.csproj" />
26+
<Project Path="src/FlexRender.ImageSharp.Render/FlexRender.ImageSharp.Render.csproj" />
27+
<Project Path="src/FlexRender.Skia/FlexRender.Skia.csproj" />
28+
<Project Path="src/FlexRender.Skia.Render/FlexRender.Skia.Render.csproj" />
29+
<Project Path="src/FlexRender.Svg/FlexRender.Svg.csproj" />
30+
<Project Path="src/FlexRender.Svg.Render/FlexRender.Svg.Render.csproj" />
31+
</Folder>
32+
<Folder Name="/src/ResourceLoaders/">
33+
<Project Path="src/FlexRender.Http/FlexRender.Http.csproj" />
34+
</Folder>
35+
<Folder Name="/src/SvgElement/">
36+
<Project Path="src/FlexRender.SvgElement.Skia.Render/FlexRender.SvgElement.Skia.Render.csproj" />
37+
<Project Path="src/FlexRender.SvgElement/FlexRender.SvgElement.csproj" />
38+
<Project Path="src/FlexRender.SvgElement.Svg.Render/FlexRender.SvgElement.Svg.Render.csproj" />
39+
</Folder>
1540
<Folder Name="/tests/">
1641
<Project Path="tests/FlexRender.Tests/FlexRender.Tests.csproj" />
1742
<Project Path="tests/FlexRender.Cli.Tests/FlexRender.Cli.Tests.csproj" />
43+
<Project Path="tests/FlexRender.ImageSharp.Tests/FlexRender.ImageSharp.Tests.csproj" />
1844
</Folder>
1945
<Folder Name="/examples/">
2046
<Project Path="examples/YamlRenderExample/YamlRenderExample.csproj" />
2147
<Project Path="examples/AstRenderExample/AstRenderExample.csproj" />
48+
<Project Path="examples/SkiaRenderExample/SkiaRenderExample.csproj" />
49+
<Project Path="examples/ImageSharpRenderExample/ImageSharpRenderExample.csproj" />
50+
<Project Path="examples/SvgRenderExample/SvgRenderExample.csproj" />
2251
</Folder>
2352
</Solution>

0 commit comments

Comments
 (0)