Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge documents #367

Closed
wants to merge 4 commits into from
Closed

Merge documents #367

wants to merge 4 commits into from

Conversation

bennetbo
Copy link
Contributor

@bennetbo bennetbo commented Oct 9, 2022

This pull request makes it possible to combine multiple QuestPDF documents into a single one.
Motivation: Combining multiple reports into a single file without the usage of PDF merging tools like PDFSharp.

Code sample:

var doc1 = Document.Create(...);
var doc2 = Document.Create(...);
var doc3 = Document.Create(...);

Document
  .Merge(doc1, doc2, doc3)
  .WithMetadata(...)
  .GeneratePdf(...)

Furthermore you can specifiy how page numbers should be handled for a merged document. There are two modes:

Seperate (Default): Each document is treated as a 'single' one in terms of page numbering.
Continuous: All documents should be treated as a 'single' document in terms of page numbering.

E.g: Merged document that contains three documents, with a page count of 2, 3, 1

Position Seperate Continuous
Doc 1 Page 1 1/2 1/6
Doc 1 Page 2 2/2 2/6
Doc 2 Page 1 1/3 3/6
Doc 2 Page 2 2/3 4/6
Doc 2 Page 3 3/3 5/6
Doc 3 Page 1 1/1 6/6

Code sample:

Document
  .Merge(...)
  .SeperatePageNumbers() //or .ContinuousPageNumbers()
  .WithMetadata(...)
  .GeneratePdf(...)

See discussion #365 for futher info and motivation of this feature.

@MarcinZiabek MarcinZiabek added this to the 2022.12 milestone Oct 19, 2022
@belaszalontai
Copy link

Hi, this is a very good feature we are looking forward to.

May I have two questions?

  1. does this new feature guarantee that the embedded fonts will not be multiple embedded after merge?
  2. When is the december release?

thnx in advance.

@bennetbo
Copy link
Contributor Author

bennetbo commented Dec 5, 2022

Regarding question number one:
Yes, custom fonts will only be embedded once.

@MarcinZiabek
Copy link
Member

@belaszalontai Hi 😁 I am planning to release the December version somewhere this week. However, I cannot guarantee that this PR will be part of it. Let me explain...

@Bebo-Maker has planned and developed this great PR. I am highly thankful for his help. Honestly, this really makes it easier to move the library forward. However, once the library is becoming more and more popular, I need to pay more attention to the API design, especially in the context of future releases. After all, the library should be backwards compatible possibly with each consecutive release.

I am not saying that there is something wrong with this PR, I just haven't got time to look into it and analyze potential impacts. At this moment, QuestPDF is still my spare time project and my actual job has always higher priority.

That being said, the library becomes more and more demanding in terms of time. I am investigating potential possibilities so to be able to dedicate more time to its development, and help it flourish. Hopefully, sometime in January, I will be able to share more details 😁

@Bebo-Maker Let me thank you for all your hard work and apologize for this delay!

@MarcinZiabek
Copy link
Member

MarcinZiabek commented Dec 7, 2022

@Bebo-Maker So far, your implementation looks great, thank you 😁

I started to think about the two generation modes that this PR proposes:

  1. SeperatePageNumbers - a default one, makes the most sense. It truly resembles the "merging" operation.
  2. ContinuousPageNumbers - I don't fully understand the purpose of this option. Technically, this functionality can be already achieved using existing API, e.g. by calling multiple times the IDocumentContainer.Page() method. Do you think that this mode is actually useful? If so, maybe we need to create even more sophisticated solution, e.g. where both document page number and total number of pages in a merged content are available.

I am trying to discover and understand various use-cases, so to avoid eventual complexity.

@Bebo-Maker @markgould @belaszalontai @girlpunk What are your thoughts about it? Let's explore this domain together.

@girlpunk
Copy link

girlpunk commented Dec 7, 2022

ContinuousPageNumbers Has some use cases wherein existing code to produce an entire document can be reused. However upon further thought I think a "generate multiple documents in one PDF" feature would be enough to justify some refactoring of that existing code (i.e. of the program using QuestPDF).

