Cykl: Automatyzacja procesów z n8n — od danych do AI w jednym narzędziu · Część 16/26

Tworzenie spisu treści i łączenie rozdziałów w PDF

Kacper Sieradziński
Kacper Sieradziński5 lutego 2025 · 4 min czytania
Streszczenie
  • Czym jest spis treści w PDF?
  • Instalacja bibliotek
  • Dodawanie prostych zakładek do PDF
  • Hierarchiczne zakładki w PDF
Tworzenie spisu treści i łączenie rozdziałów w PDF

Tworzenie spisu treści i łączenie rozdziałów w PDF

Tworzenie spisu treści i łączenie rozdziałów w PDF to ważny element automatyzacji pracy z dużymi dokumentami. Jeśli generujesz raporty, dokumentację techniczną, książki, oferty, materiały szkoleniowe albo zbiory załączników, sam plik PDF bez zakładek szybko staje się niewygodny. Użytkownik musi przewijać dziesiątki lub setki stron, aby znaleźć właściwy rozdział.

Python pozwala zautomatyzować ten proces. Za pomocą biblioteki pypdf możesz scalać wiele plików PDF, dodawać zakładki, tworzyć strukturę outline, budować hierarchiczny spis treści i porządkować rozdziały w jednym dokumencie. Z kolei reportlab przydaje się wtedy, gdy chcesz wygenerować osobną stronę ze spisem treści.

W tym artykule zobaczysz, jak tworzyć zakładki w PDF, jak budować zagnieżdżony outline, jak łączyć rozdziały w jeden dokument i jak dodać stronę ze spisem treści.

Kurs · 24 lekcje8h 14m
Kurs

Kurs Python od podstaw — PyStart

Zacznij programować w Pythonie od zera. Praktyczny kurs wideo z ćwiczeniami — bez wcześniejszego doświadczenia.

  • 24 lekcje wideo + 80 ćwiczeń
  • Realne bazy z e-commerce
  • Społeczność i code-review
699 zł
Zacznij kurs Pythona

Czym jest spis treści w PDF?

W PDF możesz spotkać dwa rodzaje spisu treści.

Pierwszy to zakładki, nazywane też outline. Są widoczne w panelu bocznym czytnika PDF i pozwalają szybko przejść do wybranej strony. To najwygodniejsza forma nawigacji w dużych dokumentach.

Drugi to strona ze spisem treści umieszczona na początku dokumentu. Wygląda jak klasyczny spis treści w książce: tytuł rozdziału, kropki prowadzące i numer strony.

Najlepsze dokumenty często mają oba elementy: stronę TOC oraz zakładki w panelu bocznym. Dzięki temu PDF jest czytelny zarówno przy drukowaniu, jak i przy pracy na komputerze.

Instalacja bibliotek

Do pracy z zakładkami i scalaniem PDF użyj pypdf. Do tworzenia strony ze spisem treści przyda się reportlab.

Bash
1 pip install pypdf reportlab

pypdf odpowiada za manipulację istniejącymi plikami PDF, a reportlab za generowanie nowych stron PDF od podstaw.

Dodawanie prostych zakładek do PDF

Najprostszy scenariusz to dodanie zakładek do istniejącego dokumentu. Zakładki wskazują konkretne strony. W pypdf numery stron są indeksowane od zera, więc pierwsza strona ma indeks 0.

