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

Add a new bezier() method to draw curves #496

Closed
Lucas-C opened this issue Aug 18, 2022 · 45 comments
Closed

Add a new bezier() method to draw curves #496

Lucas-C opened this issue Aug 18, 2022 · 45 comments

Comments

@Lucas-C
Copy link
Member

Lucas-C commented Aug 18, 2022

Documentation on Bézier curves:

There is some suggested usage example, once implemented, inspired by the existing polygon() method:

from fpdf import FPDF

pdf = FPDF()
pdf.bezier([(100, 150), (50, 100), ...], style="FD")

@torque already implemented bezier curves in PR #196 in order to support SVG:
https://github.com/PyFPDF/fpdf2/blob/2.5.6/fpdf/drawing.py#L2016

The goal here is to expose this feature as a new public FPDF method.

Of course, new unit tests should be added, as well as a new documentation section in docs/Shapes.md


By implementing this feature you, as a benevolent FLOSS developper, will provide access to the large community of fpdf2 users to a standard and 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 to the contributors list & map.

This issue can count as part of hacktoberfest

@JDeepD
Copy link

JDeepD commented Oct 1, 2022

Would like to work on it, if no one else is working.

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 3, 2022

Would like to work on it, if no one else is working.

You are very welcome, go on!

@Aashish-Upadhyay-101
Copy link

hello, if its available can I work on it?

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 4, 2022

Ping @JDeepD: have you started working on this, or are you planning to do so soon?
If so, great! 😊 Any help needed?
Else, would you be OK to leave it for @Aashish-Upadhyay-101 to work on it?

@JDeepD
Copy link

JDeepD commented Oct 4, 2022

I am planning to work on this soon. Since I have not contributed to fpdf2 before, I am looking into some code and documentation. But @Aashish-Upadhyay-101 can work on it as I would probably need some more time to understand the codebase and begin working on it.

@Aashish-Upadhyay-101
Copy link

Aashish-Upadhyay-101 commented Oct 4, 2022

nice, before starting I have a few questions
@Lucas-C do I have to create bezier() method from scratch ?

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 4, 2022

I am planning to work on this soon. Since I have not contributed to fpdf2 before, I am looking into some code and documentation. But @Aashish-Upadhyay-101 can work on it as I would probably need some more time to understand the codebase and begin working on it.

@JDeepD : thank you for you answer. If @Aashish-Upadhyay-101 creates a PR first, you input will be welcome as a reviewer on it!

do I have to create bezier() method from scratch ?

@Aashish-Upadhyay-101 yes, no method exist at this moment.

@Aashish-Upadhyay-101
Copy link

@Lucas-C, I have to make function that renders bezier curve. To actually render on the pdf which function or library this project is using? I think drawing.py has some functions inside it, right?

@gmischler
Copy link
Collaborator

I think drawing.py has some functions inside it, right?

As mentioned in the issue description, the drawing.py module has several classes that implement the geometric calculations:

It might be easiest to just create a wrapper around one (or several) of those.

Actually, I think that several of our bespoke geometry methods could be simplified by calling the drawing.py module instead of doing the work themselves.

@Aashish-Upadhyay-101
Copy link

I'm new to open source and really struggling to do this....! can you help?

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 7, 2022

Hi @Aashish-Upadhyay-101, and welcome.
I am going to be a bit busy for a few days, but I should have some time on Monday to give you some pointers.
Those pages are good starting points if you are new:

Could you try to explain precisely on what points you are struggling?

Usually, when contributing to an open source project, the first steps are :

  1. Retrieve the code on your computer using git
  2. Install the require dependencies, with pip (following the instructions on the page I mentioned above)
  3. Run the unit tests (by executing pytest)
  4. Figure how to add a new one (here a new test/shapes/test_bezier.py file seems pertinent) by taking inspiration from the existing ones. You will want to call assert_pdf_equal() with generate=True while developping your new test, to generate a new reference PDF file
  5. Read some of the internal the code to understand it, but focus on just what you need to know in order to implement your task
  6. Start coding!

