When no margin detected, reserve a bit more space for comments
This commit is contained in:
1 parent
e7094d850e
commit
78a7b7679c
17 files changed
+357
-28
No files matched your search
@@ -0,0 +1,75 @@
|
||||
"""Answer provenance and reproducible annotation spacing (200 dpi pixels)."""
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
import pymupdf
|
||||
|
||||
from copienator import configuration
|
||||
from copienator.json_io import atomic_write_json, read_json
|
||||
|
||||
|
||||
def fingerprint(path):
|
||||
return sha256(Path(path).read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def metadata_path(pdf):
|
||||
return Path(pdf).with_suffix('.layout.json')
|
||||
|
||||
|
||||
def answer_parts(pdf):
|
||||
"""Ignore stale sidecars after a PDF is replaced or manually edited."""
|
||||
if pdf is None or not Path(pdf).is_file():
|
||||
return []
|
||||
try:
|
||||
data = read_json(metadata_path(pdf), default={})
|
||||
if data.get('sha256') == fingerprint(pdf):
|
||||
parts = data.get('parts', [])
|
||||
if isinstance(parts, list) and all(isinstance(part, dict) for part in parts):
|
||||
return parts
|
||||
except (OSError, ValueError, TypeError, AttributeError):
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
def save_parts(pdf, parts):
|
||||
atomic_write_json(metadata_path(pdf), {
|
||||
'version': 1, 'sha256': fingerprint(pdf), 'parts': parts,
|
||||
})
|
||||
|
||||
|
||||
def page_parts(pdf):
|
||||
parts = answer_parts(pdf)
|
||||
if not Path(pdf).is_file():
|
||||
return []
|
||||
with pymupdf.open(pdf) as document:
|
||||
if len(parts) == len(document):
|
||||
return parts
|
||||
return [{'margin_status': 'unknown'} for _ in document]
|
||||
|
||||
|
||||
def copy_answer_pdf(source, destination):
|
||||
parts = answer_parts(source)
|
||||
shutil.copy(source, destination)
|
||||
save_parts(destination, parts)
|
||||
|
||||
|
||||
def move_answer_pdf(source, destination):
|
||||
parts = answer_parts(source)
|
||||
shutil.move(str(source), str(destination))
|
||||
save_parts(destination, parts)
|
||||
metadata_path(source).unlink(missing_ok=True)
|
||||
|
||||
|
||||
def layout_for(pdf):
|
||||
low = any(part.get('margin_status') == 'low' for part in answer_parts(pdf))
|
||||
margin = (configuration.ANNOTATION_LOW_MARGIN_LEFT if low
|
||||
else configuration.ANNOTATION_MARGIN_LEFT)
|
||||
if type(margin) is not int or margin < 0:
|
||||
raise ValueError('Annotation left margins must be non-negative integers')
|
||||
return {'version': 1, 'margin_left': margin, 'narrow_left_pad_limit': 300}
|
||||
|
||||
|
||||
def legacy_layout():
|
||||
# Old annotation scans were produced with these literal constants.
|
||||
return {'version': 1, 'margin_left': 300, 'narrow_left_pad_limit': 300}
|
||||
Reference in new issue
Block a user