Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

80 строки
5.1KB

  1. namespace EnvelopeRenderer.Cli.Render;
  2. /// <summary>
  3. /// One `&lt;text&gt;` element from the template. Sprint 5, "Mix static text and CSV fields within
  4. /// a single text element": content is now an ordered sequence of <see cref="TemplateTextRun"/>s
  5. /// (literal-text runs and field-token runs), concatenated per record at render time — the
  6. /// pre-Sprint-5 "either pure static text or a single bound column" rule is simply the one-run
  7. /// special case of this same model (<see cref="StaticText"/>/<see cref="ColumnName"/> below are
  8. /// back-compat read-only projections for that one-run case, not separate storage), not a
  9. /// parallel code path that could drift from it. <see cref="TemplateXmlParser"/> enforces "every
  10. /// run is exactly one of literal/field" at parse time.
  11. /// </summary>
  12. /// <param name="Collapsible">Sprint 4, "Collapse blank optional address lines consistently":
  13. /// when <c>true</c> and this element's resolved text is blank for a given record, the element
  14. /// is not drawn and every other element sharing its X position (an "address line stack" — see
  15. /// <see cref="AddressLineCollapser"/>) shifts upward to close the gap. Defaults to <c>false</c>
  16. /// so every template written before this story renders identically (a blank line simply prints
  17. /// nothing at its fixed position, exactly as before).</param>
  18. /// <param name="Angle">Sprint 4, "Set a rotation angle for text and dynamic field elements":
  19. /// degrees, positive or negative, applied around the element's own bounding-box center (not its
  20. /// X/Y anchor). Defaults to <c>0</c> so every template written before this story renders
  21. /// unchanged.</param>
  22. /// <param name="Width">Sprint 9, "Add an adjustable width and height with text wrapping...":
  23. /// when set (together with <paramref name="Height"/>), the element's content wraps within this
  24. /// width instead of drawing as one unbounded line. <c>null</c> (the default) means every
  25. /// template written before this story renders exactly as before — no wrap, no width concept at
  26. /// all.</param>
  27. /// <param name="Height">Paired with <paramref name="Width"/>: wrapped content that would exceed
  28. /// this height is clipped rather than growing the box or shrinking the text. Only meaningful when
  29. /// <paramref name="Width"/> is also set; <c>null</c> is the pre-Sprint-9 default.</param>
  30. public sealed record TemplateElement(
  31. double X,
  32. double Y,
  33. string FontName,
  34. double Size,
  35. IReadOnlyList<TemplateTextRun> Runs,
  36. bool Collapsible = false,
  37. double Angle = 0,
  38. int RenderOrder = 0,
  39. double? Width = null,
  40. double? Height = null)
  41. {
  42. /// <summary>True only when both <see cref="Width"/> and <see cref="Height"/> are set — a
  43. /// one-sided value (e.g. a width with no height) is treated as "no box" rather than guessing
  44. /// a missing dimension, since wrap-without-a-clip-boundary or clip-without-a-wrap-width both
  45. /// have no well-defined meaning for this story.</summary>
  46. public bool HasBox => Width is > 0 && Height is > 0;
  47. /// <summary>Back-compat convenience factory matching the pre-Sprint-5 "pure static text"
  48. /// shape — builds the equivalent one-literal-run <see cref="Runs"/> list. (A second
  49. /// constructor overload was deliberately rejected here in favor of named factory methods: a
  50. /// positional/named-argument overload alongside the primary constructor's own
  51. /// <c>Collapsible</c>/<c>Angle</c> parameters is ambiguous to the compiler — CS1744 — for any
  52. /// caller using named arguments for either, which every <c>Collapsible: true</c>/
  53. /// <c>Angle: 45</c> call site in this codebase does.)</summary>
  54. public static TemplateElement Static(
  55. double x, double y, string fontName, double size, string text, bool collapsible = false, double angle = 0) =>
  56. new(x, y, fontName, size, new[] { new TemplateTextRun(text, null) }, collapsible, angle);
  57. /// <summary>Back-compat convenience factory matching the pre-Sprint-5 "single bound column"
  58. /// shape — builds the equivalent one-field-run <see cref="Runs"/> list.</summary>
  59. public static TemplateElement Dynamic(
  60. double x, double y, string fontName, double size, string columnName, bool collapsible = false, double angle = 0) =>
  61. new(x, y, fontName, size, new[] { new TemplateTextRun(null, columnName) }, collapsible, angle);
  62. public bool IsDynamic => Runs.Any(r => r.IsField);
  63. /// <summary>True only for the legacy "exactly one field run, nothing else" shape — the only
  64. /// case that still has a single, unambiguous bound column (used by the render-time pre-flight
  65. /// column check's error-message grouping and by any code that only makes sense for a
  66. /// single-column binding).</summary>
  67. public bool HasSingleColumnRun => Runs.Count == 1 && Runs[0].IsField;
  68. /// <summary>Back-compat projection: non-null only for the legacy single-literal-run case.</summary>
  69. public string? StaticText => Runs.Count == 1 && !Runs[0].IsField ? Runs[0].Literal : null;
  70. /// <summary>Back-compat projection: non-null only for the legacy single-field-run case.</summary>
  71. public string? ColumnName => HasSingleColumnRun ? Runs[0].ColumnName : null;
  72. }

Powered by TurnKey Linux.