Compare commits

..

110 Commits

Author SHA1 Message Date
MarcinZiabek e53b586943 Implemented automated image scaling 2023-05-01 14:23:46 +02:00
Marcin Ziąbek 9637dff1dc Image scaling dpi (#540)
* DPI-based image scaling prototype (not ready for production)

* Added ImageDpi API

* Added support for image DPI scaling
2023-04-30 11:28:14 +02:00
MarcinZiabek 1c9bccbeb2 Feature: added support for shared/global images 2023-04-17 02:08:04 +02:00
MarcinZiabek 176566801b 2022.12.3 release 2023-04-17 00:10:46 +02:00
MarcinZiabek 22a696fab9 Fix(#470): the inlined element is shown only once in the header but should be repeated on each page 2023-04-17 00:01:32 +02:00
MarcinZiabek 9809faccb6 Improvement (#528): added validation for color arguments 2023-04-16 23:49:47 +02:00
MarcinZiabek 6ad465b9fa Fix(#499): inconsistent text height when using multiple lines 2023-04-16 11:42:38 +02:00
MarcinZiabek e6c7befac4 Revert "Building QuestPDF Previewer for dotnet 7"
This reverts commit b05f76f848.
2023-03-15 19:48:56 +01:00
MarcinZiabek be3e4b59ec Revert "Update dotnet-desktop.yml"
This reverts commit e751bfd2ff.
2023-03-15 19:48:56 +01:00
Marcin Ziąbek e751bfd2ff Update dotnet-desktop.yml 2023-03-15 10:53:44 +01:00
MarcinZiabek b05f76f848 Building QuestPDF Previewer for dotnet 7 2023-03-14 12:28:04 +01:00
MarcinZiabek b5d7dbb346 2022.12.2 release 2023-03-12 22:14:17 +01:00
Marcin Ziąbek 0ab73f4f2b Update README.md 2023-03-11 22:18:34 +01:00
MarcinZiabek 458b379208 Remove loop to calculate maximum (target) width, remove capturing lambda 2023-03-07 21:22:45 +01:00
Marcin Ziąbek c3c0d7631c Merge pull request #454 from thomasstevens89/main
Update DynamicOptimized Example to use non-deprecated methods
2023-03-07 21:03:04 +01:00
MarcinZiabek a3ea8bceb6 Text rendering optimization: cache codepoint for space 2023-03-06 22:29:06 +01:00
MarcinZiabek 455d187897 Code formatting improvement 2023-03-06 21:42:38 +01:00
Marcin Ziąbek 65e5d36ded Merge pull request #474 from AntonyCorbett/fix_render_bug_382
Fix #382
2023-03-06 21:38:23 +01:00
Marcin Ziąbek ad5e8ab27d Merge pull request #489 from AntonyCorbett/perf_linq_02
Small perf improvement in LINQ (2)
2023-03-06 20:15:53 +01:00
Marcin Ziąbek a17197addc Merge pull request #490 from AntonyCorbett/var_scope_change_01
reduce scope of variable (remove unneeded calc)
2023-03-06 20:12:27 +01:00
Marcin Ziąbek a1e7cbbeb3 Merge pull request #505 from avobelk/fix-fallback-not-found-glyph
fix glyph check in FontFallback.MatchFallbackOption
2023-03-06 20:10:23 +01:00
Marcin Ziąbek c00ac5e9c7 Merge pull request #493 from AntonyCorbett/var_scope_change_02
reduce variable scope
2023-03-06 11:08:53 +01:00
Marcin Ziąbek 0e0a8d5d69 Merge pull request #488 from AntonyCorbett/perf_linq_01
Small perf improvement in LINQ
2023-03-06 10:54:10 +01:00
Marcin Ziąbek bc341da6dd Merge pull request #476 from AntonyCorbett/small_dictionary_perf
prevent double lookup
2023-03-06 10:49:00 +01:00
Alexander V 6096771b51 fix glyph check in FontFallback.MatchFallbackOption 2023-02-23 23:09:30 +03:00
Antony Corbett 4f097d0fed reduce variable scope 2023-02-13 17:06:15 +00:00
Antony Corbett 24beea80c7 reduce scope of variable (remove unneeded calc) 2023-02-12 14:06:55 +00:00
Antony Corbett e1e5778ff0 Small perf improvement in LINQ 2023-02-12 14:01:41 +00:00
Antony Corbett 8679391609 Small perf improvement in LINQ 2023-02-12 13:59:31 +00:00
Marcin Ziąbek c1b60287ca Update README.md 2023-02-12 00:01:47 +01:00
Marcin Ziąbek 011e1b7ce9 Update README.md 2023-02-10 00:21:17 +01:00
Antony Corbett 5fd72cbb4b prevent double lookup 2023-02-03 15:10:57 +00:00
Antony Corbett 7284998657 Fix #382 2023-02-03 10:20:39 +00:00
Marcin Ziąbek 30bd7420e2 Merge pull request #467 from AntonyCorbett/fix_exception_ctor
Fix exception ctor call.
2023-01-29 22:22:13 +01:00
Marcin Ziąbek bd48688670 Merge pull request #466 from AntonyCorbett/fix_multiple_enum
Avoid multiple enumeration
2023-01-29 22:20:48 +01:00
Marcin Ziąbek 68e3efde59 Merge pull request #465 from AntonyCorbett/unit_test_user_agent_fix
Add user agent string
2023-01-29 22:18:05 +01:00
Marcin Ziąbek 805fd55d94 Minor code formatting 2023-01-29 22:17:22 +01:00
MarcinZiabek b3a3deb354 Added example showing how to detect span position in text block (hack) 2023-01-29 22:00:14 +01:00
MarcinZiabek 68e4a10e81 Deleted obsolete performance tests 2023-01-29 22:00:14 +01:00
Antony Corbett e15a21ab0e Fix exception ctor call. 2023-01-26 14:31:44 +00:00
Antony Corbett f851221718 avoid multiple enumeration 2023-01-26 14:29:40 +00:00
Antony Corbett 91c93e68b5 Add user agent string 2023-01-26 13:49:42 +00:00
Marcin Ziąbek 3dcc35be97 Update README.md 2023-01-23 21:21:49 +01:00
Marcin Ziąbek 3f32c6e2c5 Update README.md 2023-01-19 10:07:09 +01:00
Thomas Stevens f1f609ccc1 use content() not stack() 2023-01-14 12:17:41 -05:00
Thomas Stevens 5c07485cc1 Update DynamicOptimizedExample to use non-deprecated methods 2023-01-14 12:09:11 -05:00
Marcin Ziąbek 42684ec180 Update README.md 2023-01-14 00:54:40 +01:00
MarcinZiabek 08f9f706ce Adjusted package details 2023-01-13 22:53:25 +01:00
MarcinZiabek 1f199423b0 Package description: use images from trusted sources 2023-01-13 22:48:40 +01:00
Marcin Ziąbek d8e640fcf3 Update README.md 2023-01-13 22:30:22 +01:00
MarcinZiabek fb02d97a10 Improved package description 2023-01-13 22:28:19 +01:00
MarcinZiabek d5f8a884d6 Adjusted package description 2023-01-13 22:05:31 +01:00
Marcin Ziąbek 5de5be6429 Update dotnet-desktop.yml 2023-01-13 21:41:08 +01:00
MarcinZiabek 96d57b241e 2022.12.1 2023-01-13 21:26:41 +01:00
Marcin Ziąbek 920e61fc5e Update dotnet-desktop.yml 2023-01-11 23:15:17 +01:00
Marcin Ziąbek 1cc606261c Update dotnet-desktop.yml 2023-01-11 23:04:22 +01:00
Marcin Ziąbek 0a6c021fe9 Update README.md 2023-01-11 22:54:23 +01:00
Marcin Ziąbek b65ee32daf Update README.md 2023-01-11 22:54:01 +01:00
MarcinZiabek e496917d84 Updated readme 2023-01-11 22:27:51 +01:00
MarcinZiabek a5004b986c Exposed missing API for dynamic components 2023-01-11 22:24:59 +01:00
MarcinZiabek 84d7fcf41f Improved documentation for the DocumentLayoutExceptionThreshold setting by providing the unit information 2023-01-10 12:23:48 +01:00
MarcinZiabek 641cc6dee2 Fixed: loading fonts from embedded resource via the FontManager.RegisterFontFromEmbeddedResource method 2023-01-09 11:59:11 +01:00
Marcin Ziąbek 9dbed14559 Update README.md 2023-01-06 00:48:29 +01:00
Marcin Ziąbek 6323a8dba2 Update README.md 2023-01-06 00:47:30 +01:00
Marcin Ziąbek 356dbb020d Rename LICENSE to LICENSE.md 2023-01-06 00:46:52 +01:00
Marcin Ziąbek bad6be1a28 Rename readme.md to README.md 2023-01-06 00:46:23 +01:00
MarcinZiabek 27c7383437 Updated repository structure 2023-01-06 00:45:12 +01:00
Marcin Ziąbek ecac19ee46 Update readme.md 2023-01-06 00:14:06 +01:00
Marcin Ziąbek fd37070d3b Update readme.md 2023-01-05 23:31:04 +01:00
Marcin Ziąbek 3b433f3efb Update readme.md 2023-01-04 01:22:56 +01:00
Marcin Ziąbek b3d6a39a98 Update readme.md 2023-01-04 00:34:13 +01:00
Marcin Ziąbek 8d1a449e69 Content cleanup 2023-01-03 23:58:39 +01:00
Marcin Ziąbek 7f0be69ffc Updated stargazers section, fixed readability 2023-01-03 23:53:10 +01:00
Marcin Ziąbek 8553131b99 Readme content adjustments 2023-01-03 23:46:35 +01:00
Marcin Ziąbek 1bf9198a51 Adjusted button sizes 2023-01-03 23:32:58 +01:00
MarcinZiabek 563b782f6a Fixed text configuration for twitter share button 2022-12-26 10:45:34 +01:00
MarcinZiabek edf8db878a Updated share links 2022-12-24 00:34:09 +01:00
Marcin Ziąbek 5fd6488068 Updated github star count 2022-12-24 00:12:35 +01:00
MarcinZiabek d6a7c3703d Removed GitHub sponsors section 2022-12-23 22:32:11 +01:00
MarcinZiabek 5844d3bdba Column: improved layout stability 2022-12-23 00:11:33 +01:00
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
314 changed files with 1520 additions and 624 deletions
+54
View File
@@ -0,0 +1,54 @@
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
working-directory: ./Source
- name: 🛠 Build Solution 🛠
shell: bash
run: dotnet build -c Release --no-restore
working-directory: ./Source
- name: Upload build artifacts
uses: actions/upload-artifact@v3
if: ${{ matrix.environment == 'windows-latest' }}
with:
name: Build Package
path: |
**/*.nupkg
**/*.snupkg
!.nuget
View File
-75
View File
@@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Running;
using Microsoft.VisualStudio.TestPlatform.TestHost;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Drawing.Proxy;
using QuestPDF.Elements;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF.ReportSample.Layouts;
namespace QuestPDF.ReportSample
{
[SimpleJob(RunStrategy.Monitoring, launchCount: 0, warmupCount: 1, targetCount: 256)]
[MinColumn, MaxColumn, MeanColumn, MedianColumn]
public class PerformanceTests
{
private PageContext PageContext { get; set; }
private DocumentMetadata Metadata { get; set; }
private Container Content { get; set; }
[Test]
public void Run()
{
ImagePlaceholder.Solid = true;
var configuration = ManualConfig
.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator);
BenchmarkRunner.Run<PerformanceTests>(configuration);
}
[IterationSetup]
public void GenerateReportData()
{
ImagePlaceholder.Solid = true;
var model = DataSource.GetReport();
var report = new StandardReport(model);
Metadata = report.GetMetadata();
var documentContainer = new DocumentContainer();
report.Compose(documentContainer);
Content = documentContainer.Compose();
PageContext = new PageContext();
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, null);
var sw = new Stopwatch();
sw.Start();
Content.VisitChildren(x =>
{
if (x is ICacheable)
x.CreateProxy(y => new CacheProxy(y));
});
sw.Stop();
Console.WriteLine($"Creating cache took: {sw.ElapsedMilliseconds}");
}
[Benchmark]
public void GenerationTest()
{
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, null);
}
}
}
-62
View File
@@ -1,62 +0,0 @@
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
using SkiaSharp;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class ImageTests
{
[Test]
public void Measure_TakesAvailableSpaceRegardlessOfSize()
{
TestPlan
.For(x => new Image
{
InternalImage = GenerateImage(400, 300)
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(SpacePlan.FullRender(300, 200));
}
[Test]
public void Draw_TakesAvailableSpaceRegardlessOfSize()
{
TestPlan
.For(x => new Image
{
InternalImage = GenerateImage(400, 300)
})
.DrawElement(new Size(300, 200))
.ExpectCanvasDrawImage(new Position(0, 0), new Size(300, 200))
.CheckDrawResult();
}
[Test]
public void Fluent_RecognizesImageProportions()
{
var image = GenerateImage(600, 200).Encode(SKEncodedImageFormat.Png, 100).ToArray();
TestPlan
.For(x =>
{
var container = new Container();
container.Image(image);
return container;
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(SpacePlan.FullRender(300, 100));;
}
SKImage GenerateImage(int width, int height)
{
var imageInfo = new SKImageInfo(width, height);
using var surface = SKSurface.Create(imageInfo);
return surface.Snapshot();
}
}
}
-93
View File
@@ -1,93 +0,0 @@
[![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. All of it done with predictable and discoverable Fluent API.
## Installation
The library is available as a nuget package. You can install it as any other nuget package from your IDE, try to search by `QuestPDF`. You can find package details [on this webpage](https://www.nuget.org/packages/QuestPDF/).
```xml
// Package Manager
Install-Package QuestPDF
// .NET CLI
dotnet add package QuestPDF
// Package reference in .csproj file
<PackageReference Include="QuestPDF" Version="2022.11.0" />
```
## Documentation
[![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/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/%F0%9F%94%8D%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.
## 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/images/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/images/invoice-small.png)
-3
View File
@@ -1,3 +0,0 @@
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.
+92
View File
@@ -0,0 +1,92 @@
<a href="https://www.questpdf.com/" target="_blank">
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/logo.svg" width="400">
</a>
---
[![QuestPDF Homepage](https://img.shields.io/badge/Homepage-blue?style=for-the-badge)](https://www.questpdf.com)
[![Dotnet](https://img.shields.io/badge/platform-.NET-blue?style=for-the-badge)](https://www.nuget.org/packages/QuestPDF/)
[![GitHub Repo stars](https://img.shields.io/github/stars/QuestPDF/QuestPDF?style=for-the-badge)](https://github.com/QuestPDF/QuestPDF/stargazers)
[![Nuget version](https://img.shields.io/nuget/v/QuestPdf?style=for-the-badge)](https://www.nuget.org/packages/QuestPDF/)
[![Nuget download](https://img.shields.io/nuget/dt/QuestPDF?style=for-the-badge)](https://www.nuget.org/packages/QuestPDF/)
[![License](https://img.shields.io/github/license/QuestPDF/QuestPDF?style=for-the-badge)](https://github.com/QuestPDF/QuestPDF/blob/main/LICENSE)
<br />
### QuestPDF is a modern open-source .NET library for PDF document generation. Offering comprehensive layout engine powered by concise and discoverable C# Fluent API.
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/previewer/animation.gif?raw=true" width="100%">
<table>
<tr>
<td>👨‍💻</td>
<td>Design PDF documents using C# and employ a code-only approach. Utilize your version control system to its fullest potential.</td>
</tr>
<tr>
<td>🧱</td>
<td>Compose PDF document with a range of powerful and predictable structural elements, such as text, image, border, table, and many more.</td>
</tr>
<tr>
<td>⚙️</td>
<td>Utilize a comprehensive layout engine, specifically designed for PDF document generation and paging support.</td>
</tr>
<tr>
<td>📖</td>
<td>Write code using concise and easy-to-understand C# Fluent API. Utilize IntelliSense to quickly discover available options.</td>
</tr>
<tr>
<td>🔗</td>
<td>Don't be limited to any proprietary scripting language or format. Follow your experience and leverage all modern C# features.</td>
</tr>
<tr>
<td>⌛</td>
<td>Save time thanks to a hot-reload capability, allowing real-time PDF document preview without code recompilation.</td>
</tr>
</table>
<br />
<br />
## Please help by giving a star
Choosing a project dependency could be difficult. We need to ensure stability and maintainability of our projects. Surveys show that GitHub stars count play an important factor when assessing library quality.
⭐ Please give this repository a star. It takes seconds and help thousands of developers! ⭐
<img src="https://user-images.githubusercontent.com/9263853/224511996-9dd27a99-bef8-4d5e-bc14-9f6cfe0f5191.png" width="800" />
## Please share with the community
As an open-source project without funding, I cannot afford advertising QuestPDF in a typical way. Instead, the library relies on community interactions. Please consider sharing a post about QuestPDF and the value it provides. It really does help!
[![Share on Reddit](https://img.shields.io/badge/share%20on-reddit-red?logo=reddit&style=for-the-badge)](https://reddit.com/submit?url=https://github.com/QuestPDF/QuestPDF&title=Check%20out%20QuestPDF%20%F0%9F%8E%8A%20a%20modern%20open-source%20.NET%20library%20%20for%20PDF%20document%20generation%20%F0%9F%9A%80)
[![Share on Twitter](https://img.shields.io/badge/share%20on-twitter-03A9F4?logo=twitter&style=for-the-badge)](https://twitter.com/share?url=https://github.com/QuestPDF/QuestPDF&text=Check%20out%20QuestPDF%20%F0%9F%8E%8A%20a%20modern%20open-source%20.NET%20library%20%20for%20PDF%20document%20generation%20%F0%9F%9A%80%20%23dotnet%20%23csharp%20%23questpdf)
[![Share on HackerNews](https://img.shields.io/badge/share%20on-hacker%20news-orange?logo=ycombinator&style=for-the-badge)](https://news.ycombinator.com/submitlink?u=https://github.com/QuestPDF/QuestPDF&t=QuestPDF%20-%20a%20modern%20open-source%20.NET%20library%20%20for%20PDF%20document%20generation)
[![Share on Facebook](https://img.shields.io/badge/share%20on-facebook-1976D2?logo=facebook&style=for-the-badge)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/QuestPDF/QuestPDF)
<br />
## Let's get started
Begin exploring the QuestPDF library today. You are 250 lines of C# code away from creating a fully functional PDF invoice implementation.
Read the Getting Started tutorial to familiarize yourself with general library architecture, important layout structures as well as to better understand helpful patterns and practices. Easily start designing your PDF documents, reports, invoices and even more.
[![Getting started tutorial](https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue?style=for-the-badge)](https://www.questpdf.com/getting-started.html)
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/invoice-small.png?raw=true" width="400px">
<br />
## QuestPDF on JetBrains OSS Power-Ups
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">
</a>
[![YouTube video about QuestPDF]( https://img.shields.io/badge/watch%20on-YouTube-red?logo=youtube&style=for-the-badge)](https://www.youtube.com/watch?v=-iYvZvpLX0g)
-1
View File
@@ -1 +0,0 @@
<svg height="180" viewBox="0 0 180 180" width="180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="32.64" x2="82.77" y1="61.16" y2="85.54"><stop offset=".21" stop-color="#fe2857"/><stop offset="1" stop-color="#293896"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="17.38" x2="82.95" y1="69.86" y2="21.23"><stop offset="0" stop-color="#fe2857"/><stop offset=".01" stop-color="#fe2857"/><stop offset=".86" stop-color="#ff318c"/></linearGradient><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="74.17" x2="160.27" y1="21.58" y2="99.76"><stop offset=".02" stop-color="#ff318c"/><stop offset=".21" stop-color="#fe2857"/><stop offset=".86" stop-color="#fdb60d"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="155.46" x2="55.07" y1="89.8" y2="158.9"><stop offset=".01" stop-color="#fdb60d"/><stop offset=".86" stop-color="#fcf84a"/></linearGradient><path d="m81.56 83.71-41.35-35a15 15 0 1 0 -14.47 25.7h.15l.39.12 52.16 15.89a3.53 3.53 0 0 0 1.18.21 3.73 3.73 0 0 0 1.93-6.91z" fill="url(#a)"/><path d="m89.85 25.93a10.89 10.89 0 0 0 -16.85-9.18l-50.5 30.66a15 15 0 1 0 17.9 24l45.27-36.89.36-.3a10.93 10.93 0 0 0 3.82-8.29z" fill="url(#b)"/><path d="m163.29 92-76.62-73.79a10.91 10.91 0 1 0 -14.81 16l.14.12 81.4 68.58a7.36 7.36 0 0 0 12.09-5.65 7.39 7.39 0 0 0 -2.2-5.26z" fill="url(#c)"/><path d="m165.5 97.29a7.35 7.35 0 0 0 -11.67-6l-92.71 45.3a15 15 0 1 0 15.48 25.59l85.73-58.84a7.35 7.35 0 0 0 3.17-6.05z" fill="url(#d)"/><path d="m60 60h60v60h-60z"/><g fill="#fff"><path d="m66.53 108.75h22.5v3.75h-22.5z"/><path d="m65.59 75.47 1.67-1.58a1.88 1.88 0 0 0 1.47.87c.64 0 1.06-.45 1.06-1.32v-5.92h2.58v5.94a3.44 3.44 0 0 1 -.92 2.63 3.52 3.52 0 0 1 -2.57 1 3.84 3.84 0 0 1 -3.29-1.62z"/><path d="m73.53 67.52h7.53v2.19h-5v1.43h4.49v2h-4.45v1.49h5v2.2h-7.6z"/><path d="m84.73 69.79h-2.8v-2.27h8.21v2.27h-2.81v7.09h-2.6z"/><path d="m66.63 80.58h4.42a3.47 3.47 0 0 1 2.55.83 2.09 2.09 0 0 1 .61 1.52 2.18 2.18 0 0 1 -1.45 2.09 2.27 2.27 0 0 1 1.86 2.29c0 1.69-1.31 2.69-3.55 2.69h-4.44zm5 2.89c0-.52-.42-.8-1.18-.8h-1.29v1.64h1.25c.78 0 1.24-.27 1.24-.81zm-.9 2.66h-1.57v1.73h1.62c.8 0 1.24-.31 1.24-.86-.02-.53-.4-.87-1.27-.87z"/><path d="m75.45 80.58h4.15a4.14 4.14 0 0 1 3.05 1 2.92 2.92 0 0 1 .83 2.18 3 3 0 0 1 -1.93 2.89l2.24 3.35h-3l-1.89-2.84h-.87v2.84h-2.6zm4 4.5c.87 0 1.4-.43 1.4-1.12 0-.75-.55-1.13-1.41-1.13h-1.39v2.27z"/><path d="m87.09 80.51h2.5l4 9.44h-2.79l-.67-1.69h-3.63l-.67 1.74h-2.71zm2.28 5.73-1.05-2.65-1.06 2.65z"/><path d="m94 80.55h2.6v9.37h-2.6z"/><path d="m97.56 80.55h2.44l3.37 5v-5h2.57v9.37h-2.27l-3.53-5.14v5.14h-2.58z"/><path d="m106.37 88.53 1.44-1.73a4.86 4.86 0 0 0 3 1.13c.71 0 1.08-.25 1.08-.65 0-.41-.3-.61-1.59-.91-2-.46-3.53-1-3.53-2.93 0-1.74 1.38-3 3.63-3a5.88 5.88 0 0 1 3.85 1.25l-1.25 1.78a4.56 4.56 0 0 0 -2.62-.92c-.63 0-.94.25-.94.6 0 .43.32.62 1.63.91 2.15.47 3.48 1.17 3.48 2.92 0 1.91-1.51 3-3.78 3a6.56 6.56 0 0 1 -4.4-1.45z"/></g><path d="m0 0h180v180h-180z" fill="none"/></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

@@ -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");
});
}
}
}
@@ -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);
}
});
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Elements;
@@ -49,15 +50,15 @@ namespace QuestPDF.Examples
{
container.MinimalBox().Decoration(decoration =>
{
decoration.Header().Element(header);
decoration.Before().Element(header);
decoration.Content().Box().Stack(stack =>
decoration.Content().Column(column =>
{
foreach (var row in rows)
stack.Item().Element(row.Element);
column.Item().Element(row.Element);
});
decoration.Footer().Element(footer);
decoration.After().Element(footer);
});
});
@@ -82,15 +83,14 @@ namespace QuestPDF.Examples
.BorderBottom(1)
.BorderColor(Colors.Grey.Darken2)
.Padding(5)
.DefaultTextStyle(TextStyle.Default.SemiBold())
.Row(row =>
{
var textStyle = TextStyle.Default.SemiBold();
row.ConstantItem(30).Text("#", textStyle);
row.RelativeItem().Text("Item name", textStyle);
row.ConstantItem(50).AlignRight().Text("Count", textStyle);
row.ConstantItem(50).AlignRight().Text("Price", textStyle);
row.ConstantItem(50).AlignRight().Text("Total", textStyle);
row.ConstantItem(30).Text("#");
row.RelativeItem().Text("Item name");
row.ConstantItem(50).AlignRight().Text("Count");
row.ConstantItem(50).AlignRight().Text("Price");
row.ConstantItem(50).AlignRight().Text("Total");
});
});
}
@@ -105,7 +105,8 @@ namespace QuestPDF.Examples
.Width(context.AvailableSize.Width)
.Padding(5)
.AlignRight()
.Text($"Subtotal: {total}$", TextStyle.Default.Size(14).SemiBold());
.DefaultTextStyle(TextStyle.Default.FontSize(14).SemiBold())
.Text($"Subtotal: {total}$");
});
}
@@ -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}$");
});
@@ -164,7 +165,7 @@ namespace QuestPDF.Examples
.Decoration(decoration =>
{
decoration
.Header()
.Before()
.PaddingBottom(5)
.Text(text =>
{
@@ -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}$");
@@ -1,10 +1,12 @@
using System;
using System.IO;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
@@ -64,42 +66,24 @@ namespace QuestPDF.Examples
});
}
[Test]
public void ReusingTheSameImageFileShouldBePossible()
public void ImageResolutionScaling()
{
var fileName = Path.GetTempFileName() + ".jpg";
var image = Image.FromFile("large-image.jpg");
try
{
var image = Placeholders.Image(300, 100);
using var file = File.Create(fileName);
file.Write(image);
file.Dispose();
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.ShowResults()
.Render(container =>
Document
.Create(document =>
{
document.Page(page =>
{
container
.Padding(20)
.Column(column =>
{
column.Spacing(20);
column.Item().Image(fileName);
column.Item().Image(fileName);
column.Item().Image(fileName);
});
page.Size(210, 210);
page.Margin(50);
page.Content().Image(image);
});
}
finally
{
File.Delete(fileName);
}
})
.GeneratePdf($"test.pdf");
}
}
}
@@ -120,5 +120,42 @@ namespace QuestPDF.Examples
.Background(Placeholders.BackgroundColor());
}
}
[Test]
public void RepeatingInlinedInHeader_Test()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(document =>
{
document.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(1, Unit.Inch);
page.PageColor(Colors.White);
page.Header()
.Inlined(inlined =>
{
inlined.Spacing(10);
foreach (var i in Enumerable.Range(5, 5))
inlined.Item().Width(i * 10).Height(20).Background(Colors.Red.Medium);
});
page.Content()
.PaddingVertical(20)
.Column(column =>
{
column.Spacing(25);
foreach (var i in Enumerable.Range(10, 20))
column.Item().Width(i * 10).Height(50).Background(Colors.Grey.Lighten2);
});
});
});
}
}
}
@@ -19,10 +19,12 @@ namespace QuestPDF.Examples
{
var url = "https://picsum.photos/300/200";
if(Greyscale)
if (Greyscale)
url += "?grayscale";
using var client = new WebClient();
client.Headers.Add("user-agent", "QuestPDF/1.0 Unit Testing");
var response = client.DownloadData(url);
container.Image(response);
}
@@ -59,4 +61,4 @@ namespace QuestPDF.Examples
});
}
}
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
@@ -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>
@@ -32,6 +32,12 @@
<None Update="pdf-icon.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="large-image.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="large-image.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -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;
@@ -299,17 +300,18 @@ namespace QuestPDF.Examples
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().RowSpan(4).Element(Block).Text("1");
var image = Placeholders.Image(400, 300);
table.Cell().RowSpan(2).Element(Block).Text("2");
table.Cell().RowSpan(1).Element(Block).Text("3");
table.Cell().RowSpan(1).Element(Block).Text("4");
table.Cell().RowSpan(2).Element(Block).Text("5");
table.Cell().RowSpan(1).Element(Block).Text("6");
table.Cell().RowSpan(1).Element(Block).Text("7");
table.Cell().Image(image);
table.Cell().Image(image);
table.Cell().Image(image);
table.Cell().Image(image);
table.Cell().Image(image);
table.Cell().Image(image);
});
});
}
@@ -389,12 +391,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);
}
@@ -7,6 +7,7 @@ using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Examples
{
@@ -122,7 +123,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 +649,7 @@ namespace QuestPDF.Examples
page.Content().Column(column =>
{
column.Item().Text(null);
column.Item().Text((string) null);
column.Item().Text(text =>
{
@@ -555,7 +681,7 @@ namespace QuestPDF.Examples
page.Content().Column(column =>
{
column.Item().Text(null);
column.Item().Text((string) null);
column.Item().Text(text =>
{
@@ -691,7 +817,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 +872,137 @@ 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);
});
});
});
}
[Test]
public void DetectSpanPositionExample()
{
RenderingTest
.Create()
.PageSize(new PageSize(650, 800))
.ProduceImages()
.ShowResults()
.Render(container =>
{
var fontSize = 20;
var paint = new SKPaint
{
Color = SKColors.Red,
TextSize = fontSize
};
var fontMetrics = paint.FontMetrics;
var start = 0f;
var end = 0f;
// corner case: what if text is paged? clamp start and end?
container
.Padding(25)
.DefaultTextStyle(x => x.FontSize(fontSize).FontFamily("Calibri"))
.Layers(layers =>
{
layers.PrimaryLayer().Text(text =>
{
text.Span(Placeholders.Paragraph());
text.Span(" - ");
// record start
text.Element().Width(0).Height(0)
.Canvas((canvas, size) => start = canvas.TotalMatrix.TransY / canvas.TotalMatrix.ScaleY);
text.Span(Placeholders.LoremIpsum()).BackgroundColor(Colors.Red.Lighten4);
// record end
text.Element().Width(0).Height(0)
.Canvas((canvas, size) => end = canvas.TotalMatrix.TransY / canvas.TotalMatrix.ScaleY);
text.Span(" - ");
text.Span(Placeholders.Paragraph());
});
layers.Layer().Canvas((canvas, size) =>
{
canvas.Save();
canvas.Translate(-canvas.TotalMatrix.TransX / canvas.TotalMatrix.ScaleX, -canvas.TotalMatrix.TransY / canvas.TotalMatrix.ScaleY);
canvas.DrawRect(10, start + fontMetrics.Ascent, 5, end - start + (fontMetrics.Bottom - fontMetrics.Ascent), paint);
canvas.Restore();
});
});
});
}
[Test]
public void InconsistentLineHeightWhenUsingNewLineTest()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Background(Colors.Grey.Lighten4)
.Text(text =>
{
text.DefaultTextStyle(x => x.FontSize(16));
text.Line(Placeholders.Paragraph());
text.Line("");
text.Line(Placeholders.Paragraph());
text.Line(Placeholders.Label()).FontSize(48);
text.Line(Placeholders.Paragraph());
text.Line("");
text.Line(Placeholders.Paragraph());
});
});
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -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>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@@ -31,7 +31,7 @@ namespace QuestPDF.ReportSample
public static string FormatAsRomanNumeral(this int number)
{
if (number < 0 || number > 3999)
throw new ArgumentOutOfRangeException("Number should be in range from 1 to 3999");
throw new ArgumentOutOfRangeException(nameof(number), "Number should be in range from 1 to 3999");
return RomanNumeralCache.GetOrAdd(number, x =>
{
@@ -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>

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 993 B

After

Width:  |  Height:  |  Size: 993 B

@@ -38,20 +38,5 @@ namespace QuestPDF.ReportSample
Report.GenerateXps(path);
Process.Start("explorer.exe", path);
}
[Test]
public void Profile()
{
ImagePlaceholder.Solid = true;
var container = new DocumentContainer();
Report.Compose(container);
var content = container.Compose();
var pageContext = new PageContext();
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, null);
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, null);
}
}
}
@@ -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();
}
}
}

Some files were not shown because too many files have changed in this diff Show More