namespace LehrerApp.Templating; /// Lesbare, umbrechende Textblöcke für Berichtsbausteine. Jede Zeile ist ein eigener /// Zeichenbefehl, damit FLOWDRAWBOX auch lange Berichte an Zeilengrenzen aufteilen kann. public static class ReportTextDrawing { public const string RepeatInfo = "Exam.RepeatInfo"; public const string InterimGrades = "Student.InterimGrades"; public static DrawingValue Build(string title, IEnumerable paragraphs, float width = 170, float scale = 1, float pageHeight = 240, bool fixedBox = false) { if (!float.IsFinite(width) || !float.IsFinite(scale) || !float.IsFinite(pageHeight) || scale <= 0 || width < 20 * scale || pageHeight < 12 * scale) throw new InvalidDataException("Der Berichtsbaustein benötigt mindestens 20 × 12 mm Platz."); var commands = new List(); var lineHeight = 5f * scale; var y = 0f; // Konservative Zeichenbreiten in Gevierten der Standardschrift. Breite Buchstaben // bekommen mehr Raum; auch einzelne überlange Wörter werden umgebrochen. var available = (width - 2 * scale) / (10 * 25.4f / 72 * scale); static float Advance(char c) => char.IsWhiteSpace(c) ? .4f : c is 'm' or 'w' or 'M' or 'W' ? 1.1f : c > 255 ? 1.1f : char.IsUpper(c) ? 1f : .7f; void Line(string text, bool bold) { var offset = y % pageHeight; if (offset + lineHeight > pageHeight + .001f) y += pageHeight - offset; if (fixedBox && y + lineHeight > pageHeight + .001f) throw new InvalidDataException("Der Berichtsbaustein passt nicht in die feste DRAWBOX. Bitte FLOWDRAWBOX verwenden oder die Box vergrößern."); commands.Add(new DrawStringEx(scale, y, lineHeight, width - 2 * scale, text, DrawingTextAlignment.AlignLeft, 10, Bold: bold)); y += lineHeight; } foreach (var paragraph in new[] { title }.Concat(paragraphs)) { foreach (var original in paragraph.Replace("\r\n", "\n").Split('\n')) { var remaining = original.Trim(); if (remaining.Length == 0) { Line("", false); continue; } while (remaining.Length > 0) { var length = 0; var used = 0f; while (length < remaining.Length && used + Advance(remaining[length]) <= available) used += Advance(remaining[length++]); length = Math.Max(1, length); var cut = length; if (length < remaining.Length) { var space = remaining.LastIndexOf(' ', length - 1, length); if (space > 0) cut = space; } Line(remaining[..cut], paragraph == title); remaining = remaining[cut..].TrimStart(); } } } return new DrawingValue(commands, y); } }