GithubHelp home page GithubHelp logo

questpdf / questpdf Goto Github PK

View Code? Open in Web Editor NEW
10.5K 87.0 534.0 172.13 MB

QuestPDF is a modern open-source .NET library for PDF document generation. Offering comprehensive layout engine powered by concise and discoverable C# Fluent API. Easily generate PDF reports, invoices, exports, etc.

Home Page: https://www.questpdf.com

License: Other

C# 100.00% Smalltalk 0.01%
pdf csharp nuget generate dotnet create creation reporting report tool

questpdf's Introduction


QuestPDF Homepage Dotnet GitHub Repo stars Nuget version Nuget download QuestPDF License


QuestPDF is a modern open-source .NET library for PDF document generation. Offering comprehensive layout engine powered by concise and discoverable C# Fluent API.

๐Ÿ‘จโ€๐Ÿ’ป Design PDF documents using C# and employ a code-only approach. Utilize your version control system to its fullest potential.
๐Ÿงฑ Compose PDF document with a range of powerful and predictable structural elements, such as text, image, border, table, and many more.
โš™๏ธ Utilize a comprehensive layout engine, specifically designed for PDF document generation and paging support.
๐Ÿ“– Write code using concise and easy-to-understand C# Fluent API. Utilize IntelliSense to quickly discover available options.
๐Ÿ”— Don't be limited to any proprietary scripting language or format. Follow your experience and leverage all modern C# features.
โŒ› Save time thanks to a hot-reload capability, allowing real-time PDF document preview without code recompilation.


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! โญ

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 Share on Twitter Share on HackerNews Share on Facebook


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


Library License

We identify the importance of the library in your projects, so we want to ensure you can safely and confidently continue the development.

Being a healthy and growing community is the primary goal that motivates us to pursue professionalism.

The library is available for free to the vast majority of users. However, please look at the QuestPDF License and Pricing webpage for more details:

Library license details


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!

YouTube video about QuestPDF

questpdf's People

Contributors

antonycorbett avatar avobelk avatar bennetbo avatar ceee avatar collinalpert avatar danielchalmers avatar donmurta avatar emanueleguastella avatar fredericoregateiro avatar jcl-aadlab avatar jnyrup avatar knoxyz avatar lehonti avatar lmingle avatar loxsmoke avatar maartenba avatar marcinziabek avatar marcmognol avatar rstm-sf avatar ruyut avatar schulz3000 avatar sclarke81 avatar simusr2 avatar svizelpritula avatar thomasstevens89 avatar tinohager avatar warrantyvoids avatar wieslawsoltes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

questpdf's Issues

Support rounded corners on Border

Is your feature request related to a problem? Please describe.
I'd like to make a border around an element similar to css border-radius

QuestPDF.Infrastructure.IDocumentContainer is empty

I added the nuget package to my project.

I followed the tutorial to implement IDocument with Compose() and GetMetadata().

Intellisense shows no methods available on IDocumentContainer except for the default .net object methods. If I inspect the interface for IDocumentContainer, its an empty interface.

I've never run into this problem before when bringing in another Nuget lib.

How to keep items "together" and not break them appart?

Currently I have something like this which I append as the last content

private void ComposeSupport(IContainer container)
{
    container.PaddingTop(PADDING_TOP).Stack(stack =>
    {
        stack.Item().Text("Support Headline", _headerStyle);
        stack.Item().Text("Support text and other info");
    });
}

Which works fine, however, sometimes the content before is large enough to put the support headline on page 1 and the support text on page two.

Is there a way keep the container in the same page? Example, if everything won't fit on page 1, it will then instead be on page 2.

Container of inlined elements increase height on HorizontalSpacing > 0

Describe the bug
Container of inlined elements increase height on HorizontalSpacing > 0.

I build a 6 month scheduler with a typical 'day' container of 40mm x 5mm. In that Container I inserted image elements with the inlined container. Works fine until I wanted some horizontal spacing and therefore added the .HorizontalSpacing(50). After that the containing day element increases its height, depending on the count of images I inserted. But all images fit in and were displayed all in one Line.

** probably wrong **
I took a look in the well readable sources at ./Elements/Text/Inlined.cs and found line 67 which looks suspicious to me:
var heightWithSpacing = size.Height + (line.Count - 1) * HorizontalSpacing
Semanticaly a horizontal length is probably wrong in an height, which is an vertical measurement.

Expected behavior
Increase height only when the inlined items wrap line.

Additional context
Since this is only a short look in the code I coul'd not provide a detailled analysis and a solving pull request yet.

The Row element should offer a spacing property

Is your feature request related to a problem? Please describe.
This feature is related to development productivity. Technically, existing tools already provide a way to achieve such behaviour.

Describe the solution you'd like
The Stack element offers a spacing property that describes padding between elements. Similarly, the Row element should provide a spacing property to describe padding between columns. This seems to be a pretty common scenario when building tables and grids.

Additional context
Example code:

