Skip to content

Commit 15af379

Browse files
authored
New ul_bullet_color parameter for FPDF.write_html() (#1126)
1 parent 5c56598 commit 15af379

File tree

6 files changed

+43
-13
lines changed

6 files changed

+43
-13
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
1919
## [2.7.9] - Not released yet
2020
### Added
2121
* support for overriding paragraph direction on bidirectional text
22+
* new optional `ul_bullet_color` parameter for `FPDF.write_html()`
2223
### Fixed
2324

2425
### Changed

fpdf/drawing.py

+10
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ def colors(self):
217217
"""The color components as a tuple in order `(r, g, b)` with alpha omitted."""
218218
return self[:-1]
219219

220+
@property
221+
def colors255(self):
222+
"The color components as a tuple in order `(r, g, b)` with alpha omitted, in range 0-255."
223+
return tuple(255 * v for v in self.colors)
224+
220225
def serialize(self) -> str:
221226
return " ".join(number_to_str(val) for val in self.colors) + f" {self.OPERATOR}"
222227

@@ -260,6 +265,11 @@ def colors(self):
260265
"""The color components as a tuple in order (g,) with alpha omitted."""
261266
return self[:-1]
262267

268+
@property
269+
def colors255(self):
270+
"The color components as a tuple in order `(r, g, b)` with alpha omitted, in range 0-255."
271+
return tuple(255 * v for v in self.colors)
272+
263273
def serialize(self) -> str:
264274
return " ".join(number_to_str(val) for val in self.colors) + f" {self.OPERATOR}"
265275

fpdf/fpdf.py

+1
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ def write_html(self, text, *args, **kwargs):
405405
dd_tag_indent (int): numeric indentation of <dd> elements
406406
table_line_separators (bool): enable horizontal line separators in <table>
407407
ul_bullet_char (str): bullet character for <ul> elements
408+
ul_bullet_color (tuple | str | drawing.Device* instance): color of the <ul> bullets
408409
heading_sizes (dict): font size per heading level names ("h1", "h2"...)
409410
pre_code_font (str): font to use for <pre> & <code> blocks
410411
warn_on_tags_not_matching (bool): control warnings production for unmatched HTML tags

fpdf/html.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
from html.parser import HTMLParser
1010
import logging, re, warnings
1111

12+
from .deprecation import get_stack_level
13+
from .drawing import color_from_hex_string, convert_to_device_color
1214
from .enums import TextEmphasis, XPos, YPos
1315
from .errors import FPDFException
14-
from .deprecation import get_stack_level
1516
from .fonts import FontFace
1617
from .table import Table
1718

@@ -215,17 +216,7 @@ def color_as_decimal(color="#000000"):
215216

216217
# Checks if color is a name and gets the hex value
217218
hexcolor = COLOR_DICT.get(color.lower(), color)
218-
219-
if len(hexcolor) == 4:
220-
r = int(hexcolor[1] * 2, 16)
221-
g = int(hexcolor[2] * 2, 16)
222-
b = int(hexcolor[3] * 2, 16)
223-
return r, g, b
224-
225-
r = int(hexcolor[1:3], 16)
226-
g = int(hexcolor[3:5], 16)
227-
b = int(hexcolor[5:7], 16)
228-
return r, g, b
219+
return color_from_hex_string(hexcolor).colors255
229220

230221

231222
class HTML2FPDF(HTMLParser):
@@ -241,6 +232,7 @@ def __init__(
241232
dd_tag_indent=10,
242233
table_line_separators=False,
243234
ul_bullet_char=BULLET_WIN1252,
235+
ul_bullet_color=(190, 0, 0),
244236
heading_sizes=None,
245237
pre_code_font="courier",
246238
warn_on_tags_not_matching=True,
@@ -255,6 +247,7 @@ def __init__(
255247
dd_tag_indent (int): numeric indentation of <dd> elements
256248
table_line_separators (bool): enable horizontal line separators in <table>
257249
ul_bullet_char (str): bullet character for <ul> elements
250+
ul_bullet_color (tuple | str | drawing.Device* instance): color of the <ul> bullets
258251
heading_sizes (dict): font size per heading level names ("h1", "h2"...)
259252
pre_code_font (str): font to use for <pre> & <code> blocks
260253
warn_on_tags_not_matching (bool): control warnings production for unmatched HTML tags
@@ -265,6 +258,11 @@ def __init__(
265258
self.li_tag_indent = li_tag_indent
266259
self.dd_tag_indent = dd_tag_indent
267260
self.ul_bullet_char = ul_bullet_char
261+
self.ul_bullet_color = (
262+
color_as_decimal(ul_bullet_color)
263+
if isinstance(ul_bullet_color, str)
264+
else convert_to_device_color(ul_bullet_color).colors255
265+
)
268266
self.heading_sizes = dict(**DEFAULT_HEADING_SIZES)
269267
if heading_sizes:
270268
self.heading_sizes.update(heading_sizes)
@@ -519,7 +517,7 @@ def handle_starttag(self, tag, attrs):
519517
self._new_paragraph()
520518
if tag == "li":
521519
self._ln(2)
522-
self.set_text_color(190, 0, 0)
520+
self.set_text_color(*self.ul_bullet_color)
523521
if self.bullet:
524522
bullet = self.bullet[self.indent - 1]
525523
else:

test/html/html_ul_bullet_color.pdf

1.04 KB
Binary file not shown.

test/html/test_html.py

+20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44

55
from fpdf import FPDF, HTMLMixin, TitleStyle
6+
from fpdf.drawing import DeviceRGB
67
from fpdf.errors import FPDFException
78
from test.conftest import assert_pdf_equal, LOREM_IPSUM
89

@@ -215,6 +216,25 @@ class CustomPDF(FPDF):
215216
assert_pdf_equal(pdf, HERE / "html_customize_ul.pdf", tmp_path)
216217

217218

219+
def test_html_ul_bullet_color(tmp_path):
220+
html = """<ul>
221+
<li>item1</li>
222+
<li>item2</li>
223+
<li>item3</li>
224+
</ul>"""
225+
226+
pdf = FPDF()
227+
pdf.set_font_size(30)
228+
pdf.add_page()
229+
pdf.write_html(html, ul_bullet_color=0) # black
230+
pdf.ln()
231+
pdf.write_html(html, ul_bullet_color="green")
232+
pdf.ln()
233+
pdf.write_html(html, ul_bullet_color=DeviceRGB(r=0.5, g=1, b=0))
234+
pdf.ln()
235+
assert_pdf_equal(pdf, HERE / "html_ul_bullet_color.pdf", tmp_path)
236+
237+
218238
def test_html_align_paragraph(tmp_path):
219239
pdf = FPDF()
220240
pdf.add_page()

0 commit comments

Comments
 (0)