Browse Source

Implemented prototype of global, document-wide text style

Marcin Ziąbek 4 years ago
parent
commit
e379502a64

+ 1 - 2
QuestPDF.ReportSample/Layouts/StandardReport.cs

@@ -25,6 +25,7 @@ namespace QuestPDF.ReportSample.Layouts
         public void Compose(IDocumentContainer container)
         {
             container
+                .DefaultTextStyle(Typography.Normal)
                 .Page(page =>
                 {
                     page.MarginVertical(40);
@@ -37,8 +38,6 @@ namespace QuestPDF.ReportSample.Layouts
                     
                     page.Footer().AlignCenter().Text(text =>
                     {
-                        text.DefaultTextStyle(Typography.Normal);
-                        
                         text.CurrentPageNumber();
                         text.Span(" / ");
                         text.TotalPages();

+ 1 - 1
QuestPDF.ReportSample/Typography.cs

@@ -8,6 +8,6 @@ namespace QuestPDF.ReportSample
     {
         public static TextStyle Title => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Darken3).Size(26).Black();
         public static TextStyle Headline => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Medium).Size(16).SemiBold();
-        public static TextStyle Normal => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Black).Size(11).LineHeight(1.1f);
+        public static TextStyle Normal => TextStyle.Default.FontType(Fonts.Verdana).Color(Colors.Black).Size(10).LineHeight(1.2f);
     }
 }

+ 1 - 0
QuestPDF/Drawing/DocumentContainer.cs

@@ -9,6 +9,7 @@ namespace QuestPDF.Drawing
 {
     internal class DocumentContainer : IDocumentContainer
     {
+        internal TextStyle DefaultTextStyle { get; set; } = TextStyle.Default;
         internal List<IComponent> Pages { get; set; } = new List<IComponent>();
         
         internal Container Compose()

+ 21 - 0
QuestPDF/Drawing/DocumentGenerator.cs

@@ -1,9 +1,12 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Elements;
+using QuestPDF.Elements.Text;
+using QuestPDF.Elements.Text.Items;
 using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Drawing
@@ -36,6 +39,7 @@ namespace QuestPDF.Drawing
             var metadata = document.GetMetadata();
             var pageContext = new PageContext();
 
+            ApplyDefaultTextStyle(content, container.DefaultTextStyle);
             RenderPass(pageContext, new FreeCanvas(), content, metadata);
             RenderPass(pageContext, canvas, content, metadata);
         }
@@ -93,5 +97,22 @@ namespace QuestPDF.Drawing
                 throw new DocumentLayoutException("Composed layout generates infinite document.");
             }
         }
+
+        private static void ApplyDefaultTextStyle(Container content, TextStyle documentDefaultTextStyle)
+        {
+            documentDefaultTextStyle.ApplyGlobalStyle(TextStyle.LibraryDefault);
+            
+            content.HandleVisitor(element =>
+            {
+                var text = element as TextBlock;
+                
+                if (text == null)
+                    return;
+                
+                foreach (var child in text.Children)
+                   if (child is TextBlockSpan span)
+                       span.Style.ApplyGlobalStyle(documentDefaultTextStyle);
+            });
+        }
     }
 }

+ 5 - 5
QuestPDF/Drawing/FontManager.cs

@@ -34,7 +34,7 @@ namespace QuestPDF.Drawing
         
         internal static SKPaint ToPaint(this TextStyle style)
         {
-            return Paints.GetOrAdd(style.ToString(), key => Convert(style));
+            return Paints.GetOrAdd(style.Key, key => Convert(style));
             
             static SKPaint Convert(TextStyle style)
             {
@@ -42,7 +42,7 @@ namespace QuestPDF.Drawing
                 {
                     Color = SKColor.Parse(style.Color),
                     Typeface = GetTypeface(style),
-                    TextSize = style.Size,
+                    TextSize = (style.Size ?? 12),
                     TextEncoding = SKTextEncoding.Utf32
                 };
             }
@@ -52,16 +52,16 @@ namespace QuestPDF.Drawing
                 if (Typefaces.TryGetValue(style.FontType, out var result))
                     return result;
                 
-                var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
+                var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
                 
-                return SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant) 
+                return SKTypeface.FromFamilyName(style.FontType, (int)(style.FontWeight ?? FontWeight.Normal), (int)SKFontStyleWidth.Normal, slant) 
                        ?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
             }
         }
 
         internal static SKFontMetrics ToFontMetrics(this TextStyle style)
         {
-            return FontMetrics.GetOrAdd(style.ToString(), key => style.ToPaint().FontMetrics);
+            return FontMetrics.GetOrAdd(style.Key, key => style.ToPaint().FontMetrics);
         }
     }
 }

+ 1 - 0
QuestPDF/Elements/Text/Items/ITextBlockItem.cs

@@ -1,4 +1,5 @@
 using QuestPDF.Elements.Text.Calculation;