container.Row(section =>
{
    section.Spacing(10);

    section.ConstantColumn(100).Height(100).Background("#EEE");
    section.RelativeColumn().Height(100).Background("#DDD");
}

Using Bold / Italic with a Custom Font

Describe the bug
When using a custom font, the bold and italic methods don't render the text as bold or italic.
The font renders correctly from the file but without slant from italic and at the default font weight.

To Reproduce

private void ComposeContent(IContainer container)
{
    {
        var defaultStyle = TextStyle.Default.Bold().Italic();

        using FileStream stream = 
        File.OpenRead(@"Path\To\Custom\Font.ttf");
        FontManager.RegisterFontType("CustomFont", stream);

        TextStyle textStyle = TextStyle.Default.FontType("CustomFont").Bold().Italic();

        container.Text(text =>
        {
            text.Line("Issue date: ", textStyle);
            text.Line("Issue date: ", defaultStyle);
        });
    }

Expected behavior
Calling .Bold() / .Italic() on a custom font should render the text with the slant and weight assigned.

Screenshots
image

Doc Improvement: Supported Image Formats

From the API doc:
image

It shows loading raw byte[], so one would expect a more detailed explanation about where image support is derived from. Is it all image formats supported by Adobe PDF? Is the raw byte[] getting written directly into the PDF file without transformations?

Document how to use QuestPDF in WebAssembly

If found QuestPDF just recently and I think it's awesome.

I saw that the native libraries for WASM are available as a preview, so I thought on testing if QuestPDF would work in a Blazor WebAssembly app.

It worked without problems, creating the files 100% inside the browser. The only gotcha is that you can't embed a font as the sandbox would not allow it.

So, this is not really an issue. I would like to know how can I help to document what you need to do to use it in Wasm.

A sample would help, more documentation on the subject?

NullReferenceException in the text element

Describe the bug
When the .Text() element receives null as an argument, it throws the NullReferenceException.

Expected behavior
The Text element should treat null arguments as no text / empty string.

Arabic Language Support ?

Is your feature request related to a problem? Please describe.
I am creating a report that displays some words in Arabic however the Arabic words are not being displayed properly

Describe the solution you'd like
Arabic (or languages that are not English) support would be great

Describe alternatives you've considered
I will be using https://github.com/webgio/Rotativa.AspNetCore for my reports instead for now

Additional context
this is a screenshot of some Arabic words in the report, it's shouldn't look like that
image

The type initializer for 'SkiaSharp.SKAbstractManagedWStream' threw an exception.

Describe the bug
When attempting to generate a PDF, the exception in the title is thrown.

To Reproduce

var document = new MyDocument();
document.GeneratePdf("mypath.pdf");

Expected behavior
The PDF document should get generated and written to the disk.

Screenshots
N/A

Additional context
The exact models used that generated this bug:

    public class ReportDataSource
    {
        private static Random _random = new Random();
        
        public static ReportModel GetReportDetails()
        {
            var profiles = Enumerable
                .Range(1, 8)
                .Select(i => GenerateRandomProfile())
                .ToList();

            return new ReportModel
            {
                CustomerName = Placeholders.Name(),
                ProfileEntries = profiles,
                Timestamp = DateTime.UtcNow
            };
        }

        private static ProfileEntry GenerateRandomProfile()
        {
            return new ProfileEntry
            {
                Name = Placeholders.Label(),
                Quantity = _random.Next(1, 100)
            };
        }
    }
    public class ReportDocument : IDocument
    {
        public ReportModel Model { get; }

        public ReportDocument(ReportModel model)
        {
            Model = model;
        }

        public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
        
        public void Compose(IDocumentContainer container)
        {
            container
                .Page(page =>
                {
                    page.Margin(50);

                    page.Header().Element(ComposeHeader);
                    page.Content().Element(ComposeContent);
                    page.Footer().AlignCenter().Text(x =>
                    {
                        x.CurrentPageNumber();
                        x.Span(" / ");
                        x.TotalPages();
                    });
                });
        }

        private void ComposeHeader(IContainer container)
        {
            var titleStyle = TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium);
            
            container.Row(row =>
            {
                row.RelativeColumn().Stack(stack =>
                {
                    stack.Item().Text($"Report on {Model.Timestamp}", titleStyle);
                    
                    stack.Item().Text(text =>
                    {
                        text.Span(Model.CustomerName, TextStyle.Default.SemiBold());
                    });
                });
                
                row.ConstantColumn(100).Height(50).Placeholder();
            });
        }

        private void ComposeContent(IContainer container)
        {
            container.PaddingTop(10).Decoration(decoration =>
            {
                decoration.Header().BorderBottom(1).Padding(5).Row(row =>
                {
                    row.ConstantColumn(25).Text("#");
                    row.RelativeColumn(3).Text("Product");
                    row.RelativeColumn().AlignRight().Text("Quantity");
                });

                decoration
                    .Content()
                    .Stack(stack =>
                    {
                        foreach (var profile in Model.ProfileEntries)
                        {
                            stack.Item().BorderBottom(1).BorderColor(Colors.Grey.Lighten3).Padding(5).Row(row =>
                            {
                                row.ConstantColumn(25).Text(Model.ProfileEntries.IndexOf(profile) + 1);
                                row.RelativeColumn(3).Text(profile.Name);
                                row.RelativeColumn().AlignRight().Text(profile.Quantity.ToString());
                            });
                        }
                    });
            });
        }
    }
    public class ReportModel
    {
        public DateTime Timestamp { get; set; }
        
        public string CustomerName { get; set; }
        
        public List<ProfileEntry> ProfileEntries { get; set; }
    }

    public class ProfileEntry
    {
        public string Name { get; set; }
        
        public int Quantity { get; set; }
    }

Exact stack trace:

Unhandled exception. System.TypeInitializationException: The type initializer for 'SkiaSharp.SKAbstractManagedWStream' threw an exception.
 ---> System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibSkiaSharp: cannot open shared object file: No such file or directory
   at SkiaSharp.SkiaApi.sk_managedwstream_set_procs(SKManagedWStreamDelegates procs)
   at SkiaSharp.SKAbstractManagedWStream..cctor()
   --- End of inner exception stack trace ---
   at SkiaSharp.SKAbstractManagedWStream..ctor(Boolean owns)
   at SkiaSharp.SKManagedWStream..ctor(Stream managedStream, Boolean disposeManagedStream, Boolean owns)
   at SkiaSharp.SKManagedWStream..ctor(Stream managedStream, Boolean disposeManagedStream)
   at SkiaSharp.SKManagedWStream..ctor(Stream managedStream)
   at SkiaSharp.SKDocument.CreatePdf(Stream stream, SKDocumentPdfMetadata metadata)
   at QuestPDF.Drawing.PdfCanvas..ctor(Stream stream, DocumentMetadata documentMetadata)
   at QuestPDF.Drawing.DocumentGenerator.GeneratePdf(Stream stream, IDocument document)
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, Stream stream)
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, String filePath)
   at <my invoking code>

