Skip to content

Commit

Permalink
Renaming FontStyle to FontFace (#737)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas-C committed Mar 27, 2023
1 parent 7bc1fa2 commit 128bb54
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 46 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ in order to get warned about deprecated features used in your code.
This can also be enabled programmatically with `warnings.simplefilter('default', DeprecationWarning)`.

## [2.7.1] - not released yet
### Changed
- renamed `fonts.FontStyle` to [`fonts.FontFace`](https://pyfpdf.github.io/fpdf2/fpdf/fonts.html#fpdf.fonts.FontFace), and `FPDF.use_font_style` to [`FPDF.use_font_face`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.FPDF.FPDF.use_font_face), to avoid confusions with `FPDF.font_style`

## [2.7.0] - 2023-03-27
### Added
Expand Down
3 changes: 2 additions & 1 deletion docs/Tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ with pdf.table(first_row_as_headings=False) as table:y

## Style table headings
```python
from fpdf.fonts import FontFace
...
blue = (0, 0, 255)
grey = (128, 128, 128)
headings_style = FontStyle(emphasis="ITALICS", color=blue, fill_color=grey)
headings_style = FontFace(emphasis="ITALICS", color=blue, fill_color=grey)
with pdf.table(headings_styleheadings_style=headings_style) as table:
...
```
Expand Down
10 changes: 8 additions & 2 deletions fpdf/fonts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""
Definition of the character widths of all PDF standard fonts.
Font-related classes & constants.
Includes the definition of the character widths of all PDF standard fonts.
"""
from dataclasses import dataclass, replace
from typing import Optional, Union
Expand All @@ -9,7 +10,12 @@


@dataclass
class FontStyle:
class FontFace:
"""
Represent basic font styling properties.
This is a subset of `fpdf.graphics_state.GraphicState` properties.
"""

family: Optional[str]
emphasis: Optional[TextEmphasis]
size_pt: Optional[int]
Expand Down
40 changes: 17 additions & 23 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Image:
YPos,
)
from .errors import FPDFException, FPDFPageFormatException, FPDFUnicodeEncodingException
from .fonts import CORE_FONTS_CHARWIDTHS, FontStyle
from .fonts import CORE_FONTS_CHARWIDTHS, FontFace
from .graphics_state import GraphicsStateMixin
from .html import HTML2FPDF
from .image_parsing import SUPPORTED_IMAGE_FILTERS, get_img_info, load_image
Expand Down Expand Up @@ -143,7 +143,7 @@ def __str__(self):
return f"ImageInfo({d})"


class TitleStyle(FontStyle):
class TitleStyle(FontFace):
def __init__(
self,
font_family: Optional[str] = None,
Expand Down Expand Up @@ -4648,46 +4648,40 @@ def _use_title_style(self, title_style: TitleStyle):
self.ln(title_style.t_margin)
if title_style.l_margin:
self.set_x(title_style.l_margin)
with self.use_font_style(title_style):
with self.use_font_face(title_style):
yield
if title_style.b_margin:
self.ln(title_style.b_margin)

@contextmanager
def use_font_style(self, font_style: FontStyle):
def use_font_face(self, font_face: FontFace):
"""
Sets the provided `fpdf.font.FontStyle` in a local context,
Sets the provided `fpdf.fonts.FontFace` in a local context,
then restore font settings back to they were initially.
This method must be used as a context manager using `with`:
with pdf.use_font_style(FontStyle(emphasis="BOLD", color=255, size_pt=42)):
with pdf.use_font_face(FontFace(emphasis="BOLD", color=255, size_pt=42)):
put_some_text()
"""
if not font_style:
if not font_face:
yield
return
prev_font = (self.font_family, self.font_style, self.font_size_pt)
self.set_font(
font_style.family or self.font_family,
font_style.emphasis.style
if font_style.emphasis is not None
font_face.family or self.font_family,
font_face.emphasis.style
if font_face.emphasis is not None
else self.font_style,
font_style.size_pt or self.font_size_pt,
font_face.size_pt or self.font_size_pt,
)
prev_text_color = self.text_color
if font_style.color is not None and font_style.color != self.text_color:
self.set_text_color(font_style.color)
if font_face.color is not None and font_face.color != self.text_color:
self.set_text_color(font_face.color)
prev_fill_color = self.fill_color
if (
font_style.fill_color is not None
and font_style.fill_color != self.fill_color
):
self.set_fill_color(font_style.fill_color)
if font_face.fill_color is not None and font_face.fill_color != self.fill_color:
self.set_fill_color(font_face.fill_color)
yield
if (
font_style.fill_color is not None
and font_style.fill_color != prev_fill_color
):
if font_face.fill_color is not None and font_face.fill_color != prev_fill_color:
self.set_fill_color(prev_fill_color)
self.text_color = prev_text_color
self.set_font(*prev_font)
Expand All @@ -4710,7 +4704,7 @@ def table(self, *args, **kwargs):
col_widths (int, tuple): optional. Sets column width. Can be a single number or a sequence of numbers
first_row_as_headings (bool): optional, default to True. If False, the first row of the table
is not styled differently from the others
headings_style (fpdf.fonts.FontStyle): optional, default to bold.
headings_style (fpdf.fonts.FontFace): optional, default to bold.
Defines the visual style of the top headings row: size, color, emphasis...
line_height (number): optional. Defines how much vertical space a line of text will occupy
markdown (bool): optional, default to False. Enable markdown interpretation of cells textual content
Expand Down
20 changes: 19 additions & 1 deletion fpdf/graphics_state.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .drawing import DeviceGray
from .enums import TextMode, CharVPos
from .enums import CharVPos, TextEmphasis, TextMode
from .fonts import FontFace


class GraphicsStateMixin:
Expand Down Expand Up @@ -311,3 +312,20 @@ def denom_lift(self, v):
([docs](../TextStyling.html#subscript-superscript-and-fractional-numbers))
"""
self.__statestack[-1]["denom_lift"] = float(v)

def font_face(self):
"""
Return a `fpdf.fonts.FontFace` instance
representing a subset of properties of this GraphicsState.
"""
return FontFace(
family=self.font_family,
emphasis=TextEmphasis.coerce(self.font_style),
size_pt=self.font_size_pt,
color=self.text_color
if self.text_color != self.DEFAULT_TEXT_COLOR
else None,
fill_color=self.fill_color
if self.fill_color != self.DEFAULT_FILL_COLOR
else None,
)
6 changes: 3 additions & 3 deletions fpdf/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from html.parser import HTMLParser

from .enums import TextEmphasis, XPos, YPos
from .fonts import FontStyle
from .fonts import FontFace
from .table import Table

import re
Expand Down Expand Up @@ -281,7 +281,7 @@ def handle_data(self, data):
emphasis |= TextEmphasis.U
style = None
if bgcolor or emphasis:
style = FontStyle(emphasis=emphasis, fill_color=bgcolor)
style = FontFace(emphasis=emphasis, fill_color=bgcolor)
self.table_row.cell(text=data, align=align, style=style, colspan=colspan)
self.td_th["inserted"] = True
elif self.table is not None:
Expand Down Expand Up @@ -588,7 +588,7 @@ def handle_endtag(self, tag):
bgcolor = color_as_decimal(
self.td_th.get("bgcolor", self.tr.get("bgcolor", None))
)
style = FontStyle(fill_color=bgcolor) if bgcolor else None
style = FontFace(fill_color=bgcolor) if bgcolor else None
self.table_row.cell(text="", style=style)
self.td_th = None
if tag == "font":
Expand Down
31 changes: 20 additions & 11 deletions fpdf/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from typing import List, Union

from .enums import Align, TableBordersLayout, TableCellFillMode
from .fonts import FontStyle
from .fonts import FontFace


DEFAULT_HEADINGS_STYLE = FontStyle(emphasis="BOLD")
DEFAULT_HEADINGS_STYLE = FontFace(emphasis="BOLD")


class Table:
Expand Down Expand Up @@ -45,7 +45,7 @@ def __init__(
col_widths (int, tuple): optional. Sets column width. Can be a single number or a sequence of numbers
first_row_as_headings (bool): optional, default to True. If False, the first row of the table
is not styled differently from the others
headings_style (fpdf.fonts.FontStyle): optional, default to bold.
headings_style (fpdf.fonts.FontFace): optional, default to bold.
Defines the visual style of the top headings row: size, color, emphasis...
line_height (number): optional. Defines how much vertical space a line of text will occupy
markdown (bool): optional, default to False. Enable markdown interpretation of cells textual content
Expand All @@ -70,7 +70,7 @@ def __init__(

def row(self, cells=()):
"Adds a row to the table. Yields a `Row` object."
row = Row()
row = Row(self._fpdf)
self.rows.append(row)
for cell in cells:
row.cell(cell)
Expand Down Expand Up @@ -218,10 +218,12 @@ def _render_table_cell(
text_align = cell.align or self._text_align
if not isinstance(text_align, (Align, str)):
text_align = text_align[j]
style = cell.style
if not style and i == 0 and self._first_row_as_headings:
if i == 0 and self._first_row_as_headings:
style = self._headings_style
else:
style = cell.style or row.style
if lines_heights_only and style:
# Avoid to generate font-switching instructions: BT /F... Tf ET
style = style.replace(emphasis=None)
if style and style.fill_color:
fill = True
Expand All @@ -240,9 +242,9 @@ def _render_table_cell(
style = (
style.replace(fill_color=self._cell_fill_color)
if style
else FontStyle(fill_color=self._cell_fill_color)
else FontFace(fill_color=self._cell_fill_color)
)
with self._fpdf.use_font_style(style):
with self._fpdf.use_font_face(style):
lines = self._fpdf.multi_cell(
w=col_width,
h=row_height,
Expand Down Expand Up @@ -298,8 +300,10 @@ def _get_lines_heights_per_cell(self, i) -> List[List[int]]:
class Row:
"Object that `Table.row()` yields, used to build a row in a table"

def __init__(self):
def __init__(self, fpdf):
self._fpdf = fpdf
self.cells = []
self.style = fpdf.font_face()

@property
def cols_count(self):
Expand All @@ -315,7 +319,7 @@ def cell(
text (str): string content, can contain several lines.
In that case, the row height will grow proportionally.
align (str, fpdf.enums.Align): optional text alignment.
style (fpdf.fonts.FontStyle): optional text style.
style (fpdf.fonts.FontFace): optional text style.
img: optional. Either a string representing a file path to an image,
an URL to an image, an io.BytesIO, or a instance of `PIL.Image.Image`.
img_fill_width (bool): optional, defaults to False. Indicates to render the image
Expand All @@ -327,6 +331,11 @@ def cell(
"fpdf2 currently does not support inserting text with an image in the same table cell."
"Pull Requests are welcome to implement this 😊"
)
if not style:
# We capture the current font settings:
font_face = self._fpdf.font_face()
if font_face != self.style:
style = font_face
cell = Cell(text, align, style, img, img_fill_width, colspan)
self.cells.append(cell)
return cell
Expand All @@ -337,7 +346,7 @@ class Cell:
"Internal representation of a table cell"
text: str
align: Union[str, Align]
style: FontStyle
style: FontFace
img: str
img_fill_width: bool
colspan: int
Expand Down
2 changes: 1 addition & 1 deletion test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def handler(_, __):

def ensure_rss_memory_below(max_in_mib):
"""
Enure there is no unexpected / significant increase between
Ensure there is no unexpected / significant increase between
the process RSS memory BEFORE executing the test, and AFTER.
"""

Expand Down
Binary file added test/table/table_capture_font_settings.pdf
Binary file not shown.
22 changes: 20 additions & 2 deletions test/table/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from fpdf import FPDF
from fpdf.drawing import DeviceRGB
from fpdf.fonts import FontStyle
from fpdf.fonts import FontFace
from test.conftest import assert_pdf_equal, LOREM_IPSUM


Expand Down Expand Up @@ -190,7 +190,7 @@ def test_table_with_headings_styled(tmp_path):
pdf.set_font("Times", size=16)
blue = DeviceRGB(r=0, g=0, b=1)
grey = 128
headings_style = FontStyle(emphasis="ITALICS", color=blue, fill_color=grey)
headings_style = FontFace(emphasis="ITALICS", color=blue, fill_color=grey)
with pdf.table(headings_style=headings_style) as table:
for data_row in TABLE_DATA:
row = table.row()
Expand Down Expand Up @@ -289,3 +289,21 @@ def test_table_align(tmp_path):
for datum in data_row:
row.cell(datum)
assert_pdf_equal(pdf, HERE / "table_align.pdf", tmp_path)


def test_table_capture_font_settings(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
lightblue = (173, 216, 230)
with pdf.table() as table:
for data_row in TABLE_DATA:
with pdf.local_context(text_color=lightblue):
row = table.row()
for i, datum in enumerate(data_row):
if i == 0:
pdf.font_style = "I"
else:
pdf.font_style = ""
row.cell(datum)
assert_pdf_equal(pdf, HERE / "table_capture_font_settings.pdf", tmp_path)
4 changes: 2 additions & 2 deletions tutorial/tuto5.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import csv
from fpdf import FPDF
from fpdf.fonts import FontStyle
from fpdf.fonts import FontFace


with open("countries.txt", encoding="utf8") as csv_file:
Expand All @@ -21,7 +21,7 @@
pdf.add_page()
pdf.set_draw_color(255, 0, 0)
pdf.set_line_width(0.3)
headings_style = FontStyle(emphasis="BOLD", color=255, fill_color=(255, 100, 0))
headings_style = FontFace(emphasis="BOLD", color=255, fill_color=(255, 100, 0))
with pdf.table(
borders_layout="NO_HORIZONTAL_LINES",
cell_fill_color=(224, 235, 255),
Expand Down

0 comments on commit 128bb54

Please sign in to comment.