diff --git a/rhino/text_editor.py b/rhino/text_editor.py index 81f23ce..a204196 100644 --- a/rhino/text_editor.py +++ b/rhino/text_editor.py @@ -149,31 +149,53 @@ class TextEditorBridge(panel_base.BaseBridge): except Exception: pass te.Plane = plane - # 1. Defaults (Font, Height, Align) — gilt fuer ALLES - 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. + # RichText (Phase 2) erzeugen wenn Runs nontrivial sind. rtf = _runs_to_rtf( runs, st.get("font") or "Helvetica", base_size_m=float(st.get("size") or 0.2)) if runs else None 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: - te.RichText = rtf - except Exception as ex: - print("[TEXT-EDITOR] RichText set fail:", ex) - te.PlainText = text + 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: + te.RichText = rtf + applied = True + print("[TEXT-EDITOR] te.RichText = OK") + 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: 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 # 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) def _escape_no_par(s): - """Wie _rtf_escape aber OHNE \\par-Umwandlung von \\n - (Newlines werden separat als \\par ZWISCHEN Run-Groups emitted).""" out = [] for ch in s: cp = ord(ch) if 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) else: v = cp if cp < 0x8000 else cp - 0x10000 out.append("\\u{}?".format(v)) return "".join(out) - def _emit_run_group(run, segment): - """Emittiert {codes segment} wenn segment nicht leer.""" - if not segment: return - 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. + # State-Tracking — alle Codes werden pro Run IMMER emittiert + # (inkl. Reset-Codes wie \\b0 \\i0). Kein {}-Grouping. Klassisches + # Inline-RTF wie Word/Wordpad es ausgibt. for run in runs: raw = run.get("text") or "" segments = raw.split("\n") for i, seg in enumerate(segments): if i > 0: - parts.append("\\par ") - esc = _escape_no_par(seg) - if esc: - _emit_run_group(run, esc) + parts.append("\\par\n") + if not seg: continue + 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 + 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("}") return "".join(parts)