The preceding three classes are contained in a .NET Standard 2.1 class library, and the invoking code is in a .NET 5 app. It is also likely worth noting that this is occurring on Linux.

Ability to load fonts from file

Is your feature request related to a problem? Please describe.
I'm trying to deploy my code to an AWS lambda function. As such, I have limited control of the runtime, such as fonts. Apparently there are no fonts available. During render of the PDF, any text simply appears as blank.

Describe the solution you'd like
A way for me to be able to specify the font other than relying on SKTypeface.FromFamilyName

Describe alternatives you've considered
I'd have to work around this by adding a layer to the lambda, which will add some overhead and possibly impact cold start times.

Additional context
Originally, I was getting an error:

Unable to load shared library 'libSkiaSharp' or one of its dependencies.

I fixed that by adding a dependency on SkiaSharp.NativeAssets.Linux.NoDependencies. That ensures libSkiaSharp.so gets published.

Expected output:
image

Actual output:
image

Merge 3rd Party PDF

Is it possible to get a PDF from a 3rd party source and append it to the one I generate with QuestPDF using Quest PDF?

Text Alignment

Align the text so it will be straight in both left and right margin of the document
Is there a way to justify the text by adjusting the space between words so it would be justified?

column fit-content

Hi,

I'm trying to reproduce a report made with PDFMake, is there a way to do something like this with the library (see screenshot).

With a quick glance at the documentation,

If i do something like this :

stack.Item().BorderBottom(1).BorderColor("CCC").Padding(5).Row(row =>
{
    row.RelativeColumn().Text("blabla");
    row.RelativeColumn().AlignRight().Text("1000000000000000000000000000000000000");
    row.RelativeColumn().AlignRight().Text("hello");
    row.RelativeColumn().AlignRight().Text($"test");
    row.RelativeColumn().AlignRight().Text($"test");
    row.RelativeColumn().AlignRight().Text($"test");
    row.RelativeColumn().AlignRight().Text($"test");
    row.RelativeColumn().AlignRight().Text($"test");
});

Each column will still be the same width and the column with the big number will be displayed on 2 lines.

I would like each column to take the necessary space according to the content.
So in the example above, i would like the column with the big number to be super large so it can display the number on 1 line.

image

Printing Support

Our company is looking at upgrading some old software written in Visual Basic 6, they were using VSReport to generate invoices and other things that could easily be reworked into QuestPDF. However, we need the ability to send these to a printer so we can print them out automatically. Would it be possible to add support to QuestPDF to print the documents instead?

Null reference exception generating the PDF

Describe the bug
I'm trying to use QuestPDF in a midleaware framework. The idea behind this framework is that it could be completely abstract in a way that the final application will be able to create a PDF, a Word, whatever.
In my first tests with QuestPDF in a console application everything was fine but when I switched to the framework it starts returning a object null reference when tries to generate the PDF.

Client app calling my framework:

    `using (var stream = new MemoryStream())
        {
            IPdfGenerator generator = new QuestPdfGenerator(stream);
            generator.Generate(report);
            File.WriteAllBytes(fileName, stream.ToArray());
        }`

My framework trying to generate the PDF:

    `public void Generate(Report report)
    {
        var document = new DocumentComposer(report);
        document.GeneratePdf(_stream);
    }`

Note that the stream is created in the framework client application.
I don't know if I'm doing something wrong here.

Expected behavior
I was expecting that I was able to use stream byte array for whatever I want.

