diff --git a/Source/QuestPDF.Examples/TextExamples.cs b/Source/QuestPDF.Examples/TextExamples.cs
index 3aa3df3..d0eb2f3 100644
--- a/Source/QuestPDF.Examples/TextExamples.cs
+++ b/Source/QuestPDF.Examples/TextExamples.cs
@@ -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();
+ });
+ });
+ });
+ }
}
}
\ No newline at end of file
diff --git a/Source/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj b/Source/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj
index e062de1..622e661 100644
--- a/Source/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj
+++ b/Source/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj
@@ -3,6 +3,7 @@
netcoreapp3.1
false
+ 9
diff --git a/Source/QuestPDF.UnitTests/TextStyleTests.cs b/Source/QuestPDF.UnitTests/TextStyleTests.cs
new file mode 100644
index 0000000..8b35711
--- /dev/null
+++ b/Source/QuestPDF.UnitTests/TextStyleTests.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/QuestPDF/Drawing/DocumentGenerator.cs b/Source/QuestPDF/Drawing/DocumentGenerator.cs
index d982e3b..3e30ef0 100644
--- a/Source/QuestPDF/Drawing/DocumentGenerator.cs
+++ b/Source/QuestPDF/Drawing/DocumentGenerator.cs
@@ -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);
}
}
}
\ No newline at end of file
diff --git a/Source/QuestPDF/Elements/Dynamic.cs b/Source/QuestPDF/Elements/Dynamic.cs
index 450ec19..2e2c5f3 100644
--- a/Source/QuestPDF/Elements/Dynamic.cs
+++ b/Source/QuestPDF/Elements/Dynamic.cs
@@ -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);
diff --git a/Source/QuestPDF/Infrastructure/TextStyleManager.cs b/Source/QuestPDF/Infrastructure/TextStyleManager.cs
index 444dee9..6ab5d33 100644
--- a/Source/QuestPDF/Infrastructure/TextStyleManager.cs
+++ b/Source/QuestPDF/Infrastructure/TextStyleManager.cs
@@ -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 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);