Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5cc0397acb | |||
| 73448f483c | |||
| 7b7fac07b3 | |||
| 759655059d | |||
| fa3d912e41 | |||
| 9ce9ca85be | |||
| 553c8ab719 | |||
| e768e9b06d | |||
| fd913a777c | |||
| ed9e6daec5 | |||
| ee6249a658 | |||
| b307304f46 | |||
| f028f82e11 | |||
| 719a3385f6 | |||
| 2adff11400 | |||
| 34fed6d547 | |||
| db2df75624 | |||
| 6b535752df | |||
| 556f87ff25 | |||
| fbebbd85eb | |||
| 4650c2a4ea | |||
| 1ba01a1cf5 | |||
| d4448437ac | |||
| bc853f48fc |
@@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class ProcessRunningTime
|
||||||
|
{
|
||||||
|
public TimeSpan FluentTime { get; set; }
|
||||||
|
public TimeSpan GenerationTime { get; set; }
|
||||||
|
public float Size { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenerationBenchmark
|
||||||
|
{
|
||||||
|
public const int TestSize = 4096;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BenchmarkAsync()
|
||||||
|
{
|
||||||
|
RunTest(() => Enumerable
|
||||||
|
.Range(0, TestSize)
|
||||||
|
.AsParallel() // difference
|
||||||
|
.Select(GenerateAndCollect)
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BenchmarkSync()
|
||||||
|
{
|
||||||
|
RunTest(() => Enumerable
|
||||||
|
.Range(0, TestSize)
|
||||||
|
.Select(GenerateAndCollect)
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunTest(Func<IEnumerable<ProcessRunningTime>> handler)
|
||||||
|
{
|
||||||
|
var totalFluentTime = TimeSpan.Zero;
|
||||||
|
var totalGenerationTime = TimeSpan.Zero;
|
||||||
|
|
||||||
|
var stopWatch = new Stopwatch();
|
||||||
|
|
||||||
|
stopWatch.Start();
|
||||||
|
var results = handler();
|
||||||
|
stopWatch.Stop();
|
||||||
|
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
totalFluentTime += result.FluentTime;
|
||||||
|
totalGenerationTime += result.GenerationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Fluent: {totalFluentTime:g}");
|
||||||
|
Console.WriteLine($"Generation: {totalGenerationTime:g}");
|
||||||
|
Console.WriteLine($"Total: {stopWatch.Elapsed:g}");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProcessRunningTime GenerateAndCollect(int attemptNumber)
|
||||||
|
{
|
||||||
|
var stopwatch = new Stopwatch();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
var container = new Container();
|
||||||
|
|
||||||
|
container
|
||||||
|
.Padding(10)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.Column(column =>
|
||||||
|
{
|
||||||
|
column.Item().Text($"Attempts {attemptNumber}");
|
||||||
|
|
||||||
|
const int numberOfRows = 100;
|
||||||
|
const int numberOfColumns = 10;
|
||||||
|
|
||||||
|
for (var y = 0; y < numberOfRows; y++)
|
||||||
|
{
|
||||||
|
column.Item().Row(row =>
|
||||||
|
{
|
||||||
|
for (var x = 0; x < numberOfColumns; x++)
|
||||||
|
{
|
||||||
|
row.RelativeItem()
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten5)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten4)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten3)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten2)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten1)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Medium)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken1)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken2)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken3)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken4)
|
||||||
|
.Height(3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var fluentTime = stopwatch.Elapsed;
|
||||||
|
|
||||||
|
stopwatch.Reset();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
var size = Document
|
||||||
|
.Create(x => x.Page(page => page.Content().Element(container)))
|
||||||
|
.GeneratePdf()
|
||||||
|
.Length;
|
||||||
|
|
||||||
|
var generationTime = stopwatch.Elapsed;
|
||||||
|
|
||||||
|
return new ProcessRunningTime
|
||||||
|
{
|
||||||
|
FluentTime = fluentTime,
|
||||||
|
GenerationTime = generationTime,
|
||||||
|
Size = size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class RelativePaddingExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void ItemTypes()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.PageSize(250, 250)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Width(250)
|
||||||
|
.Height(250)
|
||||||
|
|
||||||
|
.Padding(50)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
|
||||||
|
.RelativePaddingLeft(0.1f)
|
||||||
|
.RelativePaddingTop(0.2f)
|
||||||
|
.RelativePaddingRight(0.3f)
|
||||||
|
.RelativePaddingBottom(0.4f)
|
||||||
|
|
||||||
|
.Background(Colors.Grey.Darken2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class RelativePositionExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void ItemTypes()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.PageSize(500, 500)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(100)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.RelativePositionVertical(0.5f, -0.5f)
|
||||||
|
.RelativePositionHorizontal(1f, -0.5f)
|
||||||
|
.RelativeWidth(0.4f)
|
||||||
|
.RelativeHeight(0.6f)
|
||||||
|
.Background(Colors.Grey.Darken2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class RelativeSizeExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void ItemTypes()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.PageSize(600, 600)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.Width(400)
|
||||||
|
.Height(400)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.Container()
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.RelativeWidth(0.25f)
|
||||||
|
.RelativeHeight(0.5f)
|
||||||
|
.Background(Colors.Grey.Darken2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class ShrinkExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Without()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Horizontal()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.ShrinkHorizontal()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Vertical()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.ShrinkVertical()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Both()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.Shrink()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,7 +111,7 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
page.Margin(50);
|
page.Margin(50);
|
||||||
|
|
||||||
page.Content().Column(column =>
|
page.Content().PaddingVertical(10).Column(column =>
|
||||||
{
|
{
|
||||||
column.Item().Element(Title);
|
column.Item().Element(Title);
|
||||||
column.Item().PageBreak();
|
column.Item().PageBreak();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<Authors>MarcinZiabek</Authors>
|
<Authors>MarcinZiabek</Authors>
|
||||||
<Company>CodeFlint</Company>
|
<Company>CodeFlint</Company>
|
||||||
<PackageId>QuestPDF.Previewer</PackageId>
|
<PackageId>QuestPDF.Previewer</PackageId>
|
||||||
<Version>2022.8.0</Version>
|
<Version>2022.9.1</Version>
|
||||||
<PackAsTool>true</PackAsTool>
|
<PackAsTool>true</PackAsTool>
|
||||||
<ToolCommandName>questpdf-previewer</ToolCommandName>
|
<ToolCommandName>questpdf-previewer</ToolCommandName>
|
||||||
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
||||||
|
|||||||
@@ -7,18 +7,20 @@ using QuestPDF.UnitTests.TestEngine;
|
|||||||
namespace QuestPDF.UnitTests
|
namespace QuestPDF.UnitTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class BoxTests
|
public class ShrinkTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure() => SimpleContainerTests.Measure<MinimalBox>();
|
public void Measure() => SimpleContainerTests.Measure<Shrink>();
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Draw_Wrap()
|
public void Draw_Wrap()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new MinimalBox
|
.For(x => new Shrink
|
||||||
{
|
{
|
||||||
Child = x.CreateChild()
|
Child = x.CreateChild(),
|
||||||
|
ShrinkVertical = true,
|
||||||
|
ShrinkHorizontal = true
|
||||||
})
|
})
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.Wrap())
|
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.Wrap())
|
||||||
@@ -29,9 +31,11 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_PartialRender()
|
public void Measure_PartialRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new MinimalBox
|
.For(x => new Shrink
|
||||||
{
|
{
|
||||||
Child = x.CreateChild()
|
Child = x.CreateChild(),
|
||||||
|
ShrinkVertical = true,
|
||||||
|
ShrinkHorizontal = true
|
||||||
})
|
})
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
|
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
|
||||||
@@ -43,9 +47,11 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_FullRender()
|
public void Measure_FullRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new MinimalBox
|
.For(x => new Shrink
|
||||||
{
|
{
|
||||||
Child = x.CreateChild()
|
Child = x.CreateChild(),
|
||||||
|
ShrinkVertical = true,
|
||||||
|
ShrinkHorizontal = true
|
||||||
})
|
})
|
||||||
.MeasureElement(new Size(500, 400))
|
.MeasureElement(new Size(500, 400))
|
||||||
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
|
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
|
||||||
@@ -172,7 +172,7 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
if (textBlockItem is TextBlockSpan textSpan)
|
if (textBlockItem is TextBlockSpan textSpan)
|
||||||
{
|
{
|
||||||
textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
textSpan.Style = textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||||
}
|
}
|
||||||
else if (textBlockItem is TextBlockElement textElement)
|
else if (textBlockItem is TextBlockElement textElement)
|
||||||
{
|
{
|
||||||
@@ -184,18 +184,13 @@ namespace QuestPDF.Drawing
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (content is DynamicHost dynamicHost)
|
if (content is DynamicHost dynamicHost)
|
||||||
dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||||
|
|
||||||
var targetTextStyle = documentDefaultTextStyle;
|
|
||||||
|
|
||||||
if (content is DefaultTextStyle defaultTextStyleElement)
|
if (content is DefaultTextStyle defaultTextStyleElement)
|
||||||
{
|
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||||
defaultTextStyleElement.TextStyle.ApplyParentStyle(documentDefaultTextStyle);
|
|
||||||
targetTextStyle = defaultTextStyleElement.TextStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var child in content.GetChildren())
|
foreach (var child in content.GetChildren())
|
||||||
ApplyDefaultTextStyle(child, targetTextStyle);
|
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,13 +14,13 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
public static class FontManager
|
public static class FontManager
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
|
private static readonly ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
|
||||||
private static ConcurrentDictionary<object, SKFontMetrics> FontMetrics = new();
|
private static readonly ConcurrentDictionary<TextStyle, SKFontMetrics> FontMetrics = new();
|
||||||
private static ConcurrentDictionary<object, SKPaint> FontPaints = new();
|
private static readonly ConcurrentDictionary<TextStyle, SKPaint> FontPaints = new();
|
||||||
private static ConcurrentDictionary<string, SKPaint> ColorPaints = new();
|
private static readonly ConcurrentDictionary<string, SKPaint> ColorPaints = new();
|
||||||
private static ConcurrentDictionary<object, Font> ShaperFonts = new();
|
private static readonly ConcurrentDictionary<TextStyle, Font> ShaperFonts = new();
|
||||||
private static ConcurrentDictionary<object, SKFont> Fonts = new();
|
private static readonly ConcurrentDictionary<TextStyle, SKFont> Fonts = new();
|
||||||
private static ConcurrentDictionary<object, TextShaper> TextShapers = new();
|
private static readonly ConcurrentDictionary<TextStyle, TextShaper> TextShapers = new();
|
||||||
|
|
||||||
static FontManager()
|
static FontManager()
|
||||||
{
|
{
|
||||||
@@ -110,7 +110,7 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
internal static SKPaint ToPaint(this TextStyle style)
|
internal static SKPaint ToPaint(this TextStyle style)
|
||||||
{
|
{
|
||||||
return FontPaints.GetOrAdd(style.PaintKey, key => Convert(style));
|
return FontPaints.GetOrAdd(style, Convert);
|
||||||
|
|
||||||
static SKPaint Convert(TextStyle style)
|
static SKPaint Convert(TextStyle style)
|
||||||
{
|
{
|
||||||
@@ -172,14 +172,14 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
||||||
{
|
{
|
||||||
return FontMetrics.GetOrAdd(style.FontMetricsKey, key => style.NormalPosition().ToPaint().FontMetrics);
|
return FontMetrics.GetOrAdd(style, key => key.NormalPosition().ToPaint().FontMetrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Font ToShaperFont(this TextStyle style)
|
internal static Font ToShaperFont(this TextStyle style)
|
||||||
{
|
{
|
||||||
return ShaperFonts.GetOrAdd(style.PaintKey, _ =>
|
return ShaperFonts.GetOrAdd(style, key =>
|
||||||
{
|
{
|
||||||
var typeface = style.ToPaint().Typeface;
|
var typeface = key.ToPaint().Typeface;
|
||||||
|
|
||||||
using var harfBuzzBlob = typeface.OpenStream(out var ttcIndex).ToHarfBuzzBlob();
|
using var harfBuzzBlob = typeface.OpenStream(out var ttcIndex).ToHarfBuzzBlob();
|
||||||
|
|
||||||
@@ -200,12 +200,12 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
internal static TextShaper ToTextShaper(this TextStyle style)
|
internal static TextShaper ToTextShaper(this TextStyle style)
|
||||||
{
|
{
|
||||||
return TextShapers.GetOrAdd(style.PaintKey, _ => new TextShaper(style));
|
return TextShapers.GetOrAdd(style, key => new TextShaper(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static SKFont ToFont(this TextStyle style)
|
internal static SKFont ToFont(this TextStyle style)
|
||||||
{
|
{
|
||||||
return Fonts.GetOrAdd(style.PaintKey, _ => style.ToPaint().ToFont());
|
return Fonts.GetOrAdd(style, key => key.ToPaint().ToFont());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace QuestPDF.Drawing
|
|
||||||
{
|
|
||||||
internal struct TextMeasurement
|
|
||||||
{
|
|
||||||
public int LineIndex { get; set; }
|
|
||||||
public float FragmentWidth { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -55,9 +55,6 @@ namespace QuestPDF.Drawing
|
|||||||
xOffset += glyphPositions[i].XAdvance * scaleX;
|
xOffset += glyphPositions[i].XAdvance * scaleX;
|
||||||
yOffset += glyphPositions[i].YAdvance * scaleY;
|
yOffset += glyphPositions[i].YAdvance * scaleY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.CheckIfAllTextGlyphsAreAvailableInSpecifiedFont)
|
|
||||||
CheckIfAllGlyphsAreAvailable(glyphs, text);
|
|
||||||
|
|
||||||
return new TextShapingResult(glyphs);
|
return new TextShapingResult(glyphs);
|
||||||
}
|
}
|
||||||
@@ -78,20 +75,6 @@ namespace QuestPDF.Drawing
|
|||||||
else
|
else
|
||||||
throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
|
throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckIfAllGlyphsAreAvailable(ShapedGlyph[] glyphs, string originalText)
|
|
||||||
{
|
|
||||||
var containsMissingGlyphs = glyphs.Any(x => x.Codepoint == default);
|
|
||||||
|
|
||||||
if (!containsMissingGlyphs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
throw new ArgumentException(
|
|
||||||
$"Detected missing font glyphs while rendering text. " +
|
|
||||||
$"This means that the document contains text with characters not present in the assigned font. " +
|
|
||||||
$"Such characters are replaced by placeholders, usually visible as empty rectangles. " +
|
|
||||||
$"Font family used: {TextStyle.FontFamily}. Issue detected in text: '{originalText}'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct ShapedGlyph
|
internal struct ShapedGlyph
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace QuestPDF.Elements
|
|||||||
private DynamicComponentProxy Child { get; }
|
private DynamicComponentProxy Child { get; }
|
||||||
private object InitialComponentState { get; set; }
|
private object InitialComponentState { get; set; }
|
||||||
|
|
||||||
internal TextStyle TextStyle { get; } = new();
|
internal TextStyle TextStyle { get; set; } = TextStyle.Default;
|
||||||
|
|
||||||
public DynamicHost(DynamicComponentProxy child)
|
public DynamicHost(DynamicComponentProxy child)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
using QuestPDF.Drawing;
|
|
||||||
using QuestPDF.Infrastructure;
|
|
||||||
|
|
||||||
namespace QuestPDF.Elements
|
|
||||||
{
|
|
||||||
internal class MinimalBox : ContainerElement
|
|
||||||
{
|
|
||||||
internal override void Draw(Size availableSpace)
|
|
||||||
{
|
|
||||||
var targetSize = base.Measure(availableSpace);
|
|
||||||
|
|
||||||
if (targetSize.Type == SpacePlanType.Wrap)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.Draw(targetSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class RelativePadding : ContainerElement
|
||||||
|
{
|
||||||
|
public float Top { get; set; }
|
||||||
|
public float Right { get; set; }
|
||||||
|
public float Bottom { get; set; }
|
||||||
|
public float Left { get; set; }
|
||||||
|
|
||||||
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
|
{
|
||||||
|
if (Child == null)
|
||||||
|
return SpacePlan.FullRender(0, 0);
|
||||||
|
|
||||||
|
var internalSpace = InternalSpace(availableSpace);
|
||||||
|
|
||||||
|
if (internalSpace.Width < 0 || internalSpace.Height < 0)
|
||||||
|
return SpacePlan.Wrap();
|
||||||
|
|
||||||
|
var measure = base.Measure(internalSpace);
|
||||||
|
|
||||||
|
if (measure.Type == SpacePlanType.Wrap)
|
||||||
|
return SpacePlan.Wrap();
|
||||||
|
|
||||||
|
if (measure.Type == SpacePlanType.PartialRender)
|
||||||
|
return SpacePlan.PartialRender(availableSpace);
|
||||||
|
|
||||||
|
if (measure.Type == SpacePlanType.FullRender)
|
||||||
|
return SpacePlan.FullRender(availableSpace);
|
||||||
|
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
if (Child == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var internalOffset = InternalOffset(availableSpace);
|
||||||
|
var internalSpace = InternalSpace(availableSpace);
|
||||||
|
|
||||||
|
Canvas.Translate(internalOffset);
|
||||||
|
base.Draw(internalSpace);
|
||||||
|
Canvas.Translate(internalOffset.Reverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Position InternalOffset(Size availableSpace)
|
||||||
|
{
|
||||||
|
return new Position(
|
||||||
|
availableSpace.Width * Left,
|
||||||
|
availableSpace.Height * Top);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Size InternalSpace(Size availableSpace)
|
||||||
|
{
|
||||||
|
return new Size(
|
||||||
|
availableSpace.Width * (1f - Left - Right),
|
||||||
|
availableSpace.Height * (1f - Top - Bottom));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class RelativePosition : ContainerElement
|
||||||
|
{
|
||||||
|
public float VerticalParent { get; set; }
|
||||||
|
public float VerticalChild { get; set; }
|
||||||
|
|
||||||
|
public float HorizontalParent { get; set; }
|
||||||
|
public float HorizontalChild { get; set; }
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
if (Child == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var childSize = base.Measure(availableSpace);
|
||||||
|
|
||||||
|
if (childSize.Type == SpacePlanType.Wrap)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var left = availableSpace.Width * HorizontalParent + childSize.Width * HorizontalChild;
|
||||||
|
var top = availableSpace.Height * VerticalParent + childSize.Height * VerticalChild;
|
||||||
|
|
||||||
|
Canvas.Translate(new Position(left, top));
|
||||||
|
base.Draw(childSize);
|
||||||
|
Canvas.Translate(new Position(-left, -top));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class RelativeSize : ContainerElement
|
||||||
|
{
|
||||||
|
public float? WidthFactor { get; set; } = 1f;
|
||||||
|
public float? HeightFactor { get; set; } = 1f;
|
||||||
|
|
||||||
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
|
{
|
||||||
|
var internalSpace = new Size(
|
||||||
|
availableSpace.Width * (WidthFactor ?? 1),
|
||||||
|
availableSpace.Height * (HeightFactor ?? 1));
|
||||||
|
|
||||||
|
var childSpace = Child?.Measure(internalSpace) ?? SpacePlan.FullRender(0, 0);
|
||||||
|
|
||||||
|
if (childSpace.Type == SpacePlanType.Wrap)
|
||||||
|
return SpacePlan.Wrap();
|
||||||
|
|
||||||
|
var targetSpace = new Size(
|
||||||
|
WidthFactor.HasValue ? internalSpace.Width : childSpace.Width,
|
||||||
|
HeightFactor.HasValue ? internalSpace.Height : childSpace.Height);
|
||||||
|
|
||||||
|
if (childSpace.Type == SpacePlanType.PartialRender)
|
||||||
|
return SpacePlan.PartialRender(targetSpace);
|
||||||
|
|
||||||
|
if (childSpace.Type == SpacePlanType.FullRender)
|
||||||
|
return SpacePlan.FullRender(targetSpace);
|
||||||
|
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class Shrink : ContainerElement
|
||||||
|
{
|
||||||
|
public bool ShrinkVertical { get; set; }
|
||||||
|
public bool ShrinkHorizontal { get; set; }
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
var childSize = base.Measure(availableSpace);
|
||||||
|
|
||||||
|
if (childSize.Type == SpacePlanType.Wrap)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var targetSize = new Size(
|
||||||
|
ShrinkVertical ? childSize.Width : availableSpace.Width,
|
||||||
|
ShrinkHorizontal ? childSize.Height : availableSpace.Height);
|
||||||
|
|
||||||
|
// TODO: adjust offset for RTL mode
|
||||||
|
|
||||||
|
base.Draw(targetSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,22 +85,26 @@ namespace QuestPDF.Elements.Text
|
|||||||
if (fallbackOption.Font.ContainsGlyph(codepoint))
|
if (fallbackOption.Font.ContainsGlyph(codepoint))
|
||||||
return fallbackOption;
|
return fallbackOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw CreateNotMatchingFontException(codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Exception CreateNotMatchingFontException(int codepoint)
|
||||||
|
{
|
||||||
var character = char.ConvertFromUtf32(codepoint);
|
var character = char.ConvertFromUtf32(codepoint);
|
||||||
var unicode = $"U-{codepoint:X4}";
|
var unicode = $"U-{codepoint:X4}";
|
||||||
|
|
||||||
|
|
||||||
var proposedFonts = FindFontsContainingGlyph(codepoint);
|
var proposedFonts = FindFontsContainingGlyph(codepoint);
|
||||||
var proposedFontsFormatted = proposedFonts.Any() ? string.Join(", ", proposedFonts) : "no fonts available";
|
var proposedFontsFormatted = proposedFonts.Any() ? string.Join(", ", proposedFonts) : "no fonts available";
|
||||||
|
|
||||||
throw new DocumentDrawingException(
|
return new DocumentDrawingException(
|
||||||
$"Could not find an appropriate font fallback for glyph: {unicode} '{character}'. " +
|
$"Could not find an appropriate font fallback for glyph: {unicode} '{character}'. " +
|
||||||
$"Font families available on current environment that contain this glyph: {proposedFontsFormatted}. " +
|
$"Font families available on current environment that contain this glyph: {proposedFontsFormatted}. " +
|
||||||
$"Possible solutions: " +
|
$"Possible solutions: " +
|
||||||
$"1) Use one of the listed fonts as the primary font in your document. " +
|
$"1) Use one of the listed fonts as the primary font in your document. " +
|
||||||
$"2) Configure the fallback TextStyle using the 'TextStyle.Fallback' method with one of the listed fonts. ");
|
$"2) Configure the fallback TextStyle using the 'TextStyle.Fallback' method with one of the listed fonts. ");
|
||||||
}
|
}
|
||||||
|
|
||||||
static IEnumerable<string> FindFontsContainingGlyph(int codepoint)
|
static IEnumerable<string> FindFontsContainingGlyph(int codepoint)
|
||||||
{
|
{
|
||||||
var fontManager = SKFontManager.Default;
|
var fontManager = SKFontManager.Default;
|
||||||
@@ -117,10 +121,9 @@ namespace QuestPDF.Elements.Text
|
|||||||
{
|
{
|
||||||
foreach (var textBlockItem in textBlockItems)
|
foreach (var textBlockItem in textBlockItems)
|
||||||
{
|
{
|
||||||
if (textBlockItem is TextBlockSpan textBlockSpan)
|
if (textBlockItem is TextBlockSpan textBlockSpan and not TextBlockPageNumber)
|
||||||
{
|
{
|
||||||
// perform font-fallback operation only when any fallback is available
|
if (!Settings.CheckIfAllTextGlyphsAreAvailable && textBlockSpan.Style.Fallback == null)
|
||||||
if (textBlockSpan.Style.Fallback == null)
|
|
||||||
{
|
{
|
||||||
yield return textBlockSpan;
|
yield return textBlockSpan;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ namespace QuestPDF.Elements.Text.Items
|
|||||||
internal class TextBlockSpan : ITextBlockItem
|
internal class TextBlockSpan : ITextBlockItem
|
||||||
{
|
{
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
public TextStyle Style { get; set; } = new();
|
public TextStyle Style { get; set; } = TextStyle.Default;
|
||||||
private TextShapingResult? TextShapingResult { get; set; }
|
public TextShapingResult? TextShapingResult { get; set; }
|
||||||
|
|
||||||
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new ();
|
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new ();
|
||||||
protected virtual bool EnableTextCache => true;
|
protected virtual bool EnableTextCache => true;
|
||||||
|
|||||||
@@ -154,17 +154,6 @@ namespace QuestPDF.Fluent
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.1. Please use the MinimalBox method.")]
|
|
||||||
public static IContainer Box(this IContainer element)
|
|
||||||
{
|
|
||||||
return element.Element(new MinimalBox());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IContainer MinimalBox(this IContainer element)
|
|
||||||
{
|
|
||||||
return element.Element(new MinimalBox());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IContainer Unconstrained(this IContainer element)
|
public static IContainer Unconstrained(this IContainer element)
|
||||||
{
|
{
|
||||||
return element.Element(new Unconstrained());
|
return element.Element(new Unconstrained());
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class RelativePaddingExtensions
|
||||||
|
{
|
||||||
|
private static IContainer RelativePadding(this IContainer element, Action<RelativePadding> handler)
|
||||||
|
{
|
||||||
|
var relativePadding = element as RelativePadding ?? new RelativePadding();
|
||||||
|
handler(relativePadding);
|
||||||
|
|
||||||
|
return element.Element(relativePadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativePaddingTop(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.RelativePadding(x => x.Top += value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativePaddingBottom(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.RelativePadding(x => x.Bottom += value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativePaddingLeft(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.RelativePadding(x => x.Left += value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativePaddingRight(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.RelativePadding(x => x.Right += value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class RelativePositionExtensions
|
||||||
|
{
|
||||||
|
private static IContainer RelativePosition(this IContainer element, Action<RelativePosition> handler)
|
||||||
|
{
|
||||||
|
var relativePosition = element as RelativePosition ?? new RelativePosition();
|
||||||
|
handler(relativePosition);
|
||||||
|
|
||||||
|
return element.Element(relativePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativePositionVertical(this IContainer element, float parentOffset, float childOffset)
|
||||||
|
{
|
||||||
|
return element.RelativePosition(x =>
|
||||||
|
{
|
||||||
|
x.VerticalParent = parentOffset;
|
||||||
|
x.VerticalChild = childOffset;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativePositionHorizontal(this IContainer element, float parentOffset, float childOffset)
|
||||||
|
{
|
||||||
|
return element.RelativePosition(x =>
|
||||||
|
{
|
||||||
|
x.HorizontalParent = parentOffset;
|
||||||
|
x.HorizontalChild = childOffset;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class RelativeSizeExtensions
|
||||||
|
{
|
||||||
|
private static IContainer RelativeSize(this IContainer element, Action<RelativeSize> handler)
|
||||||
|
{
|
||||||
|
var relativeSize = element as RelativeSize ?? new RelativeSize();
|
||||||
|
handler(relativeSize);
|
||||||
|
|
||||||
|
return element.Element(relativeSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativeWidth(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.RelativeSize(x => x.WidthFactor = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RelativeHeight(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.RelativeSize(x => x.HeightFactor = value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class ShrinkExtensions
|
||||||
|
{
|
||||||
|
private static IContainer Shrink(this IContainer element, Action<Shrink> handler)
|
||||||
|
{
|
||||||
|
var shrink = element as Shrink ?? new Shrink();
|
||||||
|
handler(shrink);
|
||||||
|
|
||||||
|
return element.Element(shrink);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer Shrink(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.ShrinkVertical().ShrinkHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer ShrinkVertical(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.Shrink(x => x.ShrinkVertical = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer ShrinkHorizontal(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.Shrink(x => x.ShrinkHorizontal = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Obsolete
|
||||||
|
|
||||||
|
[Obsolete("This element has been renamed since version 2022.1. Please use the Shrink method.")]
|
||||||
|
public static IContainer Box(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.Element(new Shrink());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("This element has been renamed since version 2022.11. Please use the Shrink method.")]
|
||||||
|
public static IContainer MinimalBox(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.Element(new Shrink());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,11 +12,18 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
public class TextSpanDescriptor
|
public class TextSpanDescriptor
|
||||||
{
|
{
|
||||||
internal TextStyle TextStyle { get; }
|
internal TextStyle TextStyle = TextStyle.Default;
|
||||||
|
internal Action<TextStyle> AssignTextStyle { get; }
|
||||||
|
|
||||||
internal TextSpanDescriptor(TextStyle textStyle)
|
internal TextSpanDescriptor(Action<TextStyle> assignTextStyle)
|
||||||
{
|
{
|
||||||
TextStyle = textStyle;
|
AssignTextStyle = assignTextStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void MutateTextStyle(Func<TextStyle, TextStyle> handler)
|
||||||
|
{
|
||||||
|
TextStyle = handler(TextStyle);
|
||||||
|
AssignTextStyle(TextStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,16 +31,17 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public class TextPageNumberDescriptor : TextSpanDescriptor
|
public class TextPageNumberDescriptor : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
internal PageNumberFormatter FormatFunction { get; private set; } = x => x?.ToString() ?? string.Empty;
|
internal Action<PageNumberFormatter> AssignFormatFunction { get; }
|
||||||
|
|
||||||
internal TextPageNumberDescriptor(TextStyle textStyle) : base(textStyle)
|
internal TextPageNumberDescriptor(Action<TextStyle> assignTextStyle, Action<PageNumberFormatter> assignFormatFunction) : base(assignTextStyle)
|
||||||
{
|
{
|
||||||
|
AssignFormatFunction = assignFormatFunction;
|
||||||
|
AssignFormatFunction(x => x?.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextPageNumberDescriptor Format(PageNumberFormatter formatter)
|
public TextPageNumberDescriptor Format(PageNumberFormatter formatter)
|
||||||
{
|
{
|
||||||
FormatFunction = formatter ?? FormatFunction;
|
AssignFormatFunction(formatter);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +49,7 @@ namespace QuestPDF.Fluent
|
|||||||
public class TextDescriptor
|
public class TextDescriptor
|
||||||
{
|
{
|
||||||
private ICollection<TextBlock> TextBlocks { get; } = new List<TextBlock>();
|
private ICollection<TextBlock> TextBlocks { get; } = new List<TextBlock>();
|
||||||
private TextStyle DefaultStyle { get; set; } = TextStyle.Default;
|
private TextStyle? DefaultStyle { get; set; }
|
||||||
internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
||||||
private float Spacing { get; set; } = 0f;
|
private float Spacing { get; set; } = 0f;
|
||||||
|
|
||||||
@@ -91,19 +99,15 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public TextSpanDescriptor Span(string? text)
|
public TextSpanDescriptor Span(string? text)
|
||||||
{
|
{
|
||||||
var style = DefaultStyle.Clone();
|
|
||||||
var descriptor = new TextSpanDescriptor(style);
|
|
||||||
|
|
||||||
if (text == null)
|
if (text == null)
|
||||||
return descriptor;
|
return new TextSpanDescriptor(_ => { });
|
||||||
|
|
||||||
var items = text
|
var items = text
|
||||||
.Replace("\r", string.Empty)
|
.Replace("\r", string.Empty)
|
||||||
.Split(new[] { '\n' }, StringSplitOptions.None)
|
.Split(new[] { '\n' }, StringSplitOptions.None)
|
||||||
.Select(x => new TextBlockSpan
|
.Select(x => new TextBlockSpan
|
||||||
{
|
{
|
||||||
Text = x,
|
Text = x
|
||||||
Style = style
|
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -118,7 +122,7 @@ namespace QuestPDF.Fluent
|
|||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(TextBlocks.Add);
|
.ForEach(TextBlocks.Add);
|
||||||
|
|
||||||
return descriptor;
|
return new TextSpanDescriptor(x => items.ForEach(y => y.Style = x));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextSpanDescriptor Line(string? text)
|
public TextSpanDescriptor Line(string? text)
|
||||||
@@ -134,16 +138,10 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
private TextPageNumberDescriptor PageNumber(Func<IPageContext, int?> pageNumber)
|
private TextPageNumberDescriptor PageNumber(Func<IPageContext, int?> pageNumber)
|
||||||
{
|
{
|
||||||
var style = DefaultStyle.Clone();
|
var textBlockItem = new TextBlockPageNumber();
|
||||||
var descriptor = new TextPageNumberDescriptor(style);
|
AddItemToLastTextBlock(textBlockItem);
|
||||||
|
|
||||||
AddItemToLastTextBlock(new TextBlockPageNumber
|
return new TextPageNumberDescriptor(x => textBlockItem.Style = x, x => textBlockItem.Source = context => x(pageNumber(context)));
|
||||||
{
|
|
||||||
Source = context => descriptor.FormatFunction(pageNumber(context)),
|
|
||||||
Style = style
|
|
||||||
});
|
|
||||||
|
|
||||||
return descriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextPageNumberDescriptor CurrentPageNumber()
|
public TextPageNumberDescriptor CurrentPageNumber()
|
||||||
@@ -187,20 +185,17 @@ namespace QuestPDF.Fluent
|
|||||||
if (IsNullOrEmpty(sectionName))
|
if (IsNullOrEmpty(sectionName))
|
||||||
throw new ArgumentException("Section name cannot be null or empty", nameof(sectionName));
|
throw new ArgumentException("Section name cannot be null or empty", nameof(sectionName));
|
||||||
|
|
||||||
var style = DefaultStyle.Clone();
|
|
||||||
var descriptor = new TextSpanDescriptor(style);
|
|
||||||
|
|
||||||
if (IsNullOrEmpty(text))
|
if (IsNullOrEmpty(text))
|
||||||
return descriptor;
|
return new TextSpanDescriptor(_ => { });
|
||||||
|
|
||||||
AddItemToLastTextBlock(new TextBlockSectionLink
|
var textBlockItem = new TextBlockSectionLink
|
||||||
{
|
{
|
||||||
Style = style,
|
|
||||||
Text = text,
|
Text = text,
|
||||||
SectionName = sectionName
|
SectionName = sectionName
|
||||||
});
|
};
|
||||||
|
|
||||||
return descriptor;
|
AddItemToLastTextBlock(textBlockItem);
|
||||||
|
return new TextSpanDescriptor(x => textBlockItem.Style = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.3. Please use the SectionLink method.")]
|
[Obsolete("This element has been renamed since version 2022.3. Please use the SectionLink method.")]
|
||||||
@@ -214,20 +209,17 @@ namespace QuestPDF.Fluent
|
|||||||
if (IsNullOrEmpty(url))
|
if (IsNullOrEmpty(url))
|
||||||
throw new ArgumentException("Url cannot be null or empty", nameof(url));
|
throw new ArgumentException("Url cannot be null or empty", nameof(url));
|
||||||
|
|
||||||
var style = DefaultStyle.Clone();
|
|
||||||
var descriptor = new TextSpanDescriptor(style);
|
|
||||||
|
|
||||||
if (IsNullOrEmpty(text))
|
if (IsNullOrEmpty(text))
|
||||||
return descriptor;
|
return new TextSpanDescriptor(_ => { });
|
||||||
|
|
||||||
AddItemToLastTextBlock(new TextBlockHyperlink
|
var textBlockItem = new TextBlockHyperlink
|
||||||
{
|
{
|
||||||
Style = style,
|
|
||||||
Text = text,
|
Text = text,
|
||||||
Url = url
|
Url = url
|
||||||
});
|
};
|
||||||
|
|
||||||
return descriptor;
|
AddItemToLastTextBlock(textBlockItem);
|
||||||
|
return new TextSpanDescriptor(x => textBlockItem.Style = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.3. Please use the Hyperlink method.")]
|
[Obsolete("This element has been renamed since version 2022.3. Please use the Hyperlink method.")]
|
||||||
@@ -251,7 +243,9 @@ namespace QuestPDF.Fluent
|
|||||||
internal void Compose(IContainer container)
|
internal void Compose(IContainer container)
|
||||||
{
|
{
|
||||||
TextBlocks.ToList().ForEach(x => x.Alignment = Alignment);
|
TextBlocks.ToList().ForEach(x => x.Alignment = Alignment);
|
||||||
container = container.DefaultTextStyle(DefaultStyle);
|
|
||||||
|
if (DefaultStyle != null)
|
||||||
|
container = container.DefaultTextStyle(DefaultStyle);
|
||||||
|
|
||||||
if (TextBlocks.Count == 1)
|
if (TextBlocks.Count == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace QuestPDF.Fluent
|
|||||||
if (style == null)
|
if (style == null)
|
||||||
return descriptor;
|
return descriptor;
|
||||||
|
|
||||||
descriptor.TextStyle.OverrideStyle(style);
|
descriptor.MutateTextStyle(x => x.OverrideStyle(style));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,114 +28,118 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static T FontColor<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
public static T FontColor<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.Color = value;
|
descriptor.MutateTextStyle(x => x.FontColor(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T BackgroundColor<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
public static T BackgroundColor<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.BackgroundColor = value;
|
descriptor.MutateTextStyle(x => x.BackgroundColor(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T FontFamily<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
public static T FontFamily<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.FontFamily = value;
|
descriptor.MutateTextStyle(x => x.FontFamily(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T FontSize<T>(this T descriptor, float value) where T : TextSpanDescriptor
|
public static T FontSize<T>(this T descriptor, float value) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.Size = value;
|
descriptor.MutateTextStyle(x => x.FontSize(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T LineHeight<T>(this T descriptor, float value) where T : TextSpanDescriptor
|
public static T LineHeight<T>(this T descriptor, float value) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.LineHeight = value;
|
descriptor.MutateTextStyle(x => x.LineHeight(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Italic<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
public static T Italic<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.IsItalic = value;
|
descriptor.MutateTextStyle(x => x.Italic(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Strikethrough<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
public static T Strikethrough<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.HasStrikethrough = value;
|
descriptor.MutateTextStyle(x => x.Strikethrough(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Underline<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
public static T Underline<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.HasUnderline = value;
|
descriptor.MutateTextStyle(x => x.Underline(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T WrapAnywhere<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
public static T WrapAnywhere<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
descriptor.TextStyle.WrapAnywhere = value;
|
descriptor.MutateTextStyle(x => x.WrapAnywhere(value));
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Weight
|
#region Weight
|
||||||
|
|
||||||
public static T Weight<T>(this T descriptor, FontWeight weight) where T : TextSpanDescriptor
|
|
||||||
{
|
|
||||||
descriptor.TextStyle.FontWeight = weight;
|
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Thin<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Thin<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.Thin);
|
descriptor.MutateTextStyle(x => x.Thin());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T ExtraLight<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T ExtraLight<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.ExtraLight);
|
descriptor.MutateTextStyle(x => x.ExtraLight());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Light<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Light<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.Light);
|
descriptor.MutateTextStyle(x => x.Light());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T NormalWeight<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T NormalWeight<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.Normal);
|
descriptor.MutateTextStyle(x => x.NormalWeight());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Medium<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Medium<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.Medium);
|
descriptor.MutateTextStyle(x => x.Medium());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T SemiBold<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T SemiBold<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.SemiBold);
|
descriptor.MutateTextStyle(x => x.SemiBold());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Bold<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Bold<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.Bold);
|
descriptor.MutateTextStyle(x => x.Bold());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T ExtraBold<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T ExtraBold<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.ExtraBold);
|
descriptor.MutateTextStyle(x => x.ExtraBold());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Black<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Black<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.Black);
|
descriptor.MutateTextStyle(x => x.Black());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T ExtraBlack<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T ExtraBlack<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Weight(FontWeight.ExtraBlack);
|
descriptor.MutateTextStyle(x => x.ExtraBlack());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -143,24 +147,22 @@ namespace QuestPDF.Fluent
|
|||||||
#region Position
|
#region Position
|
||||||
public static T NormalPosition<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T NormalPosition<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Position(FontPosition.Normal);
|
descriptor.MutateTextStyle(x => x.NormalPosition());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Subscript<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Subscript<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Position(FontPosition.Subscript);
|
descriptor.MutateTextStyle(x => x.Subscript());
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Superscript<T>(this T descriptor) where T : TextSpanDescriptor
|
public static T Superscript<T>(this T descriptor) where T : TextSpanDescriptor
|
||||||
{
|
{
|
||||||
return descriptor.Position(FontPosition.Superscript);
|
descriptor.MutateTextStyle(x => x.Superscript());
|
||||||
}
|
|
||||||
|
|
||||||
private static T Position<T>(this T descriptor, FontPosition fontPosition) where T : TextSpanDescriptor
|
|
||||||
{
|
|
||||||
descriptor.TextStyle.FontPosition = fontPosition;
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,24 +6,6 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
public static class TextStyleExtensions
|
public static class TextStyleExtensions
|
||||||
{
|
{
|
||||||
private static TextStyle Mutate(this TextStyle style, Action<TextStyle> handler)
|
|
||||||
{
|
|
||||||
style = style.Clone();
|
|
||||||
|
|
||||||
handler(style);
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TextStyle Fallback(this TextStyle style, TextStyle? value = null)
|
|
||||||
{
|
|
||||||
return style.Mutate(x => x.Fallback = value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TextStyle Fallback(this TextStyle style, Func<TextStyle, TextStyle> handler)
|
|
||||||
{
|
|
||||||
return style.Fallback(handler(TextStyle.Default));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.3. Please use the FontColor method.")]
|
[Obsolete("This element has been renamed since version 2022.3. Please use the FontColor method.")]
|
||||||
public static TextStyle Color(this TextStyle style, string value)
|
public static TextStyle Color(this TextStyle style, string value)
|
||||||
{
|
{
|
||||||
@@ -32,12 +14,12 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static TextStyle FontColor(this TextStyle style, string value)
|
public static TextStyle FontColor(this TextStyle style, string value)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.Color = value);
|
return style.Mutate(TextStyleProperty.Color, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle BackgroundColor(this TextStyle style, string value)
|
public static TextStyle BackgroundColor(this TextStyle style, string value)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.BackgroundColor = value);
|
return style.Mutate(TextStyleProperty.BackgroundColor, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.3. Please use the FontFamily method.")]
|
[Obsolete("This element has been renamed since version 2022.3. Please use the FontFamily method.")]
|
||||||
@@ -48,7 +30,7 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static TextStyle FontFamily(this TextStyle style, string value)
|
public static TextStyle FontFamily(this TextStyle style, string value)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.FontFamily = value);
|
return style.Mutate(TextStyleProperty.FontFamily, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.3. Please use the FontSize method.")]
|
[Obsolete("This element has been renamed since version 2022.3. Please use the FontSize method.")]
|
||||||
@@ -59,39 +41,39 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static TextStyle FontSize(this TextStyle style, float value)
|
public static TextStyle FontSize(this TextStyle style, float value)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.Size = value);
|
return style.Mutate(TextStyleProperty.Size, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle LineHeight(this TextStyle style, float value)
|
public static TextStyle LineHeight(this TextStyle style, float value)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.LineHeight = value);
|
return style.Mutate(TextStyleProperty.LineHeight, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle Italic(this TextStyle style, bool value = true)
|
public static TextStyle Italic(this TextStyle style, bool value = true)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.IsItalic = value);
|
return style.Mutate(TextStyleProperty.IsItalic, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle Strikethrough(this TextStyle style, bool value = true)
|
public static TextStyle Strikethrough(this TextStyle style, bool value = true)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.HasStrikethrough = value);
|
return style.Mutate(TextStyleProperty.HasStrikethrough, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle Underline(this TextStyle style, bool value = true)
|
public static TextStyle Underline(this TextStyle style, bool value = true)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.HasUnderline = value);
|
return style.Mutate(TextStyleProperty.HasUnderline, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle WrapAnywhere(this TextStyle style, bool value = true)
|
public static TextStyle WrapAnywhere(this TextStyle style, bool value = true)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.WrapAnywhere = value);
|
return style.Mutate(TextStyleProperty.WrapAnywhere, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Weight
|
#region Weight
|
||||||
|
|
||||||
public static TextStyle Weight(this TextStyle style, FontWeight weight)
|
public static TextStyle Weight(this TextStyle style, FontWeight weight)
|
||||||
{
|
{
|
||||||
return style.Mutate(x => x.FontWeight = weight);
|
return style.Mutate(TextStyleProperty.FontWeight, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextStyle Thin(this TextStyle style)
|
public static TextStyle Thin(this TextStyle style)
|
||||||
@@ -147,6 +129,7 @@ namespace QuestPDF.Fluent
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Position
|
#region Position
|
||||||
|
|
||||||
public static TextStyle NormalPosition(this TextStyle style)
|
public static TextStyle NormalPosition(this TextStyle style)
|
||||||
{
|
{
|
||||||
return style.Position(FontPosition.Normal);
|
return style.Position(FontPosition.Normal);
|
||||||
@@ -164,11 +147,23 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
private static TextStyle Position(this TextStyle style, FontPosition fontPosition)
|
private static TextStyle Position(this TextStyle style, FontPosition fontPosition)
|
||||||
{
|
{
|
||||||
if (style.FontPosition == fontPosition)
|
return style.Mutate(TextStyleProperty.FontPosition, fontPosition);
|
||||||
return style;
|
|
||||||
|
|
||||||
return style.Mutate(t => t.FontPosition = fontPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Fallback
|
||||||
|
|
||||||
|
public static TextStyle Fallback(this TextStyle style, TextStyle? value = null)
|
||||||
|
{
|
||||||
|
return style.Mutate(TextStyleProperty.Fallback, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextStyle Fallback(this TextStyle style, Func<TextStyle, TextStyle> handler)
|
||||||
|
{
|
||||||
|
return style.Fallback(handler(TextStyle.Default));
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using HarfBuzzSharp;
|
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
namespace QuestPDF.Infrastructure
|
namespace QuestPDF.Infrastructure
|
||||||
{
|
{
|
||||||
public class TextStyle
|
public record TextStyle
|
||||||
{
|
{
|
||||||
internal bool HasGlobalStyleApplied { get; private set; }
|
|
||||||
|
|
||||||
internal string? Color { get; set; }
|
internal string? Color { get; set; }
|
||||||
internal string? BackgroundColor { get; set; }
|
internal string? BackgroundColor { get; set; }
|
||||||
internal string? FontFamily { get; set; }
|
internal string? FontFamily { get; set; }
|
||||||
@@ -21,12 +18,8 @@ namespace QuestPDF.Infrastructure
|
|||||||
internal bool? WrapAnywhere { get; set; }
|
internal bool? WrapAnywhere { get; set; }
|
||||||
|
|
||||||
internal TextStyle? Fallback { get; set; }
|
internal TextStyle? Fallback { get; set; }
|
||||||
|
|
||||||
// TODO: without cache, this may be an expensive operation
|
internal static TextStyle LibraryDefault { get; } = new()
|
||||||
internal object PaintKey => (FontFamily, Size, FontWeight, FontPosition, IsItalic, Color);
|
|
||||||
internal object FontMetricsKey => (FontFamily, Size, FontWeight, IsItalic);
|
|
||||||
|
|
||||||
internal static readonly TextStyle LibraryDefault = new TextStyle
|
|
||||||
{
|
{
|
||||||
Color = Colors.Black,
|
Color = Colors.Black,
|
||||||
BackgroundColor = Colors.Transparent,
|
BackgroundColor = Colors.Transparent,
|
||||||
@@ -42,67 +35,6 @@ namespace QuestPDF.Infrastructure
|
|||||||
Fallback = null
|
Fallback = null
|
||||||
};
|
};
|
||||||
|
|
||||||
// it is important to create new instances for the DefaultTextStyle element to work correctly
|
public static TextStyle Default { get; } = new();
|
||||||
public static TextStyle Default => new TextStyle();
|
|
||||||
|
|
||||||
internal void ApplyGlobalStyle(TextStyle globalStyle)
|
|
||||||
{
|
|
||||||
if (HasGlobalStyleApplied)
|
|
||||||
return;
|
|
||||||
|
|
||||||
HasGlobalStyleApplied = true;
|
|
||||||
ApplyParentStyle(globalStyle);
|
|
||||||
|
|
||||||
if (Fallback != null)
|
|
||||||
ApplyFallbackStyle(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyFallbackStyle(TextStyle parentStyle)
|
|
||||||
{
|
|
||||||
ApplyParentStyle(parentStyle, false);
|
|
||||||
Fallback?.ApplyFallbackStyle(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyParentStyle(TextStyle parentStyle, bool mapFallback = true)
|
|
||||||
{
|
|
||||||
Color ??= parentStyle.Color;
|
|
||||||
BackgroundColor ??= parentStyle.BackgroundColor;
|
|
||||||
FontFamily ??= parentStyle.FontFamily;
|
|
||||||
Size ??= parentStyle.Size;
|
|
||||||
LineHeight ??= parentStyle.LineHeight;
|
|
||||||
FontWeight ??= parentStyle.FontWeight;
|
|
||||||
FontPosition ??= parentStyle.FontPosition;
|
|
||||||
IsItalic ??= parentStyle.IsItalic;
|
|
||||||
HasStrikethrough ??= parentStyle.HasStrikethrough;
|
|
||||||
HasUnderline ??= parentStyle.HasUnderline;
|
|
||||||
WrapAnywhere ??= parentStyle.WrapAnywhere;
|
|
||||||
|
|
||||||
if (mapFallback)
|
|
||||||
Fallback ??= parentStyle.Fallback?.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OverrideStyle(TextStyle parentStyle)
|
|
||||||
{
|
|
||||||
Color = parentStyle.Color ?? Color;
|
|
||||||
BackgroundColor = parentStyle.BackgroundColor ?? BackgroundColor;
|
|
||||||
FontFamily = parentStyle.FontFamily ?? FontFamily;
|
|
||||||
Size = parentStyle.Size ?? Size;
|
|
||||||
LineHeight = parentStyle.LineHeight ?? LineHeight;
|
|
||||||
FontWeight = parentStyle.FontWeight ?? FontWeight;
|
|
||||||
FontPosition = parentStyle.FontPosition ?? FontPosition;
|
|
||||||
IsItalic = parentStyle.IsItalic ?? IsItalic;
|
|
||||||
HasStrikethrough = parentStyle.HasStrikethrough ?? HasStrikethrough;
|
|
||||||
HasUnderline = parentStyle.HasUnderline ?? HasUnderline;
|
|
||||||
WrapAnywhere = parentStyle.WrapAnywhere ?? WrapAnywhere;
|
|
||||||
Fallback = parentStyle.Fallback?.Clone() ?? Fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TextStyle Clone()
|
|
||||||
{
|
|
||||||
var clone = (TextStyle)MemberwiseClone();
|
|
||||||
clone.HasGlobalStyleApplied = false;
|
|
||||||
clone.Fallback = Fallback?.Clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
|
||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
internal enum TextStyleProperty
|
||||||
|
{
|
||||||
|
Color,
|
||||||
|
BackgroundColor,
|
||||||
|
FontFamily,
|
||||||
|
Size,
|
||||||
|
LineHeight,
|
||||||
|
FontWeight,
|
||||||
|
FontPosition,
|
||||||
|
IsItalic,
|
||||||
|
HasStrikethrough,
|
||||||
|
HasUnderline,
|
||||||
|
WrapAnywhere,
|
||||||
|
Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class TextStyleManager
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<(TextStyle origin, TextStyleProperty property, object value), TextStyle> TextStyleMutateCache = new();
|
||||||
|
private static readonly ConcurrentDictionary<(TextStyle origin, TextStyle parent), TextStyle> TextStyleApplyGlobalCache = new();
|
||||||
|
private static readonly ConcurrentDictionary<(TextStyle origin, TextStyle parent), TextStyle> TextStyleOverrideCache = new();
|
||||||
|
|
||||||
|
public static TextStyle Mutate(this TextStyle origin, TextStyleProperty property, object value)
|
||||||
|
{
|
||||||
|
var cacheKey = (origin, property, value);
|
||||||
|
return TextStyleMutateCache.GetOrAdd(cacheKey, x => MutateStyle(x.origin, x.property, x.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextStyle MutateStyle(TextStyle origin, TextStyleProperty property, object? value, bool overrideValue = true)
|
||||||
|
{
|
||||||
|
if (overrideValue && value == null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.Color)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.Color != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (string?)value;
|
||||||
|
|
||||||
|
if (origin.Color == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { Color = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.BackgroundColor)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.BackgroundColor != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (string?)value;
|
||||||
|
|
||||||
|
if (origin.BackgroundColor == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { BackgroundColor = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.FontFamily)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.FontFamily != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (string?)value;
|
||||||
|
|
||||||
|
if (origin.FontFamily == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { FontFamily = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.Size)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.Size != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (float?)value;
|
||||||
|
|
||||||
|
if (origin.Size == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { Size = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.LineHeight)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.LineHeight != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (float?)value;
|
||||||
|
|
||||||
|
if (origin.LineHeight == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { LineHeight = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.FontWeight)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.FontWeight != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (FontWeight?)value;
|
||||||
|
|
||||||
|
if (origin.FontWeight == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { FontWeight = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.FontPosition)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.FontPosition != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (FontPosition?)value;
|
||||||
|
|
||||||
|
if (origin.FontPosition == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { FontPosition = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.IsItalic)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.IsItalic != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (bool?)value;
|
||||||
|
|
||||||
|
if (origin.IsItalic == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { IsItalic = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.HasStrikethrough)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.HasStrikethrough != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (bool?)value;
|
||||||
|
|
||||||
|
if (origin.HasStrikethrough == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { HasStrikethrough = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.HasUnderline)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.HasUnderline != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (bool?)value;
|
||||||
|
|
||||||
|
if (origin.HasUnderline == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { HasUnderline = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.WrapAnywhere)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.WrapAnywhere != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (bool?)value;
|
||||||
|
|
||||||
|
if (origin.WrapAnywhere == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { WrapAnywhere = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == TextStyleProperty.Fallback)
|
||||||
|
{
|
||||||
|
if (!overrideValue && origin.Fallback != null)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
var castedValue = (TextStyle?)value;
|
||||||
|
|
||||||
|
if (origin.Fallback == castedValue)
|
||||||
|
return origin;
|
||||||
|
|
||||||
|
return origin with { Fallback = castedValue };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(property), property, "Expected to mutate the TextStyle object. Provided property type is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TextStyle ApplyGlobalStyle(this TextStyle style, TextStyle parent)
|
||||||
|
{
|
||||||
|
var cacheKey = (style, parent);
|
||||||
|
return TextStyleApplyGlobalCache.GetOrAdd(cacheKey, key => ApplyStyle(key.origin, key.parent, overrideStyle: false).ApplyFontFallback());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextStyle ApplyFontFallback(this TextStyle style)
|
||||||
|
{
|
||||||
|
var targetFallbackStyle = style
|
||||||
|
?.Fallback
|
||||||
|
?.ApplyStyle(style, overrideStyle: false, applyFallback: false)
|
||||||
|
?.ApplyFontFallback();
|
||||||
|
|
||||||
|
return MutateStyle(style, TextStyleProperty.Fallback, targetFallbackStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TextStyle OverrideStyle(this TextStyle style, TextStyle parent)
|
||||||
|
{
|
||||||
|
var cacheKey = (style, parent);
|
||||||
|
|
||||||
|
return TextStyleOverrideCache.GetOrAdd(cacheKey, key =>
|
||||||
|
{
|
||||||
|
var result = ApplyStyle(key.origin, key.parent);
|
||||||
|
return MutateStyle(result, TextStyleProperty.Fallback, key.parent.Fallback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextStyle ApplyStyle(this TextStyle style, TextStyle parent, bool overrideStyle = true, bool applyFallback = true)
|
||||||
|
{
|
||||||
|
var result = style;
|
||||||
|
|
||||||
|
result = MutateStyle(result, TextStyleProperty.Color, parent.Color, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.BackgroundColor, parent.BackgroundColor, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.FontFamily, parent.FontFamily, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.Size, parent.Size, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.LineHeight, parent.LineHeight, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.FontWeight, parent.FontWeight, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.FontPosition, parent.FontPosition, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.IsItalic, parent.IsItalic, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.HasStrikethrough, parent.HasStrikethrough, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.HasUnderline, parent.HasUnderline, overrideStyle);
|
||||||
|
result = MutateStyle(result, TextStyleProperty.WrapAnywhere, parent.WrapAnywhere, overrideStyle);
|
||||||
|
|
||||||
|
if (applyFallback)
|
||||||
|
result = MutateStyle(result, TextStyleProperty.Fallback, parent.Fallback, overrideStyle);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ namespace QuestPDF.Previewer
|
|||||||
public event Action? OnPreviewerStopped;
|
public event Action? OnPreviewerStopped;
|
||||||
|
|
||||||
private const int RequiredPreviewerVersionMajor = 2022;
|
private const int RequiredPreviewerVersionMajor = 2022;
|
||||||
private const int RequiredPreviewerVersionMinor = 8;
|
private const int RequiredPreviewerVersionMinor = 9;
|
||||||
|
|
||||||
public PreviewerService(int port)
|
public PreviewerService(int port)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<Authors>MarcinZiabek</Authors>
|
<Authors>MarcinZiabek</Authors>
|
||||||
<Company>CodeFlint</Company>
|
<Company>CodeFlint</Company>
|
||||||
<PackageId>QuestPDF</PackageId>
|
<PackageId>QuestPDF</PackageId>
|
||||||
<Version>2022.8.2</Version>
|
<Version>2022.9.0</Version>
|
||||||
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
||||||
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
|
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
|
||||||
<LangVersion>9</LangVersion>
|
<LangVersion>9</LangVersion>
|
||||||
|
|||||||
@@ -1,20 +1,6 @@
|
|||||||
2022.8.0:
|
2022.9.0
|
||||||
|
- Implemented font-fallback algorithm,
|
||||||
- Improved library performance,
|
- Introduced new Settings API,
|
||||||
- Breaking change: changed default font from Calibri to an open-source Lato,
|
- Significantly reduced memory allocation cost for TextStyle objects,
|
||||||
- Default font files are included with the nuget package, making it safe to deploy on any environment,
|
- Implemented optional checking if all font glyphs are available,
|
||||||
- Default font files are significantly smaller, so output document files should be smaller too (up to 20x reduction in size),
|
- Minor text-rendering optimizations.
|
||||||
- When requested font is not available on the runtime environment, library provides list of available fonts,
|
|
||||||
- Fixed a rare layout overflow exception with the Inlined element,
|
|
||||||
- Fixed a memory leak connected to the HarfBuzz library.
|
|
||||||
|
|
||||||
|
|
||||||
2022.8.1:
|
|
||||||
- Fixed: default text style does not always work
|
|
||||||
- Fixed: page breaking rendering does not work in very specific corner cases
|
|
||||||
- Stability improvements for text wrapping
|
|
||||||
- Updated stability of rendering elements in negative space
|
|
||||||
- Optimization for the Column element: do not measure child when available height is negative
|
|
||||||
|
|
||||||
2022.8.2
|
|
||||||
- Fixed: the Column element incorrectly renders zero-height elements.
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
public static class Settings
|
public static class Settings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This value represents the maximum lenght of the document that the library produces.
|
/// This value represents the maximum length of the document that the library produces.
|
||||||
/// This is useful when layout constraints are too strong, e.g. one element does not fit in another.
|
/// This is useful when layout constraints are too strong, e.g. one element does not fit in another.
|
||||||
/// In such cases, the library would produce document of infinite length, consuming all available resources.
|
/// In such cases, the library would produce document of infinite length, consuming all available resources.
|
||||||
/// To break the algorithm and save the environment, the library breaks the rendering process after reaching specified length of document.
|
/// To break the algorithm and save the environment, the library breaks the rendering process after reaching specified length of document.
|
||||||
@@ -35,6 +35,6 @@
|
|||||||
/// However, it provides hints that used fonts are not sufficient to produce correct results.
|
/// However, it provides hints that used fonts are not sufficient to produce correct results.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>By default, this flag is enabled only when the debugger IS attached.</remarks>
|
/// <remarks>By default, this flag is enabled only when the debugger IS attached.</remarks>
|
||||||
public static bool CheckIfAllTextGlyphsAreAvailableInSpecifiedFont { get; set; } = System.Diagnostics.Debugger.IsAttached;
|
public static bool CheckIfAllTextGlyphsAreAvailable { get; set; } = System.Diagnostics.Debugger.IsAttached;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,8 @@ Choosing a project dependency could be difficult. We need to ensure stability an
|
|||||||
|
|
||||||
⭐ Please give this repository a star. It takes seconds and help thousands of developers! ⭐
|
⭐ Please give this repository a star. It takes seconds and help thousands of developers! ⭐
|
||||||
|
|
||||||
<img src="https://user-images.githubusercontent.com/9263853/184642026-27dd7567-a46a-45d4-9594-e6a70a7193e9.png" width="700" />
|
<img src="https://user-images.githubusercontent.com/9263853/190931857-8ca52ec8-cc7d-4d12-9467-4442b3342fa1.png" width="700" />
|
||||||
|
|
||||||
|
|
||||||
## Please share with the community
|
## Please share with the community
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user