DocumentLayoutException: 'Composed layout generates infinite document.' is thrown when number of invoice detail items is 4967 or over in 'example-invoice' repository

I'm talking about line 14 in InvoiceDocumentDataSource.cs:

.Range(1, 25)

  • Pdf is generated when count is < 4967.
  • Exception is thrown when count is >= 4967
  • Exception is thrown intermittently when count is 4967, at least once out of 5 tries

Only change I made is to update QuestPDF to version 2021.10.1. I'm running visual studio 2019.

As you alluded to in Under consideration, it's difficult to debug without the generated document.

Thank you for writing this awesome library.

Allow rendering of glyphs directly

I'm currently working with QuestPDF to try and render PDFs that may contain multiple languages.

These documents are dynamically generated from external content outside of my control, so I won't know the characters/languages that are needed, but I have a good idea of what combinations of fonts can be used to provide the coverage I need.

That said, can use SkiaSharp to get the font information and provide fallbacks by querying for glyph support in the fonts that I have.

This means that I'll have different array of glyphs, each which I'd like to render using a different font.

What I'm looking for from QuestPDF is a way to be able to pass an array of ushort (or whatever you feel is appropriate) representing the glyphs of a font to render.

Something analogous to Text, like so:

// Obtained from Skiasharp
ushort[] glyphs = ...;

container.Glyphs(glyphs /*, optional TextStyle here */);

As well as an overload that takes a descriptor, like Text:

// Obtained from Skiasharp
ushort[] glyphs = ...;

container.Glyphs(g => g.Span(glyphs /*, optional TextStyle here */));

The font used would be whatever is specified in the TextStyle or the global font (i.e. normal font fallbacks are used although often, I would be specifying TextStyle to indicate the font to use).

I should also note that what would help in this process is to add overloads taking ReadOnlySpan<ushort> as we'll probably have an array of glyphs which we'll want to send subsections of without allocating new arrays (also, in general, overloads of Text which take ReadOnlySpan<char> would be great as well).

Infinite Layout -> allow to skip part of the content when no enough space (e.g. show only text that fits within the constrained container)

Hello

I'm upgraded from version 2021.9.3 to 2021.10.1. In this version, ocurs
"QuestPDF.Drawing.Exceptions.DocumentLayoutException: Composed layout generates infinite document"

In old version, works!

I understand that this is problem from my code, wich, exceeds the limit of some container, but, debug this is complex, because the architeture of code this library, in lambda expressions, don't permit debug point of bug.

Whats sugestions for locate the problem?

Thanks

Add Support for Table of Contents

Is your feature request related to a problem? Please describe.
Many real world reports contain a Table of Contents to support easy navigation between sections. For QuestPDF to be useful in generating these documents, there needs to be an easy way to generate a Table of Contents table.

Describe the solution you'd like
It would be ideal to add a new component for TableOfContents. Components to be shown in the Table of Contents could have a boolean property such as "VisibleInTableOfContents" set to "True".

Describe alternatives you've considered
I've reviewed the sample TableOfContentsTemplate.cs and though this provides navigation in the document, the page numbers in the output PDF document are incorrect. I have been unable to find a way to reference the page number that a specific component is located on.

PageSizes.Letter / PageSizes.Legal Incorrect?

Describe the bug
When using the PageSizes.Letter helper, Adobe Acrobat Reader shows that the page is not the correct size. It shows 8.50 x 10.99 in. US Letter paper is 8.5 x 11 in.

At 72 points per inch, it seems like letter should be defined as new PageSize(612f, 792f) (8.5 and 11 * 72, respectively), but it is currently coded as new PageSize(612.4f, 791f).

Similarly, PageSizes.Legal results in 8.50 x 14.01 in, instead of 8.50 x 14.00 in. That should probably be new PageSize(612f, 1008f).

To Reproduce

class MyDoc : IDocument
{
	public DocumentMetadata GetMetadata()
	{
		return DocumentMetadata.Default;
	}
	
	public void Compose(IDocumentContainer container)
	{
		container.Page(page =>
		{
			page.Size(PageSizes.Letter);
			page.Margin(50);
			page.Header().Height(100).Background(Colors.Grey.Lighten1);
			page.Content().Background(Colors.Grey.Lighten3);
			page.Footer().Height(50).Background(Colors.Grey.Lighten1);
		});
	}
}

Expected behavior
The page size should be 8.5 x 11 in when using the PageSizes.Letter helper, and 8.5 x 14 in when using PagesSizes.Legal.

Screenshots
n/a

Additional context
This is my first time trying out QuestPDF, so it's entirely possible that I did something wrong!

Get maximum height of content space

Hi.
This is not a bug it's more an "how to do it".
I'm using complex grids where I need to give an height to some of the cells. Question: How do I know the maximum value of height that I can use?
I can get the Pagesize (PageSizes.A4.Landscape().Height) but if I add an header and footer to the page this will not be my available content size.
Any tip to solve this?

Xamarin.Forms - FontFamily

Hi everyone,

I am trying to use the library on a mobile application (Xamarin.Forms) to run on iOS and Android Plataform.

Using the library I faced this problem:

image

So I tried this:

