RTF: \line statt \par fuer Newlines zwischen Runs
Rhinos TextEntity-RTF-Parser rendert \par offenbar nicht als Zeilenumbruch innerhalb eines Textfeldes. \line dagegen funktioniert als soft line break. Globales pending_newlines-Counting ueber alle Runs hinweg: jedes \n im Text-Run wird zu einem \line, der erst VOR dem naechsten echten Text emittiert wird. Damit bleiben auch Leerzeilen (mehrere \n hintereinander) als mehrere \line erhalten. Why: User-Vergleich Screenshots — WYSIWYG zeigt korrekte Leerzeile zwischen Heading und Paragraph, Rhino rendert beide Runs auf der gleichen Zeile konkateniert. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+26
-12
@@ -377,19 +377,15 @@ def _runs_to_rtf(runs, default_font, base_size_m=0.20):
|
|||||||
out.append("\\u{}?".format(v))
|
out.append("\\u{}?".format(v))
|
||||||
return "".join(out)
|
return "".join(out)
|
||||||
|
|
||||||
|
# Globales Newline-Tracking ueber ALLE Runs hinweg — ein `\n`
|
||||||
|
# zwischen Runs (= eigener Newline-Run) ergibt EINEN \\line.
|
||||||
|
# Mehrere aufeinanderfolgende `\n` ergeben entsprechend mehrere
|
||||||
|
# \\line in Reihe (= Leerzeile).
|
||||||
body_parts = []
|
body_parts = []
|
||||||
for run in runs:
|
pending_newlines = 0 # Newlines die noch emittiert werden muessen
|
||||||
raw = run.get("text") or ""
|
|
||||||
segments = raw.split("\n")
|
def _emit_text_segment(run, seg):
|
||||||
for i, seg in enumerate(segments):
|
if not seg: return
|
||||||
if i > 0:
|
|
||||||
body_parts.append("\\par\n")
|
|
||||||
if not seg:
|
|
||||||
# Leere Section (aufeinanderfolgende \\n) → leerer
|
|
||||||
# Paragraph mit einem Space, damit Rhinos Parser eine
|
|
||||||
# echte Leerzeile rendert
|
|
||||||
body_parts.append(" ")
|
|
||||||
continue
|
|
||||||
codes = []
|
codes = []
|
||||||
codes.append("\\f{}".format(font_idx(run.get("font") or default_font)))
|
codes.append("\\f{}".format(font_idx(run.get("font") or default_font)))
|
||||||
ci = color_idx(run.get("color")) if run.get("color") else 0
|
ci = color_idx(run.get("color")) if run.get("color") else 0
|
||||||
@@ -408,6 +404,24 @@ def _runs_to_rtf(runs, default_font, base_size_m=0.20):
|
|||||||
else: codes.append("\\nosupersub")
|
else: codes.append("\\nosupersub")
|
||||||
body_parts.append("{} {}".format("".join(codes), _escape_no_par(seg)))
|
body_parts.append("{} {}".format("".join(codes), _escape_no_par(seg)))
|
||||||
|
|
||||||
|
for run in runs:
|
||||||
|
raw = run.get("text") or ""
|
||||||
|
# Jede \n im Text → \\line; alle \\line werden VOR der naechsten
|
||||||
|
# Text-Section emittiert. Damit bleiben auch leere Zeilen erhalten.
|
||||||
|
segments = raw.split("\n")
|
||||||
|
for i, seg in enumerate(segments):
|
||||||
|
if i > 0:
|
||||||
|
pending_newlines += 1
|
||||||
|
if seg:
|
||||||
|
# Pending Newlines emittieren bevor Text kommt
|
||||||
|
for _ in range(pending_newlines):
|
||||||
|
body_parts.append("\\line ")
|
||||||
|
pending_newlines = 0
|
||||||
|
_emit_text_segment(run, seg)
|
||||||
|
# Trailing newlines auch noch emittieren
|
||||||
|
for _ in range(pending_newlines):
|
||||||
|
body_parts.append("\\line ")
|
||||||
|
|
||||||
# ────────────────────────────────────────────────────────────────
|
# ────────────────────────────────────────────────────────────────
|
||||||
# PASS 2: RTF-Header mit JETZT vollstaendigen Tables + Body
|
# PASS 2: RTF-Header mit JETZT vollstaendigen Tables + Body
|
||||||
# ────────────────────────────────────────────────────────────────
|
# ────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user