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

New feature: implement Pattern #790

Open
Lucas-C opened this issue May 21, 2023 · 14 comments
Open

New feature: implement Pattern #790

Lucas-C opened this issue May 21, 2023 · 14 comments

Comments

@Lucas-C
Copy link
Member

Lucas-C commented May 21, 2023

Quoting the 1.7 PDF spec:

When operators such as S (stroke), f (fill), and Tj (show text) paint an area of the
page with the current color, they ordinarily apply a single color that covers the
area uniformly. However, it is also possible to apply “paint” that consists of a re-
peating graphical figure or a smoothly varying color gradient instead of a simple
color. Such a repeating figure or smooth gradient is called a pattern.
Patterns come in two varieties:
• Tiling patterns consist of a small graphical figure (called a pattern cell) that is
replicated at fixed horizontal and vertical intervals to fill the area to be painted.
• Shading patterns define a gradient fill that produces a smooth transition
between colors across the area.

fpdf2 could support patterns to fill shapes.

tiling_patterns pdf

The developper who wishes to implement this feature can start by defining the end user API
that fpdf2 users will use to define and use those PDF Patterns, in Python, to style shapes.

A starting point could be to extend the RenderStyle enum to support Patterns: https://pyfpdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.RenderStyle

Some reference documents:


By implementing this feature you, as a benevolent FLOSS developper, will provide access to the large community of fpdf2 users to a useful PDF functionality.
As a contributor you will get review feedbacks from maintainers & other contributors, and learn about the lifecycle & structure of a Python library on the way.
You will also be added into the contributors list & map.

This issue can count as part of hacktoberfest

@elrobitaille
Copy link

Will try to help with this feature

@Lucas-C
Copy link
Member Author

Lucas-C commented Jul 23, 2023

Will try to help with this feature

Great! Do you have any questions? 😊

@boushrabnd
Copy link

boushrabnd commented Nov 15, 2023

Hello,

I was wondering if I could be assigned to this issue with my teammates to work on for the next three weeks for a university software engineering course (CMU17313) project. Is that possible?

Also, this link you provided gives a 404 error: https://pyfpdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.RenderStyle

Thank you.

@Lucas-C
Copy link
Member Author

Lucas-C commented Nov 15, 2023

I was wondering if I could be assigned to this issue with my teammates to work on for the next three weeks for a university software engineering course (CMU17313) project. Is that possible?

Sure! I just assigned it to you 🙂

Please ask all the questions you need on this subject and the process of contributing to fpdf2, and I'll be happy to answer you!

Also, this link you provided gives a 404 error: https://pyfpdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.RenderStyle

This page has moved there: https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.RenderStyle

@boushrabnd
Copy link

Thank you!

I have a quick question, I have been looking through the codebase, looking at the documentation and testing out py-pdf to try to understand how things work. I took a deep dive into understanding how pdfs are represented at a very low level and I understood that they are structured as series of objects like dictionaries, arrays and streams and that shapes specifically are defined using paths. Would you mind telling me which function or process takes care of inserting this path into the pdf? are you using a library (I am assuming the point py-pdf is to be that library lol ) or is this done manually?

I am trying to have a full grasp of understanding the process from beginning to end so that we can produce the best possible result when implementing patterns.

I am currently in the process of understanding the set_fill_color() function as I think it is the closest to what we are trying to do.

@Lucas-C
Copy link
Member Author

Lucas-C commented Nov 17, 2023

I have been looking through the codebase, looking at the documentation and testing out py-pdf to try to understand how things work. I took a deep dive into understanding how pdfs are represented at a very low level and I understood that they are structured as series of objects like dictionaries, arrays and streams and that shapes specifically are defined using paths.

Great! Good job investigating the code base 😊
Just a minor note: this project is named fpdf2.
It's part of the py-pdf GitHub organization, that also includes the pypdf project.
I know, it can be confusing 😅

Would you mind telling me which function or process takes care of inserting this path into the pdf?

Sure, I'll try.
So first, the fpdf.drawing module contains all the vector drawing logic.
Then, the .new_path() or .draw_path() methods can be used to render vector graphics in a PDF document.

For example, the rendering of SVG images is performed in FPDF._vector_image(): https://github.com/py-pdf/fpdf2/blob/2.7.6/fpdf/fpdf.py#L4007 - There are basically 3 steps:

  1. Parse the SVG document into a SVGObject instance object: https://github.com/py-pdf/fpdf2/blob/2.7.6/fpdf/fpdf.py#L4018
  2. "Project" this SVGObject onto the PDF document viewport to get a drawing.GraphicsContext: https://github.com/py-pdf/fpdf2/blob/2.7.6/fpdf/fpdf.py#L4065C26-L4065C52
  3. Call .draw_path() to render this path: https://github.com/py-pdf/fpdf2/blob/2.7.6/fpdf/fpdf.py#L4077

