mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
feat: Add original text preservation in chunk overlays and enhance whitespace handling in EPUB export
This commit is contained in:
@@ -18,6 +18,7 @@ from abogen.text_extractor import ExtractedChapter, ExtractionResult
|
|||||||
class ChunkOverlay:
|
class ChunkOverlay:
|
||||||
id: str
|
id: str
|
||||||
text: str
|
text: str
|
||||||
|
original_text: Optional[str]
|
||||||
start: Optional[float]
|
start: Optional[float]
|
||||||
end: Optional[float]
|
end: Optional[float]
|
||||||
speaker_id: str
|
speaker_id: str
|
||||||
@@ -259,6 +260,7 @@ class EPUB3PackageBuilder:
|
|||||||
ChunkOverlay(
|
ChunkOverlay(
|
||||||
id=normalized_id,
|
id=normalized_id,
|
||||||
text=text or self.extraction.chapters[chapter_index].text,
|
text=text or self.extraction.chapters[chapter_index].text,
|
||||||
|
original_text=None,
|
||||||
start=_safe_float(marker.get("start")),
|
start=_safe_float(marker.get("start")),
|
||||||
end=_safe_float(marker.get("end")),
|
end=_safe_float(marker.get("end")),
|
||||||
speaker_id=speaker_id,
|
speaker_id=speaker_id,
|
||||||
@@ -284,11 +286,11 @@ class EPUB3PackageBuilder:
|
|||||||
chunk_html = "<p></p>"
|
chunk_html = "<p></p>"
|
||||||
original_block = ""
|
original_block = ""
|
||||||
if chapter.chunks:
|
if chapter.chunks:
|
||||||
original_text = "".join(chunk.text or "" for chunk in chapter.chunks)
|
original_text = "".join((chunk.original_text if chunk.original_text is not None else (chunk.text or "")) for chunk in chapter.chunks)
|
||||||
if original_text:
|
if original_text:
|
||||||
safe_original = html.escape(original_text)
|
safe_original = html.escape(original_text)
|
||||||
original_block = (
|
original_block = (
|
||||||
" <pre class=\"chapter-original\" hidden>\n"
|
" <pre class=\"chapter-original\" hidden=\"hidden\" aria-hidden=\"true\">\n"
|
||||||
f"{safe_original}\n"
|
f"{safe_original}\n"
|
||||||
" </pre>"
|
" </pre>"
|
||||||
)
|
)
|
||||||
@@ -701,12 +703,24 @@ def _restore_original_chunk_text(chapter_text: str, overlays: List[ChunkOverlay]
|
|||||||
if match is None and cursor:
|
if match is None and cursor:
|
||||||
match = _search_original_span(chapter_text, candidate, 0)
|
match = _search_original_span(chapter_text, candidate, 0)
|
||||||
if match is None:
|
if match is None:
|
||||||
|
if chunk.original_text is None:
|
||||||
|
chunk.original_text = chunk.text
|
||||||
|
chunk.text = _prepare_display_text(chunk.text or "")
|
||||||
continue
|
continue
|
||||||
start, end = match
|
start, end = match
|
||||||
chunk.text = chapter_text[start:end]
|
segment = chapter_text[start:end]
|
||||||
|
chunk.original_text = segment
|
||||||
|
chunk.text = _prepare_display_text(segment)
|
||||||
cursor = end
|
cursor = end
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_display_text(value: str) -> str:
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
cleaned = re.sub(r"(?:[ \t]*\r?\n)+\Z", "", value)
|
||||||
|
return cleaned if cleaned else ""
|
||||||
|
|
||||||
|
|
||||||
def _search_original_span(source: str, normalized: str, start: int) -> Optional[Tuple[int, int]]:
|
def _search_original_span(source: str, normalized: str, start: int) -> Optional[Tuple[int, int]]:
|
||||||
if not normalized:
|
if not normalized:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import html
|
||||||
|
import re
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
from abogen.epub3.exporter import build_epub3_package
|
from abogen.epub3.exporter import build_epub3_package
|
||||||
@@ -167,5 +169,12 @@ def test_epub3_preserves_original_whitespace(tmp_path) -> None:
|
|||||||
with zipfile.ZipFile(output_path) as archive:
|
with zipfile.ZipFile(output_path) as archive:
|
||||||
chapter_doc = archive.read("OEBPS/text/chapter_0001.xhtml").decode("utf-8")
|
chapter_doc = archive.read("OEBPS/text/chapter_0001.xhtml").decode("utf-8")
|
||||||
assert "Line one with double spaces." in chapter_doc
|
assert "Line one with double spaces." in chapter_doc
|
||||||
normalized = chapter_doc.replace(" ", "")
|
|
||||||
assert "Second line\n\nThird paragraph." in normalized
|
chunk_section = chapter_doc.replace(" ", "")
|
||||||
|
assert "Second line" in chunk_section
|
||||||
|
assert "Third paragraph." in chunk_section
|
||||||
|
|
||||||
|
match = re.search(r"<pre class=\"chapter-original\"[^>]*>(.*?)</pre>", chapter_doc, re.DOTALL)
|
||||||
|
assert match is not None
|
||||||
|
original_text = html.unescape(match.group(1))
|
||||||
|
assert "Second line\n\nThird paragraph." in original_text
|
||||||
Reference in New Issue
Block a user