Compare commits

...

6 Commits

Author SHA1 Message Date
MarcinZiabek e01835cc55 Minor code refactoring 2023-04-22 09:52:33 +02:00
MarcinZiabek fdc395f956 Bug fixes + test 2023-04-21 09:22:14 +02:00
MarcinZiabek bd4aac0594 FontStyle: making method arguments explicit 2023-04-19 22:57:16 +02:00
MarcinZiabek f79bd1db62 Code refactoring 2023-04-19 22:51:59 +02:00
MarcinZiabek 8cec0059d8 Fixed applying text style for fallback styles 2023-04-19 20:25:58 +02:00
MarcinZiabek ef89be9710 Improvement: updated the CreateNotMatchingFontException text to mention that glyph checking operation can be disabled with the Settings.CheckIfAllTextGlyphsAreAvailable setting 2023-04-17 02:15:19 +02:00
7 changed files with 126 additions and 31 deletions
+35
View File
@@ -1004,5 +1004,40 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void FontFallback_Nested()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Margin(50);
page.PageColor(Colors.White);
page.Size(PageSizes.A5.Landscape());
page.DefaultTextStyle(x => x
.FontSize(24)
.Bold()
.FontFamily("Times New Roman")
.Fallback(y => y
.FontFamily("Microsoft YaHei")
.Underline()
.BackgroundColor(Colors.Red.Lighten2)));
page.Content().Text(text =>
{
text.Line("Default times new roman 中文文本 text.");
text.Line("Normal weight and green 中文文本 text.").NormalWeight().BackgroundColor(Colors.Green.Lighten2);
text.Line("Strikethrough without underline 中文文本 text.").Strikethrough().Underline(false);
text.Line("Lato italic 中文文本 text.").FontFamily("Lato").Italic();
});
});
});
}
}
}
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>9</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -0,0 +1,59 @@
using FluentAssertions;
using NUnit.Framework;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class TextStyleTests
{
[Test]
public void ApplyInheritedAndGlobalStyle()
{
// arrange
var defaultTextStyle = TextStyle
.Default
.FontSize(20)
.FontFamily("Arial")
.BackgroundColor(Colors.Green.Lighten2)
.Fallback(y => y
.FontFamily("Microsoft YaHei")
.Underline()
.NormalWeight()
.BackgroundColor(Colors.Blue.Lighten2));
var spanTextStyle = TextStyle
.Default
.FontFamily("Times New Roman")
.Bold()
.Strikethrough()
.BackgroundColor(Colors.Red.Lighten2);
// act
var targetStyle = spanTextStyle.ApplyInheritedStyle(defaultTextStyle).ApplyGlobalStyle();
// assert
var expectedStyle = TextStyle.LibraryDefault with
{
Size = 20,
FontFamily = "Times New Roman",
FontWeight = FontWeight.Bold,
BackgroundColor = Colors.Red.Lighten2,
HasStrikethrough = true,
Fallback = TextStyle.LibraryDefault with
{
Size = 20,
FontFamily = "Microsoft YaHei",
FontWeight = FontWeight.Bold,
BackgroundColor = Colors.Red.Lighten2,
HasUnderline = true,
HasStrikethrough = true
}
};
targetStyle.Should().BeEquivalentTo(expectedStyle);
}
}
}
+9 -12
View File
@@ -64,7 +64,7 @@ namespace QuestPDF.Drawing
var container = new DocumentContainer();
document.Compose(container);
var content = container.Compose();
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
ApplyInheritedAndGlobalTexStyle(content, TextStyle.Default);
ApplyContentDirection(content, ContentDirection.LeftToRight);
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
@@ -192,7 +192,7 @@ namespace QuestPDF.Drawing
ApplyContentDirection(child, direction);
}
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
internal static void ApplyInheritedAndGlobalTexStyle(this Element? content, TextStyle documentDefaultTextStyle)
{
if (content == null)
return;
@@ -202,26 +202,23 @@ namespace QuestPDF.Drawing
foreach (var textBlockItem in textBlock.Items)
{
if (textBlockItem is TextBlockSpan textSpan)
{
textSpan.Style = textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
}
else if (textBlockItem is TextBlockElement textElement)
{
ApplyDefaultTextStyle(textElement.Element, documentDefaultTextStyle);
}
textSpan.Style = textSpan.Style.ApplyInheritedStyle(documentDefaultTextStyle).ApplyGlobalStyle();
if (textBlockItem is TextBlockElement textElement)
ApplyInheritedAndGlobalTexStyle(textElement.Element, documentDefaultTextStyle);
}
return;
}
if (content is DynamicHost dynamicHost)
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyInheritedStyle(documentDefaultTextStyle);
if (content is DefaultTextStyle defaultTextStyleElement)
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyInheritedStyle(documentDefaultTextStyle);
foreach (var child in content.GetChildren())
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
ApplyInheritedAndGlobalTexStyle(child, documentDefaultTextStyle);
}
}
}
+1 -1
View File
@@ -89,7 +89,7 @@ namespace QuestPDF.Elements
var container = new DynamicElement();
content(container);
container.ApplyDefaultTextStyle(TextStyle);
container.ApplyInheritedAndGlobalTexStyle(TextStyle);
container.ApplyContentDirection(ContentDirection);
container.InjectDependencies(PageContext, Canvas);
@@ -105,7 +105,9 @@ namespace QuestPDF.Elements.Text
$"Font families available on current environment that contain this glyph: {proposedFontsFormatted}. " +
$"Possible solutions: " +
$"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. " +
$"You can disable this check by setting the 'Settings.CheckIfAllTextGlyphsAreAvailable' option to 'false'. " +
$"However, this may result with text glyphs being incorrectly rendered without any warning.");
}
static IEnumerable<string> FindFontsContainingGlyph(int codepoint)
@@ -25,16 +25,17 @@ namespace QuestPDF.Infrastructure
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> TextStyleApplyInheritedCache = new();
private static readonly ConcurrentDictionary<TextStyle, 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));
return TextStyleMutateCache.GetOrAdd(cacheKey, x => MutateStyle(x.origin, x.property, x.value, overrideValue: true));
}
private static TextStyle MutateStyle(TextStyle origin, TextStyleProperty property, object? value, bool overrideValue = true)
private static TextStyle MutateStyle(this TextStyle origin, TextStyleProperty property, object? value, bool overrideValue)
{
if (overrideValue && value == null)
return origin;
@@ -224,37 +225,37 @@ namespace QuestPDF.Infrastructure
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)
internal static TextStyle ApplyInheritedStyle(this TextStyle style, TextStyle parent)
{
var cacheKey = (style, parent);
return TextStyleApplyGlobalCache.GetOrAdd(cacheKey, key => ApplyStyle(key.origin, key.parent, overrideStyle: false).ApplyFontFallback());
return TextStyleApplyInheritedCache.GetOrAdd(cacheKey, key => key.origin.ApplyStyleProperties(key.parent, overrideStyle: false, overrideFontFamily: false, applyFallback: true).UpdateFontFallback(overrideStyle: true));
}
private static TextStyle ApplyFontFallback(this TextStyle style)
internal static TextStyle ApplyGlobalStyle(this TextStyle style)
{
return TextStyleApplyGlobalCache.GetOrAdd(style, key => key.ApplyStyleProperties(TextStyle.LibraryDefault, overrideStyle: false, overrideFontFamily: false, applyFallback: true).UpdateFontFallback(overrideStyle: false));
}
private static TextStyle UpdateFontFallback(this TextStyle style, bool overrideStyle)
{
var targetFallbackStyle = style
?.Fallback
?.ApplyStyle(style, overrideStyle: false, applyFallback: false)
?.ApplyFontFallback();
?.ApplyStyleProperties(style, overrideStyle: overrideStyle, overrideFontFamily: false, applyFallback: false)
?.UpdateFontFallback(overrideStyle);
return MutateStyle(style, TextStyleProperty.Fallback, targetFallbackStyle);
return style.MutateStyle(TextStyleProperty.Fallback, targetFallbackStyle, overrideValue: true);
}
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);
});
return TextStyleOverrideCache.GetOrAdd(cacheKey, key => ApplyStyleProperties(key.origin, key.parent, overrideStyle: true, overrideFontFamily: true, applyFallback: true));
}
private static TextStyle ApplyStyle(this TextStyle style, TextStyle parent, bool overrideStyle = true, bool applyFallback = true)
private static TextStyle ApplyStyleProperties(this TextStyle style, TextStyle parent, bool overrideStyle, bool overrideFontFamily, bool applyFallback)
{
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);