Dla wygody funkcja poniżej przyjmuje numerację od 1, czyli taką, jaką widzi użytkownik w czytniku PDF.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 from pypdf import PdfReader, PdfWriter def add_bookmarks(input_pdf: str, output_pdf: str, bookmarks: dict[str, int]): """ Dodaje zakładki do dokumentu PDF. bookmarks: { "Wprowadzenie": 1, "Rozdział 1": 5 } """ reader = PdfReader(input_pdf) writer = PdfWriter() for page in reader.pages: writer.add_page(page) total_pages = len(reader.pages) for title, page_number in bookmarks.items(): page_index = page_number - 1 if 0 <= page_index < total_pages: writer.add_outline_item(title, page_index) else: print(f"Pominięto zakładkę '{title}': strona {page_number} nie istnieje.") with open(output_pdf, "wb") as file: writer.write(file) print(f"Dodano {len(bookmarks)} zakładek do pliku: {output_pdf}") add_bookmarks( "dokument.pdf", "dokument_z_zakladkami.pdf", { "Wprowadzenie": 1, "Rozdział 1": 5, "Rozdział 2": 15, "Zakończenie": 30 } )

To dobre rozwiązanie, gdy masz gotowy PDF i znasz strony, od których zaczynają się rozdziały.

Hierarchiczne zakładki w PDF

W większych dokumentach sama płaska lista zakładek może nie wystarczyć. Lepszy efekt daje struktura hierarchiczna: część, rozdział, podrozdział.

Przykład:

1 2 3 4 5 6 Część I Rozdział 1.1 Rozdział 1.2 Podrozdział 1.2.1 Część II Rozdział 2.1

W pypdf można tworzyć takie zagnieżdżenie przez przekazanie rodzica w parametrze parent.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 from pypdf import PdfReader, PdfWriter def add_nested_bookmarks(input_pdf: str, output_pdf: str, structure: dict): """ Dodaje hierarchiczne zakładki do dokumentu PDF. structure: { "Wprowadzenie": 1, "Część I": (5, { "Rozdział 1.1": 5, "Rozdział 1.2": 10 }) } """ reader = PdfReader(input_pdf) writer = PdfWriter() for page in reader.pages: writer.add_page(page) total_pages = len(reader.pages) def add_item(title: str, page_number: int, parent=None): page_index = page_number - 1 if not 0 <= page_index < total_pages: print(f"Pominięto '{title}': strona {page_number} nie istnieje.") return None return writer.add_outline_item(title, page_index, parent=parent) def process_items(items: dict, parent=None): for title, value in items.items(): if isinstance(value, tuple): page_number, children = value bookmark = add_item(title, page_number, parent) if bookmark and children: process_items(children, bookmark) else: add_item(title, value, parent) process_items(structure) with open(output_pdf, "wb") as file: writer.write(file) print(f"Utworzono hierarchiczne zakładki w pliku: {output_pdf}") add_nested_bookmarks( "dokument.pdf", "dokument_hierarchiczny.pdf", { "Wprowadzenie": 1, "Część I": (5, { "Rozdział 1.1": 5, "Rozdział 1.2": (10, { "Podrozdział 1.2.1": 10, "Podrozdział 1.2.2": 12 }) }), "Część II": (20, { "Rozdział 2.1": 20, "Rozdział 2.2": 25 }), "Zakończenie": 30 } )

Hierarchiczne zakładki są szczególnie przydatne w dokumentacji, e-bookach, raportach rocznych i długich materiałach szkoleniowych.

Łączenie rozdziałów PDF z automatycznymi zakładkami

Newsletter · co środę

Python co tydzień — newsletter dla programistów

Otrzymuj codzienne ćwiczenia, ciekawostki z ekosystemu Pythona i wskazówki do rozmów rekrutacyjnych.

2 312 czytelników · ⭐ 4,8

