Selaa lähdekoodia

Simple drawing text

Marcin Ziąbek 4 vuotta sitten
vanhempi
sitoutus
f4181de3d2

+ 147 - 0
QuestPDF.Examples/TextExample.cs

@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using QuestPDF.Elements;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class TextExample : ExampleTestBase
+    {
+        [ShowResult]
+        [ImageSize(1200, 500)]
+        public void Test(IContainer container)
+        {
+            List<TextElement> Lorem()
+            {
+                return new List<TextElement>
+                {
+                    new TextElement()
+                    {
+                        Style = TextStyle.Default.Size(32).BackgroundColor(Colors.Red.Lighten3),
+                        Text = "Lorem ipsum "
+                    },
+                    new TextElement()
+                    {
+                        Style = TextStyle.Default.Size(24).BackgroundColor(Colors.Orange.Lighten3),
+                        Text = " dolor "
+                    },
+                    new TextElement()
+                    {
+                        Style = TextStyle.Default.Size(64).BackgroundColor(Colors.Yellow.Lighten3),
+                        Text = " sit "
+                    },
+                    new TextElement()
+                    {
+                        Style = TextStyle.Default.Size(32).BackgroundColor(Colors.Green.Lighten3),
+                        Text = " amet"
+                    }
+                };
+            }
+
+            Func<List<TextElement>> Source = Lorem ;
+            
+            container
+                .Padding(50)
+                .Stack(row =>
+                {
+                    row.Spacing(50);
+                    
+                    row.Element().Box().Border(1).Stack(stack =>
+                    {
+                        stack
+                            .Element()
+                            .Box()
+                            //.Background(Placeholders.BackgroundColor())
+                            .Element(new TextRun()
+                            {
+                                Elements = Source()
+                            });
+
+                        stack
+                            .Element()
+                            .Box()
+                            //.Background(Placeholders.BackgroundColor())
+                            .Element(new TextRun()
+                            {
+                                Elements = Source()
+                            });
+                        
+                        stack
+                            .Element()
+                            .Box()
+                            //.Background(Placeholders.BackgroundColor())
+                            .Element(new TextRun()
+                            {
+                                Elements = Split(Source())
+                            });
+                    });
+                    
+                    row.Element().Box().Border(1).Stack(stack =>
+                    {
+                        stack
+                            .Element()
+                            .Box()
+                            .Background(Placeholders.BackgroundColor())
+                            .Element(new TextRun()
+                            {
+                                Elements = Dense(Source())
+                            });
+
+                        stack
+                            .Element()
+                            .Box()
+                            .Background(Placeholders.BackgroundColor())
+                            .Element(new TextRun()
+                            {
+                                Elements = Dense(Source())
+                            });
+                        
+                        stack
+                            .Element()
+                            .Box()
+                            .Background(Placeholders.BackgroundColor())
+                            .Element(new TextRun()
+                            {
+                                Elements = Dense(Split(Source()))
+                            });
+                    });
+                });
+        }
+
+        List<TextElement> RandomText()
+        {
+            return Placeholders
+                .Sentence()
+                .Split(" ")
+                .Select(x => new TextElement
+                {
+                    Text = $"{x} ",
+                    Style = TextStyle.Default.Size(Placeholders.Random.Next(24, 64))
+                })
+                .ToList();
+        }
+        
+        List<TextElement> Split(List<TextElement> elements)
+        {
+            return elements
+                .SelectMany(x => x
+                    .Text
+                    .Select(y => new TextElement
+                    {
+                        Style = x.Style,
+                        Text = y.ToString()
+                    }))
+                .ToList();
+        }
+        
+        List<TextElement> Dense(List<TextElement> elements)
+        {
+            elements.ForEach(x => x.Style.IsDense = true);
+            return elements;
+        }
+    }
+}

+ 1 - 1
QuestPDF/Drawing/CanvasCache.cs

@@ -37,7 +37,7 @@ namespace QuestPDF.Drawing
                     Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
                     TextSize = style.Size,
                     TextEncoding = SKTextEncoding.Utf32,
-                    
+
                     TextAlign = style.Alignment switch
                     {
                         HorizontalAlignment.Left => SKTextAlign.Left,

+ 7 - 3
QuestPDF/Elements/Stack.cs

@@ -24,6 +24,7 @@ namespace QuestPDF.Elements
                 return new FullRender(Size.Zero);
 
             var heightOnCurrentPage = 0f;
+            var maxWidth = 0f;
 
             foreach (var renderer in ChildrenQueue)
             {
@@ -34,17 +35,20 @@ namespace QuestPDF.Elements
                     if (heightOnCurrentPage < Size.Epsilon)
                         return new Wrap();
                     
-                    return new PartialRender(availableSpace.Width, heightOnCurrentPage);
+                    return new PartialRender(maxWidth, heightOnCurrentPage);
                 }
 
                 var size = space as Size;
                 heightOnCurrentPage += size.Height;
 
+                if (size.Width > maxWidth)
+                    maxWidth = size.Width;
+
                 if (space is PartialRender)
-                    return new PartialRender(availableSpace.Width, heightOnCurrentPage);
+                    return new PartialRender(maxWidth, heightOnCurrentPage);
             }
             
