MyGit

2022.11

QuestPDF/QuestPDF

版本发布时间: 2022-11-07 19:35:09

QuestPDF/QuestPDF最新发布版本:2024.3.2(2024-04-26 03:34:03)

QuestPDF offers a layout engine designed with 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.

To learn how easy it is to design documents with the library, let's quickly 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.PageColor(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:


Popularity milestone

Two major releases ago, I was excited to mention that QuestPDF has over 250 thousand downloads. Very little did I know that the next milestone will come so quickly! Three months have passed and now we are in the middle of to-the-million journey. Maybe somewhere in 2023? Something that was a dream, now sounds plausible. Thank you all for helping me in this process!

Please help by giving a star

⭐ Please consider giving a start to this repository. It takes seconds and helps thousands of developers! ⭐

What is content direction and why it is hard?

Most languages (such as English, German, Polish, etc.) are using the left-to-right content direction. However, there are languages (e.g. Arabic) that use the right-to-left content direction.

The right-to-left content direction significantly changes how the layout is planned:

  1. Text position (aligned to the right).
  2. Text direction where text starts on the right side and ends on the left side.
  3. Text word-wrapping algorithm that needs to take into account direction of text when breaking a line.
  4. Order of elements in collections, e.g. the first item in a row should be placed most to the right (in RTL) or to the left (int LTR).
  5. Default content position (aligned to the right).

Right-to-left content direction support

The new content-direction API introduces the possibility to set this feature locally to entire child content, or even override this setting:

.ContentFromRightToLeft()
.Column(column => 
{
    // this content uses inherited right-to-left content direction
    column.Item() // ... content
        
    // this item overrides the content direction to right-to-left    
    column.Item().ContentFromLeftToRight() // ... content     
});

The RTL mode is supported in all available elements. Let's quickly analyse a couple of examples to learn on how this mode infers the rendering process. In the Row element, the elements are displayed in accordance to the content direction. For example, the first element is put most to the left (in LTR mode) or most to the right (int RTL) mode:

.ContentFromRightToLeft() // LTR or RTL mode
.Row(row =>
{
    row.Spacing(5);
    
    row.AutoItem().Height(50).Width(50).Background(Colors.Red.Lighten1);
    row.AutoItem().Height(50).Width(50).Background(Colors.Green.Lighten1);
    row.AutoItem().Height(50).Width(75).Background(Colors.Blue.Lighten1);
});

It is important to notice that content direction does NOT impact element alignment. This is nicely shown when working with more advanced elements such as Inlined:

Global content direction

It is also possible to set a global content direction for the entire document. Of course, it is still possible to override content direction locally using one of the above methods. Or even to apply conditional content direction.

Let's analyse a simple example showing an invoice generated with Arabic language.

document.Page(page =>
{
    page.Size(PageSizes.A5);
    page.Margin(20);
    page.PageColor(Colors.White);
    
    page.DefaultTextStyle(x => x.FontFamily("Calibri").FontSize(20));
    page.ContentFromRightToLeft(); // this flag changes content direction to RTL in entire document
    
    page.Content().Column(column =>
    {
        column.Spacing(20);

        column.Item()
            .Text("مثال على الفاتورة") // example invoice
            .FontSize(32).FontColor(Colors.Blue.Darken2).SemiBold();
        
        column.Item().Table(table =>
        {
            table.ColumnsDefinition(columns =>
            {
                columns.RelativeColumn();
                columns.ConstantColumn(75);
                columns.ConstantColumn(100);
            });

            table.Cell().Element(HeaderStyle).Text("وصف السلعة"); // item description
            table.Cell().Element(HeaderStyle).Text("كمية"); // quantity
            table.Cell().Element(HeaderStyle).Text("سعر"); // price

            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);
        });
    });
});

Fixed word-wrapping algorithm for right-to-left languages

Previous versions of QuestPDF have introduced an advanced text shaping support. This allows to properly render text of more advanced languages, where characters in words are often combined together and displayed as single glyphs.

This release fixes the word-wrapping algorithm. This example renders the same text but the available width changes in each block:

var text = "في المعلوماتية أو الرياضيات، خوارزمية الترتيب هي خوارزمية تمكن من تنظيم مجموعة عناصر حسب ترتيب محدد.";
                    
container
    .Padding(25)
    .ContentFromRightToLeft()
    .Column(column =>
    {
        column.Spacing(20);
        
        foreach (var size in new[] { 36, 34, 32, 30, 15 })
        {
            column
                .Item()
                .ShowEntire()
                .MaxWidth(size * 25)
                .Background(Colors.Grey.Lighten3)
                .MinimalBox()
                .Background(Colors.Grey.Lighten2)
                .Text(text)
                .FontSize(20)
                .FontFamily("Segoe UI");
        }
    });

Forcing text direction

QuestPDF automatically detects text direction and applies proper text alignment. However, it is possible to override text direction.

TextStyle.Default.DirectionAuto() // default
TextStyle.Default.DirectionFromLeftToRight()
TextStyle.Default.DirectionFromRightToLeft()

This may be useful with more advanced corner cases:

.DefaultTextStyle(x => x.FontSize(24).FontFamily("Calibri"))
.Column(column =>
{
    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);
    });
});

相关地址:原始地址 下载(tar) 下载(zip)

查看:2022-11-07发行的版本