Częsty scenariusz: masz kilka osobnych plików PDF, np. wstep.pdf, rozdzial_1.pdf, rozdzial_2.pdf, zalaczniki.pdf, i chcesz połączyć je w jeden dokument. Przy okazji warto automatycznie dodać zakładki do początku każdego rozdziału.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from pathlib import Path from pypdf import PdfReader, PdfWriter def merge_chapters_with_bookmarks(chapters: list[dict], output_pdf: str): """ Łączy rozdziały PDF i dodaje zakładki. chapters: [ {"file": "wstep.pdf", "title": "Wprowadzenie"}, {"file": "rozdzial_1.pdf", "title": "Rozdział 1"} ] """ writer = PdfWriter() current_page = 0 for chapter in chapters: file_path = chapter["file"] title = chapter["title"] if not Path(file_path).exists(): print(f"Pominięto brakujący plik: {file_path}") continue reader = PdfReader(file_path) writer.add_outline_item(title, current_page) for page in reader.pages: writer.add_page(page) print(f"Dodano: {title} ({len(reader.pages)} stron)") current_page += len(reader.pages) with open(output_pdf, "wb") as file: writer.write(file) print(f"Utworzono dokument: {output_pdf}") merge_chapters_with_bookmarks( [ {"file": "wstep.pdf", "title": "Wprowadzenie"}, {"file": "rozdzial_1.pdf", "title": "Rozdział 1: Podstawy"}, {"file": "rozdzial_2.pdf", "title": "Rozdział 2: Praktyka"}, {"file": "zalaczniki.pdf", "title": "Załączniki"} ], "kompletny_dokument.pdf" )

Ten wzorzec sprawdza się bardzo dobrze, gdy każdy rozdział jest generowany osobno, a dopiero na końcu tworzysz jeden finalny dokument.

Łączenie rozdziałów z podrozdziałami

Możesz też dodać podrozdziały wewnątrz każdego pliku. W takim przypadku page_in_file oznacza numer strony w konkretnym rozdziale, liczony od 1.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 from pathlib import Path from pypdf import PdfReader, PdfWriter def merge_with_toc(chapters: list[dict], output_pdf: str): """ Łączy rozdziały PDF z automatycznym outline. chapters: [ { "file": "rozdzial_1.pdf", "title": "Rozdział 1", "subchapters": [ {"title": "1.1 Wprowadzenie", "page_in_file": 1}, {"title": "1.2 Teoria", "page_in_file": 5} ] } ] """ writer = PdfWriter() current_page = 0 for chapter in chapters: file_path = chapter["file"] title = chapter["title"] if not Path(file_path).exists(): print(f"Pominięto brakujący plik: {file_path}") continue reader = PdfReader(file_path) chapter_start_page = current_page for page in reader.pages: writer.add_page(page) main_bookmark = writer.add_outline_item(title, chapter_start_page) for subchapter in chapter.get("subchapters", []): sub_page = chapter_start_page + subchapter["page_in_file"] - 1 if sub_page < chapter_start_page + len(reader.pages): writer.add_outline_item( subchapter["title"], sub_page, parent=main_bookmark ) else: print(f"Pominięto podrozdział: {subchapter['title']}") current_page += len(reader.pages) with open(output_pdf, "wb") as file: writer.write(file) print(f"Połączono rozdziały i dodano spis treści: {output_pdf}") merge_with_toc( [ { "file": "wstep.pdf", "title": "Wprowadzenie" }, { "file": "rozdzial_1.pdf", "title": "Rozdział 1: Podstawy", "subchapters": [ {"title": "1.1 Wprowadzenie", "page_in_file": 1}, {"title": "1.2 Teoria", "page_in_file": 5} ] }, { "file": "rozdzial_2.pdf", "title": "Rozdział 2: Zaawansowane", "subchapters": [ {"title": "2.1 Przykłady", "page_in_file": 2}, {"title": "2.2 Podsumowanie", "page_in_file": 8} ] } ], "ksiazka_z_outline.pdf" )

To praktyczne rozwiązanie do tworzenia dokumentów, w których struktura rozdziałów jest znana z góry.

Tworzenie strony ze spisem treści

