From 6311227992ba509b5e5e3256504980bf4632742d Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Fri, 17 Jul 2026 05:10:53 +0200 Subject: Fix markerless line-break join gluing whole words without a space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TextBereiniger.verbindeUmbrueche assumed any letter-ending, no-trailing-space line followed by a lowercase continuation was a hyphen-less mid-word split (PDF extraction artifact) and joined it with zero separator. That assumption breaks for deliberate word-boundary breaks, e.g. the short-label/hanging- indent definition format in the UWG Anhang ("...Nachhaltigkeitssiegels" + "das Anbringen..."), producing glued words in the rendered synopsis. Gate the markerless join on the candidate line reaching a locally-typical "full column width" (90th percentile in a ±20-line window), since automatic wraps always land near the column edge while deliberate breaks don't. The window is local rather than document-wide because some source PDFs mix column widths within one document (narrower Regelungstext vs. wider Begründung), which a global statistic would otherwise penalize. Also route the single-unit Neufassung fallback through the existing normalisiereZitatText normalization, matching its sibling code paths, so that internal line breaks now more often preserved by the fix above don't leak into the HTML output as spurious line breaks instead. Co-Authored-By: Claude Sonnet 5 Change-Id: I0a79f22f9286cd4a90eea32bad9b54cb4a082cf4 --- src/test/java/eu/mulk/aendggner/EndToEndTest.java | 5 +++++ .../aenderung/parse/TextBereinigerTest.java | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'src/test/java/eu') diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index d4b0b17..a395fa1 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -84,6 +84,11 @@ class EndToEndTest { assertThat(text).doesNotContain("Dieses Gesetz dient der Umsetzung der Richtlinie (EU)"); assertThat(text).contains("wird wie folgt geändert"); + // Regression: Stichwort/Definition-Umbruch im Anhang darf nicht ohne Leerzeichen verklebt + // werden (kurze Stichwort-Zeile vor hängend eingerückter Definition, kein Wort wird getrennt). + assertThat(text).contains("Nachhaltigkeitssiegels\ndas Anbringen"); + assertThat(text).doesNotContain("Nachhaltigkeitssiegelsdas"); + var parseErgebnis = new AenderungsgesetzParser().parse(text, gesetz, null); assertThat(parseErgebnis.artikel()).containsExactly("1"); assertThat(parseErgebnis.befehle().size()).isGreaterThanOrEqualTo(10); diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java index 98dc1d3..4ca86cf 100644 --- a/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java @@ -99,6 +99,27 @@ class TextBereinigerTest { assertThat(TextBereiniger.bereinige(roh)).startsWith("die Wirk\nund Hilfsstoffe"); } + @Test + void ziehtMarkerlosNichtBeiKurzemStichwortVorEingerueckterDefinitionZusammen() { + // Definitionslisten-Muster (neues BGBl-Format, „2a. …Siegel“ + hängend eingerückte + // Definition): die Stichwort-Zeile endet ohne Trailing-Space, ist aber deutlich kürzer als + // die umgebenden Volltextzeilen — kein Wort wird getrennt, der Umbruch ist bewusst. + var roh = + "1. „Betriebsstoff“ jeder Bestandteil einer Ware, der wiederholt verbraucht wird und ersetzt" + + " oder aufgefüllt werden muss, damit die Ware ordnungsgemäß funktioniert. \n" + + "2. „Haltbarkeit“ die Fähigkeit der Waren, ihre erforderlichen Funktionen und ihre" + + " Leistung bei normaler Verwendung über einen längeren Zeitraum zu bewahren. \n" + + "3. „Zertifizierungssystem“ ein System der Überprüfung durch Dritte, durch das" + + " bestätigt wird, dass ein Produkt bestimmten Anforderungen entspricht. \n" + + "2a. unzulässiges Anbringen eines Nachhaltigkeitssiegels\n" + + "das Anbringen eines Nachhaltigkeitssiegels, das weder auf einem Zertifizierungssystem" + + " beruht noch von \n" + + "staatlichen Stellen festgesetzt wurde;"; + + assertThat(TextBereiniger.bereinige(roh)) + .contains("Nachhaltigkeitssiegels\ndas Anbringen eines Nachhaltigkeitssiegels"); + } + @Test void repariertInvertierteZitatzeichenAnAbsatzmarkern() { // BMJV-Vorlagen zeichnen das hängende „ nach dem Absatzmarker bzw. der Paragraphenangabe. -- cgit v1.2.1