Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e18b5170e6 | |||
| 5010b61565 | |||
| f8d270bd2a | |||
| f884547d16 | |||
| 4a7f9385c4 | |||
| f4181de3d2 |
@@ -0,0 +1,212 @@
|
|||||||
|
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;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class TextExample : ExampleTestBase
|
||||||
|
{
|
||||||
|
[ShowResult]
|
||||||
|
[ImageSize(1400, 600)]
|
||||||
|
public void OldText(IContainer container)
|
||||||
|
{
|
||||||
|
var fonts = new[]
|
||||||
|
{
|
||||||
|
Fonts.Arial,
|
||||||
|
Fonts.Calibri,
|
||||||
|
Fonts.Cambria,
|
||||||
|
Fonts.Candara,
|
||||||
|
Fonts.ComicSans,
|
||||||
|
Fonts.Consolas,
|
||||||
|
Fonts.Corbel,
|
||||||
|
Fonts.Courier,
|
||||||
|
Fonts.CourierNew,
|
||||||
|
Fonts.Georgia,
|
||||||
|
Fonts.Impact,
|
||||||
|
Fonts.LucidaConsole,
|
||||||
|
Fonts.SegoeSD,
|
||||||
|
Fonts.SegoeUI,
|
||||||
|
Fonts.Tahoma,
|
||||||
|
Fonts.TimesNewRoman,
|
||||||
|
Fonts.TimesRoman,
|
||||||
|
Fonts.Trebuchet,
|
||||||
|
Fonts.Verdana,
|
||||||
|
};
|
||||||
|
|
||||||
|
container
|
||||||
|
.Padding(50)
|
||||||
|
.Grid(stack =>
|
||||||
|
{
|
||||||
|
stack.Spacing(10);
|
||||||
|
stack.Columns(2);
|
||||||
|
|
||||||
|
foreach (var font in fonts)
|
||||||
|
{
|
||||||
|
stack
|
||||||
|
.Element()
|
||||||
|
.Box()
|
||||||
|
.Background(Placeholders.BackgroundColor())
|
||||||
|
.Text($"Lorem ipsum dolor sit amet {font}", TextStyle.Default.Size(24).FontType(font));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//[ShowResult]
|
||||||
|
[ImageSize(1400, 800)]
|
||||||
|
public void Test(IContainer container)
|
||||||
|
{
|
||||||
|
List<TextElement> Lorem()
|
||||||
|
{
|
||||||
|
return new List<TextElement>
|
||||||
|
{
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = "Podstawowy łaciński Tabela znaków Unicode"
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = "✔"
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = "🥛"
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = "🧀"
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = "❤🚵♀️"
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = "Lorem ipsum "
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.Size(24).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = " dolor "
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.Size(64).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = " sijt "
|
||||||
|
},
|
||||||
|
new TextElement()
|
||||||
|
{
|
||||||
|
Style = TextStyle.Default.Size(32).BackgroundColor(Placeholders.BackgroundColor()),
|
||||||
|
Text = " amet"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Func<List<TextElement>> Source = () => Split(RandomText());
|
||||||
|
|
||||||
|
container
|
||||||
|
.Padding(50)
|
||||||
|
.Box().Border(1).Stack(stack =>
|
||||||
|
{
|
||||||
|
stack
|
||||||
|
.Element()
|
||||||
|
.Box()
|
||||||
|
//.Background(Placeholders.BackgroundColor())
|
||||||
|
.Element(new TextRun()
|
||||||
|
{
|
||||||
|
Elements = Lorem()
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = Source()
|
||||||
|
});
|
||||||
|
|
||||||
|
stack
|
||||||
|
.Element()
|
||||||
|
.Box()
|
||||||
|
//.Background(Placeholders.BackgroundColor())
|
||||||
|
.Element(new TextRun()
|
||||||
|
{
|
||||||
|
Elements = Source()
|
||||||
|
});
|
||||||
|
|
||||||
|
stack
|
||||||
|
.Element()
|
||||||
|
.Box()
|
||||||
|
//.Background(Placeholders.BackgroundColor())
|
||||||
|
.Element(new TextRun()
|
||||||
|
{
|
||||||
|
Elements = Source()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TextElement> RandomText()
|
||||||
|
{
|
||||||
|
var sizes = new[] { 24, 32, 48, 64};
|
||||||
|
|
||||||
|
return Placeholders
|
||||||
|
.Sentence()
|
||||||
|
.Split(" ")
|
||||||
|
.Select(x => new TextElement
|
||||||
|
{
|
||||||
|
Text = $"{x} ",
|
||||||
|
Style = TextStyle
|
||||||
|
.Default
|
||||||
|
//.Size(sizes[Placeholders.Random.Next(0, 3)])
|
||||||
|
.Size(24)
|
||||||
|
.BackgroundColor(Placeholders.BackgroundColor())
|
||||||
|
//.LineHeight((float)Placeholders.Random.NextDouble() / 2 + 1f)
|
||||||
|
.LineHeight(1.2f)
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TextElement> Split(List<TextElement> elements)
|
||||||
|
{
|
||||||
|
return elements
|
||||||
|
.SelectMany(x => x
|
||||||
|
.Text
|
||||||
|
.Select(y => new TextElement
|
||||||
|
{
|
||||||
|
Style = x.Style,
|
||||||
|
Text = y.ToString()
|
||||||
|
}))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ namespace QuestPDF.Drawing
|
|||||||
Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
|
Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
|
||||||
TextSize = style.Size,
|
TextSize = style.Size,
|
||||||
TextEncoding = SKTextEncoding.Utf32,
|
TextEncoding = SKTextEncoding.Utf32,
|
||||||
|
|
||||||
TextAlign = style.Alignment switch
|
TextAlign = style.Alignment switch
|
||||||
{
|
{
|
||||||
HorizontalAlignment.Left => SKTextAlign.Left,
|
HorizontalAlignment.Left => SKTextAlign.Left,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using QuestPDF.Drawing;
|
using QuestPDF.Drawing;
|
||||||
using QuestPDF.Drawing.SpacePlan;
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using Size = QuestPDF.Infrastructure.Size;
|
using Size = QuestPDF.Infrastructure.Size;
|
||||||
|
|
||||||
@@ -24,7 +25,8 @@ namespace QuestPDF.Elements
|
|||||||
.DefaultIfEmpty(0)
|
.DefaultIfEmpty(0)
|
||||||
.Max();
|
.Max();
|
||||||
|
|
||||||
var realHeight = lines.Count * LineHeight;
|
var paint = Style.ToPaint().FontMetrics;
|
||||||
|
var realHeight = -paint.Ascent + paint.Descent;
|
||||||
|
|
||||||
if (realHeight > availableSpace.Height + Size.Epsilon)
|
if (realHeight > availableSpace.Height + Size.Epsilon)
|
||||||
return new Wrap();
|
return new Wrap();
|
||||||
@@ -34,12 +36,15 @@ namespace QuestPDF.Elements
|
|||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(ICanvas canvas, Size availableSpace)
|
||||||
{
|
{
|
||||||
|
var paint = Style.ToPaint().FontMetrics;
|
||||||
|
var offeset = -paint.Ascent; // paint.Ascent - Style.Size;
|
||||||
|
|
||||||
var lines = BreakLines(availableSpace.Width);
|
var lines = BreakLines(availableSpace.Width);
|
||||||
|
|
||||||
var offsetTop = 0f;
|
var offsetTop = 0f;
|
||||||
var offsetLeft = GetLeftOffset();
|
var offsetLeft = GetLeftOffset();
|
||||||
|
|
||||||
canvas.Translate(new Position(0, Style.Size));
|
canvas.Translate(new Position(0, offeset));
|
||||||
|
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
{
|
{
|
||||||
@@ -47,7 +52,7 @@ namespace QuestPDF.Elements
|
|||||||
offsetTop += LineHeight;
|
offsetTop += LineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.Translate(new Position(0, -Style.Size));
|
canvas.Translate(new Position(0, -offeset));
|
||||||
|
|
||||||
float GetLeftOffset()
|
float GetLeftOffset()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
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 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,
|
||||||
|
Width = width
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
using SkiaSharp;
|
||||||
|
using SkiaSharp.HarfBuzz;
|
||||||
|
|
||||||
|
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 metrics = Elements
|
||||||
|
.Select(x => x.Style.ToPaint())
|
||||||
|
.Select(x => x.FontMetrics)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var lineHeight = metrics.Max(x => -x.Ascent) + metrics.Max(x => x.Descent);
|
||||||
|
|
||||||
|
var width = measurements.Sum(x => x.Width);
|
||||||
|
|
||||||
|
return new FullRender(width, lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Draw(ICanvas canvas, Size availableSpace)
|
||||||
|
{
|
||||||
|
var measurements = Elements
|
||||||
|
.Select(x => x.Measure())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var metrics = Elements
|
||||||
|
.Select(x => x.Style.ToPaint())
|
||||||
|
.Select(x => x.FontMetrics)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var offset = metrics.Max(x => -x.Ascent);
|
||||||
|
var lineHeight = metrics.Max(x => -x.Ascent) + metrics.Max(x => x.Descent);
|
||||||
|
|
||||||
|
|
||||||
|
using var typeface = SKTypeface.FromFamilyName("Helvetica");
|
||||||
|
using var shaper = new SKShaper(typeface);
|
||||||
|
var shaped = shaper.Shape("Podstawowy łaciński — ✔️ ❤️ ☆ Tabela znaków Unicode", Elements.First().Style.ToPaint());
|
||||||
|
|
||||||
|
foreach (var textElement in Elements)
|
||||||
|
{
|
||||||
|
var size = textElement.Measure();
|
||||||
|
|
||||||
|
canvas.DrawRectangle(new Position(0, 0), new Size(size.Width, lineHeight), textElement.Style.BackgroundColor);
|
||||||
|
|
||||||
|
canvas.Translate(new Position(0, offset));
|
||||||
|
textElement.Draw(canvas);
|
||||||
|
canvas.Translate(new Position(0, -offset));
|
||||||
|
|
||||||
|
canvas.Translate(new Position(size.Width, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.Translate(new Position(-measurements.Sum(x => x.Width), 0));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,11 @@ namespace QuestPDF.Fluent
|
|||||||
return style.Mutate(x => x.Color = value);
|
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)
|
public static TextStyle FontType(this TextStyle style, string value)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.FontType = value);
|
return style.Mutate(x => x.FontType = value);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace QuestPDF.Helpers
|
|||||||
{
|
{
|
||||||
public static class Placeholders
|
public static class Placeholders
|
||||||
{
|
{
|
||||||
private static Random Random = new Random();
|
public static Random Random = new Random();
|
||||||
|
|
||||||
#region Word Cache
|
#region Word Cache
|
||||||
|
|
||||||
@@ -155,25 +155,25 @@ namespace QuestPDF.Helpers
|
|||||||
|
|
||||||
private static readonly string[] BackgroundColors =
|
private static readonly string[] BackgroundColors =
|
||||||
{
|
{
|
||||||
Colors.Red.Lighten2,
|
Colors.Red.Lighten3,
|
||||||
Colors.Pink.Lighten2,
|
Colors.Pink.Lighten3,
|
||||||
Colors.Purple.Lighten2,
|
Colors.Purple.Lighten3,
|
||||||
Colors.DeepPurple.Lighten2,
|
Colors.DeepPurple.Lighten3,
|
||||||
Colors.Indigo.Lighten2,
|
Colors.Indigo.Lighten3,
|
||||||
Colors.Blue.Lighten2,
|
Colors.Blue.Lighten3,
|
||||||
Colors.LightBlue.Lighten2,
|
Colors.LightBlue.Lighten3,
|
||||||
Colors.Cyan.Lighten2,
|
Colors.Cyan.Lighten3,
|
||||||
Colors.Teal.Lighten2,
|
Colors.Teal.Lighten3,
|
||||||
Colors.Green.Lighten2,
|
Colors.Green.Lighten3,
|
||||||
Colors.LightGreen.Lighten2,
|
Colors.LightGreen.Lighten3,
|
||||||
Colors.Lime.Lighten2,
|
Colors.Lime.Lighten3,
|
||||||
Colors.Yellow.Lighten2,
|
Colors.Yellow.Lighten3,
|
||||||
Colors.Amber.Lighten2,
|
Colors.Amber.Lighten3,
|
||||||
Colors.Orange.Lighten2,
|
Colors.Orange.Lighten3,
|
||||||
Colors.DeepOrange.Lighten2,
|
Colors.DeepOrange.Lighten3,
|
||||||
Colors.Brown.Lighten2,
|
Colors.Brown.Lighten3,
|
||||||
Colors.Grey.Lighten2,
|
Colors.Grey.Lighten3,
|
||||||
Colors.BlueGrey.Lighten2
|
Colors.BlueGrey.Lighten3
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string BackgroundColor()
|
public static string BackgroundColor()
|
||||||
|
|||||||
@@ -5,18 +5,19 @@ namespace QuestPDF.Infrastructure
|
|||||||
public class TextStyle
|
public class TextStyle
|
||||||
{
|
{
|
||||||
internal string Color { get; set; } = Colors.Black;
|
internal string Color { get; set; } = Colors.Black;
|
||||||
|
internal string BackgroundColor { get; set; } = Colors.Transparent;
|
||||||
internal string FontType { get; set; } = "Calibri";
|
internal string FontType { get; set; } = "Calibri";
|
||||||
internal float Size { get; set; } = 12;
|
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 HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
||||||
internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
|
internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
|
||||||
internal bool IsItalic { get; set; } = false;
|
internal bool IsItalic { get; set; } = false;
|
||||||
|
|
||||||
public static TextStyle Default => new TextStyle();
|
public static TextStyle Default => new TextStyle();
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{Color}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
|
return $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal TextStyle Clone() => (TextStyle)MemberwiseClone();
|
internal TextStyle Clone() => (TextStyle)MemberwiseClone();
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||||
|
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user