RTF: inline state codes + SetRichText method + Diagnose-Print
User: Mixed-Fonts gehen immer noch nicht — Rhino zeigt nur die letzte
gesetzte Schrift, Bold-State auch verloren.
_runs_to_rtf neu: klassisches Inline-RTF ohne {} Groups. Pro Run werden
ALLE Codes explizit gesetzt (auch Reset-Codes \\b0 \\i0 \\ulnone \\cf0
\\nosupersub) und alle in EINER Zeile mit dem Text:
\\pard \\f0\\cf0\\fs60\\b\\i0\\ulnone\\nosupersub Georgia bold
\\par \\f1\\cf0\\fs20\\b0\\i0\\ulnone\\nosupersub Helvetica regular
Damit ist klar: jeder Run hat eigene state-Definitions. RTF-Parser
nimmt nicht "letzte Code wirkt auf alles".
_commit-Reihenfolge bei RichText geaendert: KEIN _apply_font wenn RTF
verwendet wird. te.Font wuerde sonst die per-Run \\fN Codes
ueberschreiben.
Method-Switch: te.SetRichText(rtf, dimstyle) zuerst probiert (robuster
API), te.RichText property als Fallback, _apply_font als letzter
Notfall.
Diagnostic: RTF wird jetzt mit Length + 300-char-preview gedruckt
("[TEXT-EDITOR] RTF len=... preview=..."). Bitte Log copy-pasten bei
weiteren Issues damit ich seh was tatsaechlich Rhino erreicht.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+61
-44
@@ -149,31 +149,53 @@ class TextEditorBridge(panel_base.BaseBridge):
|
|||||||
except Exception: pass
|
except Exception: pass
|
||||||
te.Plane = plane
|
te.Plane = plane
|
||||||
|
|
||||||
# 1. Defaults (Font, Height, Align) — gilt fuer ALLES
|
# RichText (Phase 2) erzeugen wenn Runs nontrivial sind.
|
||||||
try: te.TextHeight = float(st.get("size") or 0.2)
|
|
||||||
except Exception: pass
|
|
||||||
text_create._apply_font(
|
|
||||||
te,
|
|
||||||
st.get("font") or "Helvetica",
|
|
||||||
st.get("bold"), st.get("italic"),
|
|
||||||
st.get("underline"))
|
|
||||||
text_create._apply_align(te, st.get("align") or "left")
|
|
||||||
|
|
||||||
# 2. Content — RichText wenn vorhanden, sonst PlainText.
|
|
||||||
# RichText-Runs ueberschreiben Defaults fuer Texte mit
|
|
||||||
# eigener Formatierung.
|
|
||||||
rtf = _runs_to_rtf(
|
rtf = _runs_to_rtf(
|
||||||
runs,
|
runs,
|
||||||
st.get("font") or "Helvetica",
|
st.get("font") or "Helvetica",
|
||||||
base_size_m=float(st.get("size") or 0.2)) if runs else None
|
base_size_m=float(st.get("size") or 0.2)) if runs else None
|
||||||
if rtf:
|
if rtf:
|
||||||
|
print("[TEXT-EDITOR] RTF len={} preview={!r}".format(
|
||||||
|
len(rtf), rtf[:300]))
|
||||||
|
|
||||||
|
# Defaults (Height + Align gelten immer)
|
||||||
|
try: te.TextHeight = float(st.get("size") or 0.2)
|
||||||
|
except Exception: pass
|
||||||
|
text_create._apply_align(te, st.get("align") or "left")
|
||||||
|
|
||||||
|
# Content. Bei RichText KEIN _apply_font — sonst ueberschreibt
|
||||||
|
# te.Font die per-Run-Fonts aus der RTF. Stattdessen lassen
|
||||||
|
# wir RichText/SetRichText das selber regeln.
|
||||||
|
if rtf:
|
||||||
|
te.PlainText = text # Initial-Content (RichText ueberschreibt)
|
||||||
|
# Bevorzugt SetRichText(rtf, dimstyle) — robusteres API
|
||||||
|
applied = False
|
||||||
|
try:
|
||||||
|
ds = doc.DimStyles.Current
|
||||||
|
te.SetRichText(rtf, ds)
|
||||||
|
applied = True
|
||||||
|
print("[TEXT-EDITOR] SetRichText OK")
|
||||||
|
except Exception as ex1:
|
||||||
|
print("[TEXT-EDITOR] SetRichText fail:", ex1)
|
||||||
|
if not applied:
|
||||||
try:
|
try:
|
||||||
te.RichText = rtf
|
te.RichText = rtf
|
||||||
except Exception as ex:
|
applied = True
|
||||||
print("[TEXT-EDITOR] RichText set fail:", ex)
|
print("[TEXT-EDITOR] te.RichText = OK")
|
||||||
te.PlainText = text
|
except Exception as ex2:
|
||||||
|
print("[TEXT-EDITOR] te.RichText = fail:", ex2)
|
||||||
|
if not applied:
|
||||||
|
# Letzter Fallback: ohne RTF, mit Toolbar-Defaults
|
||||||
|
text_create._apply_font(
|
||||||
|
te, st.get("font") or "Helvetica",
|
||||||
|
st.get("bold"), st.get("italic"),
|
||||||
|
st.get("underline"))
|
||||||
else:
|
else:
|
||||||
te.PlainText = text
|
te.PlainText = text
|
||||||
|
text_create._apply_font(
|
||||||
|
te, st.get("font") or "Helvetica",
|
||||||
|
st.get("bold"), st.get("italic"),
|
||||||
|
st.get("underline"))
|
||||||
|
|
||||||
# 3. Text-Wrap im Frame — NACH dem Content damit es nicht
|
# 3. Text-Wrap im Frame — NACH dem Content damit es nicht
|
||||||
# durch RichText-Set zurueckgesetzt wird
|
# durch RichText-Set zurueckgesetzt wird
|
||||||
@@ -347,51 +369,46 @@ def _runs_to_rtf(runs, default_font, base_size_m=0.20):
|
|||||||
BASE_PX = max(1.0, base_size_m * 100.0)
|
BASE_PX = max(1.0, base_size_m * 100.0)
|
||||||
|
|
||||||
def _escape_no_par(s):
|
def _escape_no_par(s):
|
||||||
"""Wie _rtf_escape aber OHNE \\par-Umwandlung von \\n
|
|
||||||
(Newlines werden separat als \\par ZWISCHEN Run-Groups emitted)."""
|
|
||||||
out = []
|
out = []
|
||||||
for ch in s:
|
for ch in s:
|
||||||
cp = ord(ch)
|
cp = ord(ch)
|
||||||
if ch == "\\": out.append("\\\\")
|
if ch == "\\": out.append("\\\\")
|
||||||
elif ch == "{": out.append("\\{")
|
elif ch == "{": out.append("\\{")
|
||||||
elif ch == "}": out.append("\\}")
|
elif ch == "}": out.append("\\}")
|
||||||
elif ch == "\n": out.append("\n") # passthrough, wird gesplittet
|
elif ch == "\n": out.append("\n")
|
||||||
elif cp < 128: out.append(ch)
|
elif cp < 128: out.append(ch)
|
||||||
else:
|
else:
|
||||||
v = cp if cp < 0x8000 else cp - 0x10000
|
v = cp if cp < 0x8000 else cp - 0x10000
|
||||||
out.append("\\u{}?".format(v))
|
out.append("\\u{}?".format(v))
|
||||||
return "".join(out)
|
return "".join(out)
|
||||||
|
|
||||||
def _emit_run_group(run, segment):
|
# State-Tracking — alle Codes werden pro Run IMMER emittiert
|
||||||
"""Emittiert {codes segment} wenn segment nicht leer."""
|
# (inkl. Reset-Codes wie \\b0 \\i0). Kein {}-Grouping. Klassisches
|
||||||
if not segment: return
|
# Inline-RTF wie Word/Wordpad es ausgibt.
|
||||||
codes = []
|
|
||||||
codes.append("\\f{}".format(font_idx(run.get("font") or default_font)))
|
|
||||||
ci = color_idx(run.get("color")) if run.get("color") else 0
|
|
||||||
if ci > 0: codes.append("\\cf{}".format(ci))
|
|
||||||
fsp = run.get("fontSizePx")
|
|
||||||
if fsp and abs(fsp - BASE_PX) > 0.1:
|
|
||||||
rtf_fs = max(2, int(round(20.0 * fsp / BASE_PX)))
|
|
||||||
codes.append("\\fs{}".format(rtf_fs))
|
|
||||||
# Nur AKTIVE Format-Toggles innerhalb der Group — Default ausserhalb
|
|
||||||
if run.get("bold"): codes.append("\\b")
|
|
||||||
if run.get("italic"): codes.append("\\i")
|
|
||||||
if run.get("underline"): codes.append("\\ul")
|
|
||||||
if run.get("sup"): codes.append("\\super")
|
|
||||||
elif run.get("sub"): codes.append("\\sub")
|
|
||||||
parts.append("{{{} {}}}".format("".join(codes), segment))
|
|
||||||
|
|
||||||
# Pro Run: nach Newlines splitten. Jedes Segment ist eine eigene
|
|
||||||
# Group, dazwischen \par fuer den Zeilenumbruch.
|
|
||||||
for run in runs:
|
for run in runs:
|
||||||
raw = run.get("text") or ""
|
raw = run.get("text") or ""
|
||||||
segments = raw.split("\n")
|
segments = raw.split("\n")
|
||||||
for i, seg in enumerate(segments):
|
for i, seg in enumerate(segments):
|
||||||
if i > 0:
|
if i > 0:
|
||||||
parts.append("\\par ")
|
parts.append("\\par\n")
|
||||||
esc = _escape_no_par(seg)
|
if not seg: continue
|
||||||
if esc:
|
codes = []
|
||||||
_emit_run_group(run, esc)
|
codes.append("\\f{}".format(font_idx(run.get("font") or default_font)))
|
||||||
|
ci = color_idx(run.get("color")) if run.get("color") else 0
|
||||||
|
codes.append("\\cf{}".format(ci) if ci > 0 else "\\cf0")
|
||||||
|
fsp = run.get("fontSizePx")
|
||||||
|
if fsp and abs(fsp - BASE_PX) > 0.1:
|
||||||
|
rtf_fs = max(2, int(round(20.0 * fsp / BASE_PX)))
|
||||||
|
codes.append("\\fs{}".format(rtf_fs))
|
||||||
|
else:
|
||||||
|
codes.append("\\fs20")
|
||||||
|
codes.append("\\b" if run.get("bold") else "\\b0")
|
||||||
|
codes.append("\\i" if run.get("italic") else "\\i0")
|
||||||
|
codes.append("\\ul" if run.get("underline") else "\\ulnone")
|
||||||
|
if run.get("sup"): codes.append("\\super")
|
||||||
|
elif run.get("sub"): codes.append("\\sub")
|
||||||
|
else: codes.append("\\nosupersub")
|
||||||
|
parts.append("{} {}".format("".join(codes), _escape_no_par(seg)))
|
||||||
|
|
||||||
parts.append("}")
|
parts.append("}")
|
||||||
return "".join(parts)
|
return "".join(parts)
|
||||||
|
|||||||
Reference in New Issue
Block a user