# SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved.
# Non-Commercial Freeware and Commercial Software.
# This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project.
# The latest version of the code is available on the site www.pullenti.ru

import xml.etree
import math
from pullenti.unisharp.Utils import Utils
from pullenti.unisharp.Misc import RefOutArgWrapper

from pullenti.unitext.UnitextStyle import UnitextStyle
from pullenti.unitext.internal.misc.BorderInfo import BorderInfo
from pullenti.util.MiscHelper import MiscHelper
from pullenti.util.SizeunitConverter import SizeunitConverter
from pullenti.unitext.internal.word.DocTextStyle import DocTextStyle

class DocTextStyles:
    
    def __init__(self) -> None:
        self.m_text_styles = dict()
        self.def_style = None;
        self.def_styles = dict()
        self.ustyles = list()
        self.__m_ustyles = dict()
        self.ignore = False
        self.__m_major_font = None;
        self.__m_minor_font = None;
    
    def register_style(self, st : 'UnitextStyle') -> 'UnitextStyle':
        if (self.ignore): 
            return st
        key = str(st)
        if (Utils.isNullOrEmpty(key)): 
            return None
        res = None
        wrapres471 = RefOutArgWrapper(None)
        inoutres472 = Utils.tryGetValue(self.__m_ustyles, key, wrapres471)
        res = wrapres471.value
        if (inoutres472): 
            return res
        self.__m_ustyles[key] = st
        st.id0_ = len(self.ustyles)
        self.ustyles.append(st)
        return st
    
    def get_style(self, x : xml.etree.ElementTree.Element, nam : str) -> 'DocTextStyle':
        id0_ = DocTextStyles.__read_attr_val(x, nam, None)
        if (id0_ is None): 
            return None
        res = None
        wrapres473 = RefOutArgWrapper(None)
        inoutres474 = Utils.tryGetValue(self.m_text_styles, id0_, wrapres473)
        res = wrapres473.value
        if (not inoutres474): 
            return None
        return res
    
    def get_style_by_id(self, id0_ : str) -> 'DocTextStyle':
        res = None
        wrapres475 = RefOutArgWrapper(None)
        inoutres476 = Utils.tryGetValue(self.m_text_styles, id0_, wrapres475)
        res = wrapres475.value
        if (not inoutres476): 
            return None
        return res
    
    @staticmethod
    def __read_attr_val(x : xml.etree.ElementTree.Element, nam : str, nam2 : str=None) -> str:
        if (x.attrib is not None): 
            for a in x.attrib.items(): 
                if (Utils.getXmlAttrLocalName(a) == nam): 
                    return a[1]
                elif (nam2 is not None and Utils.getXmlAttrLocalName(a) == nam2): 
                    return a[1]
        return None
    
    @staticmethod
    def __get_size_val(val : str, out_cm : bool=False) -> str:
        if (val is None): 
            return None
        for ch in val: 
            if (str.isalpha(ch)): 
                return val
        d = 0
        wrapd477 = RefOutArgWrapper(0)
        inoutres478 = MiscHelper.try_parse_double(val, wrapd477)
        d = wrapd477.value
        if (not inoutres478): 
            return val
        if (out_cm): 
            d = round(d / (((20) * 28.35)), 2)
            return "{0}cm".format(MiscHelper.out_double(d))
        d = round(d / (20), 2)
        return "{0}pt".format(MiscHelper.out_double(d))
    
    @staticmethod
    def __corr_color(val : str) -> str:
        if (val is None): 
            return None
        if (len(val) != 6): 
            return val
        for ch in val: 
            if (str.isdigit(ch)): 
                pass
            elif (ch >= 'A' and ch <= 'F'): 
                pass
            elif (ch >= 'a' and ch <= 'f'): 
                pass
            else: 
                return val
        val = "#" + val.upper()
        return val
    
    def read_unitext_style(self, node : xml.etree.ElementTree.Element, st : 'UnitextStyle', ignore_font_name : bool=False) -> None:
        for x in node: 
            if (Utils.getXmlLocalName(x) == "pStyle" or Utils.getXmlLocalName(x) == "rStyle"): 
                id0_ = DocTextStyles.__read_attr_val(x, "val", None)
                if (id0_ in self.m_text_styles): 
                    self.m_text_styles[id0_].ustyle.copy_to(st)
            elif (Utils.getXmlLocalName(x) == "rFonts" and not ignore_font_name): 
                nam = DocTextStyles.__read_attr_val(x, "cs", "ansii")
                if (nam is None): 
                    nam = DocTextStyles.__read_attr_val(x, "ascii", "hAnsii")
                if (nam is None): 
                    nam = st.get_attr("font-name")
                is_bidi = False
                if (nam is None): 
                    for a in x.attrib.items(): 
                        if (Utils.endsWithString(Utils.getXmlAttrLocalName(a), "cstheme", True) and Utils.endsWithString(a[1], "bidi", True)): 
                            is_bidi = True
                    for a in x.attrib.items(): 
                        if (Utils.endsWithString(Utils.getXmlAttrLocalName(a), "theme", True) and not Utils.endsWithString(Utils.getXmlAttrLocalName(a), "asiatheme", True)): 
                            if (is_bidi): 
                                break
                            if (Utils.startsWithString(a[1], "major", True)): 
                                nam = self.__m_major_font
                            elif (Utils.startsWithString(a[1], "minor", True)): 
                                nam = self.__m_minor_font
                if (nam is None): 
                    for a in x.attrib.items(): 
                        if (not Utils.endsWithString(Utils.getXmlAttrLocalName(a), "theme", True) and not Utils.endsWithString(Utils.getXmlAttrLocalName(a), "asia", True)): 
                            nam = a[1]
                            break
                if (not Utils.isNullOrEmpty(nam)): 
                    st.add_attr("font-name", nam, False)
        DocTextStyles.read_unitext_style_ext(node, st, True, False)
    
    @staticmethod
    def read_unitext_style_ext(node : xml.etree.ElementTree.Element, st : 'UnitextStyle', ignore_font_name : bool=False, num_regime : bool=False) -> None:
        for x in node: 
            if (Utils.getXmlLocalName(x) == "b" and not num_regime): 
                val = DocTextStyles.__read_attr_val(x, "val", None)
                if (val == "0"): 
                    val = "normal"
                else: 
                    val = "bold"
                st.add_attr("font-weight", val, False)
            elif (Utils.getXmlLocalName(x) == "i" and not num_regime): 
                val = DocTextStyles.__read_attr_val(x, "val", None)
                if (val == "0"): 
                    st.add_attr("font-style", "none", False)
                else: 
                    st.add_attr("font-style", "italic", False)
            elif (Utils.getXmlLocalName(x) == "u" and not num_regime): 
                val = DocTextStyles.__read_attr_val(x, "val", None)
                if (val == "none"): 
                    st.add_attr("text-decoration", "none", False)
                else: 
                    st.add_attr("text-decoration", "underline", False)
            elif (Utils.getXmlLocalName(x) == "strike" and not num_regime): 
                val = DocTextStyles.__read_attr_val(x, "val", None)
                if (val != "0"): 
                    st.add_attr("text-decoration", "line-through", False)
            elif (Utils.getXmlLocalName(x) == "caps" and not num_regime): 
                st.add_attr("upper-case", "true", False)
            elif (Utils.getXmlLocalName(x) == "rFonts" and not ignore_font_name): 
                nam = DocTextStyles.__read_attr_val(x, "cs", "ansii")
                if (nam is None): 
                    nam = DocTextStyles.__read_attr_val(x, "ascii", "hAnsii")
                if (nam is None): 
                    nam = st.get_attr("font-name")
                if (not Utils.isNullOrEmpty(nam)): 
                    st.add_attr("font-name", nam, False)
            elif (Utils.getXmlLocalName(x) == "sz" and not num_regime): 
                d = 0
                wrapd479 = RefOutArgWrapper(0)
                inoutres480 = MiscHelper.try_parse_double(DocTextStyles.__read_attr_val(x, "val", None), wrapd479)
                d = wrapd479.value
                if (inoutres480): 
                    st.add_attr("font-size", "{0}pt".format(MiscHelper.out_double(d / (2))), False)
            elif (Utils.getXmlLocalName(x) == "highlight" or Utils.getXmlLocalName(x) == "color"): 
                val = DocTextStyles.__corr_color(DocTextStyles.__read_attr_val(x, "val", None))
                if (val is None or val == "auto"): 
                    continue
                if (Utils.getXmlLocalName(x) == "color"): 
                    st.add_attr("color", val, False)
                else: 
                    st.add_attr("background-color", val, False)
            elif (Utils.getXmlLocalName(x) == "shd"): 
                val = DocTextStyles.__read_attr_val(x, "color", None)
                if (val == "auto" or DocTextStyles.__read_attr_val(x, "val", None) == "clear"): 
                    val = DocTextStyles.__corr_color(DocTextStyles.__read_attr_val(x, "fill", None))
                else: 
                    val = DocTextStyles.__corr_color(val)
                if (val is not None and val != "auto"): 
                    st.add_attr("background-color", val, False)
            elif (Utils.getXmlLocalName(x) == "rPr"): 
                if (Utils.getXmlLocalName(node) == "pPr"): 
                    pass
                else: 
                    DocTextStyles.read_unitext_style_ext(x, st, ignore_font_name, num_regime)
            elif (Utils.getXmlLocalName(x) == "jc" and not num_regime): 
                val = DocTextStyles.__read_attr_val(x, "val", None)
                if (val == "start"): 
                    val = "left"
                elif (val == "end"): 
                    val = "right"
                elif (val == "both"): 
                    val = "justify"
                st.add_attr("text-align", val, False)
            elif (Utils.getXmlLocalName(x) == "vAlign" and not num_regime): 
                val = DocTextStyles.__read_attr_val(x, "val", None)
                st.add_attr("vertical-align", val, False)
            elif (Utils.getXmlLocalName(x) == "spacing" and not num_regime): 
                temp_val = DocTextStyles.__read_attr_val(x, "val", None)
                if (temp_val is not None): 
                    st.add_attr("letter-spacing", DocTextStyles.__get_size_val(temp_val, False), False)
                d = 0
                rule = DocTextStyles.__read_attr_val(x, "lineRule", None)
                for a in x.attrib.items(): 
                    if (Utils.getXmlAttrLocalName(a) == "before" or Utils.getXmlAttrLocalName(a) == "top"): 
                        st.add_attr("margin-top", DocTextStyles.__get_size_val(a[1], False), False)
                    elif (Utils.getXmlAttrLocalName(a) == "after" or Utils.getXmlAttrLocalName(a) == "bottom"): 
                        st.add_attr("margin-bottom", DocTextStyles.__get_size_val(a[1], False), False)
                    elif (Utils.getXmlAttrLocalName(a) == "line"): 
                        wrapd481 = RefOutArgWrapper(0)
                        inoutres482 = MiscHelper.try_parse_double(a[1], wrapd481)
                        d = wrapd481.value
                        if (inoutres482): 
                            val = None
                            if (rule == "auto"): 
                                d = round(d / (240), 2)
                                val = "{0}".format(MiscHelper.out_double(d))
                            elif (rule == "exact"): 
                                d = round(d / (20), 2)
                                val = "{0}pt".format(MiscHelper.out_double(d))
                                pt = SizeunitConverter.convert(st.get_attr("font-size"), "pt")
                                if (pt is not None and pt > 0): 
                                    val = MiscHelper.out_double(round(d / pt, 2))
                            else: 
                                pass
                            if (val is not None): 
                                st.add_attr("line-height", val, False)
            elif (Utils.getXmlLocalName(x) == "tcW"): 
                val = DocTextStyles.__read_attr_val(x, "w", None)
                if (val is not None): 
                    st.add_attr("width", DocTextStyles.__get_size_val(val, False), False)
            elif (Utils.getXmlLocalName(x) == "ind"): 
                for a in x.attrib.items(): 
                    if (Utils.getXmlAttrLocalName(a) == "left" or Utils.getXmlAttrLocalName(a) == "start"): 
                        st.add_attr("margin-left", DocTextStyles.__get_size_val(a[1], True), False)
                    elif (Utils.getXmlAttrLocalName(a) == "right" or Utils.getXmlAttrLocalName(a) == "end"): 
                        st.add_attr("margin-right", DocTextStyles.__get_size_val(a[1], True), False)
                    elif (Utils.getXmlAttrLocalName(a) == "firstLine"): 
                        st.add_attr("text-indent", DocTextStyles.__get_size_val(a[1], True), False)
                    elif (Utils.getXmlAttrLocalName(a) == "hanging"): 
                        st.add_attr("text-indent", "-" + DocTextStyles.__get_size_val(a[1], True), False)
    
    def read_theme(self, node : xml.etree.ElementTree.Element) -> None:
        for x1 in node: 
            for x2 in x1: 
                if (Utils.getXmlLocalName(x2) == "fontScheme"): 
                    for x3 in x2: 
                        if (Utils.getXmlLocalName(x3) == "majorFont" or Utils.getXmlLocalName(x3) == "minorFont"): 
                            for x4 in x3: 
                                if (Utils.getXmlLocalName(x4) == "latin"): 
                                    if (Utils.getXmlLocalName(x3) == "majorFont"): 
                                        self.__m_major_font = DocTextStyles.__read_attr_val(x4, "typeface", None)
                                    else: 
                                        self.__m_minor_font = DocTextStyles.__read_attr_val(x4, "typeface", None)
    
    def read_all_styles(self, node : xml.etree.ElementTree.Element, nums : 'DocNumStyles') -> None:
        for xnum in node: 
            if (Utils.getXmlLocalName(xnum) == "style"): 
                is_normal = False
                for x in xnum: 
                    if (Utils.getXmlLocalName(x) == "name"): 
                        for xattr in x.attrib.items(): 
                            if (xattr[1] == "Normal"): 
                                is_normal = True
                            break
                if (not is_normal): 
                    continue
                for x in xnum: 
                    if (Utils.getXmlLocalName(x) == "rPr"): 
                        self.def_style = UnitextStyle()
                        self.read_unitext_style(x, self.def_style, False)
                if (is_normal): 
                    break
        if (self.def_style is not None): 
            if (not "font-name" in self.def_style.attrs): 
                for xnum in node: 
                    if (Utils.getXmlLocalName(xnum) == "docDefaults"): 
                        for x in xnum: 
                            if (Utils.getXmlLocalName(x) == "rPrDefault"): 
                                for xx in x: 
                                    if (Utils.getXmlLocalName(xx) == "rPr"): 
                                        self.read_unitext_style(xx, self.def_style, False)
        if (self.def_style is None): 
            for xnum in node: 
                if (Utils.getXmlLocalName(xnum) == "docDefaults"): 
                    for x in xnum: 
                        if (Utils.getXmlLocalName(x) == "rPrDefault"): 
                            for xx in x: 
                                if (Utils.getXmlLocalName(xx) == "rPr"): 
                                    self.def_style = UnitextStyle()
                                    self.read_unitext_style(xx, self.def_style, False)
        for xnum in node: 
            if (Utils.getXmlLocalName(xnum) == "style" and xnum.attrib is not None and len(xnum.attrib) >= 1): 
                id0_ = DocTextStyles.__read_attr_val(xnum, "styleId", None)
                if (id0_ is None or id0_ in self.m_text_styles): 
                    continue
                sty = DocTextStyle()
                sty.xml0_ = xnum
                typ = DocTextStyles.__read_attr_val(xnum, "type", None)
                if (self.def_style is not None): 
                    self.def_style.copy_to(sty.ustyle)
                if (typ is not None and typ in self.def_styles): 
                    self.def_styles[typ].copy_to(sty.ustyle)
                self.m_text_styles[id0_] = sty
                for x in xnum: 
                    if (Utils.getXmlLocalName(x) == "name"): 
                        if (x.attrib is not None and len(x.attrib) > 0): 
                            sty.name = Utils.getXmlAttrByIndex(x.attrib, 0)[1]
                        if (sty.name is not None and sty.name == "Body Text Indent"): 
                            pass
                    elif (Utils.getXmlLocalName(x) == "aliases"): 
                        if (x.attrib is not None and len(x.attrib) > 0): 
                            sty.aliases = Utils.getXmlAttrByIndex(x.attrib, 0)[1]
                    elif (Utils.getXmlLocalName(x) == "basedOn"): 
                        sty.based_on = DocTextStyles.__read_attr_val(x, "val", None)
        for kp in self.m_text_styles.items(): 
            sty = kp[1]
            if (sty.loaded): 
                continue
            if (kp[0] == "XX"): 
                pass
            if (sty.based_on is not None and sty.based_on in self.m_text_styles): 
                sty0 = self.m_text_styles[sty.based_on]
                if (not sty0.loaded): 
                    if (sty0.based_on is not None and sty0.based_on in self.m_text_styles): 
                        sty00 = self.m_text_styles[sty0.based_on]
                        if (not sty00.loaded): 
                            self.__read_single_style(sty00)
                        sty0.num_id = sty00.num_id
                        sty0.num_lvl = sty00.num_lvl
                        sty00.ustyle.copy_to(sty0.ustyle)
                    self.__read_single_style(sty0)
                sty.num_id = sty0.num_id
                sty.num_lvl = sty0.num_lvl
                sty0.ustyle.copy_to(sty.ustyle)
            self.__read_single_style(sty)
            if (sty.num_id is not None and nums is not None): 
                num = nums.get_style(sty.num_id)
                if (num is not None): 
                    lvl = num.get_level(sty.num_lvl)
                    if (lvl is not None and lvl.style is not None): 
                        for a in lvl.style.attrs.items(): 
                            if (not a[0] in sty.ustyle.attrs): 
                                sty.ustyle.attrs[a[0]] = a[1]
    
    def __read_single_style(self, sty : 'DocTextStyle') -> None:
        typ = DocTextStyles.__read_attr_val(sty.xml0_, "type", None)
        for x in sty.xml0_: 
            if (Utils.getXmlLocalName(x) == "rPr"): 
                self.read_unitext_style(x, sty.ustyle, sty.is_heading)
                if (DocTextStyles.__read_attr_val(sty.xml0_, "default", None) == "1"): 
                    if (not typ in self.def_styles): 
                        self.def_styles[typ] = sty.ustyle
            elif (Utils.getXmlLocalName(x) == "pPr"): 
                self.read_unitext_style(x, sty.ustyle, False)
                for xx in x: 
                    if (Utils.getXmlLocalName(xx) == "numPr"): 
                        for xxx in xx: 
                            if (Utils.getXmlLocalName(xxx) == "numId" and xxx.attrib is not None): 
                                for a in xxx.attrib.items(): 
                                    if (Utils.getXmlAttrLocalName(a) == "val"): 
                                        sty.num_id = a[1]
                            elif (Utils.getXmlLocalName(xxx) == "ilvl" and xxx.attrib is not None): 
                                for a in xxx.attrib.items(): 
                                    if (Utils.getXmlAttrLocalName(a) == "val"): 
                                        ll = 0
                                        wrapll483 = RefOutArgWrapper(0)
                                        inoutres484 = Utils.tryParseInt(a[1], wrapll483)
                                        ll = wrapll483.value
                                        if (inoutres484): 
                                            sty.num_lvl = ll
        if (sty.is_heading): 
            sty.ustyle.add_attr("heading-level", str(sty.calc_heading_level()), False)
        sty.loaded = True
    
    def read_all_excel_styles(self, node : xml.etree.ElementTree.Element) -> None:
        brdr = list()
        spec_frms = dict()
        for xx in node: 
            if (Utils.getXmlLocalName(xx) == "borders"): 
                for xxx in xx: 
                    if (Utils.getXmlLocalName(xxx) == "border"): 
                        brd = BorderInfo()
                        brdr.append(brd)
                        for y in xxx: 
                            if (y.attrib is not None and ((len(y.attrib) > 0 or len(y) > 0))): 
                                if (Utils.getXmlLocalName(y) == "left"): 
                                    brd.left = True
                                elif (Utils.getXmlLocalName(y) == "right"): 
                                    brd.right = True
                                elif (Utils.getXmlLocalName(y) == "top"): 
                                    brd.top = True
                                elif (Utils.getXmlLocalName(y) == "bottom"): 
                                    brd.bottom = True
            elif (Utils.getXmlLocalName(xx) == "numFmts"): 
                for xxx in xx: 
                    if (Utils.getXmlLocalName(xxx) == "numFmt"): 
                        cod = None
                        frm = None
                        for a in xxx.attrib.items(): 
                            if (Utils.getXmlAttrLocalName(a) == "numFmtId"): 
                                cod = a[1]
                            elif (Utils.getXmlAttrLocalName(a) == "formatCode"): 
                                frm = a[1]
                        if (cod is not None and frm is not None and not cod in spec_frms): 
                            spec_frms[cod] = frm
        for x1 in node: 
            if (Utils.getXmlLocalName(x1) == "cellXfs"): 
                id0_ = 0
                for xx in x1: 
                    if (Utils.getXmlLocalName(xx) == "xf"): 
                        st = DocTextStyle()
                        st.name = str(id0_)
                        id0_ += 1
                        self.m_text_styles[st.name] = st
                        for a in xx.attrib.items(): 
                            if (Utils.getXmlAttrLocalName(a) == "numFmtId"): 
                                st.xls_format = a[1]
                                if (a[1] in spec_frms): 
                                    st.xls_format = spec_frms[a[1]]
                            elif (Utils.getXmlAttrLocalName(a) == "borderId"): 
                                n = 0
                                wrapn485 = RefOutArgWrapper(0)
                                inoutres486 = Utils.tryParseInt(a[1], wrapn485)
                                n = wrapn485.value
                                if (inoutres486): 
                                    if (n >= 0 and (n < len(brdr))): 
                                        st.xls_border = brdr[n]