Source code for ooodev.format.inner.direct.write.para.area.hatch

# region Import
from __future__ import annotations
from typing import Any, Tuple, overload, cast, Type, TypeVar

from ooo.dyn.drawing.fill_style import FillStyle
from ooo.dyn.drawing.hatch_style import HatchStyle
from ooo.dyn.drawing.hatch import Hatch as UnoHatch

from ooodev.events.args.key_val_cancel_args import KeyValCancelArgs
from ooodev.events.format_named_event import FormatNamedEvent
from ooodev.exceptions import ex as mEx
from ooodev.format.inner.direct.structs.hatch_struct import HatchStruct
from ooodev.format.inner.direct.write.para.area.color import Color as FillColor
from ooodev.format.inner.kind.format_kind import FormatKind
from ooodev.format.inner.preset import preset_hatch as mPreset
from ooodev.format.inner.preset.preset_hatch import PresetHatchKind
from ooodev.format.inner.style_base import StyleMulti
from ooodev.loader import lo as mLo
from ooodev.units.angle import Angle
from ooodev.units.unit_convert import UnitConvert
from ooodev.units.unit_obj import UnitT
from ooodev.utils import props as mProps
from ooodev.utils.color import Color, StandardColor

# endregion Import

PARA_BACK_COLOR_FLAGS = 0x7F000000

_THatch = TypeVar(name="_THatch", bound="Hatch")


class _HatchStruct(HatchStruct):
    """Hatch Struct for para Hatch"""

    def _supported_services(self) -> Tuple[str, ...]:
        try:
            return self._supported_services_values
        except AttributeError:
            self._supported_services_values = (
                "com.sun.star.drawing.FillProperties",
                "com.sun.star.text.TextContent",
                "com.sun.star.beans.PropertySet",
                "com.sun.star.style.ParagraphStyle",
            )
        return self._supported_services_values


