You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* implement linear and radial gradient
* add tests
* start adding typing and comments
* black
* fix f strings
* fix tests; remove code duplication on bad git clone
* initial implementation of ResourceCatalog
* replace match (python 3.10+ only)
* pylint
* proceed with resource catalog implementation
* more tests and create documentation
* formatting
* add docstrings and changelog entry
* increase performance image rendering time limit
* replace jpxdecode test reference after changes in pillow 11.1.0
* skip jpxdecode test on python 3.8
* fix version check
Copy file name to clipboardexpand all lines: CHANGELOG.md
+2
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,8 @@ in order to get warned about deprecated features used in your code.
17
17
This can also be enabled programmatically with `warnings.simplefilter('default', DeprecationWarning)`.
18
18
19
19
## [2.8.3] - Not released yet
20
+
### Added
21
+
* support for [shading patterns (gradients)](https://py-pdf.github.io/fpdf2/Patterns.html)
20
22
### Fixed
21
23
*[`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html): Fixed rendering of content following `<a>` tags; now correctly resets emphasis style post `</a>` tag: hyperlink styling contained within the tag authority. - [Issue #1311](https://github.com/py-pdf/fpdf2/issues/1311)
In PDF (Portable Document Format), a **pattern** is a graphical object that can be used to fill (or stroke) shapes. Patterns can include simple color fills, images, or more advanced textures and gradients.
6
+
7
+
The **patterns** on PDF documents are grouped on 2 types:
8
+
-**Tiling patterns** for any repeating patters.
9
+
-**Shading patterns** for gradients.
10
+
11
+
*fpdf2* provides a context manager `pdf.use_pattern(...)`. Within this context, all drawn shapes or text will use the specified pattern. Once the context ends, drawing reverts to the previously defined color.
12
+
13
+
**At this moment, tiling patterns are not yet supported by `fpdf2`**.
14
+
15
+
## 2. Gradients
16
+
17
+
### 2.1 What is a Gradient?
18
+
19
+
A **gradient** is a progressive blend between two or more colors. In PDF terms, gradients are implemented as *shading patterns*—they allow a smooth color transition based on geometry.
20
+
21
+
### 2.2 Linear Gradients (axial shading)
22
+
23
+
A **linear gradient** blends colors along a straight line between two points. For instance, you can define a gradient that goes:
24
+
25
+
- Left to right
26
+
- Top to bottom
27
+
- Diagonally
28
+
29
+
or in any arbitrary orientation by specifying coordinates.
30
+
31
+
**Example: Creating a Linear Gradient**
32
+
33
+
```python
34
+
from fpdf importFPDF
35
+
from fpdf.pattern import LinearGradient
36
+
37
+
pdf = FPDF()
38
+
pdf.add_page()
39
+
40
+
# Define a linear gradient
41
+
linear_grad = LinearGradient(
42
+
pdf,
43
+
from_x=10, # Starting x-coordinate
44
+
from_y=0, # Starting y-coordinate
45
+
to_x=100, # Ending x-coordinate
46
+
to_y=0, # Ending y-coordinate
47
+
colors=["#C33764", "#1D2671"] # Start -> End color
48
+
)
49
+
50
+
with pdf.use_pattern(linear_grad):
51
+
# Draw a rectangle that will be filled with the gradient
52
+
pdf.rect(x=10, y=10, w=100, h=20, style="FD")
53
+
54
+
pdf.output("linear_gradient_example.pdf")
55
+
```
56
+
57
+
**Key Parameters**:
58
+
59
+
-**from_x, from_y, to_x, to_y**: The coordinates defining the line along which colors will blend.
60
+
-**colors**: A list of colors (hex strings or (R,G,B) tuples). The pattern will interpolate between these colors.
61
+
62
+
63
+
### 2.3 Radial Gradients
64
+
65
+
A **radial gradient** blends colors in a circular or elliptical manner from an inner circle to an outer circle. This is perfect for spotlight-like effects or circular color transitions.
66
+
67
+
**Example: Creating a Radial Gradient**
68
+
69
+
```python
70
+
from fpdf importFPDF
71
+
from fpdf.pattern import RadialGradient
72
+
73
+
pdf = FPDF()
74
+
pdf.add_page()
75
+
76
+
# Define a radial gradient
77
+
radial_grad = RadialGradient(
78
+
pdf,
79
+
start_circle_x=50, # Center X of inner circle
80
+
start_circle_y=50, # Center Y of inner circle
81
+
start_circle_radius=0, # Radius of inner circle
82
+
end_circle_x=50, # Center X of outer circle
83
+
end_circle_y=50, # Center Y of outer circle
84
+
end_circle_radius=25, # Radius of outer circle
85
+
colors=["#FFFF00", "#FF0000"], # Inner -> Outer color
86
+
)
87
+
88
+
with pdf.use_pattern(radial_grad):
89
+
# Draw a circle filled with the radial gradient
90
+
pdf.circle(x=50, y=50, radius=25, style="FD")
91
+
92
+
pdf.output("radial_gradient_example.pdf")
93
+
```
94
+
95
+
**Key Parameters**:
96
+
97
+
-**start_circle_x, start_circle_y, start_circle_radius**: Center and radius of the inner circle.
98
+
-**end_circle_x, end_circle_y, end_circle_radius**: Center and radius of the outer circle.
99
+
-**colors**: A list of colors to be interpolated from inner circle to outer circle.
100
+
101
+
## 4. Advanced Usage
102
+
103
+
### 4.1 Multiple Colors
104
+
105
+
Both linear and radial gradients support **multiple colors**. If you pass, for example, `colors=["#C33764", "#1D2671", "#FFA500"]`, the resulting pattern will interpolate color transitions through each color in that order.
106
+
107
+
### 4.2 Extending & Background for Linear Gradients
108
+
109
+
-**extend_before**: Extends the first color before the starting point (i.e., `x1,y1`).
110
+
-**extend_after**: Extends the last color beyond the end point (i.e., `x2,y2`).
111
+
-**background**: Ensures that if any area is uncovered by the gradient (e.g., a rectangle that is bigger than the gradient line), it’ll show the given background color.
112
+
113
+
### 4.3 Custom Bounds
114
+
115
+
For **linear gradients** or **radial gradients**, passing `bounds=[0.2, 0.4, 0.7, ...]` (values between 0 and 1) fine-tunes where each color transition occurs. For instance, if you have 5 colors, you can specify 3 boundary values that partition the color progression among them.
116
+
117
+
For example, taking a gradient with 5 colors and `bounds=[0.1, 0.8, 0.9]`:
118
+
- The transition from color 1 to color 2 start at the beggining (0%) and ends at 10%
119
+
- The transition from color 2 to color 3 start at 10% and ends at 80%
120
+
- The transition from color 3 to color 4 start at 80% and ends at 90%
121
+
- The transition from color 4 to color 5 start at 90% and goes to the end (100%)
122
+
123
+
In other words, each boundary value dictates where the color transitions will occur along the total gradient length.
0 commit comments