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);
}
}