+using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements.Text.Items
 {

+ 5 - 16
QuestPDF/Elements/Text/Items/TextBlockExternalLink.cs

@@ -3,33 +3,22 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements.Text.Items
 {
-    internal class TextBlockExternalLink : ITextBlockItem
+    internal class TextBlockExternalLink : TextBlockSpan
     {
-        public TextStyle Style { get; set; } = new TextStyle();
-        public string Text { get; set; }
         public string Url { get; set; }
         
-        public TextMeasurementResult? Measure(TextMeasurementRequest request)
+        public override TextMeasurementResult? Measure(TextMeasurementRequest request)
         {
-            return GetItem().MeasureWithoutCache(request);
+            return MeasureWithoutCache(request);
         }
 
-        public void Draw(TextDrawingRequest request)
+        public override 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 TextBlockSpan GetItem()
-        {
-            return new TextBlockSpan
-            {
-                Style = Style,
-                Text = Text
-            };
+            base.Draw(request);
         }
     }
 }

+ 5 - 16
QuestPDF/Elements/Text/Items/TextBlockInternalLink.cs

@@ -3,33 +3,22 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements.Text.Items
 {
-    internal class TextBlockInternalLink : ITextBlockItem
+    internal class TextBlockInternalLink : TextBlockSpan
     {
-        public TextStyle Style { get; set; } = new TextStyle();
-        public string Text { get; set; }
         public string LocationName { get; set; }
         
-        public TextMeasurementResult? Measure(TextMeasurementRequest request)
+        public override TextMeasurementResult? Measure(TextMeasurementRequest request)
         {
-            return GetItem().MeasureWithoutCache(request);
+            return MeasureWithoutCache(request);
         }
 
-        public void Draw(TextDrawingRequest request)
+        public override 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 TextBlockSpan GetItem()
-        {
-            return new TextBlockSpan
-            {
-                Style = Style,
-                Text = Text
-            };
+            base.Draw(request);
         }
     }
 }

+ 10 - 13
QuestPDF/Elements/Text/Items/TextBlockPageNumber.cs

@@ -3,34 +3,31 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements.Text.Items
 {
-    internal class TextBlockPageNumber : ITextBlockItem
+    internal class TextBlockPageNumber : TextBlockSpan
     {
-        public TextStyle Style { get; set; } = new TextStyle();
         public string SlotName { get; set; }
         
-        public TextMeasurementResult? Measure(TextMeasurementRequest request)
+        public override TextMeasurementResult? Measure(TextMeasurementRequest request)
         {
-            return GetItem(request.PageContext).MeasureWithoutCache(request);
+            SetPageNumber(request.PageContext);
+            return MeasureWithoutCache(request);
         }
 
-        public void Draw(TextDrawingRequest request)
+        public override void Draw(TextDrawingRequest request)
         {
-            GetItem(request.PageContext).Draw(request);
+            SetPageNumber(request.PageContext);
+            base.Draw(request);
         }
 
-        private TextBlockSpan GetItem(IPageContext context)
+        private void SetPageNumber(IPageContext context)
         {
             var pageNumberPlaceholder = 123;
             
             var pageNumber = context.GetRegisteredLocations().Contains(SlotName)
                 ? context.GetLocationPage(SlotName)
                 : pageNumberPlaceholder;
-            
-            return new TextBlockSpan
-            {
-                Style = Style,
-                Text = pageNumber.ToString()
-            };
+
+            Text = pageNumber.ToString();
         }
     }
 }

+ 8 - 8
QuestPDF/Elements/Text/Items/TextBlockSpan.cs

@@ -14,7 +14,7 @@ namespace QuestPDF.Elements.Text.Items
         private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache =
             new Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?>();
 
-        public TextMeasurementResult? Measure(TextMeasurementRequest request)
+        public virtual TextMeasurementResult? Measure(TextMeasurementRequest request)
         {
             var cacheKey = (request.StartIndex, request.AvailableWidth);
             
@@ -45,7 +45,7 @@ namespace QuestPDF.Elements.Text.Items
                 {
                     Width = 0,
                     
-                    LineHeight = Style.LineHeight,
+                    LineHeight = Style.LineHeight ?? 1,
                     Ascent = fontMetrics.Ascent,
                     Descent = fontMetrics.Descent
                 };
@@ -96,7 +96,7 @@ namespace QuestPDF.Elements.Text.Items
                 Ascent = fontMetrics.Ascent,
                 Descent = fontMetrics.Descent,
      
-                LineHeight = Style.LineHeight,
+                LineHeight = Style.LineHeight ?? 1,
                 
                 StartIndex = startIndex,
                 EndIndex = endIndex,
@@ -105,7 +105,7 @@ namespace QuestPDF.Elements.Text.Items
             };
         }
         