-            return new FullRender(availableSpace.Width, heightOnCurrentPage);
+            return new FullRender(maxWidth, heightOnCurrentPage);
         }
 
         internal override void Draw(ICanvas canvas, Size availableSpace)

+ 55 - 0
QuestPDF/Elements/TextElement.cs

@@ -0,0 +1,55 @@
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using QuestPDF.Drawing;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Infrastructure;
+using SkiaSharp;
+
+namespace QuestPDF.Elements
+{
+    public struct TextMeasurement
+    {
+        public float Width { get; set; }
+        public float Height { get; set; }
+        public SKRect Position { get; set; }
+    }
+    
+    internal class TextElement
+    {
+        public TextStyle Style { get; set; }
+        public string Text { get; set; }
+
+
+        private static ConcurrentDictionary<string, TextMeasurement> Measurements = new ConcurrentDictionary<string, TextMeasurement>();
+        
+        public TextMeasurement Measure()
+        {
+            return Measure(Text, Style);
+        }
+
+        public void Draw(ICanvas canvas)
+        {
+            (canvas as QuestPDF.Drawing.Canvas).SkiaCanvas.DrawText(Text, 0, 0, Style.ToPaint());
+        }
+
+        internal static TextMeasurement Measure(string text, TextStyle style)
+        {
+            var key = $"{text}_{style}";
+            
+            return Measurements.GetOrAdd(key, x =>
+            {
+                var rect = new SKRect();
+                var width = style.ToPaint().MeasureText(text, ref rect);
+                
+                return new TextMeasurement
+                {
+                    Position = rect,
+                    Height = style.IsDense 
+                        ? rect.Bottom + rect.Height 
+                        : style.LineHeight * style.Size,
+                    Width = width
+                };
+            });
+        }
+    }
+}

+ 48 - 0
QuestPDF/Elements/TextRun.cs

@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Linq;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    internal class TextRun : Element
+    {
+        public List<TextElement> Elements = new List<TextElement>();
+
+        internal override ISpacePlan Measure(Size availableSpace)
+        {
+            var measurements = Elements.Select(x => x.Measure()).ToList();
+            
+            var width = measurements.Sum(x => x.Width);
+            //var height = measurements.Max(x => x.Height);
+            var height = measurements.Max(x => x.Position.Bottom) + measurements.Max(x => x.Position.Height);
+
+
+            return new FullRender(width, height);
+        }
+
+        internal override void Draw(ICanvas canvas, Size availableSpace)
+        {
+            var measurements = Elements.Select(x => x.Measure()).ToList();
+
+            var top = measurements.Max(x => x.Position.Height);
+            var bottom = measurements.Max(x => x.Position.Bottom);
+            
+            var offset = measurements.Min(x => x.Position.Top);
+            canvas.Translate(new Position(0, -offset));
+            
+            foreach (var textElement in Elements)
+            {
+                var size = textElement.Measure();
+                
+                canvas.DrawRectangle(new Position(0, -top), new Size(size.Width, bottom + top), textElement.Style.BackgroundColor);
+                //canvas.DrawRectangle(new Position(size.Position.Left, size.Position.Top), new Size(size.Position.Width, size.Position.Height), textElement.Style.BackgroundColor);
+                textElement.Draw(canvas);
+                canvas.Translate(new Position(size.Width, 0));
+            }
+            
+            canvas.Translate(new Position(-measurements.Sum(x => x.Width), offset));
+        }
+    }
+}

+ 5 - 0
QuestPDF/Fluent/TextStyleExtensions.cs

@@ -18,6 +18,11 @@ namespace QuestPDF.Fluent
             return style.Mutate(x => x.Color = value);
         }
         
+        public static TextStyle BackgroundColor(this TextStyle style, string value)
+        {
+            return style.Mutate(x => x.BackgroundColor = value);
+        }
+        
         public static TextStyle FontType(this TextStyle style, string value)
         {
             return style.Mutate(x => x.FontType = value);

+ 1 - 1
QuestPDF/Helpers/Placeholders.cs

@@ -7,7 +7,7 @@ namespace QuestPDF.Helpers
 {
     public static class Placeholders
     {
-        private static Random Random = new Random();
+        public static Random Random = new Random();
         
         #region Word Cache
 

+ 5 - 3
QuestPDF/Infrastructure/TextStyle.cs

@@ -5,18 +5,20 @@ 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 float LineHeight { get; set; } = 1f;
         internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
         internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
         internal bool IsItalic { get; set; } = false;
-
+        internal bool IsDense { get; set; }
+        
         public static TextStyle Default => new TextStyle();
         
         public override string ToString()
         {
-            return $"{Color}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
+            return $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}|{IsDense}";
         }
 
         internal TextStyle Clone() => (TextStyle)MemberwiseClone();