Compare commits

..

1 Commits

Author SHA1 Message Date
MarcinZiabek 69b54eed5d General concept of RTL support 2022-06-12 15:13:26 +02:00
46 changed files with 240 additions and 355 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
<PackageReference Include="Svg.Skia" Version="0.5.10" />
</ItemGroup>
-51
View File
@@ -567,56 +567,5 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void TextShaping_Unicode()
{
RenderingTest
.Create()
.PageSize(600, 100)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(35)
.MinimalBox()
.Background(Colors.Grey.Lighten2)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.Span("Complex Unicode structure: ");
text.Span("T̶̖̔͆͆̽̔ḩ̷̼̫̐̈́̀͜͝͝ì̶͇̤͓̱̣͇͓͉̎s̵̡̟̹͍̜͉̗̾͛̈̐́͋͂͝͠ͅ ̴̨͙͍͇̭̒͗̀́͝ì̷̡̺͉̼̏̏̉̌͝s̷͍͙̗̰̖͙̈̑̂̔͑͊̌̓̊̇͜ ̶̛̼͚͊̅͘ṭ̷̨̘̣̙̖͉͌̏̂̅͑̄̽̕͝ȅ̶̲̲̙̭͈̬̣͔̝͔̈́͝s̸̢̯̪̫͓̭̮̓̀͆͜ț̸̢͉̞̥̤̏̌̓͝").FontFamily(Fonts.Calibri).FontColor(Colors.Red.Medium);
text.Span(".");
});
});
}
[Test]
public void TextShaping_Arabic()
{
RenderingTest
.Create()
.PageSize(500, 100)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.MinimalBox()
.Background(Colors.Grey.Lighten2)
.Text("ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا")
.FontFamily(Fonts.Calibri)
.FontSize(20);
});
}
}
}
+47
View File
@@ -0,0 +1,47 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
namespace QuestPDF.Examples
{
public class TextShapingTests
{
[Test]
public void ShapeText()
{
using var textPaint = new SKPaint
{
Color = SKColors.Black,
Typeface = SKTypeface.CreateDefault(),
IsAntialias = true,
TextSize = 20
};
using var backgroundPaint = new SKPaint
{
Color = SKColors.LightGray
};
RenderingTest
.Create()
.PageSize(550, 250)
.ProduceImages()
.ShowResults()
.Render(container =>
{
//var lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec odio ipsum, aliquam a neque a, lacinia vehicula lectus.";
//var arabic = "ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا الواجب والعمل سنتنازل غالباً ونرفض الشعور";
var lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
var arabic = "ينا الألم. في بعض (5000) الأحيان ونظراً للالتزامات التي يفرضها علينا";
container
.Padding(25)
.Text(arabic)
.FontSize(25);
});
}
}
}
+1 -2
View File
@@ -1,7 +1,6 @@
<Application x:Class="QuestPDF.Previewer.PreviewerApp"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="QuestPDF Document Preview">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Styles>
<FluentTheme Mode="Dark" />
</Application.Styles>
+2 -3
View File
@@ -4,7 +4,7 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF.Previewer</PackageId>
<Version>2022.8.0</Version>
<Version>2022.6.0</Version>
<PackAsTool>true</PackAsTool>
<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>
@@ -48,8 +48,7 @@
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.10" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.80.4" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
</Project>
@@ -33,10 +33,11 @@ namespace QuestPDF.ReportSample.Layouts
page.MarginHorizontal(50);
page.Size(PageSizes.A4);
page.ContentDirectionRightToLeft();
page.Header().Element(ComposeHeader);
page.Content().Element(ComposeContent);
page.Footer().AlignCenter().Text(text =>
{
text.CurrentPageNumber().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
@@ -12,7 +12,7 @@
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
+3 -3
View File
@@ -6,8 +6,8 @@ namespace QuestPDF.ReportSample
{
public static class Typography
{
public static TextStyle Title => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Darken3).FontSize(26).Black();
public static TextStyle Headline => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Medium).FontSize(16).SemiBold();
public static TextStyle Normal => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Black).FontSize(10).LineHeight(1.2f);
public static TextStyle Title => TextStyle.Default.FontFamily(Fonts.Calibri).FontColor(Colors.Blue.Darken3).FontSize(26).Black();
public static TextStyle Headline => TextStyle.Default.FontFamily(Fonts.Calibri).FontColor(Colors.Blue.Medium).FontSize(16).SemiBold();
public static TextStyle Normal => TextStyle.Default.FontFamily(Fonts.Verdana).FontColor(Colors.Black).FontSize(10).LineHeight(1.2f);
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
+18
View File
@@ -65,6 +65,7 @@ namespace QuestPDF.Drawing
document.Compose(container);
var content = container.Compose();
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
ApplyContentDirection(content);
var metadata = document.GetMetadata();
var pageContext = new PageContext();
@@ -199,5 +200,22 @@ namespace QuestPDF.Drawing
foreach (var child in content.GetChildren())
ApplyDefaultTextStyle(child, targetTextStyle);
}
internal static void ApplyContentDirection(this Element? content, ContentDirectionType contentDirectionType = ContentDirectionType.LeftToRight)
{
if (content == null)
return;
var targetDirection = contentDirectionType;
if (content is ContentDirection contentDirection)
targetDirection = contentDirection.Direction;
if (content is IContentDirectionAware contentDirectionAware)
contentDirectionAware.ContentDirection = targetDirection;
foreach (var child in content.GetChildren())
ApplyContentDirection(child, targetDirection);
}
}
}
@@ -4,33 +4,17 @@ namespace QuestPDF.Drawing.Exceptions
{
public class InitializationException : Exception
{
internal InitializationException(string documentType, Exception innerException) : base(CreateMessage(documentType, innerException.Message), innerException)
internal InitializationException(string documentType, Exception innerException) : base(CreateMessage(documentType), innerException)
{
}
private static string CreateMessage(string documentType, string innerExceptionMessage)
private static string CreateMessage(string documentType)
{
var (libraryName, nugetConvention) = GetLibraryName();
return $"Cannot create the {documentType} document using the {libraryName} library. " +
$"This exception usually means that, on your operating system where you run the application, {libraryName} requires installing additional dependencies. " +
$"Such dependencies are available as additional nuget packages, for example {nugetConvention}.Linux.NoDependencies. " +
$"Some operating systems may require installing multiple nugets, e.g. MacOS may need both {nugetConvention}.macOS.NoDependencies and {nugetConvention}.Linux.NoDependencies." +
$"Please refer to the {libraryName} documentation for more details. " +
$"Also, please consult the inner exception that has been originally thrown by the dependency library.";
(string GetLibraryName, string nugetConvention) GetLibraryName()
{
if (innerExceptionMessage.Contains("libSkiaSharp"))
return ("SkiaSharp", "SkiaSharp.NativeAssets");
if (innerExceptionMessage.Contains("libHarfBuzzSharp"))
return ("HarfBuzzSharp", "HarfBuzzSharp.NativeAssets");
// default
return ("SkiaSharp-related", "*.NativeAssets");
}
return $"Cannot create the {documentType} document using the SkiaSharp library. " +
$"This exception usually means that, on your operating system where you run the application, SkiaSharp requires installing additional dependencies. " +
$"Such dependencies are available as additional nuget packages, for example SkiaSharp.NativeAssets.Linux. " +
$"Please refer to the SkiaSharp documentation for more details.";
}
}
}
+3 -50
View File
@@ -1,9 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using HarfBuzzSharp;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
@@ -22,11 +20,6 @@ namespace QuestPDF.Drawing
private static ConcurrentDictionary<object, SKFont> Fonts = new();
private static ConcurrentDictionary<object, TextShaper> TextShapers = new();
static FontManager()
{
RegisterLibraryDefaultFonts();
}
private static void RegisterFontType(SKData fontData, string? customName = null)
{
foreach (var index in Enumerable.Range(0, 256))
@@ -43,13 +36,8 @@ namespace QuestPDF.Drawing
}
}
[Obsolete("Since version 2022.8 this method has been renamed. Please use the RegisterFontWithCustomName method.")]
[Obsolete("Since version 2022.3, the FontManager class offers better font type matching support. Please use the RegisterFont(Stream stream) method.")]
public static void RegisterFontType(string fontName, Stream stream)
{
RegisterFontWithCustomName(fontName, stream);
}
public static void RegisterFontWithCustomName(string fontName, Stream stream)
{
using var fontData = SKData.Create(stream);
RegisterFontType(fontData);
@@ -61,38 +49,6 @@ namespace QuestPDF.Drawing
using var fontData = SKData.Create(stream);
RegisterFontType(fontData);
}
public static void RegisterFontFromEmbeddedResource(string pathName)
{
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(pathName);
RegisterFont(stream);
}
private static void RegisterLibraryDefaultFonts()
{
var fontFileNames = new[]
{
"Lato-Black.ttf",
"Lato-BlackItalic.ttf",
"Lato-Bold.ttf",
"Lato-BoldItalic.ttf",
"Lato-Regular.ttf",
"Lato-Italic.ttf",
"Lato-Light.ttf",
"Lato-LightItalic.ttf",
"Lato-Thin.ttf",
"Lato-ThinItalic.ttf"
};
fontFileNames
.Select(x => $"QuestPDF.Resources.DefaultFont.{x}")
.ToList()
.ForEach(RegisterFontFromEmbeddedResource);
}
internal static SKPaint ColorToPaint(this string color)
{
@@ -147,15 +103,12 @@ namespace QuestPDF.Drawing
if (fontFromDefaultSource != null)
return fontFromDefaultSource;
var availableFontNames = string.Join(", ", SKFontManager.Default.GetFontFamilies());
throw new ArgumentException(
$"The typeface '{style.FontFamily}' could not be found. " +
$"Please consider the following options: " +
$"1) install the font on your operating system or execution environment. " +
$"2) load a font file specifically for QuestPDF usage via the QuestPDF.Drawing.FontManager.RegisterFontType(Stream fileContentStream) static method. " +
$"Available font family names: [{availableFontNames}]");
$"2) load a font file specifically for QuestPDF usage via the QuestPDF.Drawing.FontManager.RegisterFontType(Stream fileContentStream) static method.");
}
static float GetTextScale(TextStyle style)
@@ -203,7 +156,7 @@ namespace QuestPDF.Drawing
return TextShapers.GetOrAdd(style.PaintKey, _ => new TextShaper(style));
}
internal static SKFont ToFont(this TextStyle style)
internal static SKFont FoFont(this TextStyle style)
{
return Fonts.GetOrAdd(style.PaintKey, _ => style.ToPaint().ToFont());
}
+9
View File
@@ -0,0 +1,9 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Drawing.Proxy
{
internal class DirectionProxy : ContainerElement
{
}
}
+3 -2
View File
@@ -21,10 +21,11 @@ namespace QuestPDF.Drawing
public TextShapingResult Shape(string text)
{
using var buffer = new Buffer();
var buffer = new Buffer();
PopulateBufferWithText(buffer, text);
buffer.GuessSegmentProperties();
//buffer.Direction = Direction.RightToLeft;
Font.Shape(buffer);
@@ -132,7 +133,7 @@ namespace QuestPDF.Drawing
using var skTextBlobBuilder = new SKTextBlobBuilder();
var positionedRunBuffer = skTextBlobBuilder.AllocatePositionedRun(textStyle.ToFont(), endIndex - startIndex + 1);
var positionedRunBuffer = skTextBlobBuilder.AllocatePositionedRun(textStyle.FoFont(), endIndex - startIndex + 1);
var glyphSpan = positionedRunBuffer.GetGlyphSpan();
var positionSpan = positionedRunBuffer.GetPositionSpan();
+9
View File
@@ -0,0 +1,9 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class ContentDirection : ContainerElement
{
public ContentDirectionType Direction { get; set; }
}
}
+30 -30
View File
@@ -19,12 +19,6 @@ namespace QuestPDF.Elements
Justify,
SpaceAround
}
internal struct InlinedMeasurement
{
public Element Element { get; set; }
public SpacePlan Size { get; set; }
}
internal class Inlined : Element, IStateResettable
{
@@ -86,7 +80,10 @@ namespace QuestPDF.Elements
foreach (var line in lines)
{
var height = line.Max(x => x.Size.Height);
var height = line
.Select(x => x.Measure(Size.Max))
.Where(x => x.Type != SpacePlanType.Wrap)
.Max(x => x.Height);
DrawLine(line);
@@ -97,24 +94,24 @@ namespace QuestPDF.Elements
Canvas.Translate(new Position(0, -topOffset));
lines.SelectMany(x => x).ToList().ForEach(x => ChildrenQueue.Dequeue());
void DrawLine(ICollection<InlinedMeasurement> lineMeasurements)
void DrawLine(ICollection<InlinedElement> elements)
{
var lineSize = GetLineSize(lineMeasurements);
var lineSize = GetLineSize(elements);
var elementOffset = ElementOffset();
var leftOffset = AlignOffset();
Canvas.Translate(new Position(leftOffset, 0));
foreach (var measurement in lineMeasurements)
foreach (var element in elements)
{
var size = (Size)measurement.Size;
var size = (Size)element.Measure(Size.Max);
var baselineOffset = BaselineOffset(size, lineSize.Height);
if (size.Height == 0)
size = new Size(size.Width, lineSize.Height);
Canvas.Translate(new Position(0, baselineOffset));
measurement.Element.Draw(size);
element.Draw(size);
Canvas.Translate(new Position(0, -baselineOffset));
leftOffset += size.Width + elementOffset;
@@ -127,20 +124,20 @@ namespace QuestPDF.Elements
{
var difference = availableSpace.Width - lineSize.Width;
if (lineMeasurements.Count == 1)
if (elements.Count == 1)
return 0;
return ElementsAlignment switch
{
InlinedAlignment.Justify => difference / (lineMeasurements.Count - 1),
InlinedAlignment.SpaceAround => difference / (lineMeasurements.Count + 1),
InlinedAlignment.Justify => difference / (elements.Count - 1),
InlinedAlignment.SpaceAround => difference / (elements.Count + 1),
_ => HorizontalSpacing
};
}
float AlignOffset()
{
var difference = availableSpace.Width - lineSize.Width - (lineMeasurements.Count - 1) * HorizontalSpacing;
var difference = availableSpace.Width - lineSize.Width - (elements.Count - 1) * HorizontalSpacing;
return ElementsAlignment switch
{
@@ -167,19 +164,24 @@ namespace QuestPDF.Elements
}
}
Size GetLineSize(ICollection<InlinedMeasurement> measurements)
Size GetLineSize(ICollection<InlinedElement> elements)
{
var width = measurements.Sum(x => x.Size.Width);
var height = measurements.Max(x => x.Size.Height);
var sizes = elements
.Select(x => x.Measure(Size.Max))
.Where(x => x.Type != SpacePlanType.Wrap)
.ToList();
var width = sizes.Sum(x => x.Width);
var height = sizes.Max(x => x.Height);
return new Size(width, height);
}
// list of lines, each line is a list of elements
private ICollection<ICollection<InlinedMeasurement>> Compose(Size availableSize)
private ICollection<ICollection<InlinedElement>> Compose(Size availableSize)
{
var queue = new Queue<InlinedElement>(ChildrenQueue);
var result = new List<ICollection<InlinedMeasurement>>();
var result = new List<ICollection<InlinedElement>>();
var topOffset = 0f;
@@ -190,7 +192,10 @@ namespace QuestPDF.Elements
if (!line.Any())
break;
var height = line.Max(x => x.Size.Height);
var height = line
.Select(x => x.Measure(availableSize))
.Where(x => x.Type != SpacePlanType.Wrap)
.Max(x => x.Height);
if (topOffset + height > availableSize.Height + Size.Epsilon)
break;
@@ -201,9 +206,9 @@ namespace QuestPDF.Elements
return result;
ICollection<InlinedMeasurement> GetNextLine()
ICollection<InlinedElement> GetNextLine()
{
var result = new List<InlinedMeasurement>();
var result = new List<InlinedElement>();
var leftOffset = GetInitialAlignmentOffset();
while (true)
@@ -222,12 +227,7 @@ namespace QuestPDF.Elements
queue.Dequeue();
leftOffset += size.Width + HorizontalSpacing;
result.Add(new InlinedMeasurement
{
Element = element,
Size = size
});
result.Add(element);
}
return result;
+3
View File
@@ -19,6 +19,8 @@ namespace QuestPDF.Elements
public float MarginBottom { get; set; }
public string BackgroundColor { get; set; } = Colors.Transparent;
public ContentDirectionType ContentDirection { get; set; } = ContentDirectionType.LeftToRight;
public Element Background { get; set; } = Empty.Instance;
public Element Foreground { get; set; } = Empty.Instance;
@@ -30,6 +32,7 @@ namespace QuestPDF.Elements
public void Compose(IContainer container)
{
container
.ContentDirection(ContentDirection)
.Background(BackgroundColor)
.Layers(layers =>
{
+1 -3
View File
@@ -42,9 +42,7 @@ namespace QuestPDF.Elements.Table
public void ResetState()
{
foreach (var x in Cells)
x.IsRendered = false;
Cells.ForEach(x => x.IsRendered = false);
CurrentRow = 1;
}
@@ -125,7 +125,7 @@ namespace QuestPDF.Elements.Text.Items
}
// text contains space that can be used to wrap
if (lastSpaceIndex > 1 && lastSpaceIndex >= startIndex)
if (lastSpaceIndex >= startIndex)
return (lastSpaceIndex - 1, lastSpaceIndex + 1);
// there is no available space to wrap text
+12 -21
View File
@@ -8,35 +8,22 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements.Text
{
internal class TextBlock : Element, IStateResettable
internal class TextBlock : Element, IContentDirectionAware, IStateResettable
{
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
public ContentDirectionType ContentDirection { get; set; }
public string Text => string.Join(" ", Items.Where(x => x is TextBlockSpan).Cast<TextBlockSpan>().Select(x => x.Text));
private Queue<ITextBlockItem> RenderingQueue { get; set; }
private int CurrentElementIndex { get; set; }
public void ResetState()
{
InitializeQueue();
RenderingQueue = new Queue<ITextBlockItem>(Items);
CurrentElementIndex = 0;
void InitializeQueue()
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (RenderingQueue == null)
{
RenderingQueue = new Queue<ITextBlockItem>(Items);
return;
}
RenderingQueue.Clear();
foreach (var item in Items)
RenderingQueue.Enqueue(item);
}
}
internal override SpacePlan Measure(Size availableSpace)
@@ -84,8 +71,12 @@ namespace QuestPDF.Elements.Text
Canvas.Translate(new Position(alignmentOffset, 0));
Canvas.Translate(new Position(0, -line.Ascent));
foreach (var item in line.Elements)
var elements = ContentDirection == ContentDirectionType.LeftToRight
? line.Elements
: line.Elements.Reverse();
foreach (var item in elements)
{
var textDrawingRequest = new TextDrawingRequest
{
@@ -123,7 +114,7 @@ namespace QuestPDF.Elements.Text
.ForEach(x => RenderingQueue.Dequeue());
var lastElementMeasurement = lines.Last().Elements.Last().Measurement;
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex;
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.EndIndex;
if (!RenderingQueue.Any())
ResetState();
@@ -0,0 +1,29 @@
using System.Linq.Expressions;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class ContentDirectionExtensions
{
internal static IContainer ContentDirection(this IContainer element, ContentDirectionType contentDirectionType)
{
var contentDirection = new ContentDirection
{
Direction = contentDirectionType
};
return element.Element(contentDirection);
}
public static IContainer ContentDirectionLeftToRight(this IContainer element)
{
return element.ContentDirection(ContentDirectionType.LeftToRight);
}
public static IContainer ContentDirectionRightToLeft(this IContainer element)
{
return element.ContentDirection(ContentDirectionType.RightToLeft);
}
}
}
+10
View File
@@ -88,6 +88,16 @@ namespace QuestPDF.Fluent
DefaultTextStyle(handler(TextStyle.Default));
}
public void ContentDirectionLeftToRight()
{
Page.ContentDirection = ContentDirectionType.LeftToRight;
}
public void ContentDirectionRightToLeft()
{
Page.ContentDirection = ContentDirectionType.RightToLeft;
}
public void PageColor(string color)
{
Page.BackgroundColor = color;
-1
View File
@@ -13,7 +13,6 @@
public const string CourierNew = "Courier New";
public const string Georgia = "Georgia";
public const string Impact = "Impact";
public const string Lato = "Lato";
public const string LucidaConsole = "Lucida Console";
public const string SegoeSD = "Segoe SD";
public const string SegoeUI = "Segoe UI";
+3 -6
View File
@@ -41,17 +41,14 @@ namespace QuestPDF.Helpers
internal static string PrettifyName(this string text)
{
return Regex.Replace(text, @"([a-z])([A-Z])", "$1 $2", RegexOptions.Compiled);
return Regex.Replace(text, @"([a-z])([A-Z])", "$1 $2");
}
internal static void VisitChildren(this Element? element, Action<Element?> handler)
{
if (element == null)
return;
foreach (var child in element.GetChildren())
foreach (var child in element.GetChildren().Where(x => x != null))
VisitChildren(child, handler);
handler(element);
}
}
@@ -0,0 +1,8 @@
namespace QuestPDF.Infrastructure
{
public enum ContentDirectionType
{
LeftToRight,
RightToLeft
}
}
@@ -4,6 +4,9 @@ namespace QuestPDF.Infrastructure
{
Left,
Center,
Right
Right,
Start,
End
}
}
@@ -0,0 +1,7 @@
namespace QuestPDF.Infrastructure
{
internal interface IContentDirectionAware
{
public ContentDirectionType ContentDirection { get; set; }
}
}
+3 -9
View File
@@ -22,14 +22,11 @@ namespace QuestPDF.Infrastructure
internal object PaintKey { get; private set; }
internal object FontMetricsKey { get; private set; }
// REVIEW: Should this be a method call that news up a TextStyle,
// or can it be a static variable?
// (style mutations seem to create a clone anyway)
internal static readonly TextStyle LibraryDefault = new TextStyle
internal static TextStyle LibraryDefault => new TextStyle
{
Color = Colors.Black,
BackgroundColor = Colors.Transparent,
FontFamily = Fonts.Lato,
FontFamily = Fonts.Calibri,
Size = 12,
LineHeight = 1.2f,
FontWeight = Infrastructure.FontWeight.Normal,
@@ -40,10 +37,7 @@ namespace QuestPDF.Infrastructure
WrapAnywhere = false
};
// REVIEW: Should this be a method call that news up a TextStyle,
// or can it be a static variable?
// (style mutations seem to create a clone anyway)
public static readonly TextStyle Default = new TextStyle();
public static TextStyle Default => new TextStyle();
internal void ApplyGlobalStyle(TextStyle globalStyle)
{
+2 -5
View File
@@ -17,9 +17,6 @@ namespace QuestPDF.Previewer
private HttpClient HttpClient { get; }
public event Action? OnPreviewerStopped;
private const int RequiredPreviewerVersionMajor = 2022;
private const int RequiredPreviewerVersionMinor = 8;
public PreviewerService(int port)
{
@@ -97,12 +94,12 @@ namespace QuestPDF.Previewer
private void CheckVersionCompatibility(Version version)
{
if (version.Major == RequiredPreviewerVersionMajor && version.Minor == RequiredPreviewerVersionMinor)
if (version.Major == 2022 && version.Minor == 6)
return;
throw new Exception($"Previewer version is not compatible. Possible solutions: " +
$"1) Update the QuestPDF library to newer version. " +
$"2) Update the QuestPDF previewer tool using the following command: 'dotnet tool update --global QuestPDF.Previewer --version {RequiredPreviewerVersionMajor}.{RequiredPreviewerVersionMinor}'");
$"2) Update the QuestPDF previewer tool using the following command: 'dotnet tool update --global QuestPDF.Previewer --version 2022.5'");
}
private async Task WaitForConnection()
+3 -23
View File
@@ -3,7 +3,7 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2022.8.0</Version>
<Version>2022.6.0-prerelease</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>
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
<LangVersion>9</LangVersion>
@@ -23,8 +23,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
@@ -44,25 +44,5 @@
<Visible>false</Visible>
<PackagePath>\</PackagePath>
</None>
<None Remove="Resources\DefaultFont\Lato-Black.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Black.ttf" />
<None Remove="Resources\DefaultFont\Lato-BlackItalic.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-BlackItalic.ttf" />
<None Remove="Resources\DefaultFont\Lato-Bold.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Bold.ttf" />
<None Remove="Resources\DefaultFont\Lato-BoldItalic.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-BoldItalic.ttf" />
<None Remove="Resources\DefaultFont\Lato-Italic.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Italic.ttf" />
<None Remove="Resources\DefaultFont\Lato-Light.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Light.ttf" />
<None Remove="Resources\DefaultFont\Lato-LightItalic.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-LightItalic.ttf" />
<None Remove="Resources\DefaultFont\Lato-Regular.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Regular.ttf" />
<None Remove="Resources\DefaultFont\Lato-Thin.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Thin.ttf" />
<None Remove="Resources\DefaultFont\Lato-ThinItalic.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-ThinItalic.ttf" />
</ItemGroup>
</Project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-93
View File
@@ -1,93 +0,0 @@
Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+4 -7
View File
@@ -1,7 +1,4 @@
- Improved library performance,
- Breaking change: changed default font from Calibri to an open-source Lato,
- Default font files are included with the nuget package, making it safe to deploy on any environment,
- Default font files are significantly smaller, so output document files should be smaller too (up to 20x reduction in size),
- 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.
Implemented support for the text shaping algorithm that fixes rendering more advanced languages such as Arabic.
Improved exception message when SkiaSharp throws the TypeInitializationException (when additional dependencies are needed).
Fixed: a rare case when the Row.AutoItem does not calculate properly the width of its content.
Fixed: the QuestPDF Previewer does not work with content-rich documents.
-7
View File
@@ -1,7 +0,0 @@
{
"sdk": {
"version": "6.0.300",
"rollForward": "latestMinor",
"allowPrerelease": true
}
}
+14 -11
View File
@@ -1,5 +1,5 @@
<a href="https://www.questpdf.com/" target="_blank">
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/logo.svg" width="400">
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/logo.svg" width="300">
</a>
---
@@ -59,32 +59,35 @@ Install-Package QuestPDF
dotnet add package QuestPDF
// Package reference in .csproj file
<PackageReference Include="QuestPDF" Version="2022.6.0" />
<PackageReference Include="QuestPDF" Version="2022.5.0" />
```
[![Nuget version](https://img.shields.io/badge/package%20details-QuestPDF-blue?logo=nuget)](https://www.nuget.org/packages/QuestPDF/)
## Documentation
[![Getting started tutorial]( https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue)](https://www.questpdf.com/getting-started.html)
[![Getting started tutorial]( https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue)](https://www.questpdf.com/documentation/getting-started.html)
A short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
[![API reference](https://img.shields.io/badge/%F0%9F%93%96%20read-API%20reference-blue)](https://www.questpdf.com/api-reference/index.html)
[![API reference](https://img.shields.io/badge/%F0%9F%93%96%20read-API%20reference-blue)](https://www.questpdf.com/documentation/api-reference.html)
A detailed description of behavior of all available components and how to use them with C# Fluent API.
[![Patterns and Practices](https://img.shields.io/badge/%E2%9C%A8%20read-patterns%20and%20practices-blue)](https://www.questpdf.com/design-patterns.html)
[![Patterns and Practices](https://img.shields.io/badge/%E2%9C%A8%20read-patterns%20and%20practices-blue)](https://www.questpdf.com/documentation/patterns-and-practices.html#document-metadata)
Everything that may help you designing great reports and create reusable code that is easy to maintain.
## QuestPDF Previewer
The QuestPDF Previewer is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task!
[![Learn more](https://img.shields.io/badge/%F0%9F%93%96%20Previewer-learn%20more-blue)](https://www.questpdf.com/document-previewer.html)
[![Learn more](https://img.shields.io/badge/%F0%9F%93%96%20Previewer-learn%20more-blue)](https://www.questpdf.com/documentation/document-previewer.html)
<video src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/a6f54912ee761af14dfbe1f96aa70d7fcf7ff94f/images/previewer/video.mp4?raw=true"></video>
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/previewer/animation.gif?raw=true" width="100%">
<img src="https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/images/previewer/animation.gif" width="100%">
## Simplicity is the key
@@ -133,13 +136,13 @@ Document.Create(container =>
And compare it to the produced PDF file:
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/minimal-example-shadow.png?raw=true" width="250px">
<img src="https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/images/minimal-api.png" width="250px">
## Are you ready for more?
The Fluent API of QuestPDF scales really well. It is easy to create and maintain even most complex documents. Read [the Getting started tutorial](https://www.questpdf.com/getting-started.html) to learn QuestPDF basics and implement an invoice under 200 lines of code. You can also investigate and play with the code from [the example repository](https://github.com/QuestPDF/example-invoice).
The Fluent API of QuestPDF scales really well. It is easy to create and maintain even most complex documents. Read [the Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html) to learn QuestPDF basics and implement an invoice under 200 lines of code. You can also investigate and play with the code from [the example repository](https://github.com/QuestPDF/example-invoice).
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/invoice-small.png?raw=true" width="400px">
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="400px">
## QuestPDF on JetBrains OSS Power-Ups
@@ -147,7 +150,7 @@ The Fluent API of QuestPDF scales really well. It is easy to create and maintain
QuestPDF was presented on one of the episodes of OSS Power-Ups hosted by JetBrains. Huge thanks for Matthias Koch and entire JetBrains team for giving me a chance to show QuestPDF. You are the best!
<a href="https://www.youtube.com/watch?v=-iYvZvpLX0g">
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/jetbrains-oss-powerups-youtube.png?raw=true" width="600px">
<img src="https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/images/jetbrains-oss-powerups-youtube.png" width="600px">
</a>