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

Mixing template/non-template content in a document? #216

Closed
gmischler opened this issue Sep 13, 2021 · 4 comments
Closed

Mixing template/non-template content in a document? #216

gmischler opened this issue Sep 13, 2021 · 4 comments
Labels

Comments

@gmischler
Copy link
Collaborator

If I'm not reading the documentation and code wrongly, then the template functionality is currently designed to support only one template per document. However, it would be quite practical if each page in a document could be treated with a different template, or even several templates could be overlayed on a single page.
Use cases for the former would typically be a title page, content pages, and eg. an index page.
The latter could be useful for combining templates for page headers, footers, etc., and then filling in the rest of the content by other means, all on one page.

I'm aware that I can write pages to individual files and combine them with pdfrw.
But it would be much more flexible to be able to do this in one place.

In case I have missed a good way to do this, then this would turn into a help request...
Otherwise, I may not have studied the code closely enough, but would it be possible to pass a "normal" page from FPDF() to a Template() for processing? This would require that Template() doesn't create a new FPDF instance, and that Template.render() doesn't create new pages, but just adds the items from the template to the existing page. In fact, I'd like to reverse the relationship, where currently Template() is using FPDF(), but I'd prefer it the other way round. If backwards compatibility is desired, that might turn out to be a bit of a challenge...

Does my idea even make sense to anyone? 😉

@Lucas-C
Copy link
Member

Lucas-C commented Sep 16, 2021

If I'm not reading the documentation and code wrongly, then the template functionality is currently designed to support only one template per document.

Indeed

it would be quite practical if each page in a document could be treated with a different template

Why that ?
The Template class can currently produce documents with multiple pages.

or even several templates could be overlayed on a single page.
Use cases for the former would typically be a title page, content pages, and eg. an index page.
The latter could be useful for combining templates for page headers, footers, etc.

The FPDF.header() & footer() methods do not match your needs?

For the index page, we currently have some basic support for this in FPDF:
https://pyfpdf.github.io/fpdf2/DocumentOutlineAndTableOfContents.html

I'm aware that I can write pages to individual files and combine them with pdfrw.
But it would be much more flexible to be able to do this in one place.

Agreed

would it be possible to pass a "normal" page from FPDF() to a Template() for processing?

Have you tried the following approach?

pdf = ... # Create you initial PDF

tmpl = Template(format="A4", elements=elements, title="Sample Invoice")
tmpl.pdf = pdf
tmpl.render()
... # perform other actions on the pdf object
pdf.output('doc.pdf')

If backwards compatibility is desired, that might turn out to be a bit of a challenge...

Yes, backward compatibility is important.

However, if you have a clear vision for an alternative implementation, I encourage you to go for it!
Feel free even to open a draft PR.

Backward compatibility can be adressed in several way,
just convince us that the new mechanism you have in mind
would be better for the users in the long term 😊

@gmischler
Copy link
Collaborator Author

The Template class can currently produce documents with multiple pages.

Multiple pages with a single template. I'm sure that's useful for many people, but not exactly what I was hoping for.

The FPDF.header() & footer() methods do not match your needs?

Those seem to be just a way to help organize your (client) code, and not in any way related to templating.

For the index page, we currently have some basic support for this in FPDF:
https://pyfpdf.github.io/fpdf2/DocumentOutlineAndTableOfContents.html

That is not an "index page", but machine readable index data elsewhere in the file, which can get accessed and displayed eg. by viewer programs.

would it be possible to pass a "normal" page from FPDF() to a Template() for processing?

Have you tried the following approach?

pdf = ... # Create you initial PDF

tmpl = Template(format="A4", elements=elements, title="Sample Invoice")
tmpl.pdf = pdf
tmpl.render()
... # perform other actions on the pdf object
pdf.output('doc.pdf')

tmpl.render() adds a new page to the pdf, which is not really helpful for my use case.

However, if you have a clear vision for an alternative implementation, I encourage you to go for it!
Feel free even to open a draft PR.

After I spent some time with the code, I now have some better ideas than before on how to proceed. More details about that shortly.

@gmischler
Copy link
Collaborator Author

gmischler commented Sep 22, 2021

My plan now is to mercylessly refactor template.py.

It will be split in two seperate classes, whith one of them, say TemplateCore(), handling all the template specific functionality, while the remaining Template() will handle management of FPDF() and its pages.

The purpose of TemplateCore() is to load template data, parse it, and apply it to a pdf page provided to it. It does not create any FPDF()s itself, or add new pages to it, it just adds content to them, possibly at an indicated offset on the page.

The "new" Template() class, probably a subclass of TemplateCore(), will handle FPDF()s as before, add pages as necessary, and then ask TemplateCore() to add the actual content to them. In its external API and functionality it will be exactly equivalent to the current version, and pass all the existing tests unchanged.

I'm not even sure if any changes to FPDF() will be necessary after that to realize the more flexible way of applying templates.
A user can just pass a FPDF() instance to TemplateCore(), which will then add the elements of the template definition to the current page. This can be done with a different template for each page, and with several templates after each other on the same page (eg. one for the header, the footer, or other standard elements each).

All the other methods of FPDF() will still be available to populate any non-templated areas of each page. Since the templates don't really do anything different than the user would otherwise do manually, there should be no issue in combining the two approaches as appropriate.

It's a bit of a pity that the special case of "single template per document" already occupies the generic class name Template(), but I guess it would be a hassle for existing users to change that. And if anyone can think of a better name than TemplateCore(), I'm all ears.

@Lucas-C
Copy link
Member

Lucas-C commented Sep 22, 2021

Sounds like an interesting plan!

I'd be curious to see how you are planning for the new TemplateCore class to be used, in Python code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants