namespace QWERTYkez.WordProcessor; internal static class FontPropsExtractor { // ============================================================ // Создаёт FontProps на основе Run + разрешает унаследованный шрифт. // ============================================================ internal static FontProps CreateFontPropsFromRun(Run run, Paragraph paragraph) { var runProperties = run?.RunProperties; var fontProps = new FontProps(); if (runProperties is not null) { var runFonts = runProperties.GetFirstChild(); var ff = ExtractFontFamily(runFonts, paragraph); if (!string.IsNullOrEmpty(ff)) fontProps = fontProps with { FontFamily = ff }; var fontSize = runProperties.GetFirstChild(); if (fontSize is not null && !string.IsNullOrEmpty(fontSize.Val) && uint.TryParse(fontSize.Val, out uint halfPoints)) { fontProps = fontProps with { Size = halfPoints / 2.0 }; } var bold = runProperties.GetFirstChild(); if (bold is not null) { bool isBold = bold.Val?.Value ?? true; fontProps = fontProps with { IsBold = isBold }; } var italic = runProperties.GetFirstChild(); if (italic is not null) { bool isItalic = italic.Val?.Value ?? true; fontProps = fontProps with { IsItalic = isItalic }; } var underline = runProperties.GetFirstChild(); if (underline?.Val is not null) fontProps = fontProps with { Underline = underline.Val.Value }; var color = runProperties.GetFirstChild(); if (color is not null && !string.IsNullOrEmpty(color.Val)) fontProps = fontProps with { Color = color.Val }; var verticalAlignment = runProperties.GetFirstChild(); if (verticalAlignment?.Val is not null) fontProps = fontProps with { SubSup = verticalAlignment.Val.Value }; } // Фолбэк: если в run шрифт не задан — идём по цепочке стилей if (string.IsNullOrEmpty(fontProps.FontFamily)) { var fallback = ResolveInheritedFontFamily(paragraph); if (!string.IsNullOrEmpty(fallback)) fontProps = fontProps with { FontFamily = fallback }; } return fontProps; } // ============================================================ // Разрешение шрифта по цепочке: // pStyle → BasedOn → default paragraph style → DocDefaults // ============================================================ private static string? ResolveInheritedFontFamily(Paragraph paragraph) { if (paragraph is null) return null; var mainPart = paragraph.Ancestors().FirstOrDefault()?.OpenXmlPart as MainDocumentPart; var styles = mainPart?.StyleDefinitionsPart?.Styles; // 1. Стиль абзаца var pStyleId = paragraph.ParagraphProperties?.ParagraphStyleId?.Val?.Value; if (!string.IsNullOrEmpty(pStyleId) && styles is not null) { var ff = ResolveFromStyleChain(styles, pStyleId, mainPart); if (!string.IsNullOrEmpty(ff)) return ff; } // 2. Дефолтный стиль абзаца if (styles is not null) { var defaultParaStyle = styles.Elements