[docs]class Hatch(StyleMulti): """ Class for Fill Properties Fill Hatch. .. seealso:: - :ref:`help_writer_format_direct_para_area_hatch` .. versionadded:: 0.9.0 """
[docs] def __init__( self, *, style: HatchStyle = HatchStyle.SINGLE, color: Color = Color(0), space: float | UnitT = 0.0, angle: Angle | int = 0, bg_color: Color = Color(-1), name: str = "", auto_name: bool = False, ) -> None: """ Constructor Args: style (HatchStyle, optional): Specifies the kind of lines used to draw this hatch. Default ``HatchStyle.SINGLE``. color (:py:data:`~.utils.color.Color`, optional): Specifies the color of the hatch lines. Default ``0``. space (float, UnitT, optional): Specifies the space between the lines in the hatch (in ``mm`` units) or :ref:`proto_unit_obj`. Default ``0.0`` angle (Angle, int, optional): Specifies angle of the hatch in degrees. Default to ``0``. bg_color(:py:data:`~.utils.color.Color`, optional): Specifies the background Color. Set this ``-1`` (default) for no background color. name (str, optional): Specifies the Hatch Name. auto_name (bool, optional): Specifies if Hatch is give an auto name such as ``Hatch``. Default ``False``. Returns: None: See Also: - :ref:`help_writer_format_direct_para_area_hatch` """ self._auto_name = auto_name self._name = name # this may change in _set_fill_hatch() init_vals = {"ParaBackTransparent": False, "FillStyle": FillStyle.HATCH} bk_color = FillColor(bg_color) # FillStyle is set by this class bk_color._remove("FillStyle") # add event listener to prevent FillStyle from being set bk_color.add_event_listener(FormatNamedEvent.STYLE_SETTING, _on_bg_color_setting) bk_color.prop_color = bg_color init_vals["FillBackground"] = bg_color >= 0 super().__init__(**init_vals) self._set_bg_color(color) in_hatch = self._get_inner_hatch()(style=style, color=color, distance=space, angle=angle) self._set_fill_hatch(in_hatch, False) self._set_style("fill_color", bk_color, *bk_color.get_attrs()) # type: ignore
# region Internal Methods def _get_inner_hatch(self) -> Type[HatchStruct]: return _HatchStruct def _set_bg_color(self, color: int) -> None: # Writer stores ParaBackColor as flag values when there is no background color # When there is background color then ParaBackColor contains the actual color. # To find the flag value a known value was used. # In this case the Hatch Color is used as part of the flag. # # background colors # ParaBackColor HEX Name # 2130706432 0x7f000000 "Black 0 Degrees" # 2130706432 0x7f000000 "Black 90 Degrees" # 2130706432 0x7f000000 "Black 180 Degrees Crossed" # 2133483673 0x7f2a6099 "Blue 45 Degrees" # 2133483673 0x7f2a6099 "Blue -45 Degrees" # 2133483673 0x7f2a6099 "Blue 45 Degrees Crossed" # 2147467008 0x7fffbf00 "Yellow 45 Degrees" # 2130749747 0x7f00a933 "Green 30 Degrees" # 2147418112 0x7fff0000 "Red 45 Degrees" # # BLACK_HATCH = 0x7F000000 # flags = BLUE_HATCH & ~StandardColor.BLUE # print(hex(flags)) # '0x7F000000' # # This is the flags value assigned to PARA_BACK_COLOR_FLAGS # see also: # https://github.com/LibreOffice/core/blob/1a79594a27f41ad369e7c387c51e00afb1352872/xmloff/source/text/txtprmap.cxx#L396 # https://github.com/LibreOffice/core/blob/776ea34deefe7bdce2fb8a06e5c55ef27ec87ea7/xmloff/source/style/prstylei.cxx#L121 # https://github.com/LibreOffice/core/blob/1a79594a27f41ad369e7c387c51e00afb1352872/include/xmloff/xmltypes.hxx fb = cast(bool, self._get("FillBackground")) para_bg_color = color if fb else PARA_BACK_COLOR_FLAGS | color self._set("ParaBackColor", para_bg_color) def _set_fill_hatch(self, hatch_struct: HatchStruct, ignore_preset: bool) -> None: self._del_attribs("_direct_inner_hatch") name_hatch = self._get_named_hatch_struct(hatch_struct, self._name, ignore_preset) self._set("FillHatchName", self._name) self._set_style("fill_hatch", name_hatch, *name_hatch.get_attrs()) def _get_named_hatch_struct(self, hatch_struct: HatchStruct, name: str, ignore_preset: bool) -> HatchStruct: # if the name passed in already exist in the Hatch Table then it is returned. # Otherwise, the hatch is added to the Hatch Table and then returned # unless name is a present name. # If it is a preset name any value in the Hatch Table is ignored. # This is due to Writer alters the stored Struct for some unknown reason. # Presets are not added to Hatch Table. Write handles this for hatch. # # After Hatch is added to table all other subsequent call of this name will return (unless preset) # that Hatch from the Table. Except auto_name which will force a new entry # into the Table each time. self._is_preset = False if name: if not ignore_preset and PresetHatchKind.is_preset(name): self._is_preset = True # When getting value from Hatch table that Writer has inserted # The values are somehow changed by Writer # For this reason will return directly here return hatch_struct else: self._auto_name = True name = "Hatch" nc = self._container_get_inst() if self._auto_name: # if auto name is True then will no longer be treated as a preset. # for preset expect name similar to 'Black 0 Degrees 1' self._is_preset = False name = f"{name.rstrip()} " self._name = self._container_get_unique_el_name(name, nc) else: self._name = name hatch = self._container_get_value(self._name, nc) # raises value error if name is empty if hatch is not None: return self._get_inner_hatch().from_uno_struct(hatch) self._container_add_value(name=self._name, obj=hatch_struct.get_uno_struct(), allow_update=False, nc=nc) hatch = self._container_get_value(self._name, nc) return self._get_inner_hatch().from_uno_struct(hatch) def _on_hatch_property_change(self) -> None: if self._is_preset: self._auto_name = True self._set_fill_hatch(self.prop_inner_hatch, True) self._is_preset = False # endregion Internal Methods # region Overrides def _supported_services(self) -> Tuple[str, ...]: try: return self._supported_services_values except AttributeError: self._supported_services_values = ( "com.sun.star.drawing.FillProperties", "com.sun.star.text.TextContent", "com.sun.star.beans.PropertySet", "com.sun.star.style.ParagraphStyle", ) return self._supported_services_values def _container_get_service_name(self) -> str: # https://github.com/LibreOffice/core/blob/d9e044f04ac11b76b9a3dac575f4e9155b67490e/chart2/source/tools/PropertyHelper.cxx#L229 return "com.sun.star.drawing.HatchTable" # region copy() @overload def copy(self: _THatch) -> _THatch: ... @overload def copy(self: _THatch, **kwargs) -> _THatch: ...
[docs] def copy(self: _THatch, **kwargs) -> _THatch: """Gets a copy of instance as a new instance""" # pylint: disable=protected-access cp = super().copy(**kwargs) cp._name = self._name cp._is_preset = self._is_preset cp._auto_name = self._auto_name return cp
# endregion copy() # region apply() @overload def apply(self, obj: Any) -> None: ... @overload def apply(self, obj: Any, **kwargs) -> None: ...
[docs] def apply(self, obj: Any, **kwargs) -> None: """ Applies styles to object Args: obj (object): UNO object. Returns: None: """ super().apply(obj, **kwargs)
def _props_set(self, obj: Any, **kwargs: Any) -> None: try: super()._props_set(obj, **kwargs) except mEx.MultiError as e: mLo.Lo.print("Hatch.apply(): Unable to set Property") for err in e.errors: mLo.Lo.print(f" {err}") # endregion apply() # endregion Overrides # region Static Methods # region from_preset() @overload @classmethod def from_preset(cls: Type[_THatch], preset: PresetHatchKind) -> _THatch: ... @overload @classmethod def from_preset(cls: Type[_THatch], preset: PresetHatchKind, **kwargs) -> _THatch: ...
[docs] @classmethod def from_preset(cls: Type[_THatch], preset: PresetHatchKind, **kwargs) -> _THatch: """ Gets an instance from a preset Args: preset (PresetHatchKind): Preset Returns: Hatch: Instance from preset. """ # kargs = mPreset.get_write_preset(preset) kargs = mPreset.get_preset(preset) kargs["name"] = str(preset) kargs["auto_name"] = False kargs.update(kwargs) return cls(**kargs)
# endregion from_preset() # region from_obj() @overload @classmethod def from_obj(cls: Type[_THatch], obj: Any) -> _THatch: ... @overload @classmethod def from_obj(cls: Type[_THatch], obj: Any, **kwargs) -> _THatch: ...
[docs] @classmethod def from_obj(cls: Type[_THatch], obj: Any, **kwargs) -> _THatch: """ Gets instance from object Args: obj (object): UNO object. Raises: NotSupportedError: If ``obj`` is not supported. Returns: Hatch: ``Hatch`` instance that represents ``obj`` hatch pattern. """ # pylint: disable=protected-access nu = cls(**kwargs) if not nu._is_valid_obj(obj): raise mEx.NotSupportedError("Object is not support to convert to Hatch") hatch = cast(UnoHatch, mProps.Props.get(obj, "FillHatch")) fc = FillColor.from_obj(obj) result = cls( style=hatch.Style, color=hatch.Color, # type: ignore space=UnitConvert.convert_mm100_mm(hatch.Distance), angle=hatch.Angle, bg_color=fc.prop_color, name=mProps.Props.get(obj, "FillHatchName"), auto_name=False, **kwargs, ) result.set_update_obj(obj) return result
# endregion from_obj() # endregion Static Methods # region Properties @property def prop_format_kind(self) -> FormatKind: """Gets the kind of style""" try: return self._format_kind_prop except AttributeError: self._format_kind_prop = FormatKind.TXT_CONTENT | FormatKind.FILL | FormatKind.PARA return self._format_kind_prop @property def prop_name(self) -> str: """Gets the name of the hatch.""" return self._name @property def prop_bg_color(self) -> Color: """Gets sets if fill image is tiled""" return self.prop_inner_color.prop_color @prop_bg_color.setter def prop_bg_color(self, value: Color): self.prop_inner_color.prop_color = value if value < 0: self._set("FillBackground", False) else: self._set("FillBackground", True) self._set_bg_color(self.prop_color) @property def prop_style(self) -> HatchStyle: """Gets/Sets the style of the hatch.""" return self.prop_inner_hatch.prop_style @prop_style.setter def prop_style(self, value: HatchStyle): self._on_hatch_property_change() self.prop_inner_hatch.prop_style = value @property def prop_color(self) -> Color: """Gets/Sets the color of the hatch lines.""" return self.prop_inner_hatch.prop_color @prop_color.setter def prop_color(self, value: Color): self._on_hatch_property_change() self.prop_inner_hatch.prop_color = value self._set_bg_color(value) @property def prop_space(self) -> UnitT: """Gets/Sets the distance between the lines in the hatch (in ``mm`` units).""" return self.prop_inner_hatch.prop_distance @prop_space.setter def prop_space(self, value: float | UnitT): self._on_hatch_property_change() self.prop_inner_hatch.prop_distance = value @property def prop_angle(self) -> Angle: """Gets/Sets angle of the hatch.""" return self.prop_inner_hatch.prop_angle @prop_angle.setter def prop_angle(self, value: Angle | int): self._on_hatch_property_change() self.prop_inner_hatch.prop_angle = value @property def prop_inner_hatch(self) -> HatchStruct: """Gets Hatch struct instance""" try: return self._direct_inner_hatch except AttributeError: self._direct_inner_hatch = cast(HatchStruct, self._get_style_inst("fill_hatch")) return self._direct_inner_hatch @property def prop_inner_color(self) -> FillColor: """Gets Fill color instance""" try: return self._direct_inner_color except AttributeError: self._direct_inner_color = cast(FillColor, self._get_style_inst("fill_color")) return self._direct_inner_color
# endregion Properties def _on_bg_color_setting(source: Any, event_args: KeyValCancelArgs, *args, **kwargs) -> None: if event_args.key == "FillStyle": event_args.cancel = True elif event_args.key == "FillColor": # -1 means automatic color. # Fillcolor for hatch has not automatic color if event_args.value == -1: # strictly speaking this is not needed but follows how Draw handles it. event_args.value = StandardColor.DEFAULT_BLUE