Add pipeline code
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"""Read a PDF's text layer and image positions with poppler. No guessing: coordinates in, text out."""
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def _run(cmd):
|
||||
return subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
|
||||
|
||||
|
||||
def page_geometry(pdf):
|
||||
"""[(width, height)] per page as displayed (rotation applied)."""
|
||||
n = int(re.search(r"^Pages:\s+(\d+)", _run(["pdfinfo", pdf]), re.M).group(1))
|
||||
out = _run(["pdfinfo", "-f", "1", "-l", str(n), pdf])
|
||||
sizes = {int(m[0]): (float(m[1]), float(m[2])) for m in re.findall(r"^Page\s+(\d+) size:\s+([\d.]+) x ([\d.]+)", out, re.M)}
|
||||
rots = {int(m[0]): int(m[1]) for m in re.findall(r"^Page\s+(\d+) rot:\s+(\d+)", out, re.M)}
|
||||
geo = []
|
||||
for p in range(1, n + 1):
|
||||
w, h = sizes[p]
|
||||
if rots.get(p, 0) in (90, 270):
|
||||
w, h = h, w
|
||||
geo.append((w, h))
|
||||
return geo
|
||||
|
||||
|
||||
def halves(width, height):
|
||||
"""A landscape page that holds two book pages is read as two units, left then right."""
|
||||
if width > height * 1.2:
|
||||
return [("L", 0, width / 2), ("R", width / 2, width / 2)]
|
||||
return [(None, 0, width)]
|
||||
|
||||
|
||||
def chars_per_page(pdf):
|
||||
"""Number of non-space characters in the text layer of each page."""
|
||||
n = len(page_geometry(pdf))
|
||||
out = []
|
||||
for p in range(1, n + 1):
|
||||
txt = _run(["pdftotext", "-f", str(p), "-l", str(p), pdf, "-"])
|
||||
out.append(len(re.sub(r"\s", "", txt)))
|
||||
return out
|
||||
|
||||
|
||||
def units(pdf):
|
||||
"""Reading units in order: [{'pdf_page', 'half', 'text'}]. Text keeps the layout (indentation, hyphens)."""
|
||||
result = []
|
||||
for p, (w, h) in enumerate(page_geometry(pdf), start=1):
|
||||
for half, x, width in halves(w, h):
|
||||
text = _run(["pdftotext", "-layout", "-f", str(p), "-l", str(p),
|
||||
"-x", str(int(x)), "-y", "0", "-W", str(int(width)), "-H", str(int(h)), pdf, "-"])
|
||||
result.append({"pdf_page": p, "half": half, "text": text.replace("\f", "")})
|
||||
return result
|
||||
|
||||
|
||||
def images(pdf):
|
||||
"""Images with their page, half and pixel size: [{'pdf_page', 'half', 'width', 'height'}]."""
|
||||
geo = page_geometry(pdf)
|
||||
found = []
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
subprocess.run(["pdftohtml", "-xml", "-q", pdf, os.path.join(tmp, "o")], check=True, capture_output=True)
|
||||
root = ET.parse(os.path.join(tmp, "o.xml")).getroot()
|
||||
for page in root.iter("page"):
|
||||
p = int(page.get("number"))
|
||||
w, h = geo[p - 1]
|
||||
scale = float(page.get("width")) / w
|
||||
for img in page.iter("image"):
|
||||
cx = (float(img.get("left")) + float(img.get("width")) / 2) / scale
|
||||
half = None
|
||||
for name, x, width in halves(w, h):
|
||||
if x <= cx < x + width:
|
||||
half = name
|
||||
found.append({"pdf_page": p, "half": half,
|
||||
"width": round(float(img.get("width")) / scale),
|
||||
"height": round(float(img.get("height")) / scale)})
|
||||
return found
|
||||
Reference in New Issue
Block a user