Compare commits

...

30 Commits

Author SHA1 Message Date
Marcin Ziąbek 011d44304a Update readme.md 2022-12-14 13:36:56 +01:00
MarcinZiabek a087be8afe Adjusted release notes 2022-12-14 12:33:19 +01:00
MarcinZiabek 483cbb6d46 Nuget packaging: added configuration for readme files 2022-12-13 13:17:53 +01:00
Marcin Ziąbek ec76cbd696 Update dotnet-desktop.yml 2022-12-13 12:26:19 +01:00
Marcin Ziąbek a83d8c43fe Update dotnet-desktop.yml 2022-12-13 12:14:04 +01:00
Marcin Ziąbek 5f25e37f7f Test build script 2022-12-13 12:03:37 +01:00
MarcinZiabek cafd27a60d Upgraded library version, added release notes 2022-12-13 00:08:40 +01:00
MarcinZiabek 5082bbbdf2 Changed letter-spacing to be relative to the font size 2022-12-10 17:34:07 +01:00
MarcinZiabek d2ba21821f Fixed: the Alignment element enforces size constraints in both direction, even if only one is provided 2022-12-09 01:02:47 +01:00
MarcinZiabek 9bef7d64d5 Letter spacing: minor comment improvements 2022-12-06 23:23:19 +01:00
Marcin Ziąbek ca4352addd Merge pull request #398 from Bebo-Maker/letter-spacing
Letter spacing
2022-12-06 23:10:21 +01:00
MarcinZiabek 2906315fa7 Improved obsolete message 2022-12-06 23:05:43 +01:00
Marcin Ziąbek 6f036388b9 Merge pull request #362 from rstm-sf/feature/Text_TextSpanDescriptor
Replace type parameter text for Text
2022-12-06 23:03:44 +01:00
MarcinZiabek 9cd4fd740e Updated nuget dependencies in test projects 2022-12-06 22:51:32 +01:00
MarcinZiabek c97acada76 Upgraded SkiaSharp to 2.88.3 + Avalonia upgrade 2022-12-06 22:49:42 +01:00
Bennet Bo Fenner 19ff9062ad apply letter spacing to each unicode cluster 2022-11-18 15:34:30 +01:00
Bennet Bo Fenner e4f8243c9b add more examples for letter spacing 2022-11-18 15:32:04 +01:00
MarcinZiabek 1060e4a44a Updated QuestPDF.Previewer version to 2022.11.1 2022-11-11 13:02:43 +01:00
MarcinZiabek 983edd5f76 Added example for Arabic support 2022-11-07 12:44:00 +01:00
Marcin Ziąbek 5315165cf5 Update readme.md 2022-11-07 12:31:41 +01:00
MarcinZiabek ee3d87010e Added example: forcing text direction 2022-11-07 12:22:38 +01:00
MarcinZiabek 3ac91c4971 Improved content-direction examples 2022-11-06 01:18:13 +01:00
MarcinZiabek 8e450934ab 2022.11.0 2022-11-06 00:26:33 +01:00
Marcin Ziąbek bf193eec23 Update readme.md 2022-11-05 19:57:35 +01:00
Marcin Ziąbek c9104540aa Merge pull request #349 from QuestPDF/content-direction
Right-to-left content support
2022-11-05 19:54:41 +01:00
Bennet Bo Fenner 4345ce857f add example for letter spacing 2022-11-03 16:45:49 +01:00
Bennet Bo Fenner 823572bf25 implement letter spacing 2022-11-03 16:45:40 +01:00
Bennet Bo Fenner e6bd63b3ec handle letter spacing in TextStyleManager 2022-11-03 16:43:49 +01:00
Bennet Bo Fenner bbf3d71e7e add letter spacing to TextStyle 2022-11-03 16:43:29 +01:00
Rustam Sayfutdinov 8a57dceb9d Replace type parameter text for Text 2022-10-08 09:07:03 +03:00
28 changed files with 561 additions and 116 deletions
+51
View File
@@ -0,0 +1,51 @@
name: Build And Create Nuget Package
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
schedule:
- cron: '3 0 * * 0'
jobs:
main:
runs-on: ${{ matrix.environment }}
strategy:
matrix:
environment:
- macos-latest
- ubuntu-latest
- windows-latest
env:
DOTNET_NOLOGO: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
UNIT_TEST_PROJECT: QuestPDF.UnitTests/QuestPDF.UnitTests.csproj
steps:
- name: 📝 Fetch Sources 📝
uses: actions/checkout@v2
- name: ⚙ Setup .NET 6.0 SDK ⚙
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- name: 🔄 Restore Nuget Packages 🔄
shell: bash
run: dotnet restore
- name: 🛠 Build Solution 🛠
shell: bash
run: dotnet build -c Release --no-restore
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: Build Package
path: |
**/*.nupkg
**/*.snupkg
!.nuget
+29
View File
@@ -0,0 +1,29 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class AlignmentExamples
{
[Test]
public void Example()
{
RenderingTest
.Create()
.PageSize(400, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.Border(1)
.AlignBottom()
.Background(Colors.Grey.Lighten1)
.Text("Test");
});
}
}
}
+42 -45
View File
@@ -17,30 +17,29 @@ namespace QuestPDF.Examples
return container =>
{
container
.Padding(20)
.MinimalBox()
.Border(1)
.ExtendHorizontal()
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.ConstantColumn(1);
columns.RelativeColumn();
});
table.Header(header =>
{
header.Cell().ContentFromLeftToRight().Element(HeaderCell("Left-to-right"));
header.Cell().LineVertical(1).LineColor(Colors.Grey.Medium);
header.Cell().ContentFromRightToLeft().Element(HeaderCell("Right-to-left"));
static Action<IContainer> HeaderCell(string label)
{
return container => container
.Border(1)
.BorderColor(Colors.Grey.Medium)
.Background(Colors.Grey.Lighten3)
.PaddingHorizontal(10)
.Background(Colors.Grey.Lighten2)
.PaddingHorizontal(15)
.PaddingVertical(5)
.Text(label)
.FontSize(18)
@@ -49,15 +48,12 @@ namespace QuestPDF.Examples
});
table.Cell().Element(TestCell).ContentFromLeftToRight().Element(content);
table.Cell()
.Element(TestCell)
.ContentFromRightToLeft()
.Element(content);
table.Cell().LineVertical(1).LineColor(Colors.Grey.Medium);
table.Cell().Element(TestCell).ContentFromRightToLeft().Element(content);
static IContainer TestCell(IContainer container)
{
return container.Border(1).BorderColor(Colors.Grey.Medium).Padding(10);
return container.Padding(15);
}
});
};
@@ -79,46 +75,47 @@ namespace QuestPDF.Examples
page.Margin(20);
page.PageColor(Colors.White);
//page.ContentFromRightToLeft();
page.DefaultTextStyle(x => x.FontFamily("Calibri").FontSize(20));
page.ContentFromRightToLeft();
page.Content().Column(column =>
{
column.Spacing(20);
column.Item().Row(row =>
{
row.Spacing(10);
row.AutoItem().AlignMiddle().Width(20).Height(20).Image(Placeholders.Image);
row.RelativeItem()
.Text("Document title")
.FontSize(24).FontColor(Colors.Blue.Accent1).SemiBold();
});
column.Item()
.Text("مثال على الفاتورة") // example invoice
.FontSize(32).FontColor(Colors.Blue.Darken2).SemiBold();
column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.ConstantColumn(75);
columns.ConstantColumn(100);
});
foreach (var i in Enumerable.Range(0, 9))
{
var width = (i % 4 == 0) ? 2 : 1;
table.Cell().Element(HeaderStyle).Text("وصف السلعة"); // item description
table.Cell().Element(HeaderStyle).Text("كمية"); // quantity
table.Cell().Element(HeaderStyle).Text("سعر"); // price
table
.Cell()
.ColumnSpan((uint)width)
.Background(i % 4 == 0 ? Colors.Grey.Lighten1 : Colors.Grey.Lighten2)
.Padding(5)
.AlignCenter()
.Text(i)
.FontSize(20);
var items = new[]
{
"دورة البرمجة", // programming course
"دورة تصميم الرسومات", // graphics design course
"تحليل وتصميم الخوارزميات", // analysis and design of algorithms
};
foreach (var item in items)
{
var price = Placeholders.Random.NextDouble() * 100;
table.Cell().Text(item);
table.Cell().Text(Placeholders.Random.Next(1, 10));
table.Cell().Text($"USD${price:F2}");
}
static IContainer HeaderStyle(IContainer x) => x.BorderBottom(1).PaddingVertical(5);
});
});
});
@@ -265,16 +262,16 @@ namespace QuestPDF.Examples
{
column.Spacing(10);
column.Item().Background(Colors.Grey.Lighten3).Text("Default alignment").FontSize(13);
column.Item().Text("Default alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(null));
column.Item().Background(Colors.Grey.Lighten3).Text("Left alignment").FontSize(14);
column.Item().Text("Left alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(InlinedAlignment.Left));
column.Item().Background(Colors.Grey.Lighten3).Text("Center alignment").FontSize(14);
column.Item().Text("Center alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(InlinedAlignment.Center));
column.Item().Background(Colors.Grey.Lighten3).Text("Right alignment").FontSize(14);
column.Item().Text("Right alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(InlinedAlignment.Right));
});
@@ -315,16 +312,16 @@ namespace QuestPDF.Examples
{
column.Spacing(10);
column.Item().Background(Colors.Grey.Lighten3).Text("Default alignment").FontSize(13);
column.Item().Text("Default alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(null));
column.Item().Background(Colors.Grey.Lighten3).Text("Left alignment").FontSize(14);
column.Item().Text("Left alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Left));
column.Item().Background(Colors.Grey.Lighten3).Text("Center alignment").FontSize(14);
column.Item().Text("Center alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Center));
column.Item().Background(Colors.Grey.Lighten3).Text("Right alignment").FontSize(14);
column.Item().Text("Right alignment").FontSize(14).SemiBold();
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Right));
});
@@ -1,4 +1,5 @@
using System.Drawing;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
@@ -43,7 +44,7 @@ namespace QuestPDF.Examples
.Height(50)
.AlignCenter()
.AlignMiddle()
.Text(i)
.Text(i.ToString(CultureInfo.InvariantCulture))
.FontSize(16 + i / 4);
}
});
+3 -2
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Elements;
@@ -126,9 +127,9 @@ namespace QuestPDF.Examples
.Padding(5)
.Row(row =>
{
row.ConstantItem(30).Text(index + 1);
row.ConstantItem(30).Text((index + 1).ToString(CultureInfo.InvariantCulture));
row.RelativeItem().Text(item.ItemName);
row.ConstantItem(50).AlignRight().Text(item.Count);
row.ConstantItem(50).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture));
row.ConstantItem(50).AlignRight().Text($"{item.Price}$");
row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$");
});
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Elements;
@@ -96,9 +97,9 @@ namespace QuestPDF.Examples
{
var item = Items[index];
table.Cell().Element(Style).Text(index + 1);
table.Cell().Element(Style).Text((index + 1).ToString(CultureInfo.InvariantCulture));
table.Cell().Element(Style).Text(item.ItemName);
table.Cell().Element(Style).AlignRight().Text(item.Count);
table.Cell().Element(Style).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture));
table.Cell().Element(Style).AlignRight().Text($"{item.Price}$");
table.Cell().Element(Style).AlignRight().Text($"{item.Count*item.Price}$");
+4 -4
View File
@@ -9,10 +9,10 @@
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="microcharts" Version="0.9.5.9" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="Svg.Skia" Version="0.5.10" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
<PackageReference Include="Svg.Skia" Version="0.5.18" />
</ItemGroup>
<ItemGroup>
+5 -4
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Drawing;
@@ -389,12 +390,12 @@ namespace QuestPDF.Examples
table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
// inches
table.Cell().Element(CellStyle).Text(page.width);
table.Cell().Element(CellStyle).Text(page.height);
table.Cell().Element(CellStyle).Text(page.width.ToString(CultureInfo.InvariantCulture));
table.Cell().Element(CellStyle).Text(page.height.ToString(CultureInfo.InvariantCulture));
// points
table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
table.Cell().Element(CellStyle).Text((page.width * inchesToPoints).ToString(CultureInfo.InvariantCulture));
table.Cell().Element(CellStyle).Text((page.height * inchesToPoints).ToString(CultureInfo.InvariantCulture));
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White);
}
+190 -4
View File
@@ -122,7 +122,132 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void LetterSpacing()
{
RenderingTest
.Create()
.PageSize(500, 700)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Column(column =>
{
var letterSpacing = new[] { -0.1f, 0f, 0.2f };
var paragraph = Placeholders.Sentence().ToUpper();
foreach (var spacing in letterSpacing)
{
column
.Item()
.Border(1)
.Padding(10)
.Column(nestedColumn =>
{
nestedColumn.Item()
.Text(paragraph)
.FontSize(16)
.LetterSpacing(spacing);
nestedColumn.Item()
.Text($"Letter spacing of {spacing} em")
.FontSize(10)
.Italic()
.FontColor(Colors.Blue.Medium);
});
}
});
});
}
[Test]
public void LetterSpacing_Arabic()
{
RenderingTest
.Create()
.PageSize(500, 700)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(50)
.Column(column =>
{
var letterSpacing = new[] { -0.1f, 0f, 0.2f };
var paragraph = "ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا";
foreach (var spacing in letterSpacing)
{
column
.Item()
.Border(1)
.Padding(10)
.Column(nestedColumn =>
{
nestedColumn.Item()
.Text(paragraph)
.FontSize(16)
.FontFamily(Fonts.Calibri)
.LetterSpacing(spacing);
nestedColumn.Item()
.Text($"Letter spacing of {spacing} em")
.FontSize(10)
.Italic()
.FontColor(Colors.Blue.Medium);
});
}
});
});
}
[Test]
public void LetterSpacing_Unicode()
{
RenderingTest
.Create()
.PageSize(500, 700)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(50)
.Column(column =>
{
var letterSpacing = new[] { 0f, 0.5f };
var paragraph = "Ţ̴̡̧̤̮̺̤̗͎̱̹͙͎͖͂̿̓́̉̊̀̍͜h̵̞̘͇̾̎̏̅į̵̹̖͔͉̰̎̉̄̐̏͑͂̅̃̃͘͝s̷͓͉̭̭̯̬̥̻̰̩̦̑̀̀͌́̒̍̒̌̇͛̀͛́̎ ̷̡̡̟͕̳̺̝̼͇͔̬̟̖͍̈́̽͜͝͝i̶͔͚̟̊̐͛́͛̄̌ṡ̸̡̤̪͙͍̥͙̟̼̝̰̥͈̿̓̄̿̓͠ ̶̢̦̙͍̯̖̱̰̯͕͔͎̯̝̎͑t̸͖̲̱̼̎͐̎̉̾̎̾̌̅̔̏͘ȩ̶̝̫̙͓̙̣̔̀̌̔̋̂̑̈́̏̀̈͘̕͜͝s̸̫̝̮̻̼͐̅̄̎̎̑͝ț̷̨̢̨̻͈̮̞̆͗̓͊̃̌͂̑̉̕̕͜͝͝";
foreach (var spacing in letterSpacing)
{
column.Item()
.Text($"Letter spacing of {spacing} em")
.FontSize(10)
.Italic()
.FontColor(Colors.Blue.Medium);
column.Item()
.PaddingVertical(50)
.Text(paragraph)
.FontSize(16)
.FontFamily(Fonts.Calibri)
.LetterSpacing(spacing);
}
});
});
}
[Test]
public void SuperscriptSubscript_Simple()
{
@@ -523,7 +648,7 @@ namespace QuestPDF.Examples
page.Content().Column(column =>
{
column.Item().Text(null);
column.Item().Text((string) null);
column.Item().Text(text =>
{
@@ -555,7 +680,7 @@ namespace QuestPDF.Examples
page.Content().Column(column =>
{
column.Item().Text(null);
column.Item().Text((string) null);
column.Item().Text(text =>
{
@@ -691,7 +816,28 @@ namespace QuestPDF.Examples
}
[Test]
public void TextDirectionality()
public void AdvancedLanguagesSupport()
{
RenderingTest
.Create()
.PageSize(new PageSize(400, 400))
.ProduceImages()
.ShowResults()
.Render(container =>
{
var text = "في المعلوماتية أو الرياضيات، خوارزمية الترتيب هي خوارزمية تمكن من تنظيم مجموعة عناصر حسب ترتيب محدد.";
container
.Padding(20)
.ContentFromRightToLeft()
.Text(text)
.FontFamily(Fonts.Calibri)
.FontSize(22);
});
}
[Test]
public void WordWrappingWhenRightToLeft()
{
RenderingTest
.Create()
@@ -725,5 +871,45 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void ForcingTextDirection()
{
RenderingTest
.Create()
.PageSize(new PageSize(1000, 500))
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.DefaultTextStyle(x => x.FontSize(24).FontFamily("Calibri"))
.Column(column =>
{
column.Spacing(10);
var word = "الجوريتم";
var definition = "algorithm in Arabic";
var text = $"{word} - {definition}";
// text direction is automatically detected using the first word
column.Item().Text(text);
// it is possible to force specific content direction
column.Item().Text(text).DirectionFromLeftToRight();
column.Item().Text(text).DirectionFromRightToLeft();
// to combine text in various content directions, split it into segments
column.Item().Text(text =>
{
text.Span(word);
text.Span(" - ");
text.Span(definition);
});
});
});
}
}
}
+14 -14
View File
@@ -4,7 +4,7 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF.Previewer</PackageId>
<Version>2022.11.0</Version>
<Version>2022.12.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>
@@ -13,6 +13,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIcon>Logo.png</PackageIcon>
<PackageIconUrl>https://www.questpdf.com/images/package-logo.png</PackageIconUrl>
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>
<RepositoryUrl>https://github.com/QuestPDF/library.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
@@ -33,23 +34,22 @@
<ItemGroup>
<EmbeddedResource Include="Resources\Logo.png" />
<None Include="Resources\Logo.png">
<Pack>true</Pack>
<Visible>false</Visible>
<PackagePath>\</PackagePath>
</None>
<None Include="Resources\Logo.png" Pack="true" PackagePath="\" />
<None Include="Resources\PackageReadme.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Avalonia" Version="0.10.10" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.10" />
<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="Avalonia" Version="0.10.18" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
<PackageReference Include="ReactiveUI" Version="18.4.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.3" />
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="2.88.3" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="2.88.3" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
</ItemGroup>
</Project>
@@ -0,0 +1,78 @@
[![Dotnet](https://img.shields.io/badge/platform-.NET-blue)](https://www.nuget.org/packages/QuestPDF/)
[![GitHub Repo stars](https://img.shields.io/github/stars/QuestPDF/QuestPDF)](https://github.com/QuestPDF/QuestPDF/stargazers)
[![Nuget version](https://img.shields.io/nuget/v/QuestPdf)](https://www.nuget.org/packages/QuestPDF/)
[![Nuget download](https://img.shields.io/nuget/dt/QuestPDF)](https://www.nuget.org/packages/QuestPDF/)
[![License](https://img.shields.io/github/license/QuestPDF/QuestPDF)](https://github.com/QuestPDF/QuestPDF/blob/main/LICENSE)
[![Sponsor project](https://img.shields.io/badge/sponsor-project-red)](https://github.com/sponsors/QuestPDF)
QuestPDF is an open-source .NET library for PDF documents generation.
It offers a layout engine designed with a full paging support in mind. The document consists of many simple elements (e.g. border, background, image, text, padding, table, grid etc.) that are composed together to create more complex structures. This way, as a developer, you can understand the behavior of every element and use them with full confidence. Additionally, the document and all its elements support paging functionality. For example, an element can be moved to the next page (if there is not enough space) or even be split between pages like table's rows.
## Documentation
[![Getting started tutorial]( https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue)](https://www.questpdf.com/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)
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/%F0%9F%94%8D%20read-patterns%20and%20practices-blue)](https://www.questpdf.com/design-patterns.html)
Everything that may help you designing great reports and create reusable code that is easy to maintain.
## Simplicity is the key
How easy it is to start and prototype with QuestPDF? Really easy thanks to its minimal API! Please analyse the code below:
```#
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
// code in your main method
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.Background(Colors.White);
page.DefaultTextStyle(x => x.FontSize(20));
page.Header()
.Text("Hello PDF!")
.SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Column(x =>
{
x.Spacing(20);
x.Item().Text(Placeholders.LoremIpsum());
x.Item().Image(Placeholders.Image(200, 100));
});
page.Footer()
.AlignCenter()
.Text(x =>
{
x.Span("Page ");
x.CurrentPageNumber();
});
});
})
.GeneratePdf("hello.pdf");
```
And compare it to the produced PDF file:
![invoice](https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/docs/public/minimal-example-shadow.png)
## 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/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).
![invoice](https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/docs/public/invoice-small.png)
@@ -10,9 +10,9 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<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="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
</ItemGroup>
<ItemGroup>
+38
View File
@@ -86,5 +86,43 @@ namespace QuestPDF.UnitTests
.ExpectCanvasTranslate(new Position(-300, 0))
.CheckDrawResult();
}
[Test]
public void Draw_HorizontalCenter_VerticalNone()
{
TestPlan
.For(x => new Alignment
{
Horizontal = HorizontalAlignment.Center,
Vertical = null,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(150, 0))
.ExpectChildDraw(new Size(100, 300))
.ExpectCanvasTranslate(new Position(-150, 0))
.CheckDrawResult();
}
[Test]
public void Draw_HorizontalNone_VerticalMiddle()
{
TestPlan
.For(x => new Alignment
{
Horizontal = null,
Vertical = VerticalAlignment.Middle,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(0, 125))
.ExpectChildDraw(new Size(400, 50))
.ExpectCanvasTranslate(new Position(0, -125))
.CheckDrawResult();
}
}
}
+4 -4
View File
@@ -6,11 +6,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="FluentAssertions" Version="6.8.0" />
<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="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
</ItemGroup>
<ItemGroup>
+15 -2
View File
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using HarfBuzzSharp;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using SkiaSharp;
using Buffer = HarfBuzzSharp.Buffer;
@@ -47,16 +48,28 @@ namespace QuestPDF.Drawing
var xOffset = 0f;
var yOffset = 0f;
var glyphs = new ShapedGlyph[length];
// used for letter spacing calculation
var lastCluster = glyphInfos.LastOrDefault().Cluster;
var letterSpacing = (TextStyle.LetterSpacing ?? 0) * (TextStyle.Size ?? 16);
var glyphs = new ShapedGlyph[length];
for (var i = 0; i < length; i++)
{
// letter spacing should be applied between glyph clusters, not between individual glyphs,
// different cluster id indicates the end of the glyph cluster
if (lastCluster != glyphInfos[i].Cluster)
{
lastCluster = glyphInfos[i].Cluster;
xOffset += letterSpacing;
}
glyphs[i] = new ShapedGlyph
{
Codepoint = (ushort)glyphInfos[i].Codepoint,
Position = new SKPoint(xOffset + glyphPositions[i].XOffset * scaleX, yOffset - glyphPositions[i].YOffset * scaleY),
Width = glyphPositions[i].XAdvance * scaleX
};
};
xOffset += glyphPositions[i].XAdvance * scaleX;
yOffset += glyphPositions[i].YAdvance * scaleY;
+11 -7
View File
@@ -6,19 +6,23 @@ namespace QuestPDF.Elements
{
internal class Alignment : ContainerElement
{
public VerticalAlignment Vertical { get; set; } = VerticalAlignment.Top;
public HorizontalAlignment Horizontal { get; set; } = HorizontalAlignment.Left;
public VerticalAlignment? Vertical { get; set; }
public HorizontalAlignment? Horizontal { get; set; }
internal override void Draw(Size availableSpace)
{
if (Child == null)
return;
var childSize = base.Measure(availableSpace);
var childMeasurement = base.Measure(availableSpace);
if (childSize.Type == SpacePlanType.Wrap)
if (childMeasurement.Type == SpacePlanType.Wrap)
return;
var childSize = new Size(
Horizontal.HasValue ? childMeasurement.Width : availableSpace.Width,
Vertical.HasValue ? childMeasurement.Height : availableSpace.Height);
var top = GetTopOffset(availableSpace, childSize);
var left = GetLeftOffset(availableSpace, childSize);
@@ -36,7 +40,7 @@ namespace QuestPDF.Elements
VerticalAlignment.Top => 0,
VerticalAlignment.Middle => difference / 2,
VerticalAlignment.Bottom => difference,
_ => throw new NotSupportedException()
_ => 0
};
}
@@ -49,7 +53,7 @@ namespace QuestPDF.Elements
HorizontalAlignment.Left => 0,
HorizontalAlignment.Center => difference / 2,
HorizontalAlignment.Right => difference,
_ => throw new NotSupportedException()
_ => 0
};
}
}
+2 -2
View File
@@ -60,8 +60,8 @@ namespace QuestPDF.Fluent
{
var descriptor = new GridDescriptor();
if (element is Alignment alignment)
descriptor.Alignment(alignment.Horizontal);
if (element is Alignment alignment && alignment.Horizontal.HasValue)
descriptor.Alignment(alignment.Horizontal.Value);
handler(descriptor);
element.Component(descriptor.Grid);
+10 -4
View File
@@ -276,16 +276,22 @@ namespace QuestPDF.Fluent
descriptor.Compose(element);
}
[Obsolete("This element has been renamed since version 2022.3. Please use the overload that returns a TextSpanDescriptor object which allows to specify text style.")]
[Obsolete("This method has been deprecated since version 2022.3. Please use the overload that returns a TextSpanDescriptor object which allows to specify text style.")]
public static void Text(this IContainer element, object? text, TextStyle style)
{
element.Text(text).Style(style);
}
[Obsolete("This method has been deprecated since version 2022.12. Please use an overload where the text parameter is passed explicitly as a string.")]
public static TextSpanDescriptor Text(this IContainer element, object? text)
{
var descriptor = (TextSpanDescriptor) null;
element.Text(x => descriptor = x.Span(text?.ToString()));
return element.Text(text?.ToString());
}
public static TextSpanDescriptor Text(this IContainer element, string? text)
{
var descriptor = (TextSpanDescriptor) null!;
element.Text(x => descriptor = x.Span(text));
return descriptor;
}
}
@@ -55,7 +55,18 @@ namespace QuestPDF.Fluent
descriptor.MutateTextStyle(x => x.LineHeight(value));
return descriptor;
}
/// <summary>
/// Letter spacing controls space between characters. Value 0 corresponds to normal spacing defined by a font.
/// Positive values create additional space, whereas negative values reduce space between characters.
/// Added / reduced space is relative to the font size.
/// </summary>
public static T LetterSpacing<T>(this T descriptor, float value) where T : TextSpanDescriptor
{
descriptor.MutateTextStyle(x => x.LetterSpacing(value));
return descriptor;
}
public static T Italic<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
{
descriptor.MutateTextStyle(x => x.Italic(value));
+11 -1
View File
@@ -48,7 +48,17 @@ namespace QuestPDF.Fluent
{
return style.Mutate(TextStyleProperty.LineHeight, value);
}
/// <summary>
/// Letter spacing controls space between characters. Value 0 corresponds to normal spacing defined by a font.
/// Positive values create additional space, whereas negative values reduce space between characters.
/// Added / reduced space is relative to the font size.
/// </summary>
public static TextStyle LetterSpacing(this TextStyle style, float value)
{
return style.Mutate(TextStyleProperty.LetterSpacing, value);
}
public static TextStyle Italic(this TextStyle style, bool value = true)
{
return style.Mutate(TextStyleProperty.IsItalic, value);
+1 -1
View File
@@ -7,7 +7,7 @@ namespace QuestPDF.Helpers
{
public static class Placeholders
{
public static readonly Random Random = new Random(0);
public static readonly Random Random = new Random();
#region Word Cache
+2
View File
@@ -10,6 +10,7 @@ namespace QuestPDF.Infrastructure
internal string? FontFamily { get; set; }
internal float? Size { get; set; }
internal float? LineHeight { get; set; }
internal float? LetterSpacing { get; set; }
internal FontWeight? FontWeight { get; set; }
internal FontPosition? FontPosition { get; set; }
internal bool? IsItalic { get; set; }
@@ -27,6 +28,7 @@ namespace QuestPDF.Infrastructure
FontFamily = Fonts.Lato,
Size = 12,
LineHeight = 1.2f,
LetterSpacing = 0,
FontWeight = Infrastructure.FontWeight.Normal,
FontPosition = Infrastructure.FontPosition.Normal,
IsItalic = false,
@@ -11,6 +11,7 @@ namespace QuestPDF.Infrastructure
FontFamily,
Size,
LineHeight,
LetterSpacing,
FontWeight,
FontPosition,
IsItalic,
@@ -102,6 +103,19 @@ namespace QuestPDF.Infrastructure
return origin with { LineHeight = castedValue };
}
if(property == TextStyleProperty.LetterSpacing)
{
if (!overrideValue && origin.LetterSpacing != null)
return origin;
var castedValue = (float?)value;
if (origin.LetterSpacing == castedValue)
return origin;
return origin with { LetterSpacing = castedValue };
}
if (property == TextStyleProperty.FontWeight)
{
@@ -246,6 +260,7 @@ namespace QuestPDF.Infrastructure
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.LetterSpacing, parent.LetterSpacing, 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);
+1 -1
View File
@@ -19,7 +19,7 @@ namespace QuestPDF.Previewer
public event Action? OnPreviewerStopped;
private const int RequiredPreviewerVersionMajor = 2022;
private const int RequiredPreviewerVersionMinor = 11;
private const int RequiredPreviewerVersionMinor = 12;
public PreviewerService(int port)
{
+8 -8
View File
@@ -3,7 +3,7 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2022.11.0-alpha1</Version>
<Version>2022.12.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>
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
<LangVersion>9</LangVersion>
@@ -11,6 +11,7 @@
<PackageIcon>Logo.png</PackageIcon>
<PackageIconUrl>https://www.questpdf.com/images/package-logo.png</PackageIconUrl>
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/QuestPDF/library.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Copyright>Marcin Ziąbek, QuestPDF contributors</Copyright>
@@ -23,8 +24,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.80.4" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.4" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.3" />
</ItemGroup>
<ItemGroup>
@@ -39,11 +40,10 @@
<EmbeddedResource Include="Resources\ImagePlaceholder.png" />
<EmbeddedResource Include="Resources\Logo.png" />
<None Remove="ImagePlaceholder.png" />
<None Include="Resources\Logo.png">
<Pack>true</Pack>
<Visible>false</Visible>
<PackagePath>\</PackagePath>
</None>
<None Include="Resources\Logo.png" Pack="true" PackagePath="\" />
<None Include="Resources\PackageReadme.md" Pack="true" PackagePath="\" />
<None Remove="Resources\DefaultFont\Lato-Black.ttf" />
<EmbeddedResource Include="Resources\DefaultFont\Lato-Black.ttf" />
<None Remove="Resources\DefaultFont\Lato-BlackItalic.ttf" />
+1 -1
View File
@@ -21,7 +21,7 @@ Install-Package QuestPDF
dotnet add package QuestPDF
// Package reference in .csproj file
<PackageReference Include="QuestPDF" Version="2022.11.0" />
<PackageReference Include="QuestPDF" Version="2022.12.0" />
```
## Documentation
+4 -3
View File
@@ -1,3 +1,4 @@
Added support for the right-to-left content direction.
Fixed: word-wrapping algorithm does not work correctly with right-to-left languages.
Fixed: Page.Size() API incorrectly uses Unit.Inch as default unit. Replaced with Unit.Point for consistency.
Feature: implemented LetterSpacing property for the Text element
Improvement: the Text element API accepts now only string values, objects are not automatically converted anymore
Fix: the Alignment element incorrectly limits size of its child when only one axis is set (horizontal or vertical)
Maintenance: Updated SkiaSharp dependency to 2.88.3
+2 -2
View File
@@ -23,7 +23,7 @@ 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! ⭐
<img src="https://user-images.githubusercontent.com/9263853/190931857-8ca52ec8-cc7d-4d12-9467-4442b3342fa1.png" width="700" />
<img src="https://user-images.githubusercontent.com/9263853/207596765-f3b84375-e4da-4597-b6a4-f2e22dd2b479.png" width="800" />
## Please share with the community
@@ -63,7 +63,7 @@ Install-Package QuestPDF
dotnet add package QuestPDF
// Package reference in .csproj file
<PackageReference Include="QuestPDF" Version="2022.9.0" />
<PackageReference Include="QuestPDF" Version="2022.12.0" />
```
[![Nuget version](https://img.shields.io/badge/package%20details-QuestPDF-blue?logo=nuget)](https://www.nuget.org/packages/QuestPDF/)