private static string _FontFamily = Xamarin.Forms.Device.RuntimePlatform == Xamarin.Forms.Device.iOS ? "Helvetica" :
                                            Xamarin.Forms.Device.RuntimePlatform == Xamarin.Forms.Device.Android ? "Droid Sans" :
                                            throw new NotImplementedException();

public void Compose(IDocumentContainer container)
{
	container
		.Page(page =>
		{
			page.Margin(50);
		   
			page.DefaultTextStyle(TextStyle.Default.FontType(_FontFamily));

			page.Header().Element(ComposeHeader);
			page.Content().Element(ComposeElement);
			page.Footer().Element(ComposeFooter);
		});
}

And I tried some fonts, but all I tried did not work, I got the same error: The typeface (font's name) could not be found...
Can someone please tell me any font that I can use?

Paging of Row element is confusing and badly documented

The paging of a Row element works relatively intuitively as long as exactly one Columnt overflows to the next page. In that case that Column gets split between multiple pages and all other Columns get duplicated.

Weirdness arises, however, when multiple Columns overflow. In that case all of them get repeated until all happen to end on the same page. This easly makes the document very long. For example, the following code:

using QuestPDF.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace Multicolumn;

public class Document : IDocument
{
    public DocumentMetadata GetMetadata() => DocumentMetadata.Default;

    public void Compose(IDocumentContainer container)
    {
        container
            .Page(page =>
            {
                page.Margin(50);
                page.Size(PageSizes.A4);

                page.Content().Row(row =>
                {
                    row.RelativeColumn().Element(c => CreateBoxes(c, 5));
                    row.RelativeColumn().Element(c => CreateBoxes(c, 6));
                    row.RelativeColumn().Element(c => CreateBoxes(c, 7));
                });
            });
    }

    void CreateBoxes(IContainer container, int count)
    {
        container.Stack(s =>
        {
            for (int i = 0; i < count; i++)
            {
                s.Item().Box()
                    .ExtendHorizontal().ExtendVertical()
                    .Background(Colors.Grey.Lighten3).Border(5)
                    .AlignCenter().AlignMiddle()
                    .Text(i);
            }
        });
    }
}

generates a 210 page PDF.

using QuestPDF.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace Multicolumn;

public class Document : IDocument
{
    public DocumentMetadata GetMetadata() => DocumentMetadata.Default;

    public void Compose(IDocumentContainer container)
    {
        container
            .Page(page =>
            {
                page.Margin(50);
                page.Size(PageSizes.A4);

                page.Content().Row(row =>
                {
                    row.RelativeColumn().Element(c =>
                    {
                        c.Stack(s =>
                        {
                            CreateBox(s.Item().ShowOnce(), "X");

                            CreateBoxes(s.Item(), 2);
                        });
                    });
                    row.RelativeColumn().Element(c => CreateBoxes(c, 2));
                });
            });
    }

    void CreateBoxes(IContainer container, int count)
    {
        container.Stack(s =>
        {
            for (int i = 0; i < count; i++)
            {
                CreateBox(s.Item(), i.ToString());
            }
        });
    }

    void CreateBox(IContainer container, string text)
    {
        container.Box()
            .ExtendHorizontal().ExtendVertical()
            .Background(Colors.Grey.Lighten3).Border(5)
            .AlignCenter().AlignMiddle()
            .Text(text);
    }
}

generates an infinite PDF.

Most of this can be avoided with ShowOnce, but I think it should be mentioned in the documentation.
I also think it might not be a bad idea not to repeat any column that overflows itself.

Is there a way to prevent wrapping?

I am using the new Table layout and have a column with sometimes longish text. Is there a way for the layout to truncate the text instead of wrap?

Throws exception when running in Windows Nano Server container

When running the example inside a Windows Nano Server container it throws TypeInitializationException, but it works inside Windows Server Core container.

When built using mcr.microsoft.com/dotnet/runtime:5.0 as the base image the following exception is thrown:

Unhandled exception. System.TypeInitializationException: The type initializer for 'SkiaSharp.SKAbstractManagedWStream' threw an exception.
 ---> System.DllNotFoundException: Unable to load DLL 'libSkiaSharp' or one of its dependencies: The specified module could not be found. (0x8007007E)
   at SkiaSharp.SkiaApi.sk_managedwstream_set_procs(SKManagedWStreamDelegates procs)
   at SkiaSharp.SKAbstractManagedWStream..cctor()
   --- End of inner exception stack trace ---
   at SkiaSharp.SKAbstractManagedWStream..ctor(Boolean owns)
   at SkiaSharp.SKManagedWStream..ctor(Stream managedStream, Boolean disposeManagedStream, Boolean owns)
   at SkiaSharp.SKManagedWStream..ctor(Stream managedStream, Boolean disposeManagedStream)
   at SkiaSharp.SKManagedWStream..ctor(Stream managedStream)
   at SkiaSharp.SKDocument.CreatePdf(Stream stream, SKDocumentPdfMetadata metadata)
   at QuestPDF.Drawing.PdfCanvas..ctor(Stream stream, DocumentMetadata documentMetadata) in C:\Work\QuestPDF\QuestPDF\Drawing\PdfCanvas.cs:line 13
   at QuestPDF.Drawing.DocumentGenerator.GeneratePdf(Stream stream, IDocument document) in C:\Work\QuestPDF\QuestPDF\Drawing\DocumentGenerator.cs:line 19
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, Stream stream) in C:\Work\QuestPDF\QuestPDF\Fluent\GenerateExtensions.cs:line 26
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, String filePath) in C:\Work\QuestPDF\QuestPDF\Fluent\GenerateExtensions.cs:line 21
   at QuestPDF.Examples.Engine.RenderingTest.Render(IDocument document) in C:\Work\QuestPDF\QuestPDF.Examples\Engine\RenderingTest.cs:line 109
   at QuestPDF.Examples.Engine.RenderingTest.RenderDocument(Action`1 content) in C:\Work\QuestPDF\QuestPDF.Examples\Engine\RenderingTest.cs:line 92
   at QuestPDF.Examples.Engine.RenderingTest.Render(Action`1 content) in C:\Work\QuestPDF\QuestPDF.Examples\Engine\RenderingTest.cs:line 72
   at QuestPDF.Examples.TextBenchmark.Generate() in C:\Work\QuestPDF\QuestPDF.Examples\TextBenchmark.cs:line 21