@Aashish-Upadhyay-101
Copy link

Aashish-Upadhyay-101 commented Oct 7, 2022

@Lucas-C I tried to Setup locally, but got error when I run test

1. first of all the test failed with errors

i ran pytest -p test
Screen Shot 2022-10-07 at 5 10 57 PM

2. After that i'm also confused where to start like from where should i began.

3. Afer that, I'm also confused which file to modify and how to add bezier() function

As I'm purely a beginner and really want to success in this area, I will do whatever it takes to learn so please guide me through

Or maybe you can assign me a little bit biginner friendly simple issue to get started with?

@JDeepD
Copy link

JDeepD commented Oct 7, 2022

@Lucas-C I tried to Setup locally, but got error when I run test

1. first of all the test failed with errors

i ran pytest -p test Screen Shot 2022-10-07 at 5 10 57 PM

Did you install the requirements first?

Install dependencies using pip install -r test/requirements.txt

2. After that i'm also confused where to start like from where should i began.

The docs/Development.md is a pretty good starting point.

3. Afer that, I'm also confused which file to modify and how to add bezier() function

The codebase should generally remain uniform. See how other functions are implemented and try to implement the bezier() function in a similar manner.

@Aashish-Upadhyay-101
Copy link

Screen Shot 2022-10-07 at 9 43 43 PM

@JDeepD I got this error message when I tried to install the requirements.txt

@JDeepD
Copy link

JDeepD commented Oct 7, 2022

Screen Shot 2022-10-07 at 9 43 43 PM

@JDeepD I got this error message when I tried to install the requirements.txt

First thing that you should do when you get errors is to google it and try out some top solutions.

For ex, the error here is error: command swig failed...

After a bit of googling, this is what I found.

@Aashish-Upadhyay-101
Copy link

@JDeepD now everything is setup, now I will try to implement bezier

@Aashish-Upadhyay-101
Copy link

@JDeepD can you help me with bezier() function.
I created a empty function inside fpdf's class FPDF like this

def bezier(self, point_list, style):
    pass

but don't know afterwards how to implement it, can you guide me

@JDeepD
Copy link

JDeepD commented Oct 8, 2022

@JDeepD can you help me with bezier() function. I created a empty function inside fpdf's class FPDF like this

def bezier(self, point_list, style):
    pass

but don't know afterwards how to implement it, can you guide me

You need to read some internal code for it. See the links mentioned in the top comment by Lucas.

@Aashish-Upadhyay-101
Copy link

@JDeepD I think if you have already understanded the code base, you can work on this issue.
I'm just a beginner and like literally no idea whats going on the project

@JDeepD
Copy link

JDeepD commented Oct 9, 2022

@Aashish-Upadhyay-101 If you have left working on this issue, then please let us know.

@Aashish-Upadhyay-101
Copy link

@JDeepD I left working on this issue, I think I need some more time to understand whats going on.

@Shikharishere
Copy link

Shikharishere commented Oct 15, 2022

I would like to work on this issue? Just let me know.....

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 16, 2022

Great!

Feel free to drop a comment if you start working on this.
I also suggest to quickly submit a pull request, as a draft, so that you can share your ongoing work.

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 17, 2022

Also, thank you for your help & answers here @JDeepD!
And thanks for your interest in fpdf2, your contributions will be very welcome 😊

@all-contributors please add @JDeepD for question

@py-pdf py-pdf deleted a comment from allcontributors bot Oct 17, 2022
@jeettrivedi
Copy link

jeettrivedi commented Oct 22, 2022

