py-pdf / fpdf2

Simple PDF generation for Python
https://py-pdf.github.io/fpdf2/
GNU Lesser General Public License v3.0
1.05k stars 241 forks source link

Feature request: new and more functional TOC class #1122

Open CayhanBoran opened 6 months ago

CayhanBoran commented 6 months ago

Hey everyone, I am trying to create a table of contents with different font style for landscape format. The aligments of dots in the texts are not aligning. I tried different formats, but the only best option is using the Courier format.

used function for table of contents:

        pdf.set_font("Arial", size=12)
        for section in outline:

            link = pdf.add_link(page=section.page_number, x=80, y=0)
            if pdf.get_y() >= 170: # passing the header for the next page
                pdf.page += 1
                pdf.set_y(35)
                pdf.set_x(10)
                pdf.cell(w=pdf.epw,
                         h=pdf.font_size,
                         text=f'{" " * section.level * 2} {section.name} {"." * (60 - section.level*2 - len(section.name))} {section.page_number}',
                         new_x="LMARGIN",
                         new_y="NEXT",
                         align="C",
                         link=link)  # type: ignore
            else:
                pdf.cell(w=pdf.epw,
                         h=pdf.font_size,
                         text=f'{" " * section.level * 2} {section.name} {"." * (60 - section.level*2 - len(section.name))} {section.page_number}',
                         new_x="LMARGIN",
                         new_y="NEXT",
                         align="C",
                         link=link)  # type: ignore

If I make it with Courier and font size 12:

courier_font

If I make it with Arial and font size 12:

arial_font

If you could help me and solve this issue of font style in table of contents, I would be really happy. Thanks!

Lucas-C commented 6 months ago

Hi @CayhanBoran!

Basically your issue is that Courrier is a monospace (or fixed-width) font: https://en.wikipedia.org/wiki/Monospaced_font Whereas Arial is not monospace, leading to varying alignements in the resulting table of content.

You could try using a render function that right-aligns page numbers, instead of using dots, like this:

def render_toc_with_right_aligned_page_numbers(pdf, outline):
    pdf.set_font("Helvetica", size=16)
    for section in outline:
        link = pdf.add_link(page=section.page_number)
        pdf.cell(text=f'{" " * section.level * 2} {section.name}', link=link, new_x="LEFT")
        pdf.cell(text=f"{section.page_number}", link=link, w=pdf.epw, align="R")
        pdf.ln()

Does this answer solve your issue? 🙂

CayhanBoran commented 6 months ago

Yes solves partially, I wanted to use Helvetica or Arial with dots, but if that is not the case. I guess I will not use dots in the table of contents. Thanks.

gmischler commented 6 months ago

The underlying issue is that using dots to align the page number is a very ugly hack, which was cobbled together decades ago, and never fixed.

Ideally, someone would submit a PR creating a new and more functional TOC class, which contains adaptable TocEntry instances. Desirable features might be:

Any takers?

Lucas-C commented 3 months ago

Just a note: following @gmischler comment, I renamed this issue into Feature request: new and more functional TOC class and added the up-for-grabs label.