Same executables work when using mcr.microsoft.com/dotnet/runtime:5.0-windowsservercore-ltsc2019 as the base image.

A simple PDF is 1.5megabyte: 10x the size of what you'd expect. Is there a way to "trim" fonts?

It seems to be due to how SkiaSharp handles embedding the fonts. The default font Calibri is also quite large.
From what I understand, SkiaSharp embeds the entire font file.
The solution we currently use (Crystal Reports), does some kind of "subsetting" of the fonts...like trimming of unused objects.

Since we need to generate thousands of PDF's, a 10x increase in filesize is something I'd like to avoid this.
I quite like the programming model of this library plus the ability to generate pdf's in a linux docker environment, so I'm looking for ways around this:

  • Explicitly using a small font file. Which reduces the filesize to 200kb (arial narrow + arial narrow bold)

  • use iTextSharp to removed embedded fonts. This seems to work and reduces a file to 8kb. Don't really have a grasp on what the consequences might be of this option.

  • look into using iTextSharp to change the kind of embedding,

  • There doesn't seem to be a way to manipulate the embedding of fonts to this degree with SkiaSharp itself.

Anyone else have suggestions or ideas?

The code to unembed fonts with iTextSharp looks like this @ https://github.com/nxproject/itextsharp/blob/d2047adb4e7b1a3df9af05b9c57e530574f23d4e/src/extras/itextsharp.sandbox/iTextSharp/sandbox/fonts/UnembedFont.cs

       public void ManipulatePdf()
       {
           var fileStream = File.OpenRead(@"C:\Data\test.pdf");

           var reader = new PdfReader(fileStream);
           PdfObject obj;
           for (int i = 1; i < reader.XrefSize; i++)
           {
               obj = reader.GetPdfObject(i);
               // we skip all objects that aren't a dictionary
               if (obj == null || !obj.IsDictionary())
                   continue;
               // we process all dictionaries
               UnembedTTF(((PdfDictionary)obj));
           }
           reader.RemoveUnusedObjects();

           Stream os = new FileStream(@"C:\Data\test2.pdf", FileMode.Create);
           PdfStamper stamper = new PdfStamper(reader, os);
           stamper.Close();
       }

       public void UnembedTTF(PdfDictionary dict)
       {
           // we ignore all dictionaries that aren't font dictionaries
           if (!dict.IsFont())
               return;
           // we only remove TTF fonts
           if (dict.GetAsDict(PdfName.FONTFILE2) != null)
           {
               return;
           }
           // check if a subset was used (in which case we remove the prefix)
           PdfName baseFont = dict.GetAsName(PdfName.BASEFONT);
           if (baseFont.GetBytes()[7] == '+')
           {
               baseFont = new PdfName(baseFont.ToString().Substring(8));
               dict.Put(PdfName.BASEFONT, baseFont);
           }
           // we check if there's a font descriptor
           PdfDictionary fontDescriptor = dict.GetAsDict(PdfName.FONTDESCRIPTOR);
           if (fontDescriptor == null)
               return;
           // is there is, we replace the fontname and remove the font file
           fontDescriptor.Put(PdfName.FONTNAME, baseFont);
           fontDescriptor.Remove(PdfName.FONTFILE2);
       }

Expose FontManager internal properties/methods

Is your feature request related to a problem? Please describe.
I'm trying to achieve some effects on text rendering that are only possible through the use of SkCanvas. I also want to reuse the same styles with the rest of the file. I see that FontManager has some extremely useful utility methods to convert a TextStyle to SkPaint with custom typefaces lookup, but they are all internal. It would allow us to use the convenient high-level API with the low level SkPaint to customise text rendering if we have access to those methods.

Describe the solution you'd like
Exposing utility methods such as ToPaint() in FontManager.

Describe alternatives you've considered
Constructing SkPaint manually from an existing TextStyle, basically copying exactly what FontManager.ToPaint() method is doing, but without the ability to access internal properties of TextStyle.