I've begun work on this issue. I've opened draft PR (#596) so that I can share my progress.

From what I understand, here is my approach:
Add a public method to class fpdf called bezier. This method has 1 required arguments.
point_list: Must contain atleast 4 points. The first and last set of coordinates are taken as the starting and ending points respectively. The remainder of the points are used as control points for the Bezier curve.

The Bezier curve methods created in #196 can't be used here as they are for the case of 2 control points only. However, they can be seen as a special case of the general Bezier curve with arbitrarily many control points. My revised plan is to write a more general Bezier curve method of which the original methods will be special cases.
This method will take a list of two or more points. The method will simply iterate over this set of points and render bezier curves using the methods created in PR #196.

I notice that in the description of the issue, the method should also take a style argument. Given that we are rendering a line (which may not necessarily be closed unless the last point is the same coordinates as the first point), does it make sense to have style options that we would normally have for closed shapes (such as fill and draw).

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 23, 2022

does it make sense to have style options that we would normally have for closed shapes

As can be seen in the CodePen snippet on the following page, yes bezier curves with filling make sense:
https://www.sitepoint.com/html5-svg-cubic-curves/

Your plan sounds great 😊
It is important that, to draw those curves, the same code is used in this method and in the drawing module of fpdf2, to avoid code duplication.

@jeettrivedi
Copy link

I see. I read through some of the implemented methods in the drawing module and realized that all I need to do is to generate the string representing the complete Bezier curve (as in the examples you linked) that can be passed to the path render function. Does that solution make sense?

@Lucas-C
Copy link
Member Author

Lucas-C commented Oct 30, 2022

all I need to do is to generate the string representing the complete Bezier curve (as in the examples you linked) that can be passed to the path render function. Does that solution make sense?

Yes, absolutely 😊
In bezier() you can use the existing drawing_context() or new_path() methods that can be used as context managers

@gmischler
Copy link
Collaborator

@jeettrivedi, you might want to check out the svg.py module and compare how it uses the bezier methods in drawing.py. You can probably keep it simpler than that because you don't need to deal with other path elements, but the parts handling curves (especially continuous curves) may still be useful to study.

@Lucas-C
Copy link
Member Author

Lucas-C commented Nov 15, 2022

@jeettrivedi: are you still working on this issue?
If you do, can I help you with anything?
If not, no worries, please just notify us 😊

@jeettrivedi
Copy link

@jeettrivedi: are you still working on this issue? If you do, can I help you with anything? If not, no worries, please just notify us blush

Hey, I am actively working on it right now. I will have questions likely next week. Thank you for checking in!

@Lucas-C
Copy link
Member Author

Lucas-C commented Nov 15, 2022

Hey, I am actively working on it right now. I will have questions likely next week. Thank you for checking in!

Great!

@jeettrivedi
Copy link

@Lucas-C I have a question. So far, I wrote a simple method to generate the path string for a bezier curve given the points and the method then passes that path string to the method fpdf._out. I wrote a test to see whether this produces the output I am expecting.

While debugging this, I examined the path string (after unit transformations) that is passed to fpdf._out by fpdf.rect. Here's an example

283.46 558.43 283.46 -141.73 re B

I tried to look for an re or B operator for SVG paths but didn't find any information. Could you maybe elaborate on what these are? I was looking at (this page)[https://www.w3schools.com/graphics/svg_path.asp] for SVG path information. I also attempted to render the above mentioned path string in an online SVG path renderer but it only gave me a vertical line. I maybe have misunderstanding something minor. Thanks in advance!

@Lucas-C
Copy link
Member Author

Lucas-C commented Dec 3, 2022

You can check the annex A of the 1.7 PDF spec for the meaning of PDF operators.
B is the fill & stroke operator in PDF syntax.

@Lucas-C
Copy link
Member Author

Lucas-C commented Dec 12, 2022

Hi @jeettrivedi
Was my answer helpful to you? Were you able to perform those path drawing tests?

@Lucas-C
Copy link
Member Author

Lucas-C commented Aug 2, 2023

Without news from @jeettrivedi, and given that no PR is currently open regarding this,
I think that this feature is up-for-grabs, anyone willing to contribute can work on this!

@awmc000
Copy link

awmc000 commented Mar 11, 2024

Hello, just wondering if this is still up for grabs? I am interested in working on this.

@Lucas-C
Copy link
Member Author

Lucas-C commented Mar 11, 2024

Hello, just wondering if this is still up for grabs? I am interested in working on this.

Yes, you can work on implementing this feature, a PR would be welcome!

👍

@awmc000
Copy link

awmc000 commented Mar 25, 2024

I'm set up and have started working on this. I have some questions about how I should implement this.

the drawing.py module has several classes that implement the geometric calculations:

[BezierCurve()](https://github.com/PyFPDF/fpdf2/blob/4bcd4559ac9dea1f1e2d12a96f934fa5643983ee/fpdf/drawing.py#L2015)
[RelativeBezierCurve()](https://github.com/PyFPDF/fpdf2/blob/4bcd4559ac9dea1f1e2d12a96f934fa5643983ee/fpdf/drawing.py#L2087)
[QuadraticBezierCurve()](https://github.com/PyFPDF/fpdf2/blob/4bcd4559ac9dea1f1e2d12a96f934fa5643983ee/fpdf/drawing.py#L2167)
[RelativeQuadraticBezierCurve()](https://github.com/PyFPDF/fpdf2/blob/4bcd4559ac9dea1f1e2d12a96f934fa5643983ee/fpdf/drawing.py#L2260)

It might be easiest to just create a wrapper around one (or several) of those.

These seem to be used by PaintedPath. Should FPDF2.bezier() create a PaintedPath object?

with self.drawing_context(debug_stream=debug_stream) as ctxt:
            path = PaintedPath(x=x, y=y)
            path.style.paint_rule = paint_rule
            yield path
            ctxt.add_item(path)

It looks like in order to add a shape to the document, other methods are getting a drawing context and then using the context's add_item method, like here. Should bezier() work like this?

Thanks in advance. This will be my first significant (taking >10min) FOSS contribution, so the codebase is imposing, but the code makes sense and I will keep reading and playing with the code to figure out as much as possible on my own and ask questions when needed.

@Lucas-C
Copy link
Member Author

Lucas-C commented Apr 9, 2024

These seem to be used by PaintedPath. Should FPDF2.bezier() create a PaintedPath object?

That seems like a valid approach to me, yes!
You could also consider using with self.new_path(): ...

It looks like in order to add a shape to the document, other methods are getting a drawing context and then using the context's add_item method, like here. Should bezier() work like this?

Yes. You can take inspiration from the usages of .new_path() & PaintedPath() in fpdf/svg.py.

Thanks in advance. This will be my first significant (taking >10min) FOSS contribution, so the codebase is imposing, but the code makes sense and I will keep reading and playing with the code to figure out as much as possible on my own and ask questions when needed.

Sorry for the delay in answering you 😞
We want to welcome new FLOSS contributors on this project, so I will try to be quicker to answer all that questions you may have! 🙂

@awmc000
Copy link

awmc000 commented May 5, 2024

Thanks for your advice. I will get back on this this week!

@awmc000
Copy link

awmc000 commented May 8, 2024

Update: Have wrote an early version that uses PaintedPath.curve_to or PaintedPath.quadratic_curve_to depending on whether 2 or 3 tuples were given.

Python 3.11.8 (main, Feb 12 2024, 14:50:05) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from fpdf import FPDF
>>> pdf = FPDF()
>>> pdf.add_page()
>>> pdf.set_draw_color(200, 5, 5)
>>> pdf.bezier([(50, 50), (90, 45), (60, 100)])
>>> pdf.set_draw_color(5, 5, 200)
>>> pdf.bezier([(150, 150), (190, 145)])
>>> pdf.output("beztest.pdf")

image
Glad to have a function doing something and will report back as I work toward having this do exactly what is intended.

@Lucas-C
Copy link
Member Author

Lucas-C commented May 13, 2024

Glad to have a function doing something and will report back as I work toward having this do exactly what is intended.

Good job @awmc000!
I'm eager to see your Pull Request 😊

@awmc000
Copy link

awmc000 commented Jun 3, 2024

I think we can close this issue? I'll be looking through the issues on this project, I'm interested in doing more.

@gmischler
Copy link
Collaborator

Yup, the PR didn't use the standard phrasing of "fixes ###", so it didn't get closed automatically.

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

7 participants