Zeilenhöhe-Regler statt "+ Text"; Linienstil editierbar; DOSSIER-Audit gesichert
- "+ Text"-Knopf entfernt (war ohnehin immer deaktiviert, kein Werkzeug dahinter — Text wird ein eigenständiges Zeichenwerkzeug, AUDIT B1). An seiner Stelle ein Zeilenhöhe-Regler (Stepper, Vielfaches der Schriftgrösse) — neues Paragraph.lineHeight, durchgereicht bis in HTML-Vorschau (line-height) und SVG-Render (kumulierte Zeilen- Vorschübe statt festem lineGap). - Linienstil im Attribute-Panel war rein informativ (kein Setter im Host-Kontrakt). onSetSelectionLineStyle ergänzt (nur Drawing2D), jetzt echtes Dropdown statt "—"-Anzeige. - docs/design/dossier-feature-audit.md: der ausführliche A1-A6/B1-B4/ C1-C3/D1-D3/E-Auditbericht aus einer alten Session gesichert (lag bisher nur im Transkript, nicht im Repo) — Quelle der HANDOVER.md- AUDIT-Kürzel.
This commit is contained in:
+18
-6
@@ -85,10 +85,13 @@ export function docToHtml(doc: RichTextDoc, opts: HtmlOptions = {}): string {
|
||||
}
|
||||
|
||||
function paragraphToHtml(p: Paragraph, basePt: number, color?: string): string {
|
||||
const align = p.align && p.align !== "left" ? ` style="text-align:${p.align}"` : "";
|
||||
if (p.runs.length === 0) return `<div${align}><br></div>`;
|
||||
const styles: string[] = [];
|
||||
if (p.align && p.align !== "left") styles.push(`text-align:${p.align}`);
|
||||
if (p.lineHeight != null) styles.push(`line-height:${p.lineHeight}`);
|
||||
const style = styles.length ? ` style="${styles.join(";")}"` : "";
|
||||
if (p.runs.length === 0) return `<div${style}><br></div>`;
|
||||
const inner = p.runs.map((r) => runToHtmlSpan(r, basePt, color)).join("");
|
||||
return `<div${align}>${inner}</div>`;
|
||||
return `<div${style}>${inner}</div>`;
|
||||
}
|
||||
|
||||
function runToHtmlSpan(run: TextRun, basePt: number, color?: string): string {
|
||||
@@ -119,6 +122,8 @@ export interface SvgTspan {
|
||||
export interface SvgLine {
|
||||
tspans: SvgTspan[];
|
||||
align: "left" | "center" | "right";
|
||||
/** Zeilenhöhe dieses Absatzes (Vielfaches der Basisgrösse), s. `Paragraph.lineHeight`. */
|
||||
lineHeight: number;
|
||||
}
|
||||
|
||||
export interface SvgTextOptions {
|
||||
@@ -126,7 +131,12 @@ export interface SvgTextOptions {
|
||||
x: number;
|
||||
/** Baseline der ERSTEN Zeile, Y in Benutzereinheiten. */
|
||||
y: number;
|
||||
/** Zeilenvorschub (Baseline zu Baseline) in Benutzereinheiten. */
|
||||
/**
|
||||
* Zeilenvorschub (Baseline zu Baseline) in Benutzereinheiten für Zeilen OHNE
|
||||
* eigene `lineHeight` (Rückwärts-Kompatibilität/Fallback). Absätze mit
|
||||
* gesetzter `Paragraph.lineHeight` berechnen ihren Vorschub stattdessen
|
||||
* selbst aus `basePt · unitPerPt · lineHeight`.
|
||||
*/
|
||||
lineHeight: number;
|
||||
/**
|
||||
* Umrechnung Schriftgrösse: `sizePt` × `unitPerPt` = Grösse in Benutzereinheiten.
|
||||
@@ -142,13 +152,15 @@ export interface SvgTextOptions {
|
||||
/**
|
||||
* Zerlegt das Dokument in Zeilen/Tspans mit bereits in Benutzereinheiten
|
||||
* umgerechneten Grössen. Der Aufrufer positioniert die Zeilen selbst (Baseline
|
||||
* der Zeile i = `y + i·lineHeight`).
|
||||
* der Zeile i = `y + Σ lineHeight[0..i-1]`, s. `docToSvgText`).
|
||||
*/
|
||||
export function docToLines(doc: RichTextDoc, opts: SvgTextOptions): SvgLine[] {
|
||||
const basePt = opts.basePt ?? 12;
|
||||
const color = opts.color ?? "#000000";
|
||||
const baseUnit = basePt * opts.unitPerPt;
|
||||
return doc.paragraphs.map((p) => ({
|
||||
align: p.align ?? "left",
|
||||
lineHeight: p.lineHeight != null ? baseUnit * p.lineHeight : opts.lineHeight,
|
||||
tspans: p.runs
|
||||
.filter((r) => r.text.length > 0)
|
||||
.map((r) => runToTspan(r, basePt, opts.unitPerPt, color)),
|
||||
@@ -184,7 +196,7 @@ export function docToSvgText(doc: RichTextDoc, opts: SvgTextOptions): string {
|
||||
const lines = docToLines(doc, opts);
|
||||
const parts: string[] = [];
|
||||
lines.forEach((line, i) => {
|
||||
const dy = i === 0 ? 0 : opts.lineHeight;
|
||||
const dy = i === 0 ? 0 : line.lineHeight;
|
||||
const anchor = line.align === "center" ? "middle" : line.align === "right" ? "end" : "start";
|
||||
const runSpans = line.tspans.map((t) => tspanToSvg(t)).join("");
|
||||
// Leere Zeile: ein Zero-Width-Space, damit dy dennoch wirkt.
|
||||
|
||||
@@ -44,10 +44,15 @@ export interface TextRun {
|
||||
/** Absatz-Ausrichtung. */
|
||||
export type Align = "left" | "center" | "right";
|
||||
|
||||
/** Ein Absatz: eine Zeile aus Runs plus optionale Ausrichtung. */
|
||||
/** Zeilenhöhe als Vielfaches der Basisgrösse, wenn nicht am Absatz gesetzt. */
|
||||
export const DEFAULT_LINE_HEIGHT = 1.3;
|
||||
|
||||
/** Ein Absatz: eine Zeile aus Runs plus optionale Ausrichtung/Zeilenhöhe. */
|
||||
export interface Paragraph {
|
||||
runs: TextRun[];
|
||||
align?: Align;
|
||||
/** Zeilenhöhe als Vielfaches der Basisgrösse (z. B. 1.3 = 130 %). */
|
||||
lineHeight?: number;
|
||||
}
|
||||
|
||||
/** Das gesamte Rich-Text-Dokument. */
|
||||
|
||||
Reference in New Issue
Block a user