Is there an example of streaming directly to the response stream in ASP.NET Core?

Version: 2021.10.1

First, I want to say this popped up on my radar because of the Reddit thread that was posted:

https://www.reddit.com/r/csharp/comments/ox3klz/questpdf_my_opensource_c_library_for_creating_pdf/

And it's exactly what I was looking for.

That said, I've generated a document, and can get the results by calling the GeneratePdf method to return a byte array or write to an instance of a MemoryStream.

However, in an attempt to save memory allocations, I'm looking to write directly to the result stream in ASP.NET Core.

When I do this:

// document is an IDocument implementation
document.GeneratePdf(HttpContext.Response.Body);

I get a NullReferenceException with the following stack trace:

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=QuestPDF
  StackTrace:
   at QuestPDF.Drawing.PdfCanvas.EndDocument()
   at QuestPDF.Drawing.DocumentGenerator.RenderPass[TCanvas](PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata)
   at QuestPDF.Drawing.DocumentGenerator.RenderDocument[TCanvas](TCanvas canvas, IDocument document)
   at QuestPDF.Drawing.DocumentGenerator.GeneratePdf(Stream stream, IDocument document)
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, Stream stream)

I've looked in the closed issues and seen that there was an issue at one point around closing the stream, but it's been resolved.

Is there anything special that needs to be done when writing directly to the response stream in ASP.NET Core?

As mentioned, when making the following calls instead:

// Render to bytes
var result = document.GeneratePdf();

// Render to a stream.
document.GeneratePdf(new MemoryStream());

It does not throw.

Continuous Page / Bobine

is it possible to generate a continuous page, for to print in bobine paper?

I see wich is possible to informe PageSize, but, how to calculate the total height for to generate single page?

thanks.

Sorry for my bad english :)

Support for making a Grid cell spanning multiple rows/columns

Is your feature request related to a problem? Please describe.
After finding out about this awesome library, I tried to port an existing solution we have to generate reports in our company. We use Puppeteer to generate PDFs from HTMLs produced by a template engine. The HTML template heavily utilises CSS grid and grid-area to make a cell spanning multiple rows/columns. However, it seems like your library doesn't support this for Grid, or at least that is what I can see from reading the documentation.

Describe the solution you'd like
The ability to make a Grid's cell spanning multiple rows/columns.

Describe alternatives you've considered
An alternative that I can think of is to manually compose the cells without using Grid, but it would be very tedious.

Example project fails

Hi,

Running on Visual Studio 2019, Windows 11, all latest updates.

When running the example project it fails at document.GeneratePdf(filePath);

QuestPDF.Drawing.Exceptions.DocumentDrawingException
HResult=0x80131500
Message=An exception occured during document drawing.
Source=QuestPDF
StackTrace:
at QuestPDF.Drawing.DocumentGenerator.RenderPass[TCanvas](PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata, DebuggingState debuggingState)
at QuestPDF.Drawing.DocumentGenerator.RenderDocument[TCanvas](TCanvas canvas, IDocument document)
at QuestPDF.Drawing.DocumentGenerator.GeneratePdf(Stream stream, IDocument document)
at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, Stream stream)
at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, String filePath)
at QuestPDF.ExampleInvoice.Program.Main(String[] args) in C:\Users\ddebono\Source\repos\QuestPDF-ExampleInvoice-main\QuestPDF-ExampleInvoice-main\Program.cs:line 21

This exception was originally thrown at this call stack:
[External Code]

Inner Exception 1:
ArgumentNullException: Value cannot be null. (Parameter 'key')

CMYK Support

So I realize this is a deep problem. I'm hoping for a way to accomplish this with QuestPDF, or perhaps some direction on a different library I could use.

The workflow I'm doing is this:
Save CMYK document to SVG file (this is the problem!)
Use SKCanvas with Quest to draw the SVG into a PDF.