I wonder if a simpler approach would be IDocumentContainer.ResetPageNumbers() or similar? Not that I'm against the current approach, just that that feels more inline with the fluent style used by QuestPDF.

@MarcinZiabek
Copy link
Member

MarcinZiabek commented Dec 7, 2022

@girlpunk Thank you for taking a look, highly appreciated 😁

ContinuousPageNumbers Has some use cases wherein existing code to produce an entire document can be reused. However upon further thought I think a "generate multiple documents in one PDF" feature would be enough to justify some refactoring of that existing code (i.e. of the program using QuestPDF).

Would you like please to elaborate more about those use cases?

Apart from explicit API, similar functionality can be achieved by just using the Column element or calling the IDocumentContainer.Page(page => { ... }) multiple times.

I wonder if a simpler approach would be IDocumentContainer.ResetPageNumbers() or similar? Not that I'm against the current approach, just that that feels more inline with the fluent style used by QuestPDF.

This is possible, yet slightly more difficult.

I feel that IDocumentContainer is about content description and its hierarchy, and the ContinuousPageNumbers option is about how this hierarchy is converted to the actual document. Like they belong ot separate domains and should not be merged.

@belaszalontai
Copy link

To understand why we are waiting for this merge PDF feature and consider ContinuousPageNumbers here is our use case:

We wanted to generate one document where there are multiple Users in header. There is of corse a dynamically varied content for every user and we have a footer, where a Sperated Page Number was a requirement. Due to the fact that the page rendering depends on the content size dinamically what I would like to render for each user, it is not possible to predict the total number of pages per user. For example The first page footer is 1/5 but if I change something on any size or padding or whatever, it will be 1/6. And if the content for the given user is bigger then it could be 1/9.
In other words: when footer is rendered for the actual page it is not possible to predict the total number of pages for the given user with dynamic content.
Long story short I needed to generate separated PDF documents in a loop for every Users to be able to display properly the curent page/total page in the footer.

image

In this picture above, after merging separate documents we need to keep the already rendered footers (separated page numbers) see red number 1, but we need a continuous paging for the new merged PDF document see red number 2.

@girlpunk
Copy link

girlpunk commented Dec 7, 2022

I think belaszalontai's example shows the use cases I was thinking fairly well. To further elaborate on the example, it would be common to include the user's name in the footer, thus having "page 1/9 for belaszalontai" or similar.

@MarcinZiabek
Copy link
Member

In the implementation below, you can nicely separate footer content for each section/user, while keeping the page number counter. This strongly resembles the ContinuousPageNumbers mode. The only thing, this is not as explicit.

Document
    .Create(document =>
    {
        document.Page(page =>
        {
            page.Content().Text("Content for user A");
            page.Footer().Text(text => text.CurrentPageNumber());
        });
        
        document.Page(page =>
        {
            page.Content().Text("Content for user B");
            page.Footer().Text(text => text.CurrentPageNumber());
        });
        
        document.Page(page =>
        {
            page.Content().Text("Content for user C");
            page.Footer().Text(text => text.CurrentPageNumber());
        });
    })
    .GeneratePdf("hello.pdf");

Technically, you can even simulate separate page counting for each section/user, that corresponds to the SeperatePageNumbers mode, yet not as convinient:

Document
    .Create(document =>
    {
        document.Page(page =>
        {
            page.Content().Section("Content-A").Text("Content for user A");
            page.Footer().Text(text => text.PageNumberWithinSection("Content-A"));
        });
        
        // other users with similar concept
    })
    .GeneratePdf("hello.pdf");

I agree that the @Bebo-Maker proposition is more explicit and more discoverable. I am just careful when introducing new APIs. Hey, I am just learning that it is always easy to introduce, but extremally difficult to remove, when it comes to libraries 😁

@belaszalontai
Copy link

