feat: port p-rec-annot line-number asset settings

This commit is contained in:
codex
2026-08-07 08:32:15 +03:00
parent ed7218983b
commit a17ad87fc2
3 changed files with 48 additions and 0 deletions
@@ -60,6 +60,11 @@ public sealed record ExistingLineNumberAnnotation(string StableId, string Source
public sealed record LineNumberCommandOperation(string StableId, AnnotationOperationKind Kind, string? Text, AnnotationPoint Position, double Rotation, string Layer, string TextStyle, IReadOnlyList<string> Reasons); public sealed record LineNumberCommandOperation(string StableId, AnnotationOperationKind Kind, string? Text, AnnotationPoint Position, double Rotation, string Layer, string TextStyle, IReadOnlyList<string> Reasons);
public sealed record LineNumberCommandPlan(IReadOnlyList<LineNumberCommandOperation> Operations); public sealed record LineNumberCommandPlan(IReadOnlyList<LineNumberCommandOperation> Operations);
public sealed record LineNumberApplyPreflight(string StableId, string Text, AnnotationPoint Position, double Rotation, string Layer, string TextStyle); public sealed record LineNumberApplyPreflight(string StableId, string Text, AnnotationPoint Position, double Rotation, string Layer, string TextStyle);
public sealed record LineNumberTextAsset(string Layer, string TextStyle, double Size, string TextValue, Point2Dto Position);
public sealed record LineNumberLineAsset(string Layer, Point2Dto Start, Point2Dto End);
public sealed record LineNumberMarkerAsset(string Layer, double MaskScaleFactor, IReadOnlyList<LineNumberTextAsset> Texts, IReadOnlyList<LineNumberLineAsset> Lines);
public sealed record LineNumberListAsset(string Layer, double MaskScaleFactor, double LineHeight, double LineSpacingDistance, LineNumberTextAsset MarkerText, LineNumberTextAsset LineNumberText, LineNumberLineAsset Line);
public sealed record AnnotateLineNumbersAssets(LineNumberMarkerAsset Marker, LineNumberListAsset List);
public sealed record GeometrySnapshot public sealed record GeometrySnapshot
{ {
@@ -0,0 +1,35 @@
using System.Globalization;
using System.Xml.Linq;
using PRecAnnot.Abstractions;
namespace PRecAnnot.Core;
public sealed class AnnotateLineNumbersSettingsLoader
{
public AnnotateLineNumbersAssets Load(string xml)
{
var lineNumbers = XElement.Parse(xml).Element("LineNumbers") ?? throw Invalid("LineNumbers");
var marker = lineNumbers.Element("Marker") ?? throw Invalid("Marker");
var list = lineNumbers.Element("List") ?? throw Invalid("List");
var texts = marker.Elements("Text").Select(x => Text(x)).ToArray();
var lines = marker.Elements("Line").Select(Line).ToArray();
if (texts.Length == 0 || lines.Length == 0) throw Invalid("MarkerContent");
return new(
new(Required(marker, "layer"), Number(marker, "maskscalefactor"), texts, lines),
new(Required(list, "layer"), Number(list, "maskscalefactor"), Number(list, "lineheight"), Number(list, "linespacingdistance"), Text(RequiredElement(list, "MarkerText"), false), Text(RequiredElement(list, "LineNumberText"), false), Line(RequiredElement(list, "Line"))));
}
private static LineNumberTextAsset Text(XElement value, bool readValue = true) => new(Required(value, "layer"), Required(value, "textstyle"), Number(value, "size"), readValue ? Required(value, "textvalue", true) : string.Empty, new(Number(value, "positionx"), Number(value, "positiony")));
private static LineNumberLineAsset Line(XElement value) => new(Required(value, "layer"), new(Number(value, "startx"), Number(value, "starty")), new(OptionalNumber(value, "endx"), OptionalNumber(value, "endy")));
private static XElement RequiredElement(XElement parent, string name) => parent.Element(name) ?? throw Invalid(name);
private static string Required(XElement value, string name, bool emptyAllowed = false)
{
var attribute = value.Attribute(name) ?? throw Invalid(name);
if (!emptyAllowed && string.IsNullOrWhiteSpace(attribute.Value)) throw Invalid(name);
return attribute.Value;
}
private static double Number(XElement value, string name) => Parse(Required(value, name));
private static double OptionalNumber(XElement value, string name) => value.Attribute(name) is { } attribute ? Parse(attribute.Value) : 0;
private static double Parse(string value) => double.TryParse(value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) ? result : throw Invalid("number");
private static InvalidDataException Invalid(string field) => new("LINE_NUMBER_SETTINGS_INVALID:" + field);
}
@@ -0,0 +1,8 @@
using PRecAnnot.Core;
namespace PRecAnnot.Core.Tests;
public sealed class AnnotateLineNumbersSettingsLoaderTests
{
private const string Xml = "<Main><LineNumbers><Marker layer='M' maskscalefactor='1,5'><Text layer='T' textstyle='S' size='2,5' textvalue='' positionx='1' positiony='2'/><Line layer='L' startx='0' starty='0' endx='3' endy='4'/></Marker><List layer='LIST' maskscalefactor='1.2' lineheight='5' linespacingdistance='6'><MarkerText layer='T' textstyle='S' size='2' textvalue='' positionx='0' positiony='0'/><LineNumberText layer='T' textstyle='S' size='2' textvalue='' positionx='4' positiony='0'/><Line layer='L' startx='0' starty='0'/></List></LineNumbers></Main>";
[Fact] public void LoadsLegacyMarkerAndListContract() { var x = new AnnotateLineNumbersSettingsLoader().Load(Xml); Assert.Equal(1.5, x.Marker.MaskScaleFactor); Assert.Equal("M", x.Marker.Layer); Assert.Equal(6, x.List.LineSpacingDistance); Assert.Equal(0, x.List.Line.End.X); }
[Fact] public void RejectsMissingRequiredLayer() => Assert.Equal("LINE_NUMBER_SETTINGS_INVALID:layer", Assert.Throws<InvalidDataException>(() => new AnnotateLineNumbersSettingsLoader().Load(Xml.Replace(" layer='M'", ""))).Message);
}