58 lines
2.6 KiB
Python
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)
|