@MarcinZiabek thank you so much for this suggested solution using Sections. It works for me without using multiple PDF generation and merge.
Now I can see that the API documentation contains reference for this:
Section can span multiple pages depending on how much content is inside.
here: https://www.questpdf.com/api-reference/section but I missed this until now.

thnx again..

@KHUZUM
Copy link

KHUZUM commented Feb 7, 2023

Hi
Providing a merge feature of already existing documents would be of great help for me. I would love to avoid to use any additional tools like bennetbo mentioned in his initial comment. Are there plans to merge this PR anytime soon?

Thank you in advance and best regards.

@bennetbo
Copy link
Contributor Author

@MarcinZiabek As @girlpunk already said, ContinuousPageNumbers is useful, if you have multiple Documents created already and want to merge them with continuous page numbers .

As you pointed out, this can already be achieved by using the existing API. It is not an absolutely necessary feature, nevertheless it is convenient.

PS: Sorry for the late reply, there has been a lot going on in the last few months.

@asolopovas
Copy link

hi when do you plan to get this merged?

@donkee
Copy link

donkee commented Mar 9, 2023

any update on this? would also love to be able to just loop through a directory that contains various images/pdfs and combine them all into one single pdf

@MarcinZiabek
Copy link
Member

@bennetbo Once again, thank you for implementing this feature 😁

Currently, I am working towards the QuestPDF license change to ensure library stability in the future (more details here). I am planning to publish it soon as the first or second 2023.X release, and therefore under the new license.

As the author of this PR, feel free to present you preference. If you want it to be available under the MIT license (in 2022.12.X release), please let me know. I value your opinion very highly 😊

@bennetbo
Copy link
Contributor Author

Feel free to release it for the next 2023.X release. You put tons of work into QuestPDF for free.
Im happy if this feature can help to cover some costs and support the library development.

@brockallen
Copy link

Hello -- I'm curious if this PR is still being considered?

@MarcinZiabek
Copy link
Member

MarcinZiabek commented May 16, 2023

Hello -- I'm curious if this PR is still being considered?

It is! 😁 Initially, I planned this feature as part of the 2023.5 release. However, the current implementation contains a bug related to Sections and SectionLinks (which must be unique across documents to work properly). I need to figure out how to support them first.

I assigned this feature to the 2023.6 release with high priority. I am very sorry for the delay. Roadmap

Finally, I see some hope for library development funding after switching to a dual-licensing model. I expect more frequent updates from now on 😁

(And once again, huge thanks to the author of this PR)

@MarcinZiabek MarcinZiabek modified the milestones: 2022.12, 2023.6 May 16, 2023
MarcinZiabek added a commit that referenced this pull request May 26, 2023
@MarcinZiabek MarcinZiabek mentioned this pull request May 26, 2023
@bennetbo
Copy link
Contributor Author

bennetbo commented Jun 9, 2023

Superseded by #572

@bennetbo bennetbo closed this Jun 9, 2023
MarcinZiabek added a commit that referenced this pull request Jun 28, 2023
* Transferred changes from another #367

* Update qodana.yml

* Update qodana.yml

* Simplified generated document structure

* Fixed: section links work correctly in merged documents

* Code refactoring

* Code refactoring

* Fixed rendering merged documents

* Merging documents: minor code adjustments
@MarcinZiabek
Copy link
Member

The Merge Documents API is now available in the 2023.6.0 release! 😁

Thanks to @bennetbo, who started this effort and completed most of it. Your help is great!

Also, I apologize to @bennetbo and everybody waiting for this feature. There were countless obstacles along the way that required my attention and focus. Thank you for your understanding and patience.

legao9 added a commit to legao9/PDF that referenced this pull request Jun 7, 2024
* Transferred changes from another QuestPDF/QuestPDF#367

* Update qodana.yml

* Update qodana.yml

* Simplified generated document structure

* Fixed: section links work correctly in merged documents

* Code refactoring

* Code refactoring

* Fixed rendering merged documents

* Merging documents: minor code adjustments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants