Files
Copies/copienator/pdf_cut.py
T

58 lines
2.6 KiB
Python

"""Split a PDF along its cumulative visible page height, in reading order."""
import math
from pathlib import Path
import pymupdf
from copienator.commands.splitting_int import _prepare_split_pages
# Percentages emitted by the GUI have six decimal places. Recover exact page
# boundaries despite rounding, without turning nearby in-page cuts into breaks.
BOUNDARY_TOLERANCE_PERCENT = 0.000001
def cut_position(heights: list[float], percent: float) -> tuple[int, float]:
"""Return (page index, offset); offset zero denotes an exact page break."""
if not heights or any(height <= 0 for height in heights):
raise ValueError("Le PDF doit contenir des pages non vides.")
if not math.isfinite(percent) or not 0 < percent < 100:
raise ValueError("Le pourcentage de coupe doit être strictement entre 0 et 100.")
total = sum(heights)
position = total * percent / 100
start = 0.0
for index, height in enumerate(heights):
if index and abs(percent - start / total * 100) <= BOUNDARY_TOLERANCE_PERCENT:
return index, 0.0
end = start + height
if index + 1 < len(heights) and abs(percent - end / total * 100) <= BOUNDARY_TOLERANCE_PERCENT:
return index + 1, 0.0
if position < end:
return index, position - start
start = end
raise ValueError("Coupe hors du document.")
def split_pdf(source: Path, percent: float, first_path: Path, second_path: Path) -> None:
"""Preserve whole pages; clip only the page actually crossed by the cut."""
with pymupdf.open(source) as document, pymupdf.open() as first, pymupdf.open() as second:
index, offset = cut_position([page.rect.height for page in document], percent)
if index:
first.insert_pdf(document, from_page=0, to_page=index - 1)
if offset == 0:
second.insert_pdf(document, from_page=index)
else:
with pymupdf.open() as page_document:
page_document.insert_pdf(document, from_page=index, to_page=index)
visible = _prepare_split_pages(page_document)[0]
for target, clip in (
(first, pymupdf.Rect(visible.x0, visible.y0, visible.x1, visible.y0 + offset)),
(second, pymupdf.Rect(visible.x0, visible.y0 + offset, visible.x1, visible.y1)),
):
page = target.new_page(width=clip.width, height=clip.height)
page.show_pdf_page(page.rect, page_document, 0, clip=clip)
if index + 1 < len(document):
second.insert_pdf(document, from_page=index + 1)
first.save(first_path)
second.save(second_path)