-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathllms-full.txt
More file actions
2052 lines (1632 loc) · 85.2 KB
/
Copy pathllms-full.txt
File metadata and controls
2052 lines (1632 loc) · 85.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# FlexRender -- Full Reference
> A modular .NET library for rendering images from YAML templates with flexbox layout. Render-backend agnostic with SkiaSharp as the default backend. Fully AOT-compatible with no reflection.
## Project Structure
```
src/FlexRender.Core/ # Core library (0 external dependencies)
Abstractions/ # ILayoutRenderer<T>, ITemplateParser, IResourceLoader
Configuration/ # FlexRenderBuilder, FlexRenderOptions, ResourceLimits
Layout/ # Two-pass flexbox layout engine (LayoutEngine, LayoutNode, LayoutSize)
Units/ # Unit, UnitParser, PaddingValues, PaddingParser
FlexEnums.cs # FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, AlignSelf, TextDirection
FontSizeResolver.cs # Resolve fontSize string to pixels
LayoutEngine.cs # Main layout calculator
LayoutNode.cs # Computed layout tree node
LayoutContext.cs # Layout computation context
LayoutSize.cs # Width/Height measurement result
IntrinsicSize.cs # Min/Max width/height measurements
LineHeightResolver.cs # Resolve lineHeight string to pixels
Loaders/ # FileResourceLoader, Base64ResourceLoader, EmbeddedResourceLoader
Parsing/
Ast/ # AST models: Template, CanvasSettings, TemplateElement, TextElement, FlexElement, QrElement, BarcodeElement, ImageElement, SvgElement, SeparatorElement, TableElement, TableColumn, TableRow
TemplateEngine/ # TemplateProcessor, ExpressionLexer, ExpressionEvaluator, TemplateContext, InlineExpressionParser, InlineExpressionEvaluator, FilterRegistry, ITemplateFilter
Filters/ # Built-in filters: CurrencyFilter, CurrencySymbolFilter, NumberFilter, UpperFilter, LowerFilter, TrimFilter, TruncateFilter, FormatFilter
Values/ # TemplateValue (abstract), StringValue, NumberValue, BoolValue, NullValue, ArrayValue, ObjectValue
src/FlexRender.Yaml/ # YAML template parser (-> Core + YamlDotNet)
Parsing/
TemplateParser.cs # YAML to AST parser (includes EachElement, IfElement)
KnownProperties.cs # YAML property validation with Levenshtein "Did you mean?" suggestions
src/FlexRender.Xml/ # XML template parser, alternative to YAML (same Template AST)
Parsing/
XmlTemplateParser.cs # XML to AST parser; lowers XML tree to the shared element parsers, same KnownProperties validation + ResourceLimits
src/FlexRender.Http/ # HTTP resource loader (-> Core)
HttpResourceLoader.cs # Load images/fonts from HTTP/HTTPS URLs
FlexRenderBuilderExtensions.cs # WithHttpLoader() extension
src/FlexRender.Skia.Render/ # SkiaSharp renderer (-> Core + SkiaSharp)
Abstractions/ # ISkiaRenderer, IFontLoader, IImageLoader, IFontManager
Rendering/ # SkiaRenderer, TextRenderer, FontManager, ColorParser, RotationHelper, BmpEncoder, BoxShadowParser, GradientParser
Loaders/ # FontLoader, ImageLoader
Providers/ # IContentProvider<T,O>, ImageProvider
src/FlexRender.Skia/ # Skia backend meta-package (renderer + providers)
src/FlexRender.QrCode.Skia.Render/ # QR provider for Skia (-> Skia + QRCoder)
Providers/ # QrProvider (implements IContentProvider<QrElement, SKBitmap>)
src/FlexRender.QrCode.Svg.Render/ # QR provider for SVG (-> Svg)
Providers/ # QrSvgProvider (implements ISvgContentProvider<QrElement>)
src/FlexRender.QrCode.ImageSharp.Render/ # QR provider for ImageSharp (-> ImageSharp + QRCoder)
Providers/ # QrImageSharpProvider (implements IImageSharpContentProvider<QrElement>)
src/FlexRender.QrCode/ # QR meta-package (references all renderers)
src/FlexRender.Barcode.Skia.Render/ # Barcode provider for Skia
Providers/ # BarcodeProvider (implements IContentProvider<BarcodeElement, SKBitmap>)
src/FlexRender.Barcode.Svg.Render/ # Barcode provider for SVG
Providers/ # BarcodeSvgProvider (implements ISvgContentProvider<BarcodeElement>)
src/FlexRender.Barcode.ImageSharp.Render/ # Barcode provider for ImageSharp
Providers/ # BarcodeImageSharpProvider (implements IImageSharpContentProvider<BarcodeElement>)
src/FlexRender.Barcode/ # Barcode meta-package (references all renderers)
src/FlexRender.HarfBuzz/ # HarfBuzz text shaping (-> Skia + SkiaSharp.HarfBuzz)
HarfBuzzTextShaper.cs # Shaped text measurement and drawing
SkiaBuilderExtensions.cs # .WithHarfBuzz() extension
src/FlexRender.ImageSharp.Render/ # ImageSharp renderer (-> Core + SixLabors.ImageSharp)
Rendering/ # ImageSharpRenderingEngine, ImageSharpTextRenderer, ImageSharpFontManager
ImageSharpRender.cs # IFlexRender implementation for ImageSharp
ImageSharpBuilder.cs # Builder for ImageSharp renderer configuration
src/FlexRender.ImageSharp/ # ImageSharp backend meta-package (renderer + providers)
src/FlexRender.SvgElement.Skia.Render/ # SvgElement provider for Skia (-> Svg.Skia)
Providers/ # SvgElementProvider (implements IContentProvider<SvgElement, SKBitmap>)
src/FlexRender.SvgElement.Svg.Render/ # SvgElement provider for SVG (native)
Providers/ # SvgElementSvgProvider (implements ISvgContentProvider<SvgElement>)
src/FlexRender.SvgElement/ # SvgElement meta-package (references all renderers)
src/FlexRender.Svg.Render/ # SVG output renderer (-> Core)
SvgRender.cs # Renders templates to SVG format
SvgBuilder.cs # Builder for SVG renderer configuration
src/FlexRender.Svg/ # SVG backend meta-package (renderer + providers)
src/FlexRender.Content.Markdown/ # Markdown content parser (-> Core + Markdig)
src/FlexRender.Content.Html/ # HTML content parser (-> Core + HtmlAgilityPack)
src/FlexRender.Content.Ndc/ # NDC (ATM receipt) content parser (-> Core)
src/FlexRender.DependencyInjection/ # Microsoft.Extensions.DI integration
ServiceCollectionExtensions.cs # AddFlexRender() extension method
src/FlexRender.MetaPackage/ # Meta-package (core + all backends + DI)
src/FlexRender.Cli/ # CLI tool (System.CommandLine)
Commands/ # render, validate, info, watch, debug-layout
tests/FlexRender.Tests/ # Unit + snapshot tests
tests/FlexRender.Cli.Tests/ # CLI integration tests
tests/FlexRender.ImageSharp.Tests/ # ImageSharp visual snapshot tests
examples/ # Example YAML templates with data and output
```
## NuGet Package Structure
```
FlexRender.Core (0 external deps)
^ ^ ^ ^
| | | |
FlexRender.Yaml FlexRender.Http FlexRender.Skia.Render FlexRender.ImageSharp.Render FlexRender.Svg.Render
^ ^ ^ ^
| | | |
Qr/Bar/SvgElement providers per renderer (Skia/Svg/ImageSharp)
| | |
FlexRender.QrCode / FlexRender.Barcode / FlexRender.SvgElement (meta)
| |
FlexRender.Skia / FlexRender.ImageSharp / FlexRender.Svg (backend meta)
|
FlexRender.DependencyInjection (Microsoft.Extensions.DI)
|
FlexRender.MetaPackage (references all)
```
| Package | Depends On | External Deps |
|---------|-----------|---------------|
| FlexRender.Core | -- | (none) |
| FlexRender.Yaml | Core | YamlDotNet 16.3.0 |
| FlexRender.Http | Core | (none) |
| FlexRender.Skia.Render | Core | SkiaSharp 3.119.2 |
| FlexRender.Skia | Skia.Render + providers | SkiaSharp 3.119.2 |
| FlexRender.QrCode.Skia.Render | Skia.Render | QRCoder 1.7.0 |
| FlexRender.QrCode.Svg.Render | Svg.Render | QRCoder 1.7.0 |
| FlexRender.QrCode.ImageSharp.Render | ImageSharp.Render | QRCoder 1.7.0 |
| FlexRender.QrCode | All renderers | -- |
| FlexRender.Barcode.Skia.Render | Skia.Render | (none) |
| FlexRender.Barcode.Svg.Render | Svg.Render | (none) |
| FlexRender.Barcode.ImageSharp.Render | ImageSharp.Render | (none) |
| FlexRender.Barcode | All renderers | -- |
| FlexRender.HarfBuzz | Skia.Render | SkiaSharp.HarfBuzz 3.119.2, HarfBuzzSharp 8.3.1.3 |
| FlexRender.ImageSharp.Render | Core | SixLabors.ImageSharp, SixLabors.ImageSharp.Drawing, SixLabors.Fonts |
| FlexRender.ImageSharp | ImageSharp.Render + providers | SixLabors.ImageSharp, SixLabors.ImageSharp.Drawing, SixLabors.Fonts |
| FlexRender.DependencyInjection | Core | Microsoft.Extensions.DI |
| FlexRender.SvgElement.Skia.Render | Skia.Render | Svg.Skia |
| FlexRender.SvgElement.Svg.Render | Svg.Render | (none) |
| FlexRender.SvgElement | All renderers | -- |
| FlexRender.Svg.Render | Core | (none) |
| FlexRender.Svg | Svg.Render + providers | (none) |
| FlexRender.Content.Markdown | Core | Markdig 1.1.1 |
| FlexRender.Content.Html | Core | HtmlAgilityPack 1.12.4 |
| FlexRender.Content.Ndc | Core | (none) |
| FlexRender.MetaPackage | All | -- |
| flexrender-cli | All | System.CommandLine |
**Linux/Docker:** SkiaSharp requires native libraries on Linux. Add `SkiaSharp.NativeAssets.Linux` (or `SkiaSharp.NativeAssets.Linux.NoDependencies` for minimal containers without fontconfig/freetype) to your executable project to avoid `DllNotFoundException: libSkiaSharp` at runtime. When using `FlexRender.HarfBuzz`, also add `HarfBuzzSharp.NativeAssets.Linux` to avoid `DllNotFoundException: libHarfBuzzSharp`.
## Rendering Pipeline
```
YAML Template
-> TemplateParser (YAML -> AST: Template with CanvasSettings + TemplateElement tree, including EachElement/IfElement)
+ KnownProperties (validate YAML keys, warn on unknown properties with "Did you mean?" suggestions)
-> TemplateExpander (expand EachElement/IfElement to concrete elements based on data) [async only]
-> TemplateProcessor (resolve {{variable}} expressions in element properties) [async only]
-> LayoutEngine (two-pass: MeasureAllIntrinsics -> ComputeLayout -> LayoutNode tree)
-> SkiaRenderer (traverse LayoutNode tree -> draw to SKBitmap via SkiaSharp)
OR ImageSharpRenderer (traverse LayoutNode tree -> draw via SixLabors.ImageSharp)
```
**Async-only API:** The entire pipeline from expansion through rendering is async. There are no synchronous `Expand()`, `Process()`, `Measure()`, `ComputeLayout()`, or `Render()` methods. All public API methods return `Task` or `Task<T>`. This is required because `ContentElement` expansion involves async I/O (loading external content sources).
### XML as an Alternative Input
Templates may be authored in **XML** (`FlexRender.Xml`) instead of YAML. The XML parser (`XmlTemplateParser`) produces the **same `Template` AST** as `TemplateParser` -- same element types, same properties, same `KnownProperties` validation (with "Did you mean?" suggestions), same `ResourceLimits`. XML is offered because LLMs often emit attributes more reliably than YAML indentation. Render with the `RenderXml` extension (mirrors `RenderYaml`):
```csharp
var render = new FlexRenderBuilder().WithSkia().Build();
byte[] png = await render.RenderXml(xml, data);
// Parse once, reuse:
var parser = new XmlTemplateParser();
byte[] png = await render.RenderXml(xml, data, parser: parser);
```
Mapping rules (XML -> same AST as YAML):
- Single root `<flexrender>`; `<canvas width="300" .../>` child for canvas; `template`/`fonts` via attributes/`<fonts>`; every other child of the root is a layout element.
- Element type = XML local-name: `<text/>`, `<flex>`, `<chart/>`, `<rect/>`, `<each>`, `<if>`, `<table>`, `<draw>`.
- Scalar properties = attributes (`<text size="1em" color="#f00"/>`); names identical to YAML (kebab-case and camelCase pass through).
- `<text>Hello</text>` is equivalent to `<text content="Hello"/>` (content attribute wins if both present; svg `content` follows the same rule).
- Layout containers: `flex`/`each` children are nested elements directly (`children`). Wrappers: `if` -> `<then>`/`<else>`/`<else-if>`; `table` -> `<columns>`/`<rows>`; `chart` -> `<series .../>`, `<categories>`, `<x-labels>`/`<y-labels>`, `<palette>`; `draw` -> `<shapes>`.
- List attributes: `data="12,30,22,48"` -> number array; scatter/bubble `points="1,2; 3,4"` -> tuples; `palette="#f00,#0f0"` -> color list (or `palette="ocean"` named).
Minimal example:
```xml
<flexrender>
<canvas width="300"/>
<text content="Hello" size="1.5em"/>
</flexrender>
```
See `docs/wiki/Xml-Syntax.md` for the full XML<->YAML mapping and a side-by-side flex+chart example.
### Template Caching
Templates can be parsed once and cached, then rendered with different data:
```csharp
// Parse once (at startup)
var parser = new TemplateParser();
var template = parser.Parse(yaml);
templateCache["receipt"] = template;
// Render many times (per request)
var template = templateCache["receipt"];
var bytes = await render.Render(template, data); // Expander called internally
```
### Two-Pass Layout Engine
1. **Pass 1 -- Intrinsic Measurement** (`MeasureAllIntrinsics`): Bottom-up traversal computes `IntrinsicSize` (MinWidth, MaxWidth, MinHeight, MaxHeight) for every element. Uses `TextMeasurer` delegate for content-based text sizing.
2. **Pass 2 -- Layout** (`ComputeLayout`): Top-down traversal assigns positions and sizes, producing a `LayoutNode` tree with (X, Y, Width, Height).
`IntrinsicSize` is a `readonly record struct` with `WithPadding(float)`, `WithPadding(PaddingValues)`, and `WithMargin(float)` helper methods.
## Template YAML Structure
```yaml
template: # Required: template metadata
name: "my-template" # Template name (string)
version: 1 # Template version (int)
# culture: "ru-RU" # Optional: culture for number/date formatting
fonts: # Optional: list format (recommended) or dictionary format
- "assets/fonts/Inter-Regular.ttf" # First unnamed = default/main
- "assets/fonts/Inter-Bold.ttf" # File path, embedded://, or http://
canvas: # Required: canvas configuration
fixed: width # Which dimension is fixed (width|height|both|none)
width: 300 # Canvas width in pixels (default: 300)
height: 0 # Canvas height in pixels (0 = auto when not fixed)
background: "#ffffff" # Background color (default: "#ffffff")
rotate: "none" # Canvas rotation (see below)
layout: # Required: array of elements
- type: text # Element type
# ... element properties
```
## Canvas Settings
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fixed | FixedDimension | width | Which dimension is fixed: `width`, `height`, `both`, `none` |
| width | int | 300 | Canvas width in pixels |
| height | int | 0 | Canvas height in pixels (0 = auto when unfixed) |
| background | string | "#ffffff" | Background color in hex format |
| rotate | string | "none" | Post-render rotation |
| text-direction | TextDirection | Ltr | Text direction: `ltr` (default), `rtl`. Alias: `dir` |
### Canvas Rotation Values
| Value | Effect |
|-------|--------|
| `"none"` | No rotation |
| `"left"` | 270 degrees (90 CCW) -- swaps width/height |
| `"right"` | 90 degrees CW -- swaps width/height |
| `"flip"` | 180 degrees |
| `"<number>"` | Arbitrary degrees (e.g., `"45"`) |
Rotation is applied AFTER rendering. For thermal printers: use `"right"` to rotate a wide receipt into a tall image. All element sizes are specified for the PRE-rotation layout.
## Common Element Properties
All elements inherit from `TemplateElement`:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| padding | string | "0" | Inner spacing (px, %, em). Supports non-uniform CSS shorthand |
| margin | string | "0" | Outer spacing (px, %, em). Supports non-uniform CSS shorthand and `auto` |
| background | string? | null | Background color in hex, or CSS gradient (null = transparent) |
| opacity | float | 1.0 | Element opacity (0.0-1.0). Applied via SaveLayer, affects element and all children |
| box-shadow | string? | null | Box shadow: `"offsetX offsetY blurRadius color"` (e.g., `"4 4 8 #00000040"`) |
| rotate | string | "none" | Element rotation (none/left/right/flip/degrees). Rendered for: text, image, qr, barcode. Rotation is around the element's center point |
| position | Position | Static | Positioning mode: `static`, `relative`, `absolute` |
| top | string? | null | Top inset for positioned elements (px, %, em) |
| right | string? | null | Right inset for positioned elements (px, %, em) |
| bottom | string? | null | Bottom inset for positioned elements (px, %, em) |
| left | string? | null | Left inset for positioned elements (px, %, em) |
| aspectRatio | float? | null | Width/height ratio. When one dimension is known, the other is computed |
| minWidth | string? | null | Minimum width constraint (px, %, em) |
| maxWidth | string? | null | Maximum width constraint (px, %, em) |
| minHeight | string? | null | Minimum height constraint (px, %, em) |
| maxHeight | string? | null | Maximum height constraint (px, %, em) |
| display | Display | Flex | Display mode: `flex` (default), `none` (removes from layout flow) |
| text-direction | TextDirection? | null | Text direction override: `ltr`, `rtl` (null = inherit from parent/canvas). Alias: `dir` |
| border | string? | null | Border shorthand for all sides: `"width style color"` (e.g., `"2 solid #333"`) |
| borderTop | string? | null | Per-side border shorthand for top: `"width style color"` |
| borderRight | string? | null | Per-side border shorthand for right side |
| borderBottom | string? | null | Per-side border shorthand for bottom side |
| borderLeft | string? | null | Per-side border shorthand for left side |
| borderWidth | string? | null | Border width override for all sides (px, em) |
| borderColor | string? | null | Border color override for all sides |
| borderStyle | string? | null | Border style override: `solid`, `dashed`, `dotted`, `none` |
| borderRadius | string? | null | Corner rounding radius (px, em, %) |
## Non-Uniform Padding and Margin
Both `padding` and `margin` accept CSS-like shorthand with 1 to 4 space-separated values:
| Format | Meaning |
|--------|---------|
| `"20"` | All sides = 20 |
| `"20 40"` | Top/Bottom = 20, Left/Right = 40 |
| `"20 40 30"` | Top = 20, Left/Right = 40, Bottom = 30 |
| `"20 40 30 10"` | Top = 20, Right = 40, Bottom = 30, Left = 10 |
Each value can include a unit suffix: `"10px 5% 2em 20"`. Parsed by `PaddingParser` into `PaddingValues` (a `readonly record struct` with Top, Right, Bottom, Left, Horizontal, Vertical).
During intrinsic measurement (Pass 1), percentage values resolve against 0 and em values against 16px default. During layout (Pass 2), percentage resolves against parent size and em against element font size.
## Positioning
Elements support CSS-like positioning via the `position` property:
| Value | Behavior |
|-------|----------|
| `static` | Normal flow (default). Inset properties are ignored |
| `relative` | Offset from its normal flow position. Still occupies space in the flow |
| `absolute` | Removed from normal flow. Positioned relative to nearest flex container |
Inset properties (`top`, `right`, `bottom`, `left`) accept px, %, or em values.
```yaml
# Badge overlay in top-right corner
- type: flex
width: 300
height: 200
children:
- type: text
content: "Background content"
- type: text
content: "Badge"
position: absolute
top: 5
right: 5
background: "#ff0000"
color: "#ffffff"
padding: "2 8"
```
## Aspect Ratio
The `aspectRatio` property enforces a width-to-height ratio. When one dimension is specified, the other is computed automatically:
```yaml
# 16:9 aspect ratio image container
- type: flex
width: 320
aspectRatio: 1.7778
background: "#000000"
```
## Auto Margins
Margins support `auto` values for centering elements within flex containers:
```yaml
# Horizontal centering with auto margins
- type: flex
direction: row
width: 300
children:
- type: text
content: "Centered"
width: 100
margin: "0 auto"
# Push element to the right
- type: flex
direction: row
width: 300
children:
- type: text
content: "Right-aligned"
margin: "0 0 0 auto"
```
Auto margins on the main axis consume free space before `justify-content` is applied. Auto margins on the cross axis override `align-items`/`align-self`.
## Overflow
FlexElement containers support content clipping via the `overflow` property:
| Value | Behavior |
|-------|----------|
| `visible` | Content renders outside container bounds (default) |
| `hidden` | Content is clipped at container bounds |
```yaml
# Clipped container
- type: flex
width: 200
height: 100
overflow: hidden
children:
- type: text
content: "This text will be clipped if it exceeds the container bounds"
```
## Borders
All elements support CSS-like border properties. Borders consume space in layout (added to element size alongside padding).
**Shorthand format:** `"width style color"` (e.g., `"2 solid #333"`, `"1 dashed"`, `"3"`)
**CSS cascade order:**
1. `border` shorthand sets all four sides
2. `borderWidth`, `borderColor`, `borderStyle` override individual properties on all sides
3. `borderTop`, `borderRight`, `borderBottom`, `borderLeft` override specific sides
**Border styles:** `solid`, `dashed`, `dotted`, `none`
```yaml
# Solid border with rounded corners
- type: flex
border: "2 solid #3498db"
border-radius: "12"
padding: "16"
children:
- type: text
content: "Rounded box"
# Per-side borders
- type: flex
border-top: "3 solid #3498db"
border-bottom: "1 dashed #cccccc"
padding: "16"
children:
- type: text
content: "Different borders"
```
## Order
The `order` property controls the visual display order of flex items. Items are sorted by `order` before layout -- lower values appear first. Items with equal `order` preserve source order (stable sort). Default is `0`. Negative values are supported. Absolute-positioned children are excluded from order sorting.
```yaml
# Display order: B (0), C (1), A (2)
- type: flex
direction: row
children:
- type: text
content: "A"
order: 2
- type: text
content: "B"
order: 0
- type: text
content: "C"
order: 1
```
## RTL (Right-to-Left) Support
- Canvas `text-direction`: `ltr` (default), `rtl` -- sets default text direction for the entire template
- Element `text-direction`: `ltr`, `rtl`, or null (inherit from parent/canvas) -- per-element override
- Text `align`: `start` and `end` are logical values that resolve based on direction
- Row layout is mirrored in RTL: items flow right-to-left
- Column layout is unaffected by direction
- Arabic font support: use an Arabic-capable font (e.g., Noto Sans Arabic) in the `fonts` section for Arabic text rendering
- HarfBuzz text shaping: optional `FlexRender.HarfBuzz` package provides proper Arabic/Hebrew glyph shaping via `.WithHarfBuzz()` on the Skia builder
## Fonts
Fonts are registered in the `fonts:` section of the YAML template. Two formats are supported. Supported file types: `.ttf` and `.otf`. Font sources can be local file paths, `embedded://` resources, or `http://` URLs. System fonts are used when no custom font is registered.
### Font Registration -- Dictionary Format (Legacy)
Key-value pairs where the key is a reference name used in `font:` properties:
```yaml
fonts:
default: "assets/fonts/Inter-Regular.ttf"
heading: "assets/fonts/Roboto-Regular.ttf"
icon: "embedded://MyApp.Fonts.icons.ttf"
remote: "https://example.com/font.ttf"
```
### Font Registration -- List Format (Recommended)
An array of font entries. Simple strings and objects with `path`/`name`/`fallback` can be mixed:
```yaml
fonts:
# Simple strings -- first unnamed font automatically becomes "default" (and "main")
- "assets/fonts/Inter-Regular.ttf"
- "assets/fonts/Inter-Bold.ttf"
- "assets/fonts/Inter-Italic.ttf"
# With optional name and fallback
- path: "assets/fonts/Roboto-Regular.ttf"
name: heading
fallback: "Arial"
```
**Rules:**
- The first unnamed font automatically becomes `default` (and `main`)
- Fonts can be mixed: simple strings and objects with `path`/`name`/`fallback`
- Named fonts are referenced via `font:` on elements (e.g., `font: heading`)
### Font Properties on Text Elements
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `font` | string | `main` | Reference to a registered font name |
| `fontFamily` | string | (empty) | CSS-like font family name -- searches registered fonts by FamilyName metadata, then system fonts |
| `fontWeight` | string/number | `normal` | Font weight (see table below), or numeric 100-900 |
| `fontStyle` | string | `normal` | Font style: `normal`, `italic`, `oblique` |
### Font Resolution Priority
```
font (registered name) > fontFamily (family name) > fallback (default)
```
- If `font` is set to a non-default value, resolve by registered name
- If `fontFamily` is set, search registered fonts by FamilyName metadata, then system fonts
- Otherwise, use the default font (`main`)
For weight/style variants: **automatic sibling discovery** scans the same directory as the base font file for `.ttf`/`.otf` files with matching family name and weight/style.
### Automatic Sibling Font Discovery
Only the regular/default font file needs to be registered per family. When a text element uses `fontWeight` or `fontStyle`, FlexRender automatically scans the same directory as the registered font for sibling files with a matching family name and weight/style (within +/-100 units for weight, case-insensitive).
**Convention:** Place all weight/style variants of a font family in the same directory:
```
assets/fonts/
Inter-Regular.ttf # weight 400, upright
Inter-Bold.ttf # weight 700, upright
Inter-SemiBold.ttf # weight 600, upright
Inter-Italic.ttf # weight 400, italic
Inter-BoldItalic.ttf # weight 700, italic
```
Then in templates:
```yaml
- type: text
content: "Bold text"
fontWeight: bold # uses Inter-Bold.ttf automatically
- type: text
content: "Light italic"
fontWeight: light
fontStyle: italic # discovers Inter-LightItalic.ttf if present
```
For multiple font families, register each family's regular font and use `font:` to select:
```yaml
fonts:
- "assets/fonts/Inter-Regular.ttf"
- path: "assets/fonts/Roboto-Regular.ttf"
name: heading
```
```yaml
- type: text
font: heading
fontWeight: bold # discovers Roboto-Bold.ttf in the same directory
content: "Bold Heading"
```
### Table Header Font Properties
Tables support font properties on the header row:
| Property | Aliases | Description |
|----------|---------|-------------|
| `headerFont` | `header-font` | Font name for header cells |
| `headerFontWeight` | `header-fontWeight` | Font weight for headers |
| `headerFontStyle` | `header-fontStyle` | Font style for headers (normal, italic, oblique) |
| `headerFontFamily` | `header-fontFamily` | CSS-like font family for headers |
### fontWeight Values
| Name | Numeric |
|------|---------|
| `thin` | 100 |
| `extra-light` | 200 |
| `light` | 300 |
| `normal` (default) | 400 |
| `medium` | 500 |
| `semi-bold` | 600 |
| `bold` | 700 |
| `extra-bold` | 800 |
| `black` | 900 |
Numeric values (100-900) are also accepted: `fontWeight: 600`.
### fontStyle Values
`normal` (default), `italic`, `oblique`.
### Examples
**Minimal (system fonts only, no registration):**
```yaml
layout:
- type: text
content: "Hello"
fontFamily: "Arial"
fontWeight: bold
```
**List registration with fontWeight/fontStyle:**
```yaml
fonts:
- "assets/fonts/Inter-Regular.ttf"
- "assets/fonts/Inter-Bold.ttf"
- "assets/fonts/Inter-Italic.ttf"
layout:
- type: text
content: "Bold text"
fontWeight: bold
- type: text
content: "Italic text"
fontStyle: italic
```
**Mixed: named + unnamed, font + fontFamily:**
```yaml
fonts:
- "assets/fonts/Inter-Regular.ttf"
- path: "assets/fonts/NotoSansArabic-Regular.ttf"
name: arabic
layout:
- type: text
content: "Inter bold"
fontWeight: bold
- type: text
content: "System Georgia"
fontFamily: "Georgia"
- type: text
content: "Arabic"
font: arabic
```
### Limitations
- **Variable fonts are NOT supported** -- SkiaSharp 3.x does not expose an API for font variation axes. Use separate static font files per weight/style instead.
- Sibling discovery relies on font file metadata (family name, weight, slant). If files use non-standard naming, register them explicitly in the `fonts:` section.
## Flex-Item Properties
All leaf elements (text, image, qr, barcode, separator, rect, circle, ellipse, draw, chart) and flex containers (when nested) have these flex-item properties:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| grow | float | 0 | Flex grow factor |
| shrink | float | 1 | Flex shrink factor |
| basis | string | "auto" | Flex basis (px, %, em, auto) |
| order | int | 0 | Display order for sorting. Lower values appear first. Negative values supported. Stable sort preserves source order for equal values |
| alignSelf | AlignSelf | Auto | Individual alignment override |
| width | string? | null | Explicit width (px, %, em, auto) |
| height | string? | null | Explicit height (px, %, em, auto) |
## Element Type: text (TextElement)
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| content | string | "" | Text content, may contain `{{variable}}` expressions |
| font | string | "main" | Font reference name from `fonts` section |
| fontFamily | string | "" | CSS-like font family name -- searches registered fonts by FamilyName, then system fonts |
| fontWeight | string? | null | Font weight: `thin` (100), `extra-light` (200), `light` (300), `normal` (400), `medium` (500), `semi-bold` (600), `bold` (700), `extra-bold` (800), `black` (900), or numeric 100-900 |
| fontStyle | string? | null | Font style: `normal`, `italic`, `oblique` |
| size | string | "1em" | Font size (px, em, %) |
| color | string | "#000000" | Text color in hex format |
| align | TextAlign | Left | Text alignment: `left`, `center`, `right`, `start` (logical), `end` (logical) |
| wrap | bool | true | Whether text wraps to multiple lines |
| overflow | TextOverflow | Ellipsis | Overflow handling: `ellipsis`, `clip`, `visible` |
| maxLines | int? | null | Maximum number of lines (null = unlimited) |
| lineHeight | string | "" | Line height for multi-line text (see below) |
### lineHeight Values
| Format | Example | Behavior |
|--------|---------|----------|
| Empty string | `""` | Use font-defined default spacing |
| Plain number | `"1.8"` | Multiplier of fontSize |
| Pixel units | `"24px"` | Absolute pixel value |
| Em units | `"2em"` | Relative to element's fontSize |
Resolved by `LineHeightResolver.Resolve(lineHeight, fontSize, defaultLineHeight)`. Values are clamped to >= 0.
## Element Type: flex (FlexElement)
### Container Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| direction | FlexDirection | Column | Main axis direction: `row`, `column`, `row-reverse`, `column-reverse` |
| wrap | FlexWrap | NoWrap | Wrapping behavior: `nowrap`, `wrap`, `wrap-reverse` |
| gap | string | "0" | Gap between items (px, %, em). Shorthand for both row-gap and column-gap |
| rowGap | string? | null | Gap between wrapped lines. Overrides gap for the row direction |
| columnGap | string? | null | Gap between columns. Overrides gap for the column direction |
| justify | JustifyContent | Start | Main axis alignment: `start`, `center`, `end`, `space-between`, `space-around`, `space-evenly` |
| align | AlignItems | Stretch | Cross axis alignment: `start`, `center`, `end`, `stretch`, `baseline` |
| alignContent | AlignContent | Start | Wrapped lines alignment: `start`, `center`, `end`, `stretch`, `space-between`, `space-around`, `space-evenly` |
| overflow | Overflow | Visible | Content clipping: `visible`, `hidden` |
| children | TemplateElement[] | [] | Child elements |
## Element Type: image (ImageElement)
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| src | string | "" | Image source: file path, base64 data URL, embedded://, http:// |
| width | int? | null | Image container width in pixels (null = natural width) |
| height | int? | null | Image container height in pixels (null = natural height) |
| fit | ImageFit | Contain | How the image fits within bounds |
### Image Fit Modes
| Mode | Description |
|------|-------------|
| `fill` | Stretch to fill bounds (may distort aspect ratio) |
| `contain` | Scale to fit within bounds preserving ratio (may have empty space) |
| `cover` | Scale to cover bounds preserving ratio (may be cropped) |
| `none` | Use image's natural size without scaling |
## Element Type: qr (QrElement)
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| data | string | "" | Data to encode in the QR code |
| size | int | 100 | QR code size in pixels (width = height) |
| errorCorrection | ErrorCorrectionLevel | M | Error correction level |
| foreground | string | "#000000" | Foreground (module) color in hex |
### QR Error Correction Levels
| Level | Recovery Capacity |
|-------|------------------|
| `L` | ~7% recovery |
| `M` | ~15% recovery (default) |
| `Q` | ~25% recovery |
| `H` | ~30% recovery |
## Element Type: barcode (BarcodeElement)
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| data | string | "" | Data to encode |
| format | BarcodeFormat | Code128 | Barcode format |
| width | int | 200 | Barcode width in pixels |
| height | int | 80 | Barcode height in pixels |
| showText | bool | true | Show encoded text below barcode |
| foreground | string | "#000000" | Bar color in hex |
### Barcode Formats
| Format | Description |
|--------|-------------|
| `code128` | Alphanumeric, high density |
| `code39` | Alphanumeric, widely supported |
| `ean13` | 13 digits, retail |
| `ean8` | 8 digits, compact retail |
| `upc` | 12 digits, North American retail (UPC-A) |
## Element Type: table (TableElement)
Tabular data with configurable columns, optional header, and support for dynamic (array-based) and static rows. During template expansion, the `table` element is expanded into a tree of `flex` and `text` elements.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| array | string | "" | Path to data array for dynamic rows |
| as | string? | null | Variable name for current item |
| columns | TableColumn[] | -- | Column definitions (required, at least one) |
| rows | TableRow[] | [] | Static rows (alternative to array) |
| headerFont | string? | null | Font for header row |
| headerFontWeight | string? | null | Font weight for header row |
| headerFontStyle | string? | null | Font style for header row (normal, italic, oblique) |
| headerFontFamily | string? | null | CSS-like font family for header row |
| headerColor | string? | null | Text color for header row |
| headerSize | string? | null | Font size for header row |
| headerBackground | string? | null | Background color for header row |
### TableColumn Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| key | string | -- | Data field name (required) |
| label | string? | null | Header text |
| width | string? | null | Explicit column width (px, %, em) |
| grow | float | 0 | Flex grow factor |
| align | TextAlign | Left | Text alignment: left, center, right |
| font | string? | null | Font override for cells |
| color | string? | null | Text color override for cells |
| size | string? | null | Font size override for cells |
| format | string? | null | Format string for cell values |
### TableRow Properties (Static Rows)
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| values | Dictionary<string,string> | -- | Column key to value mapping (required, case-insensitive) |
| font | string? | null | Font override for this row |
| color | string? | null | Text color override |
| size | string? | null | Font size override |
```yaml
# Dynamic table with header
- type: table
array: items
as: item
headerFont: bold
headerBackground: "#333333"
headerColor: "#ffffff"
columns:
- key: name
label: "Product"
grow: 1
- key: price
label: "Price"
width: "80"
align: right
# Static summary table
- type: table
columns:
- key: label
grow: 1
font: bold
- key: value
width: "80"
align: right
rows:
- values: { label: "Subtotal", value: "85.00 $" }
- values: { label: "Tax", value: "8.50 $" }
- values: { label: "Total", value: "93.50 $" }
font: bold
```
## Element Type: separator (SeparatorElement)
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| orientation | SeparatorOrientation | Horizontal | Direction: `horizontal`, `vertical` |
| style | SeparatorStyle | Dotted | Line style: `dotted`, `dashed`, `solid` |
| thickness | float | 1 | Line thickness in pixels |
| color | string | "#000000" | Line color in hex |
| width | string? | null | Explicit width override |
| height | string? | null | Explicit height override |
Horizontal separators stretch to full width and use thickness as height. Vertical separators stretch to full height and use thickness as width. The default style is `dotted`, chosen for the primary receipt printing use case.
## Shape Elements
The `rect`, `circle`, and `ellipse` elements are **box shapes** -- flex boxes that are painted as a filled and/or stroked vector shape. They participate in flex layout exactly like any other box: they honor `width`, `height`, `margin`, `padding`, and all flex-item properties (`grow`, `shrink`, `basis`, `order`, `alignSelf`). The shape is drawn to fill the box content area.
### Shared Box-Shape Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fill | string OR object | none | Solid hex color (e.g. `"#4A90D9"`) OR a gradient object (see below) |
| stroke | string | none | Stroke (outline) color in hex |
| stroke-width | number | 0 | Stroke width in pixels |
| opacity | number | 1.0 | Inherited base opacity, `0..1` |
| radius | unit | none | Corner radius in px/em (**rect only**) |
| size | unit | none | Shorthand setting both width and height = diameter (**circle only**) |
### Gradient Fill Object
The `fill` property accepts an object form to paint a gradient. It is converted internally to a CSS gradient string.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| gradient | string | -- | `linear` or `radial` (required) |
| colors | string[] | -- | Two or more hex color stops (required, minimum 2) |
| angle | number | 0 | Angle in degrees (linear only; radial ignores `angle`) |
```yaml
- type: rect
width: 100
height: 100
fill:
gradient: linear
colors: ["#f00", "#00f"]
angle: 45
```
## Element Type: rect (RectElement)
A flex box drawn as a filled/stroked rectangle, optionally with rounded corners.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fill | string OR object | none | Solid hex color or gradient object |
| stroke | string | none | Stroke color in hex |
| stroke-width | number | 0 | Stroke width in pixels |
| opacity | number | 1.0 | Base opacity, `0..1` |
| radius | unit | none | Corner radius (px/em) |
```yaml
- type: rect
width: 100
height: 50
fill: "#4A90D9"
stroke: "#333333"
stroke-width: 2
radius: 4
```
## Element Type: circle (CircleElement)
A flex box drawn as a circle. Use `size` as a shorthand to set both width and height (the diameter); the circle is inscribed in the resulting box.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fill | string OR object | none | Solid hex color or gradient object |
| stroke | string | none | Stroke color in hex |
| stroke-width | number | 0 | Stroke width in pixels |
| opacity | number | 1.0 | Base opacity, `0..1` |
| size | unit | none | Sets both width and height (diameter) |
```yaml
- type: circle
size: 40
fill: "#e74c3c"
```
## Element Type: ellipse (EllipseElement)
A flex box drawn as an ellipse that fills its `width` by `height` box.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fill | string OR object | none | Solid hex color or gradient object |
| stroke | string | none | Stroke color in hex |
| stroke-width | number | 0 | Stroke width in pixels |
| opacity | number | 1.0 | Base opacity, `0..1` |
```yaml
- type: ellipse
width: 120
height: 60
fill: "#2ecc71"
```
## Element Type: draw (DrawElement)
A flex box that holds an ordered list of absolute-coordinate `shapes`. Shapes are painted in list order (painter's algorithm -- later shapes are drawn on top). All coordinates are relative to the `draw` element's top-left corner. The number of shapes is capped by `ResourceLimits.MaxShapesPerDraw` (default 1000).
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| width | unit | none | Box width |
| height | unit | none | Box height |
| shapes | Shape[] | [] | Ordered list of shapes (painted in order) |
Each entry in `shapes` is a single-key object whose key selects the shape kind:
| Shape | Keys | Description |
|-------|------|-------------|
| line | `x1`, `y1`, `x2`, `y2`, `stroke`, `stroke-width` | Straight line segment |
| polyline | `points` (`[[x,y],...]`), `stroke`, `stroke-width`, `fill` | Connected line segments, optionally filled |
| rect | `x`, `y`, `width`, `height`, `fill`, `stroke`, `stroke-width`, `radius` | Rectangle (optional rounded corners) |
| circle | `cx`, `cy`, `r`, `fill`, `stroke`, `stroke-width` | Circle centered at (`cx`,`cy`) with radius `r` |
| path | `d`, `fill`, `stroke`, `stroke-width` | SVG-style path (see grammar below) |
### Path `d` Grammar
The `path` shape's `d` string supports **absolute commands only**: `M` (moveto), `L` (lineto), `Q` (quadratic Bézier), `C` (cubic Bézier), and `Z` (closepath). Lowercase/relative commands are rejected, and non-finite numbers are rejected.
```
M x y # move to (x, y)
L x y # line to (x, y)
Q cx cy x y # quadratic Bézier, control (cx, cy), end (x, y)
C c1x c1y c2x c2y x y # cubic Bézier, two controls, end point
Z # close path
```
Example: `d: "M 0 0 L 100 50 Q 150 0 200 50 Z"`
```yaml
- type: draw
width: 400
height: 200
shapes:
- line: {x1: 0, y1: 100, x2: 400, y2: 50, stroke: "#333", stroke-width: 2}
- circle: {cx: 200, cy: 75, r: 30, fill: "#e74c3c"}
- path: {d: "M 0 0 L 100 50 Q 150 0 200 50 Z", fill: "#2ecc71"}
```