Zakładki są wygodne w czytniku PDF, ale czasem potrzebujesz też klasycznej strony ze spisem treści. Możesz ją wygenerować za pomocą reportlab.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 from reportlab.lib.pagesizes import letter from reportlab.pdfgen import canvas def create_toc_page(toc_items: list[tuple[str, int, int]], output_pdf: str): """ Tworzy stronę PDF ze spisem treści. toc_items: [ ("Wprowadzenie", 1, 0), ("Rozdział 1", 5, 0), ("Podrozdział 1.1", 7, 1) ] Trzeci element oznacza poziom zagnieżdżenia. """ c = canvas.Canvas(output_pdf, pagesize=letter) width, height = letter c.setFont("Helvetica-Bold", 20) c.drawString(50, height - 50, "Spis treści") y = height - 100 c.setFont("Helvetica", 12) for title, page, level in toc_items: indent = 50 + level * 20 if y < 50: c.showPage() y = height - 50 c.setFont("Helvetica", 12) c.drawString(indent, y, title) title_width = c.stringWidth(title, "Helvetica", 12) dots_start = indent + title_width + 10 dots_end = width - 100 x = dots_start while x < dots_end: c.drawString(x, y, ".") x += 5 c.drawRightString(width - 50, y, str(page)) y -= 20 c.save() print(f"Utworzono stronę spisu treści: {output_pdf}") create_toc_page( [ ("Wprowadzenie", 1, 0), ("Część I", 5, 0), ("Rozdział 1.1", 5, 1), ("Rozdział 1.2", 10, 1), ("Podrozdział 1.2.1", 12, 2), ("Część II", 20, 0), ("Zakończenie", 30, 0) ], "toc.pdf" )

Taki spis treści jest statyczną stroną PDF. Możesz go wydrukować razem z dokumentem lub dodać na początku finalnego pliku.

Łączenie strony TOC z głównym dokumentem

Kurs · 24 lekcje8h 14m
Kurs

Kurs Python dla początkujących — PyStart

Zacznij programować w Pythonie! Idealne dla osób bez doświadczenia. Praktyczne zadania, projekty i wsparcie społeczności.

  • 24 lekcje wideo + 80 ćwiczeń
  • Realne bazy z e-commerce
  • Społeczność i code-review
699 zł
Rozpocznij naukę

Po utworzeniu strony spisu treści możesz dodać ją na początek dokumentu.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pypdf import PdfReader, PdfWriter def merge_toc_with_document(toc_pdf: str, main_pdf: str, output_pdf: str): """Dodaje stronę spisu treści na początek dokumentu.""" writer = PdfWriter() toc_reader = PdfReader(toc_pdf) main_reader = PdfReader(main_pdf) for page in toc_reader.pages: writer.add_page(page) for page in main_reader.pages: writer.add_page(page) with open(output_pdf, "wb") as file: writer.write(file) print(f"Połączono TOC z dokumentem: {output_pdf}") merge_toc_with_document( "toc.pdf", "kompletny_dokument.pdf", "dokument_z_toc.pdf" )

Ważna uwaga: jeśli dodasz stronę spisu treści na początku, numery stron w zakładkach i na stronie TOC mogą wymagać przesunięcia. Najlepiej uwzględnić to już na etapie generowania struktury dokumentu.

Łączenie rozdziałów, strony TOC i zakładek w jednym pipeline

Najlepszy efekt daje jeden proces, który:

  1. zna listę rozdziałów,
  2. oblicza strony startowe,
  3. tworzy stronę spisu treści,
  4. scala TOC i rozdziały,
  5. dodaje zakładki z przesunięciem stron.

