Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e01835cc55 | |||
| fdc395f956 | |||
| bd4aac0594 | |||
| f79bd1db62 | |||
| 8cec0059d8 |
@@ -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>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<LangVersion>9</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ namespace QuestPDF.Drawing
|
|||||||
var container = new DocumentContainer();
|
var container = new DocumentContainer();
|
||||||
document.Compose(container);
|
document.Compose(container);
|
||||||
var content = container.Compose();
|
var content = container.Compose();
|
||||||
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
ApplyInheritedAndGlobalTexStyle(content, TextStyle.Default);
|
||||||
ApplyContentDirection(content, ContentDirection.LeftToRight);
|
ApplyContentDirection(content, ContentDirection.LeftToRight);
|
||||||
|
|
||||||
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
|
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
|
||||||
@@ -192,7 +192,7 @@ namespace QuestPDF.Drawing
|
|||||||
ApplyContentDirection(child, direction);
|
ApplyContentDirection(child, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
internal static void ApplyInheritedAndGlobalTexStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
||||||
{
|
{
|
||||||
if (content == null)
|
if (content == null)
|
||||||
return;
|
return;
|
||||||
@@ -202,26 +202,23 @@ namespace QuestPDF.Drawing
|
|||||||
foreach (var textBlockItem in textBlock.Items)
|
foreach (var textBlockItem in textBlock.Items)
|
||||||
{
|
{
|
||||||
if (textBlockItem is TextBlockSpan textSpan)
|
if (textBlockItem is TextBlockSpan textSpan)
|
||||||
{
|
textSpan.Style = textSpan.Style.ApplyInheritedStyle(documentDefaultTextStyle).ApplyGlobalStyle();
|
||||||
textSpan.Style = textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
|
||||||
}
|
if (textBlockItem is TextBlockElement textElement)
|
||||||
else if (textBlockItem is TextBlockElement textElement)
|
ApplyInheritedAndGlobalTexStyle(textElement.Element, documentDefaultTextStyle);
|
||||||
{
|
|
||||||
ApplyDefaultTextStyle(textElement.Element, documentDefaultTextStyle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content is DynamicHost dynamicHost)
|
if (content is DynamicHost dynamicHost)
|
||||||
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyInheritedStyle(documentDefaultTextStyle);
|
||||||
|
|
||||||
if (content is DefaultTextStyle defaultTextStyleElement)
|
if (content is DefaultTextStyle defaultTextStyleElement)
|
||||||
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyInheritedStyle(documentDefaultTextStyle);
|
||||||
|
|
||||||
foreach (var child in content.GetChildren())
|
foreach (var child in content.GetChildren())
|
||||||
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
ApplyInheritedAndGlobalTexStyle(child, documentDefaultTextStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ namespace QuestPDF.Elements
|
|||||||
var container = new DynamicElement();
|
var container = new DynamicElement();
|
||||||
content(container);
|
content(container);
|
||||||
|
|
||||||
container.ApplyDefaultTextStyle(TextStyle);
|
container.ApplyInheritedAndGlobalTexStyle(TextStyle);
|
||||||
container.ApplyContentDirection(ContentDirection);
|
container.ApplyContentDirection(ContentDirection);
|
||||||
|
|
||||||
container.InjectDependencies(PageContext, Canvas);
|
container.InjectDependencies(PageContext, Canvas);
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ namespace QuestPDF.Infrastructure
|
|||||||
internal static class TextStyleManager
|
internal static class TextStyleManager
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<(TextStyle origin, TextStyleProperty property, object value), TextStyle> TextStyleMutateCache = new();
|
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();
|
private static readonly ConcurrentDictionary<(TextStyle origin, TextStyle parent), TextStyle> TextStyleOverrideCache = new();
|
||||||
|
|
||||||
public static TextStyle Mutate(this TextStyle origin, TextStyleProperty property, object value)
|
public static TextStyle Mutate(this TextStyle origin, TextStyleProperty property, object value)
|
||||||
{
|
{
|
||||||
var cacheKey = (origin, property, 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)
|
if (overrideValue && value == null)
|
||||||
return origin;
|
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.");
|
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);
|
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
|
var targetFallbackStyle = style
|
||||||
?.Fallback
|
?.Fallback
|
||||||
?.ApplyStyle(style, overrideStyle: false, applyFallback: false)
|
?.ApplyStyleProperties(style, overrideStyle: overrideStyle, overrideFontFamily: false, applyFallback: false)
|
||||||
?.ApplyFontFallback();
|
?.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)
|
internal static TextStyle OverrideStyle(this TextStyle style, TextStyle parent)
|
||||||
{
|
{
|
||||||
var cacheKey = (style, parent);
|
var cacheKey = (style, parent);
|
||||||
|
return TextStyleOverrideCache.GetOrAdd(cacheKey, key => ApplyStyleProperties(key.origin, key.parent, overrideStyle: true, overrideFontFamily: true, applyFallback: true));
|
||||||
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)
|
private static TextStyle ApplyStyleProperties(this TextStyle style, TextStyle parent, bool overrideStyle, bool overrideFontFamily, bool applyFallback)
|
||||||
{
|
{
|
||||||
var result = style;
|
var result = style;
|
||||||
|
|
||||||
result = MutateStyle(result, TextStyleProperty.Color, parent.Color, overrideStyle);
|
result = MutateStyle(result, TextStyleProperty.Color, parent.Color, overrideStyle);
|
||||||
result = MutateStyle(result, TextStyleProperty.BackgroundColor, parent.BackgroundColor, overrideStyle);
|
result = MutateStyle(result, TextStyleProperty.BackgroundColor, parent.BackgroundColor, overrideStyle);
|
||||||
result = MutateStyle(result, TextStyleProperty.FontFamily, parent.FontFamily, overrideStyle);
|
result = MutateStyle(result, TextStyleProperty.FontFamily, parent.FontFamily, overrideStyle);
|
||||||
|
|||||||
Reference in New Issue
Block a user