From b9cfaf3846c9bc56171c3f8895118faece6923b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zi=C4=85bek?= Date: Fri, 27 Aug 2021 01:38:26 +0200 Subject: [PATCH] Text component: internal and external links --- QuestPDF.Examples/TextBenchmark.cs | 35 ++++++++++++++--- QuestPDF/Elements/Text/TextBlock.cs | 2 +- QuestPDF/Elements/Text/TextItem.cs | 60 +++++++++++++++++++++++++++++ QuestPDF/Fluent/TextExtensions.cs | 42 +++++++++++++++++++- 4 files changed, 130 insertions(+), 9 deletions(-) diff --git a/QuestPDF.Examples/TextBenchmark.cs b/QuestPDF.Examples/TextBenchmark.cs index 0225508..11c725e 100644 --- a/QuestPDF.Examples/TextBenchmark.cs +++ b/QuestPDF.Examples/TextBenchmark.cs @@ -127,6 +127,8 @@ namespace QuestPDF.Examples stack.Item().PageBreak(); Chapters(stack); + + stack.Item().Element(Summary); }); decoration.Footer().Element(Footer); @@ -141,8 +143,8 @@ namespace QuestPDF.Examples .AlignBottom() .Stack(stack => { - stack.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2)); - stack.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2)); + stack.Item().Debug().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2)); + stack.Item().Debug().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2)); }); } @@ -150,8 +152,7 @@ namespace QuestPDF.Examples { container.Stack(stack => { - stack.Item().Text("Table of contents", subtitleStyle); - stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal(); + SectionTitle(stack, "Table of contents"); foreach (var chapter in chapters) { @@ -176,8 +177,7 @@ namespace QuestPDF.Examples { container.Stack(stack => { - stack.Item().Location(title).Text(title, subtitleStyle); - stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal(); + SectionTitle(stack, title); stack.Item().Text(text => { @@ -189,6 +189,29 @@ namespace QuestPDF.Examples }); } + void Summary(IContainer container) + { + container.Stack(stack => + { + SectionTitle(stack, "Acknowledgements"); + + stack.Item().Text(text => + { + text.DefaultTextStyle(normalStyle); + + text.Span("This document was generated based on the book available on the "); + text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underlined()); + text.Span(" website. Thank you!"); + }); + }); + } + + void SectionTitle(StackDescriptor stack, string text) + { + stack.Item().Location(text).Text(text, subtitleStyle); + stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal(); + } + void Footer(IContainer container) { container diff --git a/QuestPDF/Elements/Text/TextBlock.cs b/QuestPDF/Elements/Text/TextBlock.cs index 8391447..a883c4f 100644 --- a/QuestPDF/Elements/Text/TextBlock.cs +++ b/QuestPDF/Elements/Text/TextBlock.cs @@ -148,7 +148,7 @@ namespace QuestPDF.Elements.Text if (!line.Elements.Any()) yield break; - if (currentHeight + line.LineHeight > availableHeight) + if (currentHeight + line.LineHeight > availableHeight + Size.Epsilon) yield break; currentHeight += line.LineHeight; diff --git a/QuestPDF/Elements/Text/TextItem.cs b/QuestPDF/Elements/Text/TextItem.cs index d4fd44e..d3acbdc 100644 --- a/QuestPDF/Elements/Text/TextItem.cs +++ b/QuestPDF/Elements/Text/TextItem.cs @@ -165,4 +165,64 @@ namespace QuestPDF.Elements.Text }; } } + + internal class InternalLinkTextItem : ITextElement + { + public TextStyle Style { get; set; } = new TextStyle(); + public string Text { get; set; } + public string LocationName { get; set; } + + public TextMeasurementResult? Measure(TextMeasurementRequest request) + { + return GetItem().MeasureWithoutCache(request); + } + + public void Draw(TextDrawingRequest request) + { + request.Canvas.Translate(new Position(0, request.TotalAscent)); + request.Canvas.DrawLocationLink(LocationName, new Size(request.TextSize.Width, request.TextSize.Height)); + request.Canvas.Translate(new Position(0, -request.TotalAscent)); + + GetItem().Draw(request); + } + + private TextItem GetItem() + { + return new TextItem + { + Style = Style, + Text = Text + }; + } + } + + internal class ExternalLinkTextItem : ITextElement + { + public TextStyle Style { get; set; } = new TextStyle(); + public string Text { get; set; } + public string Url { get; set; } + + public TextMeasurementResult? Measure(TextMeasurementRequest request) + { + return GetItem().MeasureWithoutCache(request); + } + + public void Draw(TextDrawingRequest request) + { + request.Canvas.Translate(new Position(0, request.TotalAscent)); + request.Canvas.DrawExternalLink(Url, new Size(request.TextSize.Width, request.TextSize.Height)); + request.Canvas.Translate(new Position(0, -request.TotalAscent)); + + GetItem().Draw(request); + } + + private TextItem GetItem() + { + return new TextItem + { + Style = Style, + Text = Text + }; + } + } } \ No newline at end of file diff --git a/QuestPDF/Fluent/TextExtensions.cs b/QuestPDF/Fluent/TextExtensions.cs index f140277..8ada5df 100644 --- a/QuestPDF/Fluent/TextExtensions.cs +++ b/QuestPDF/Fluent/TextExtensions.cs @@ -10,9 +10,15 @@ namespace QuestPDF.Fluent public class TextDescriptor { private ICollection TextBlocks { get; } = new List(); + private TextStyle DefaultStyle { get; set; } = TextStyle.Default; private HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left; private float Spacing { get; set; } = 0f; + public void DefaultTextStyle(TextStyle style) + { + DefaultStyle = style; + } + public void AlignLeft() { Alignment = HorizontalAlignment.Left; @@ -35,7 +41,7 @@ namespace QuestPDF.Fluent public void Span(string text, TextStyle? style = null) { - style ??= TextStyle.Default; + style ??= DefaultStyle; if (string.IsNullOrWhiteSpace(text)) return; @@ -71,7 +77,7 @@ namespace QuestPDF.Fluent private void PageNumber(string slotName, TextStyle? style = null) { - style ??= TextStyle.Default; + style ??= DefaultStyle; if (!TextBlocks.Any()) TextBlocks.Add(new TextBlock()); @@ -98,8 +104,40 @@ namespace QuestPDF.Fluent PageNumber(locationName, style); } + public void InternalLocation(string text, string locationName, TextStyle? style = null) + { + style ??= DefaultStyle; + + if (!TextBlocks.Any()) + TextBlocks.Add(new TextBlock()); + + TextBlocks.First().Children.Add(new InternalLinkTextItem + { + Style = style, + Text = text, + LocationName = locationName + }); + } + + public void ExternalLocation(string text, string url, TextStyle? style = null) + { + style ??= DefaultStyle; + + if (!TextBlocks.Any()) + TextBlocks.Add(new TextBlock()); + + TextBlocks.First().Children.Add(new ExternalLinkTextItem + { + Style = style, + Text = text, + Url = url + }); + } + internal void Compose(IContainer container) { + TextBlocks.ToList().ForEach(x => x.Alignment = Alignment); + container.Stack(stack => { stack.Spacing(Spacing);