From b2812c7b24356e5f807c9da7cc59ed6ee05a1653 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 5 Mar 2026 22:08:14 +0100 Subject: [PATCH] Require minimum length at right hand side of apostrophe too --- lib/Epub/Epub/hyphenation/Hyphenator.cpp | 13 +++++++++++-- lib/Epub/Epub/hyphenation/Hyphenator.h | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/Epub/Epub/hyphenation/Hyphenator.cpp b/lib/Epub/Epub/hyphenation/Hyphenator.cpp index 28c3f44f..31e0b35c 100644 --- a/lib/Epub/Epub/hyphenation/Hyphenator.cpp +++ b/lib/Epub/Epub/hyphenation/Hyphenator.cpp @@ -98,6 +98,8 @@ void appendSegmentPatternBreaks(const std::vector& cps, const Lan void appendApostropheContractionBreaks(const std::vector& cps, std::vector& outBreaks) { + constexpr size_t kMinLeftSegmentLen = 3; + constexpr size_t kMinRightSegmentLen = 2; size_t segmentStart = 0; for (size_t i = 0; i < cps.size(); ++i) { @@ -111,8 +113,15 @@ void appendApostropheContractionBreaks(const std::vector& cps, } } - // Avoid stranding very short clitics like "l'" or "d'" at line end. - if (leftPrefixLen >= 3) { + size_t rightSuffixLen = 0; + for (size_t j = i + 1; j < cps.size() && !isSegmentSeparator(cps[j].value); ++j) { + if (isAlphabetic(cps[j].value)) { + ++rightSuffixLen; + } + } + + // Avoid stranding short clitics like "l'"/"d'" or tiny suffixes like "'t". + if (leftPrefixLen >= kMinLeftSegmentLen && rightSuffixLen >= kMinRightSegmentLen) { outBreaks.push_back({cps[i + 1].byteOffset, false}); } } diff --git a/lib/Epub/Epub/hyphenation/Hyphenator.h b/lib/Epub/Epub/hyphenation/Hyphenator.h index 9efdd8cb..74886ba6 100644 --- a/lib/Epub/Epub/hyphenation/Hyphenator.h +++ b/lib/Epub/Epub/hyphenation/Hyphenator.h @@ -26,7 +26,8 @@ class Hyphenator { // 2. Apostrophe contractions between letters (e.g. all'improvviso). // Liang patterns are run per alphabetic segment around apostrophes. // A direct break at the apostrophe boundary is allowed only when the left - // segment has at least 3 letters to avoid stranding short clitics (e.g. l', d'). + // segment has at least 3 letters and the right segment has at least 2 letters, + // avoiding short clitics (e.g. l', d') and short contraction tails (e.g. can't). // 3. Language-specific Liang patterns (e.g. German de_patterns). // Example: "Quadratkilometer" -> Qua|drat|ki|lo|me|ter. // 4. Fallback every-N-chars splitting (only when includeFallback is true AND no