forked from ScoDoc/ScoDoc
477 lines
13 KiB
Python
477 lines
13 KiB
Python
|
import abc
|
||
|
from enum import Enum
|
||
|
|
||
|
import openpyxl.styles
|
||
|
from openpyxl.styles import Side, Border, Font, PatternFill
|
||
|
from openpyxl.styles.numbers import FORMAT_GENERAL, FORMAT_NUMBER_00
|
||
|
|
||
|
|
||
|
class SCO_COLORS(Enum):
|
||
|
def __new__(cls, value, argb):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.argb = argb
|
||
|
return obj
|
||
|
|
||
|
BLACK = (1, "FF000000")
|
||
|
WHITE = (2, "FFFFFFFF")
|
||
|
RED = (3, "FFFF0000")
|
||
|
BROWN = (4, "FF993300")
|
||
|
PURPLE = (5, "FF993366")
|
||
|
BLUE = (6, "FF0000FF")
|
||
|
ORANGE = (7, "FFFF3300")
|
||
|
LIGHT_YELLOW = (8, "FFFFFF99")
|
||
|
BUT1 = (9, "FF95B3D7")
|
||
|
RCUE1 = (10, "FFB8CCE4")
|
||
|
UE1 = (11, "FFDCE6F1")
|
||
|
BUT2 = (12, "FFC4D79B")
|
||
|
RCUE2 = (13, "FFD8E4BC")
|
||
|
UE2 = (14, "FFEBF1DE")
|
||
|
BUT3 = (15, "FFFABF8F")
|
||
|
RCUE3 = (16, "FFFCD5B4")
|
||
|
UE3 = (17, "FFFDE9D9")
|
||
|
|
||
|
|
||
|
class SCO_BORDERTHICKNESS(Enum):
|
||
|
def __new__(cls, value, width):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.width = width
|
||
|
return obj
|
||
|
|
||
|
BORDER_NONE = (1, None)
|
||
|
BORDER_HAIR = (2, "hair")
|
||
|
BORDER_THIN = (3, "thin")
|
||
|
BORDER_MEDIUM = (4, "medium")
|
||
|
BORDER_THICK = (5, "thick")
|
||
|
|
||
|
|
||
|
class SCO_FONTNAME(Enum):
|
||
|
def __new__(cls, value, fontname):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.fontname = fontname
|
||
|
return obj
|
||
|
|
||
|
FONTNAME_ARIAL = (1, "Arial")
|
||
|
FONTNAME_COURIER = (2, "Courier New")
|
||
|
FONTNAME_CALIBRI = (3, "Calibri")
|
||
|
FONTNAME_TIMES = (4, "Times New Roman")
|
||
|
|
||
|
|
||
|
class SCO_FONTSIZE(Enum):
|
||
|
def __new__(cls, value, fontsize):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.fontsize = fontsize
|
||
|
return obj
|
||
|
|
||
|
FONTSIZE_9 = (1, 9)
|
||
|
FONTSIZE_10 = (1, 10)
|
||
|
FONTSIZE_11 = (1, 11)
|
||
|
FONTSIZE_13 = (1, 13)
|
||
|
FONTSIZE_16 = (1, 16)
|
||
|
|
||
|
|
||
|
class SCO_NUMBER_FORMAT(Enum):
|
||
|
def __new__(cls, value, format):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.format = format
|
||
|
return obj
|
||
|
|
||
|
NUMBER_GENERAL = (0, FORMAT_GENERAL)
|
||
|
NUMBER_00 = (1, FORMAT_NUMBER_00)
|
||
|
|
||
|
|
||
|
class SCO_HALIGN(Enum):
|
||
|
def __new__(cls, value, position):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.position = position
|
||
|
return obj
|
||
|
|
||
|
HALIGN_CENTER = (1, "center")
|
||
|
HALIGN_RIGHT = (2, "right")
|
||
|
HALIGN_LEFT = (3, "left")
|
||
|
|
||
|
|
||
|
class SCO_VALIGN(Enum):
|
||
|
def __new__(cls, value, position):
|
||
|
obj = object.__new__(cls)
|
||
|
obj._value_ = value
|
||
|
obj.position = position
|
||
|
return obj
|
||
|
|
||
|
VALIGN_TOP = (1, "top")
|
||
|
VALIGN_BOTTOM = (2, "bottom")
|
||
|
VALIGN_CENTER = (3, "center")
|
||
|
|
||
|
|
||
|
free = 0
|
||
|
|
||
|
|
||
|
class Composante:
|
||
|
def __init__(self, base=None, width: int = 1):
|
||
|
global free
|
||
|
self.cache = {}
|
||
|
if base is None:
|
||
|
self.base = free
|
||
|
free += width
|
||
|
else:
|
||
|
self.base = base
|
||
|
self.width = width
|
||
|
self.end = self.base + self.width
|
||
|
self.mask = ((1 << width) - 1) << self.base
|
||
|
|
||
|
def read(self, signature: int) -> int:
|
||
|
return (signature & self.mask) >> self.base
|
||
|
|
||
|
def clear(self, signature: int) -> int:
|
||
|
return signature & ~self.mask
|
||
|
|
||
|
def write(self, index, signature=0) -> int:
|
||
|
return self.clear(signature) + index << self.base
|
||
|
|
||
|
def lookup_or_cache(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
if not value in self.cache:
|
||
|
self.cache[signature] = self.build(value)
|
||
|
return self.cache[value]
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def build(self, value: int):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class Composante_boolean(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=1)
|
||
|
|
||
|
def build(self, signature):
|
||
|
value = self.read(signature)
|
||
|
return value == 1
|
||
|
|
||
|
|
||
|
class Composante_number_format(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=2)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_NUMBER_FORMAT(value) or None
|
||
|
|
||
|
|
||
|
class Composante_Colors(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=5)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_COLORS(value) or SCO_COLORS.BLACK
|
||
|
|
||
|
|
||
|
class Composante_borderThickness(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=2)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_BORDERTHICKNESS(value) or None
|
||
|
|
||
|
|
||
|
class Composante_fontname(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=3)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_FONTNAME(value) or None
|
||
|
|
||
|
|
||
|
class Composante_fontsize(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=3)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_FONTSIZE(value) or None
|
||
|
|
||
|
|
||
|
class Composante_halign(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=2)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_HALIGN(value) or None
|
||
|
|
||
|
|
||
|
class Composante_valign(Composante):
|
||
|
def __init__(self):
|
||
|
super().__init__(width=2)
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
value = self.read(signature)
|
||
|
return SCO_VALIGN(signature) or None
|
||
|
|
||
|
|
||
|
class Sco_BorderSide:
|
||
|
def __init__(
|
||
|
self,
|
||
|
thickness: SCO_BORDERTHICKNESS = None,
|
||
|
color: SCO_COLORS = SCO_COLORS.WHITE,
|
||
|
):
|
||
|
self.thickness = thickness
|
||
|
self.color: SCO_COLORS = color
|
||
|
|
||
|
def get_openxl(self):
|
||
|
side: Side = Side(border_style=self.thickness.width, color=self.color.argb)
|
||
|
return side
|
||
|
|
||
|
|
||
|
class Sco_Borders:
|
||
|
def __init__(
|
||
|
self,
|
||
|
left: Sco_BorderSide = None,
|
||
|
right: Sco_BorderSide = None,
|
||
|
top: Sco_BorderSide = None,
|
||
|
bottom: Sco_BorderSide = None,
|
||
|
):
|
||
|
self.left = left
|
||
|
self.right = right
|
||
|
self.top = top
|
||
|
self.bottom = bottom
|
||
|
|
||
|
def get_openxl(self):
|
||
|
border: Border = Border(
|
||
|
left=self.left.get_openxl(),
|
||
|
right=self.right.get_openxl(),
|
||
|
top=self.top.get_openxl(),
|
||
|
bottom=self.bottom.get_openxl(),
|
||
|
)
|
||
|
|
||
|
|
||
|
class Sco_Alignment:
|
||
|
def __init__(
|
||
|
self,
|
||
|
halign: SCO_HALIGN = None,
|
||
|
valign: SCO_VALIGN = None,
|
||
|
):
|
||
|
self.halign = halign
|
||
|
self.valign = valign
|
||
|
|
||
|
def get_openxl(self):
|
||
|
al = openpyxl.styles.Alignment()
|
||
|
al.horizontal = self.halign.position
|
||
|
al.vertical = self.valign.position
|
||
|
return al
|
||
|
|
||
|
|
||
|
class Sco_Font:
|
||
|
def __init__(
|
||
|
self,
|
||
|
name: str = None,
|
||
|
fontsize: int = None,
|
||
|
bold: bool = None,
|
||
|
italic: bool = None,
|
||
|
outline: bool = None,
|
||
|
color: "SCO_COLORS" = None,
|
||
|
):
|
||
|
self.name = name
|
||
|
self.bold = bold
|
||
|
self.italic = italic
|
||
|
self.outline = outline
|
||
|
self.color = color
|
||
|
self.fontsize = fontsize
|
||
|
|
||
|
|
||
|
class Sco_Style:
|
||
|
from openpyxl.cell import WriteOnlyCell, Cell
|
||
|
|
||
|
def __init__(
|
||
|
self,
|
||
|
font: Sco_Font = None,
|
||
|
bgcolor: SCO_COLORS = None,
|
||
|
alignment: Sco_Alignment = None,
|
||
|
borders: Sco_Borders = None,
|
||
|
number_format: SCO_NUMBER_FORMAT = None,
|
||
|
):
|
||
|
self.font = font
|
||
|
self.bgcolor = bgcolor
|
||
|
self.alignment = alignment
|
||
|
self.borders = borders
|
||
|
self.number_format = number_format
|
||
|
|
||
|
def apply(self, cell: Cell):
|
||
|
if self.font:
|
||
|
cell.font = self.font.get_openxl()
|
||
|
if self.bgcolor:
|
||
|
cell.fill = PatternFill(fill_type="solid", fgColor=self.bgcolor.argb)
|
||
|
if self.alignment:
|
||
|
cell.alignment = self.alignment.get_openxl()
|
||
|
if self.borders:
|
||
|
cell.border = self.borders.get_openxl()
|
||
|
if self.number_format:
|
||
|
cell.number_format = self.number_format.get_openxl()
|
||
|
|
||
|
|
||
|
class Composante_group(Composante):
|
||
|
def __init__(self, composantes: list[Composante]):
|
||
|
self.composantes = composantes
|
||
|
mini = min([comp.base for comp in composantes])
|
||
|
maxi = max([comp.end for comp in composantes])
|
||
|
width = sum([comp.width for comp in composantes])
|
||
|
if not width == (maxi - mini):
|
||
|
raise Exception("Composante group non complete ou non connexe")
|
||
|
super().__init__(base=mini, width=width)
|
||
|
|
||
|
|
||
|
class Composante_font(Composante_group):
|
||
|
def __init__(
|
||
|
self,
|
||
|
name: Composante_fontname,
|
||
|
fontsize: Composante_fontsize,
|
||
|
color: Composante_Colors,
|
||
|
bold: Composante_boolean,
|
||
|
italic: Composante_boolean,
|
||
|
outline: Composante_boolean,
|
||
|
):
|
||
|
super().__init__([name, fontsize, color, bold, italic, outline])
|
||
|
self.name = name
|
||
|
self.fontsize = fontsize
|
||
|
self.color = color
|
||
|
self.bold = bold
|
||
|
self.italic = italic
|
||
|
self.outline = outline
|
||
|
|
||
|
def build(self, signature):
|
||
|
return Sco_Font(
|
||
|
name=self.name.lookup_or_cache(signature),
|
||
|
fontsize=self.fontsize.lookup_or_cache(signature),
|
||
|
color=self.color.lookup_or_cache(signature),
|
||
|
bold=self.bold.lookup_or_cache(signature),
|
||
|
italic=self.italic.lookup_or_cache(signature),
|
||
|
outline=self.outline.lookup_or_cache(signature),
|
||
|
)
|
||
|
|
||
|
|
||
|
class Composante_border(Composante_group):
|
||
|
def __init__(self, thick: Composante_borderThickness, color: Composante_Colors):
|
||
|
super().__init__([thick, color])
|
||
|
self.thick = thick
|
||
|
self.color = color
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
return Sco_BorderSide(
|
||
|
thickness=self.thick.lookup_or_cache(signature),
|
||
|
color=self.color.lookup_or_cache(signature),
|
||
|
)
|
||
|
|
||
|
|
||
|
class Composante_borders(Composante_group):
|
||
|
def __init__(
|
||
|
self,
|
||
|
left: Composante_border,
|
||
|
right: Composante_border,
|
||
|
top: Composante_border,
|
||
|
bottom: Composante_border,
|
||
|
):
|
||
|
super().__init__([left, right, top, bottom])
|
||
|
self.left = left
|
||
|
self.right = right
|
||
|
self.top = top
|
||
|
self.bottom = bottom
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
return Sco_Borders(
|
||
|
left=self.left.lookup_or_cache(signature),
|
||
|
right=self.right.lookup_or_cache(signature),
|
||
|
top=self.top.lookup_or_cache(signature),
|
||
|
bottom=self.bottom.lookup_or_cache(signature),
|
||
|
)
|
||
|
|
||
|
|
||
|
class Composante_alignment(Composante_group):
|
||
|
def __init__(self, halign: Composante_halign, valign: Composante_valign):
|
||
|
super().__init__([halign, valign])
|
||
|
self.halign = halign
|
||
|
self.valign = valign
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
return Sco_Alignment(
|
||
|
halign=self.halign.lookup_or_cache(signature),
|
||
|
valign=self.valign.lookup_or_cache(signature),
|
||
|
)
|
||
|
|
||
|
# ALL = Composante_all(FONT, BGCOLOR, BORDERS, ALIGNMENT, NUMBER_FORMAT)
|
||
|
|
||
|
|
||
|
class Composante_all(Composante_group):
|
||
|
def __init__(
|
||
|
self,
|
||
|
font: Composante_font,
|
||
|
bgcolor: Composante_Colors,
|
||
|
borders: Composante_borders,
|
||
|
alignment: Composante_alignment,
|
||
|
number_format: Composante_number_format,
|
||
|
):
|
||
|
super().__init__([font, bgcolor, borders, alignment, number_format])
|
||
|
self.font = font
|
||
|
self.bgcolor = bgcolor
|
||
|
self.borders = borders
|
||
|
self.alignment = alignment
|
||
|
self.number_format = number_format
|
||
|
|
||
|
def build(self, signature: int):
|
||
|
return Sco_Style(
|
||
|
bgcolor=self.bgcolor.lookup_or_cache(signature),
|
||
|
font=self.font.lookup_or_cache(signature),
|
||
|
borders=self.borders.lookup_or_cache(signature),
|
||
|
alignment=self.alignment.lookup_or_cache(signature),
|
||
|
number_format=self.number_format.lookup_or_cache(signature),
|
||
|
)
|
||
|
|
||
|
def get_style(self, signature: int):
|
||
|
return self.lookup_or_cache(signature)
|
||
|
|
||
|
|
||
|
class FMT(Enum):
|
||
|
def __init__(self, composante: Composante):
|
||
|
self.composante = composante
|
||
|
|
||
|
def write(self, value, signature=0) -> int:
|
||
|
return self.composante.write(value, signature)
|
||
|
|
||
|
def get_style(self, signature: int):
|
||
|
return self.composante.lookup_or_cache(signature)
|
||
|
|
||
|
FONT_NAME = Composante_fontname()
|
||
|
FONT_SIZE = Composante_fontsize()
|
||
|
FONT_COLOR = Composante_Colors()
|
||
|
FONT_BOLD = Composante_boolean()
|
||
|
FONT_ITALIC = Composante_boolean()
|
||
|
FONT_OUTLINE = Composante_boolean()
|
||
|
BORDER_LEFT_STYLE = Composante_borderThickness()
|
||
|
BORDER_LEFT_COLOR = Composante_Colors()
|
||
|
BORDER_RIGHT_STYLE = Composante_borderThickness()
|
||
|
BORDER_RIGHT_COLOR = Composante_Colors()
|
||
|
BORDER_TOP_STYLE = Composante_borderThickness()
|
||
|
BORDER_TOP_COLOR = Composante_Colors()
|
||
|
BORDER_BOTTOM_STYLE = Composante_borderThickness()
|
||
|
BORDER_BOTTOM_COLOR = Composante_Colors()
|
||
|
BGCOLOR = Composante_Colors()
|
||
|
ALIGNMENT_HALIGN = Composante_halign()
|
||
|
ALIGNEMENT_VALIGN = Composante_valign()
|
||
|
NUMBER_FORMAT = Composante_number_format()
|
||
|
FONT = Composante_font(
|
||
|
FONT_NAME, FONT_SIZE, FONT_COLOR, FONT_BOLD, FONT_ITALIC, FONT_OUTLINE
|
||
|
)
|
||
|
BORDER_LEFT = Composante_border(BORDER_LEFT_STYLE, BORDER_LEFT_COLOR)
|
||
|
BORDER_RIGHT = Composante_border(BORDER_RIGHT_STYLE, BORDER_RIGHT_COLOR)
|
||
|
BORDER_TOP = Composante_border(BORDER_TOP_STYLE, BORDER_TOP_COLOR)
|
||
|
BORDER_BOTTOM = Composante_border(BORDER_BOTTOM_STYLE, BORDER_BOTTOM_COLOR)
|
||
|
BORDERS = Composante_borders(BORDER_LEFT, BORDER_RIGHT, BORDER_TOP, BORDER_BOTTOM)
|
||
|
ALIGNMENT = Composante_alignment(ALIGNMENT_HALIGN, ALIGNMENT_HALIGN)
|
||
|
ALL = Composante_all(FONT, BGCOLOR, BORDERS, ALIGNMENT, NUMBER_FORMAT)
|