-        public void Draw(TextDrawingRequest request)
+        public virtual void Draw(TextDrawingRequest request)
         {
             var fontMetrics = Style.ToFontMetrics();
 
@@ -115,12 +115,12 @@ namespace QuestPDF.Elements.Text.Items
             request.Canvas.DrawText(text, Position.Zero, Style);
 
             // draw underline
-            if (Style.HasUnderline && fontMetrics.UnderlinePosition.HasValue)
-                DrawLine(fontMetrics.UnderlinePosition.Value, fontMetrics.UnderlineThickness.Value);
+            if ((Style.HasUnderline ?? false) && fontMetrics.UnderlinePosition.HasValue)
+                DrawLine(fontMetrics.UnderlinePosition.Value, fontMetrics.UnderlineThickness ?? 1);
             
             // draw stroke
-            if (Style.HasStrikethrough && fontMetrics.StrikeoutPosition.HasValue)
-                DrawLine(fontMetrics.StrikeoutPosition.Value, fontMetrics.StrikeoutThickness.Value);
+            if ((Style.HasStrikethrough ?? false) && fontMetrics.StrikeoutPosition.HasValue)
+                DrawLine(fontMetrics.StrikeoutPosition.Value, fontMetrics.StrikeoutThickness ?? 1);
 
             void DrawLine(float offset, float thickness)
             {

+ 6 - 0
QuestPDF/Fluent/PageExtensions.cs

@@ -94,6 +94,12 @@ namespace QuestPDF.Fluent
     
     public static class PageExtensions
     {
+        public static IDocumentContainer DefaultTextStyle(this IDocumentContainer document, TextStyle textStyle)
+        {
+            (document as DocumentContainer).DefaultTextStyle = textStyle;
+            return document;
+        }
+        
         public static IDocumentContainer Page(this IDocumentContainer document, Action<PageDescriptor> handler)
         {
             var descriptor = new PageDescriptor();

+ 51 - 16
QuestPDF/Infrastructure/TextStyle.cs

@@ -4,26 +4,61 @@ namespace QuestPDF.Infrastructure
 {
     public class TextStyle
     {
-        internal string Color { get; set; } = Colors.Black;
-        internal string BackgroundColor { get; set; } = Colors.Transparent;
-        internal string FontType { get; set; } = "Calibri";
-        internal float Size { get; set; } = 12;
-        internal float LineHeight { get; set; } = 1.2f;
-        internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
-        internal bool IsItalic { get; set; } = false;
-        internal bool HasStrikethrough { get; set; } = false;
-        internal bool HasUnderline { get; set; } = false;
-
-        public static TextStyle Default => new TextStyle();
+        internal bool HasGlobalStyleApplied { get; private set; }
+        
+        internal string? Color { get; set; }
+        internal string? BackgroundColor { get; set; }
+        internal string? FontType { get; set; }
+        internal float? Size { get; set; }
+        internal float? LineHeight { get; set; }
+        internal FontWeight? FontWeight { get; set; }
+        internal bool? IsItalic { get; set; }
+        internal bool? HasStrikethrough { get; set; }
+        internal bool? HasUnderline { get; set; }
 
-        private string? KeyCache { get; set; }
+        internal string? Key { get; private set; }
         
-        public override string ToString()
+        internal static TextStyle LibraryDefault => new TextStyle
+        {
+            Color = Colors.Black,
+            BackgroundColor = Colors.Transparent,
+            FontType = Fonts.Calibri,
+            Size = 12,
+            LineHeight = 1.2f,
+            FontWeight = Infrastructure.FontWeight.Normal,
+            IsItalic = false,
+            HasStrikethrough = false,
+            HasUnderline = false
+        };
+
+        private static TextStyle DefaultTextStyleCache = new TextStyle();
+        public static TextStyle Default => DefaultTextStyleCache;
+
+        internal void ApplyGlobalStyle(TextStyle global)
         {
-            KeyCache ??= $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{FontWeight}|{IsItalic}|{HasStrikethrough}|{HasUnderline}";
-            return KeyCache;
+            if (HasGlobalStyleApplied)
+                return;
+            
+            HasGlobalStyleApplied = true;
+
+            Color ??= global.Color;
+            BackgroundColor ??= global.BackgroundColor;
+            FontType ??= global.FontType;
+            Size ??= global.Size;
+            LineHeight ??= global.LineHeight;
+            FontWeight ??= global.FontWeight;
+            IsItalic ??= global.IsItalic;
+            HasStrikethrough ??= global.HasStrikethrough;
+            HasUnderline ??= global.HasUnderline;
+            
+            Key ??= $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{FontWeight}|{IsItalic}|{HasStrikethrough}|{HasUnderline}";
         }
 
-        internal TextStyle Clone() => (TextStyle)MemberwiseClone();
+        internal TextStyle Clone()
+        {
+            var clone = (TextStyle)MemberwiseClone();
+            clone.HasGlobalStyleApplied = false;
+            return clone;
+        }
     }
 }