Poniżej uproszczony przykład takiego procesu.

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 from pathlib import Path from pypdf import PdfReader, PdfWriter def build_pdf_book(chapters: list[dict], toc_pdf: str, output_pdf: str): """Buduje finalny PDF z TOC, rozdziałami i zakładkami.""" writer = PdfWriter() toc_reader = PdfReader(toc_pdf) toc_pages_count = len(toc_reader.pages) for page in toc_reader.pages: writer.add_page(page) current_page = toc_pages_count for chapter in chapters: file_path = chapter["file"] title = chapter["title"] if not Path(file_path).exists(): print(f"Pominięto brakujący plik: {file_path}") continue reader = PdfReader(file_path) chapter_start = current_page for page in reader.pages: writer.add_page(page) main_bookmark = writer.add_outline_item(title, chapter_start) for subchapter in chapter.get("subchapters", []): sub_page = chapter_start + subchapter["page_in_file"] - 1 writer.add_outline_item( subchapter["title"], sub_page, parent=main_bookmark ) current_page += len(reader.pages) with open(output_pdf, "wb") as file: writer.write(file) print(f"Utworzono finalny dokument: {output_pdf}") chapters = [ { "file": "wstep.pdf", "title": "Wprowadzenie" }, { "file": "rozdzial_1.pdf", "title": "Rozdział 1: Podstawy", "subchapters": [ {"title": "1.1 Start", "page_in_file": 1}, {"title": "1.2 Przykłady", "page_in_file": 4} ] }, { "file": "rozdzial_2.pdf", "title": "Rozdział 2: Praktyka" } ] build_pdf_book(chapters, "toc.pdf", "finalny_dokument.pdf")

To dobry fundament pod automatyczne składanie większych dokumentów z wielu plików źródłowych.

Dobre praktyki przy tworzeniu spisu treści PDF

Przy automatyzacji dużych dokumentów PDF warto trzymać się kilku zasad:

  • używaj jednej numeracji stron w konfiguracji, najlepiej od 1,
  • pamiętaj, że pypdf używa indeksów stron od 0,
  • przed dodaniem zakładki sprawdzaj, czy strona istnieje,
  • przy dodaniu strony TOC uwzględnij przesunięcie numeracji,
  • nazywaj pliki rozdziałów z numerami, np. 01_wstep.pdf,
  • dodawaj zakładki główne i podzakładki,
  • testuj dokument w kilku czytnikach PDF,
  • zapisuj finalny PDF jako nowy plik, nie nadpisuj źródeł,
  • trzymaj strukturę rozdziałów w słowniku lub pliku JSON,
  • przy dużych dokumentach loguj liczbę dodanych stron.

Dzięki temu finalny dokument będzie czytelny, łatwy w nawigacji i mniej podatny na błędy.

Podsumowanie

Tworzenie spisu treści i łączenie rozdziałów w PDF w Pythonie pozwala budować profesjonalne dokumenty z wygodną nawigacją. Dzięki pypdf możesz scalać rozdziały, dodawać proste i hierarchiczne zakładki oraz kontrolować strony startowe każdej sekcji. Dzięki reportlab możesz wygenerować klasyczną stronę ze spisem treści.

Najważniejsze jest dobre zaplanowanie struktury dokumentu: lista rozdziałów, numery stron, podrozdziały i ewentualne przesunięcie po dodaniu strony TOC. Gdy ta struktura jest opisana w kodzie, cały proces można powtarzać automatycznie dla kolejnych raportów, książek, ofert lub dokumentacji.

Jeśli chcesz zacząć praktycznie, przygotuj trzy pliki PDF jako rozdziały, połącz je w jeden dokument i dodaj zakładkę do początku każdego rozdziału. Potem rozbuduj skrypt o podrozdziały i osobną stronę spisu treści.

➡️ Następny artykuł

Teraz, gdy znasz różne techniki pracy z PDF, poznaj dostępne narzędzia i wybierz najlepsze dla swoich potrzeb:

Narzędzia do PDF w Pythonie: przegląd bibliotek — porównanie bibliotek pypdf, pdfminer.six, reportlab, WeasyPrint i PyMuPDF wraz z zaleceniami dotyczącymi wyboru narzędzia.

Część 17 z 26

Walidacja i czyszczenie tekstu w Pythonie

druga lekcja cyklu „Automatyzacja procesów z n8n — od danych do AI w jednym narzędziu"

Czytaj kolejny →