Skip to content

Commit

Permalink
Allow users to set HTML links colors
Browse files Browse the repository at this point in the history
  • Loading branch information
dmail00 authored and Lucas-C committed Feb 27, 2024
1 parent e85ec4e commit 2244a18
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 34 deletions.
32 changes: 3 additions & 29 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,9 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
### Added
* support for overriding paragraph direction on bidirectional text
### Fixed

### Changed


## [2.7.8] - 2024-02-09
### Added
* support for `<path>` elements in SVG `<clipPath>` elements
* support for `bidirectional` text shaping - thanks to @andersonhc
* documentation on how to combine `fpdf2` with [mistletoe](https://pypi.org/project/kaleido/) in order to [generate PDF documents from Markdown (link)](https://py-pdf.github.io/fpdf2/CombineWithMistletoeoToUseMarkdown.html)
* tutorial in Dutch: [Handleiding](https://py-pdf.github.io/fpdf2/Tutorial-nl.md) - thanks to @Polderrider
* support for `Table` cells that span multiple rows via the `rowspan` attribute, which can be combined with `colspan` - thanks to @mjasperse
* `TableSpan.COL` and `TableSpan.ROW` enums that can be used as placeholder table entries to identify span extents - thanks to @mjasperse
### Fixed
* when adding a link on a table cell, an extra link was added erroneously on the left. Moreover, now `FPDF._disable_writing()` properly disable link writing.
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html) now handles linking directly to other pages - thanks to @mjasperse
* non-bold `TitleStyle` is now rendered as non-bold even when the current font is bold
* calling `.table()` inside the `render_toc_function`
* using `.set_text_shaping(True)` & `.offset_rendering()`
* fixed gutter handing when a pagebreak occurs within a table with header rows - thanks to @mjasperse
* fixed handling of `border=0` in HTML table - thanks to @mjasperse
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html) now properly honors `align=` attributes in `<th>` tags
* fixed problem using bold italic standard fonts in markdown - thanks to @Alan-Collins
### Changed
* refactored [`FPDF.multi_cell()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.multi_cell) to generate fewer PDF component objects - thanks to @mjasperse
* outer table borders are now drawn continuously for nonzero `gutter_width`/`gutter_height`, with spacing applied inside the border similar to HTML tables - thanks to @mjasperse - cf. [#1071](https://github.com/py-pdf/fpdf2/issues/1071)
* removed the requirement that all rows in a `Table` have the same number of columns - thanks to @mjasperse
### Deprecated
- font aliases (`Arial``Helvetica`, `CourierNew``Courer`, `TimesNewRoman``Times`). They will be removed in a later release.

* When adding a link on a table cell, an extra link was added erroneously on the left. Moreover, now `FPDF._disable_writing()` properly disable link writing.
### Added
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html) now accepts a dictionary of colors for HTML elements (link, blockquote, li and headings)
## [2.7.7] - 2023-12-10
### Added
* Basic support for `<image>` elements in SVG vector graphics inserted
Expand Down
7 changes: 7 additions & 0 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,13 @@ def write_html(self, text, *args, **kwargs):
heading_sizes (dict): font size per heading level names ("h1", "h2"...)
pre_code_font (str): font to use for <pre> & <code> blocks
warn_on_tags_not_matching (bool): control warnings production for unmatched HTML tags
element_colors (dict): dictionary of colors for elements. Possible keys:
* "link" sets the link color for a href tag
* "li" sets the bullet or number for <li> tags
* "blockquote" sets the color for <blockquote> tags
* "headings" sets the color is not specified in a h1, h2 etc. heading tag
The values for the dictionary are a tuple of three ints, being the r,g and b values
"""
kwargs2 = vars(self)
# Method arguments must override class & instance attributes:
Expand Down
41 changes: 36 additions & 5 deletions fpdf/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
They may change at any time without prior warning or any deprecation period,
in non-backward-compatible ways.
"""

from __future__ import annotations
from html.parser import HTMLParser
import logging, re, warnings

Expand Down Expand Up @@ -244,6 +244,7 @@ def __init__(
heading_sizes=None,
pre_code_font="courier",
warn_on_tags_not_matching=True,
element_colors: dict[str, tuple[int, int, int]] | None = None,
**_,
):
"""
Expand Down Expand Up @@ -311,6 +312,34 @@ def __init__(
self.td_th = None # becomes a dict of attributes when processing <td>/<th> tags
# "inserted" is a special attribute indicating that a cell has be inserted in self.table_row

self.link_color: tuple[int, int, int]
self.li_color: tuple[int, int, int]
self.blockquote_color: tuple[int, int, int]
self.headings_color: tuple[int, int, int]
self._set_color_scheme(element_colors)

def _set_color_scheme(
self, element_colors: dict[str, tuple[int, int, int]] | None
) -> None:
_old_default_colors: dict[str, tuple[int, int, int]] = {
"link": (0, 0, 255),
"li": (190, 0, 0),
"blockquote": (100, 0, 45),
"headings": (150, 0, 0),
}

if element_colors is None:
element_colors = {}

self.link_color = element_colors.get("link", _old_default_colors["link"])
self.li_color = element_colors.get("li", _old_default_colors["li"])
self.blockquote_color = element_colors.get(
"blockquote", _old_default_colors["blockquote"]
)
self.headings_color = element_colors.get(
"headings", _old_default_colors["headings"]
)

def _new_paragraph(
self, align=None, line_height=1.0, top_margin=0, bottom_margin=0
):
Expand Down Expand Up @@ -490,7 +519,9 @@ def handle_starttag(self, tag, attrs):
bottom_margin=self.heading_below * hsize,
)
color = (
color_as_decimal(attrs["color"]) if "color" in attrs else (150, 0, 0)
color_as_decimal(attrs["color"])
if "color" in attrs
else self.headings_color
)
self.set_text_color(*color)
self.set_font(size=hsize_pt)
Expand All @@ -506,7 +537,7 @@ def handle_starttag(self, tag, attrs):
self._new_paragraph()
self._pre_started = True
if tag == "blockquote":
self.set_text_color(100, 0, 45)
self.set_text_color(*self.blockquote_color)
self.indent += 1
self._new_paragraph(top_margin=3, bottom_margin=3)
if tag == "ul":
Expand All @@ -519,7 +550,7 @@ def handle_starttag(self, tag, attrs):
self._new_paragraph()
if tag == "li":
self._ln(2)
self.set_text_color(190, 0, 0)
self.set_text_color(*self.li_color)
if self.bullet:
bullet = self.bullet[self.indent - 1]
else:
Expand Down Expand Up @@ -799,7 +830,7 @@ def set_text_color(self, r=None, g=0, b=0):

def put_link(self, text):
# Put a hyperlink
self.set_text_color(0, 0, 255)
self.set_text_color(*self.link_color)
self.set_style("u", True)
self._write_paragraph(text, link=self.href)
self.set_style("u", False)
Expand Down
Binary file added test/html/html_blockquote.pdf
Binary file not shown.
Binary file added test/html/html_headings_color.pdf
Binary file not shown.
Binary file added test/html/html_link_color.pdf
Binary file not shown.
Binary file added test/html/html_ordered_li_color.pdf
Binary file not shown.
Binary file added test/html/html_unordered_li_color.pdf
Binary file not shown.
40 changes: 40 additions & 0 deletions test/html/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,43 @@ def test_html_and_section_title_styles(): # issue 1080
<p>This will not overflow</p>
"""
)


def test_html_link_color(tmp_path):
pdf = FPDF()
pdf.add_page()
text = '<a href="www.example.com">foo</a>'
pdf.write_html(text, element_colors={"link": (255, 0, 0)})
assert_pdf_equal(pdf, HERE / "html_link_color.pdf", tmp_path)


def test_html_unordered_li_color(tmp_path):
pdf = FPDF()
pdf.add_page()
text = "<ul><li>foo</li></ul>"
pdf.write_html(text, element_colors={"li": (0, 255, 0)})
assert_pdf_equal(pdf, HERE / "html_unordered_li_color.pdf", tmp_path)


def test_html_ordered_li_color(tmp_path):
pdf = FPDF()
pdf.add_page()
text = "<ol><li>foo</li></ol>"
pdf.write_html(text, element_colors={"li": (0, 255, 0)})
assert_pdf_equal(pdf, HERE / "html_ordered_li_color.pdf", tmp_path)


def test_html_blockquote_color(tmp_path):
pdf = FPDF()
pdf.add_page()
text = "Text before<blockquote>foo</blockquote>Text afterwards"
pdf.write_html(text, element_colors={"blockquote": (125, 125, 0)})
assert_pdf_equal(pdf, HERE / "html_blockquote.pdf", tmp_path)


def test_html_headings_color(tmp_path):
pdf = FPDF()
pdf.add_page()
text = "<h1>foo</h1><h2>bar</h2>"
pdf.write_html(text, element_colors={"headings": (148, 139, 139)})
assert_pdf_equal(pdf, HERE / "html_headings_color.pdf", tmp_path)

0 comments on commit 2244a18

Please sign in to comment.