It works fantastic except SVG is NOT a CMYK format. I need proper CMYK colors in my PDF. Some reading on Skia says that Skia does support CMYK color space, so if I could somehow get the CMYK image into Skia, we're gold. Unfortunately, the only vector format I'm finding support for with Skia is SVG :(.

Does anyone know of a way to bring .EPS or .AI or .PDF files into Skia? Any of those would allow transport of CMYK format from application to Skia to PDF to printer.

Signing your Assemblies with a Strongname

It would be fine, if you would sign your assemblies with a strong name. All our assemblies are signed with a strong name and we cannot use your assemblies without signing it with our own certificate.

It would be a little breaking change because you have to recompile your solution. Maybe something for a new major version.

Bug on Resize Image

Hello,

I'm testing this library with images, but, in the somethimes cases, ocurrs crash in PdfCanvas.cs, at point: Canvas.Dispose()

    public override void EndDocument()
    {
        Canvas.Dispose();  <-- Canvas is null

        Document.Close();
        Document.Dispose();
    }

I build a little project for the simulate bug.
testpdf.zip

Thanks

Ability to have more then 1 Text object on a element.

Is your feature request related to a problem? Please describe.
We want to be able to change the style of single word, part of a sentence or any where in-between without messing up the flow if the sentence.

Describe the solution you'd like
The ability to stack Text objects so you can give different parts of sentence different stylings without interrupting the flow of the sentence.

Describe alternatives you've considered
Styling markup in a string. To allow for styling markdown like * to make a part bold or italic. Or a HTML to text convertor.

Additional context
Basically we want full control over how the text in a row/column/element looks so the generated PDF to the vision we have for the final product.

GeneratePdf should not close the stream

Is your feature request related to a problem? Please describe.
I want to generate the PDF into a memory stream. Which then, some abstractions later is processed somewhere else. When I try to seek the memory stream back to 0, the system complains that the stream is already closed (likely by GeneratePdf)

Describe the solution you'd like
A flag allowing to control, whether the stream is closed or just flushed

Describe alternatives you've considered
Well I have to directly hand in the stream where I want to write it. That is a bit reversing the layers.

Additional context
Thanks for the awesome work.

Ability to set Text Style on a higher level for child text elements

Is your feature request related to a problem? Please describe.
I want to be able to set a default text size for a document. At the moment if I want to set a bunch of items the same size on the page, I have to provide TextStyle to all the elements I use. For example:

private void ComposeContent(IContainer container)
{
	container.Stack(stack =>
	{
		stack.Item().Row(row =>
		{
			row.RelativeColumn().Text(text =>
			{
				text.Line(Model.CompanyAddress.CompanyName, TextStyle.Default.Bold());
				text.Line(Model.CompanyAddress.AddressOne);
				text.Line(Model.CompanyAddress.AddressTwo);
				text.Line(Model.CompanyAddress.Country);
				text.Line(Model.CompanyAddress.PostCode);
				text.EmptyLine();
				text.Line(Model.InvoiceAddress.CompanyName, TextStyle.Default.Bold());
				text.Line(Model.InvoiceAddress.AddressOne);
				text.Line(Model.InvoiceAddress.AddressTwo);
				text.Line(Model.InvoiceAddress.Country);
				text.Line(Model.InvoiceAddress.PostCode);
			});

			row.ConstantColumn(170)
				.Row(innerRow =>
				{
					innerRow
						.RelativeColumn()
						.Text(text =>
						{
							text.Line("Invoice Number");
							text.Line("Invoice Date");
						});

					innerRow
						.RelativeColumn()
						.Text(text =>
						{
							text.Line(Model.InvoiceNumber);
							text.Line(Model.InvoiceDate.ToString("dd/MM/yyyy"));
						});
				});
		});
	}
}

In this example, I would have to provide the text font to all the lines that are being used here which is very repetitive.

Describe the solution you'd like
I'd like to be able to set the text style for at either a page level or document level. Something like:

public void Compose(IDocumentContainer container)
{
	var text = TextStyle.Default.Size(20);
	container
                 //Document Level
		.TextStyle(TextStyle(TextStyle.Default.Size(12))
		.Page(page =>
		{
                        //Page Level
			page.TextStyle(TextStyle.Default.Size(12));
			page.MarginTop(25);
			page.MarginHorizontal(40);

			page.Header().Element(ComposeHeader);
			page.Content().Element(ComposeContent);
			page.Footer().Element(ComposeFooter);
		});
}

The Image element should provide better control over its size

Is your feature request related to a problem? Please describe.
I want to decide which scaling rule is used in order to display the Image element. Depending on the scenario, the image element should preserve its aspect ratio and fit available width/height/space. There are also cases when the image should be scaled unproportionally.

Currently, the Image element changes its size according to provided rules:

  1. It tries to fit the available space.
  2. It always prevents its aspect ratio.

Describe the solution you'd like
Provide a flag to decide how the image is going to be scaled:

  1. To fit the available width.
  2. To fit the available height.
  3. To fit the available space (both width and height = safest).
  4. To cover provided area without preserving its aspect ratio (also safe but usually not desired).

Additional context
Important: this change needs to be properly documented. There are cases when a selected option may lead to infinite layouts. For example:

// Infinite layout example:

.Width(400) // constrained size: width and height
.Height(200)
.Image(data, ImageScaling.FitWidth); // image width aspect ratio 4x3

// Example 2:
.Row(row => 
{
    row.ConstantColumn(200)
       .Height(300)
       .Image(data, ImageScaling.FitHeight); // image width aspect ratio 4x3
})

Support row columns taking some constant and and some relative space / Table algorithm

Is your feature request related to a problem? Please describe.
I'm in progress of adding QuestPDF as a PDF rendering backend for our reporing services. One of the requirement is to create complex tables. Our tables are defined with columns that have a relative width or a constant width. This is easy as pie with QuestPDF. However there are some merged cells which can span multiple columns. If these columns are all defined with constant width or all defined with relative width, I can merge them, so it is still doable.

However if a merged cell spans both constant and relative sized columns, I cannot set the width of it properly.

Describe the solution you'd like
The feature request is to be able to add a column to a Row with a constant width AND a relative width. For example:

row.MixedColumn(constantWidth: 30, relativeWidth: 3).Text("Product");

This column will take 30 pt + the calculated portion of the remaining space based on the relative width value.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.