fix: Anwesenheitskalender/Fehltagesliste nutzen die real deklarierte Boxgröße
CI / build-and-test (push) Waiting to run

Die DrawingValue-Builder gingen bisher immer von einer festen Breite (170)
aus, unabhängig davon, wie breit die DRAWBOX/FLOWDRAWBOX in der jeweiligen
Vorlage tatsächlich deklariert ist. Bei einer breiteren Box (oder einer in
"pt" statt "mm" deklarierten Seite, wie im echten Fehlzeitenbrief-Template)
blieb dadurch ein Großteil der eigentlich verfügbaren Fläche ungenutzt leer
- sichtbar als "Luft" rechts und unterhalb des Kalenders im generierten PDF.

CreateLetterDialogViewModel/LetterTemplateTools ermitteln jetzt die real
deklarierte Boxbreite samt Maßeinheit aus dem geladenen Template
(LetterPlaceholderBuilder.FindDeclaredDrawingBox) und reichen sie an die
Builder durch, die ihr mm-entworfenes Raster entsprechend skalieren.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 14:27:05 +02:00
co-authored by Claude Sonnet 5
parent 763d7744be
commit 86a97d91a7
6 changed files with 197 additions and 73 deletions
@@ -12,8 +12,8 @@ public partial class CreateLetterDialogViewModel : ObservableObject
private readonly Student _student;
private readonly TemplateStore _templates;
private readonly ITemplateRenderer _renderer;
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _attendanceCalendarFactory;
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _absenceDayListFactory;
private readonly Func<AttendanceCalendarOptions, float, float, DrawingValue>? _attendanceCalendarFactory;
private readonly Func<AttendanceCalendarOptions, float, float, DrawingValue>? _absenceDayListFactory;
private readonly Func<AttendanceCalendarOptions, CancellationToken, Task>? _attendanceDataRefresher;
private AttendanceCalendarOptions _attendanceCalendarOptions = new(
new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1), 1);
@@ -52,8 +52,8 @@ public partial class CreateLetterDialogViewModel : ObservableObject
public CreateLetterDialogViewModel(Student student, TemplateStore templates, ITemplateRenderer renderer,
IGroupMembershipRepository memberships, IGroupRepository groups,
Func<AttendanceCalendarOptions, DrawingValue>? attendanceCalendarFactory = null,
Func<AttendanceCalendarOptions, DrawingValue>? absenceDayListFactory = null,
Func<AttendanceCalendarOptions, float, float, DrawingValue>? attendanceCalendarFactory = null,
Func<AttendanceCalendarOptions, float, float, DrawingValue>? absenceDayListFactory = null,
Func<AttendanceCalendarOptions, CancellationToken, Task>? attendanceDataRefresher = null)
{
_student = student; _templates = templates; _renderer = renderer;
@@ -137,16 +137,32 @@ public partial class CreateLetterDialogViewModel : ObservableObject
private IReadOnlyDictionary<string, PlaceholderValue> BuildValues()
{
var calendarBox = DeclaredDrawingBox(StudentAttendanceCalendarDrawingBuilder.PlaceholderName);
var absenceDayListBox = DeclaredDrawingBox(StudentAbsenceDayListDrawingBuilder.PlaceholderName);
var values = LetterPlaceholderBuilder.BuildStandardValues(
_student, SelectedContact?.Model, SelectedGroup?.Model,
DateOnly.FromDateTime((LetterDate ?? DateTimeOffset.Now).LocalDateTime), LetterText, TeacherName,
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions) : null,
UsesAbsenceDayList ? _absenceDayListFactory?.Invoke(_attendanceCalendarOptions) : null);
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions,
calendarBox.Width, calendarBox.MillimeterScale) : null,
UsesAbsenceDayList ? _absenceDayListFactory?.Invoke(_attendanceCalendarOptions,
absenceDayListBox.Width, absenceDayListBox.MillimeterScale) : null);
values["Anrede"] = new TextValue(Anrede); values["Letter.Salutation"] = new TextValue(Anrede);
foreach (var custom in CustomPlaceholders) values[custom.Name] = custom.ToPlaceholderValue();
return values;
}
/// <summary>Tatsächliche DRAWBOX/FLOWDRAWBOX-Größe der aktuell gewählten Vorlage für diesen
/// Platzhalter, damit der Kalender/die Fehltagesliste die real verfügbare Fläche ausfüllen statt
/// eine feste Breite zu raten (siehe LetterPlaceholderBuilder.FindDeclaredDrawingBox). Fällt auf
/// 170mm zurück, wenn keine Vorlage gewählt ist oder die Box nicht gefunden wird.</summary>
private LetterPlaceholderBuilder.DeclaredDrawingBox DeclaredDrawingBox(string placeholderName)
{
var fallback = new LetterPlaceholderBuilder.DeclaredDrawingBox(170, 1);
if (SelectedTemplate is null) return fallback;
try { return LetterPlaceholderBuilder.FindDeclaredDrawingBox(_templates.Load(SelectedTemplate.Model), placeholderName) ?? fallback; }
catch (Exception ex) when (ex is IOException or InvalidDataException or TemplateValidationException) { return fallback; }
}
private void RebuildCustomPlaceholders(LetterTemplateChoice? choice)
{
foreach (var existing in CustomPlaceholders) existing.PropertyChanged -= OnCustomPlaceholderChanged;