TemplateDesigner: Redesign des TemplateSkripts
This commit is contained in:
@@ -15,9 +15,14 @@ public sealed class QuestTemplateRenderer : ITemplateRenderer, ITemplatePreviewR
|
||||
BuildDocument(template, ValidateData(template, data)).GeneratePdf();
|
||||
|
||||
public byte[] RenderFirstPageToPng(LoadedTemplate template, ITemplateDataProvider data, int dpi = 120)
|
||||
{
|
||||
return RenderPagesToPng(template, data, dpi).First();
|
||||
}
|
||||
|
||||
public IReadOnlyList<byte[]> RenderPagesToPng(LoadedTemplate template, ITemplateDataProvider data, int dpi = 120)
|
||||
{
|
||||
var settings = new ImageGenerationSettings { ImageFormat = ImageFormat.Png, RasterDpi = dpi };
|
||||
return BuildDocument(template, ValidateData(template, data)).GenerateImages(settings).First();
|
||||
return BuildDocument(template, ValidateData(template, data)).GenerateImages(settings).ToList();
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, PlaceholderValue> ValidateData(LoadedTemplate template,
|
||||
@@ -30,6 +35,11 @@ public sealed class QuestTemplateRenderer : ITemplateRenderer, ITemplatePreviewR
|
||||
}
|
||||
|
||||
private static IDocument BuildDocument(LoadedTemplate template,
|
||||
IReadOnlyDictionary<string, PlaceholderValue> values) => template.Layout.UsesPageTemplates
|
||||
? BuildFlowDocument(template, values)
|
||||
: BuildLegacyDocument(template, values);
|
||||
|
||||
private static IDocument BuildLegacyDocument(LoadedTemplate template,
|
||||
IReadOnlyDictionary<string, PlaceholderValue> values) => Document.Create(document =>
|
||||
{
|
||||
document.Page(page =>
|
||||
@@ -50,6 +60,147 @@ public sealed class QuestTemplateRenderer : ITemplateRenderer, ITemplatePreviewR
|
||||
});
|
||||
});
|
||||
|
||||
private static IDocument BuildFlowDocument(LoadedTemplate template,
|
||||
IReadOnlyDictionary<string, PlaceholderValue> values)
|
||||
{
|
||||
var firstPage = template.Layout.PageTemplates.First(x =>
|
||||
x.Name.Equals("first", StringComparison.OrdinalIgnoreCase));
|
||||
var continuationPage = template.Layout.PageTemplates.FirstOrDefault(x =>
|
||||
x.Name.Equals("continuation", StringComparison.OrdinalIgnoreCase)) ?? firstPage;
|
||||
var populatedFlows = template.Layout.ContentFlows.Where(x => x.Elements.Count > 0).ToList();
|
||||
if (populatedFlows.Count > 1)
|
||||
throw new InvalidDataException("Aktuell darf genau ein Content-Flow Inhalt enthalten. Weitere Flow-Slots können bereits gestaltet werden, parallele paginierende Flows folgen in einer späteren Formatstufe.");
|
||||
var primaryFlow = populatedFlows.FirstOrDefault() ?? template.Layout.ContentFlows.FirstOrDefault();
|
||||
|
||||
return Document.Create(document =>
|
||||
{
|
||||
document.Page(page =>
|
||||
{
|
||||
var pageWidth = UnitConverter.Points(template.Layout.Width, template.Layout.Unit);
|
||||
var pageHeight = UnitConverter.Points(template.Layout.Height, template.Layout.Unit);
|
||||
page.Size(pageWidth, pageHeight);
|
||||
page.Margin(0);
|
||||
ConfigurePageCanvas(page.Background(), firstPage, continuationPage, template, values);
|
||||
|
||||
if (primaryFlow is null)
|
||||
{
|
||||
page.Content().Height(1);
|
||||
return;
|
||||
}
|
||||
|
||||
var firstSlot = FindSlot(firstPage, primaryFlow.Name);
|
||||
var continuationSlot = FindSlot(continuationPage, primaryFlow.Name) ?? firstSlot;
|
||||
if (firstSlot is null)
|
||||
throw new InvalidDataException($"Für content-flow „{primaryFlow.Name}“ fehlt ein flow-slot auf der ersten Seite.");
|
||||
|
||||
if (Math.Abs(firstSlot.X - continuationSlot!.X) > 0.01f ||
|
||||
Math.Abs(firstSlot.Width - continuationSlot.Width) > 0.01f)
|
||||
throw new InvalidDataException($"Der primäre Flow „{primaryFlow.Name}“ muss vorerst auf erster und Folgeseite dieselbe X-Position und Breite besitzen.");
|
||||
|
||||
var left = UnitConverter.Points(firstSlot.X, template.Layout.Unit);
|
||||
var right = UnitConverter.Points(template.Layout.Width - firstSlot.X - firstSlot.Width, template.Layout.Unit);
|
||||
page.MarginLeft(left);
|
||||
page.MarginRight(right);
|
||||
ConfigureVariableVerticalSlot(page, firstSlot, continuationSlot, template.Layout);
|
||||
page.Content().Column(column => RenderFlowColumn(column, primaryFlow.Elements, template, values));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void ConfigureVariableVerticalSlot(PageDescriptor page, FlowSlotDefinition first,
|
||||
FlowSlotDefinition continuation, TemplateLayout layout)
|
||||
{
|
||||
var firstTop = UnitConverter.Points(first.Y, layout.Unit);
|
||||
var continuationTop = UnitConverter.Points(continuation.Y, layout.Unit);
|
||||
var firstBottom = UnitConverter.Points(layout.Height - first.Y - first.Height, layout.Unit);
|
||||
var continuationBottom = UnitConverter.Points(layout.Height - continuation.Y - continuation.Height, layout.Unit);
|
||||
page.Header().Column(column =>
|
||||
{
|
||||
column.Item().ShowOnce().Height(firstTop);
|
||||
column.Item().SkipOnce().Height(continuationTop);
|
||||
});
|
||||
page.Footer().Column(column =>
|
||||
{
|
||||
column.Item().ShowOnce().Height(firstBottom);
|
||||
column.Item().SkipOnce().Height(continuationBottom);
|
||||
});
|
||||
}
|
||||
|
||||
private static FlowSlotDefinition? FindSlot(PageTemplateDefinition page, string name) =>
|
||||
page.FlowSlots.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
private static void ConfigurePageCanvas(IContainer canvas, PageTemplateDefinition first,
|
||||
PageTemplateDefinition continuation, LoadedTemplate template,
|
||||
IReadOnlyDictionary<string, PlaceholderValue> values)
|
||||
{
|
||||
canvas.Layers(layers =>
|
||||
{
|
||||
layers.PrimaryLayer().Background(Colors.White);
|
||||
foreach (var element in first.Elements)
|
||||
{
|
||||
var current = element;
|
||||
layers.Layer().ShowOnce().Element(root => RenderElement(root, current, template, values));
|
||||
}
|
||||
foreach (var element in continuation.Elements)
|
||||
{
|
||||
var current = element;
|
||||
layers.Layer().SkipOnce().Element(root => RenderElement(root, current, template, values));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void RenderFlowColumn(ColumnDescriptor column, IReadOnlyList<TemplateElement> elements,
|
||||
LoadedTemplate template, IReadOnlyDictionary<string, PlaceholderValue> values)
|
||||
{
|
||||
for (var index = 0; index < elements.Count; index++)
|
||||
{
|
||||
var element = elements[index];
|
||||
var gap = UnitConverter.Points(ParseFloat(element.Attributes, "gap", 0), template.Layout.Unit);
|
||||
var keepWithNext = ParseBool(element.Attributes, "keep-with-next") && index + 1 < elements.Count;
|
||||
if (keepWithNext)
|
||||
{
|
||||
var next = elements[++index];
|
||||
column.Item().PaddingTop(gap).PreventPageBreak().Column(group =>
|
||||
{
|
||||
group.Item().Element(container => RenderFlowElement(container, element, template, values));
|
||||
var nextGap = UnitConverter.Points(ParseFloat(next.Attributes, "gap", 0), template.Layout.Unit);
|
||||
group.Item().PaddingTop(nextGap).Element(container => RenderFlowElement(container, next, template, values));
|
||||
});
|
||||
}
|
||||
else
|
||||
column.Item().PaddingTop(gap).Element(container => RenderFlowElement(container, element, template, values));
|
||||
}
|
||||
}
|
||||
|
||||
private static void RenderFlowElement(IContainer container, TemplateElement element, LoadedTemplate template,
|
||||
IReadOnlyDictionary<string, PlaceholderValue> values)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case TextElement text:
|
||||
RenderResolvedText(container, text.Content, text.Placeholder, text.Format,
|
||||
values, template.Manifest, text.Attributes);
|
||||
break;
|
||||
case TextBoxElement box:
|
||||
RenderResolvedText(container, box.Content, box.Placeholder, box.Format,
|
||||
values, template.Manifest, box.Attributes);
|
||||
break;
|
||||
case ImageElement image:
|
||||
var imageContainer = container;
|
||||
if (image.Width > 0) imageContainer = imageContainer.Width(UnitConverter.Points(image.Width, template.Layout.Unit));
|
||||
if (image.Height > 0) imageContainer = imageContainer.Height(UnitConverter.Points(image.Height, template.Layout.Unit));
|
||||
imageContainer.Image(GetAsset(template, image.Path)).FitArea();
|
||||
break;
|
||||
case TableElement table when values.GetValueOrDefault(table.Placeholder) is TableValue tableValue:
|
||||
TableElementRenderer.Render(container, tableValue, table.Attributes);
|
||||
break;
|
||||
case ChartElement chart when values.GetValueOrDefault(chart.Placeholder) is ChartValue chartValue:
|
||||
var chartHeight = UnitConverter.Points(ParseFloat(chart.Attributes, "h", 50), template.Layout.Unit);
|
||||
ChartElementRenderer.Render(container.Height(chartHeight), chartValue, chart.ChartType, chart.Attributes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RenderElement(IContainer root, TemplateElement element, LoadedTemplate template,
|
||||
IReadOnlyDictionary<string, PlaceholderValue> values)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user