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

Added TextStyle Horizontal Alignment #1300

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
* new optional parameter `border` for table cells [issue #1192](https://github.com/py-pdf/fpdf2/issues/1192) users can define specific borders (left, right, top, bottom) for individual cells
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html): now parses `<title>` tags to set the [document title](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_title). By default, it is added as PDF metadata, but not rendered in the document body. However, this can be enabled by passing `render_title_tag=True` to `FPDF.write_html()`.
* support for LZWDecode compression [issue #1271](https://github.com/py-pdf/fpdf2/issues/1271)
* support for passing `Align` values (along with string values like `'C'`, `'L'`, `'R'`) in `l_margin` of `TextStyle` to horizontally align text [issue #1282](https://github.com/py-pdf/fpdf2/issues/1282)
* [text_annotation()](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.text_annotation) has a new optional `title` parameter
### Fixed
* `FPDF.set_text_shaping(False)` was broken since version 2.7.8 and is now working properly - [issue #1287](https://github.com/py-pdf/fpdf2/issues/1287)
Expand Down
14 changes: 11 additions & 3 deletions fpdf/fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __deepcopy__(self, _memo):

from .deprecation import get_stack_level
from .drawing import convert_to_device_color, DeviceGray, DeviceRGB
from .enums import FontDescriptorFlags, TextEmphasis
from .enums import FontDescriptorFlags, TextEmphasis, Align
from .syntax import Name, PDFObject
from .util import escape_parens

Expand Down Expand Up @@ -125,7 +125,7 @@ def __init__(
fill_color: Union[int, tuple] = None, # grey scale or (red, green, blue),
underline: bool = False,
t_margin: Optional[int] = None,
l_margin: Optional[int] = None,
l_margin: Union[Optional[int], Optional[Align], Optional[str]] = None,
b_margin: Optional[int] = None,
):
super().__init__(
Expand All @@ -136,7 +136,15 @@ def __init__(
fill_color,
)
self.t_margin = t_margin or 0
self.l_margin = l_margin or 0

# added support for 'Align' and 'str' type values for l_margin
if isinstance(l_margin, (Align, int)):
self.l_margin = l_margin
elif isinstance(l_margin, str):
self.l_margin = Align.coerce(l_margin)
else:
self.l_margin = 0
Comment on lines +140 to +146
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# added support for 'Align' and 'str' type values for l_margin
if isinstance(l_margin, (Align, int)):
self.l_margin = l_margin
elif isinstance(l_margin, str):
self.l_margin = Align.coerce(l_margin)
else:
self.l_margin = 0
self.l_margin = Align.coerce(l_margin)

Isn't this one line enough?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Lucas-C,

I dont think Align.coerce can handle integer values, so I added conditions for the 3 possible types integer, string and Align.


self.b_margin = b_margin or 0

def __repr__(self):
Expand Down
20 changes: 16 additions & 4 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5115,6 +5115,10 @@ def start_section(self, name, level=0, strict=True):
if text_style.size_pt is not None:
prev_font_size_pt = self.font_size_pt
self.font_size_pt = text_style.size_pt
# check if l_margin value is of type Align or string
align = Align.L
if isinstance(text_style.l_margin, (Align, str)):
align = text_style.l_margin
page_break_triggered = self.multi_cell(
w=self.epw,
h=self.font_size,
Expand All @@ -5123,9 +5127,14 @@ def start_section(self, name, level=0, strict=True):
new_y=YPos.NEXT,
dry_run=True, # => does not produce any output
output=MethodReturnValue.PAGE_BREAK,
align=align,
padding=Padding(
top=text_style.t_margin or 0,
left=text_style.l_margin or 0,
left=(
text_style.l_margin
if isinstance(text_style.l_margin, int)
else 0
),
bottom=text_style.b_margin or 0,
),
)
Expand All @@ -5136,25 +5145,28 @@ def start_section(self, name, level=0, strict=True):
self.add_page()
with self._marked_sequence(title=name) as struct_elem:
outline_struct_elem = struct_elem
with self._use_text_style(text_style):
with self._use_title_style(text_style):
self.multi_cell(
w=self.epw,
h=self.font_size,
text=name,
align=align,
new_x=XPos.LMARGIN,
new_y=YPos.NEXT,
center=text_style.l_margin == Align.C,
)
self._outline.append(
OutlineSection(name, level, self.page, dest, outline_struct_elem)
)

@contextmanager
def _use_text_style(self, text_style: TextStyle):
def _use_title_style(self, text_style: TextStyle):
if text_style:
if text_style.t_margin:
self.ln(text_style.t_margin)
if text_style.l_margin:
self.set_x(text_style.l_margin)
if isinstance(text_style.l_margin, int):
self.set_x(text_style.l_margin)
with self.use_font_face(text_style):
yield
if text_style and text_style.b_margin:
Expand Down
28 changes: 27 additions & 1 deletion test/outline/test_outline.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from fpdf import FPDF, TextStyle, TitleStyle, errors
from fpdf import FPDF, TextStyle, TitleStyle, errors, Align

from test.conftest import assert_pdf_equal

Expand Down Expand Up @@ -66,6 +66,32 @@ def test_incoherent_start_section_hierarchy():
pdf.start_section("Subtitle", level=2)


def test_start_section_horizontal_alignment(tmp_path): # issue-1282
Lucas-C marked this conversation as resolved.
Show resolved Hide resolved

pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", "", 20)

# left align
level0 = TextStyle("Helvetica", "", 20, (0, 0, 0), l_margin=Align.L)
pdf.set_section_title_styles(level0)
pdf.start_section("left aligned section")

# center align
level0 = TextStyle("Helvetica", "", 20, (0, 0, 0), l_margin=Align.C)
pdf.set_section_title_styles(level0)
pdf.start_section("center aligned section")

# right align
level0 = TextStyle("Helvetica", "", 20, (0, 0, 0), l_margin=Align.R)
pdf.set_section_title_styles(level0)
pdf.start_section("right aligned section")

assert_pdf_equal(
pdf, HERE / "test_start_section_horizontal_alignment.pdf", tmp_path
)


def test_set_section_title_styles_with_invalid_arg_type():
pdf = FPDF()
with pytest.raises(TypeError):
Expand Down
Binary file not shown.
Loading