I am currently in the process of understanding the set_fill_color() function as I think it is the closest to what we are trying to do.

Regarding this, there is currently a PR open about this feature, and how it behaves with tables: #934 You can have a look at it and even give some feedback or ask questions if you want!

By the way, I'll be busy until Monday, but you can expect more answers from me in a couple of days 😊

@boushrabnd
Copy link

Thank you so much for the detailed answer, this is very helpful! I will do my best.

Good luck on your work!

@boushrabnd
Copy link

Hello again,

I have been trying to integrate example 2 provided in the 1.7 PDF spec (Page 184 of the pdf) into a blank pdf generated by fpdf to see how the end product would look like so that I can implement it in the same manner. However, it refuses to show up. Could this be because of the fact that the specification is old? Or is there something in fpdf preventing this from showing up?

@Lucas-C
Copy link
Member Author

Lucas-C commented Nov 26, 2023

However, it refuses to show up. Could this be because of the fact that the specification is old? Or is there something in fpdf preventing this from showing up?

You can check your PDF file using a PDF checker.
We use 3 different ones in our GitHub Actions pipeline:
https://github.com/py-pdf/fpdf2/blob/master/.github/workflows/continuous-integration-workflow.yml#L45

qpdf is especially simple to use: qpdf --check $file.pdf

You can also share the PDF document you generated in this thread, and I'll have a look at it.

@boushrabnd
Copy link

Hello Lucas,

I have made some good progress in trying to implement the Pattern Feature and now my teammates are going to complete it. I tried to make a pull request with my changes so that my teammates could work on that branch however I was denied permission. How do you think I should go about this?

Also, some feature findings:

  • Set_fill_color while helpful does not represent the complexity of which set_fill_pattern has to be. This is because set_fill_color aims to add one line describing the fill colour within the pdf object (shape, text etc.) in the final pdf script.
  • Set_font is more of a similar approach. This is because we have first to create a pattern object (multiple if needed for different objects) in which we describe the pattern and then we add a line setting the pattern to the object we want to fill with that pattern

I know these findings might be trivial but it took me a while to grasp how pdfs were represented (so I studied multiple pdfs generated by fpdf) but I put in my best and I am required to let my teammates continue the work as a part of our project.

Please let me know, thank you!

@Lucas-C
Copy link
Member Author

Lucas-C commented Dec 4, 2023

I tried to make a pull request with my changes so that my teammates could work on that branch however I was denied permission. How do you think I should go about this?

I cannot help you much without details on the error you faced 😅
However, one thing is certain : you cannot push directly a branch to this git repository. You will have to create a fork of this repository, then push a branch on this forked repository and finally submit a PR.

Regarding your other comments, I agree that the FPDF.set_fill_color() & FPDF.set_font() methods are relatively "basic" and do not cover all the features of the PDF format. There are 2 main reasons for this:

  • we want to provide a relatively simple interface to our users
  • the fpdf2 library used to be a lot more limited in what it could do, and we progressively added more features over time

Regarding the Pattern feature that you are working on, it simply means that some thought should be given on how to "expose" this functionality to fpdf2 users in the end.
Maybe you could share in comments your ideas about this? 😊
Do you suggest to add new methods, add new optional parameters to existing methods, or even another approach?

@boushrabnd
Copy link

I have made a PR with the subtasks we suggested. We are suggesting a new method using the same structure set_font() uses. On extra thing that tilling patterns require is specifying the cell to be repeated in the stream so we'll have to work on that and see is we want to have set options or allow that much customization from the user.

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 14, 2024

@boushrabnd started some work on this in PR #1041

However he was not able to finish it, so this issue is up-for-grabs! 🙂

Anyone is welcome to implement this.

@andersonhc
Copy link
Collaborator

I just merged #1334 which implements one type of PDF patterns—Shading. However, Tiling patterns are still not implemented.

If you’d like to contribute Tiling patterns, please first familiarize yourself with the relevant sections of the PDF specification. Then take a look at:

  • The new file pattern.py, where Shading patterns are defined
  • The use_pattern() context manager in fpdf.py

These should give you a solid starting point for how patterns integrate into the library.

To implement Tiling patterns, you’ll need to create pattern cells content streams similar to page contents, but stored in a separate PDF object. A potential approach is to use a context manager that, while active, redirects all drawing, text, and images to the pattern cell instead of the current page. You’ll also need keep track of the named resources used to produce a resource dictionary specific to the pattern.

Look forward to seeing Tiling patterns in fpdf2.

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

No branches or pull requests

4 participants