1# Copyright (c) 2010-2024 openpyxl
2
3"""
4Excel specific descriptors
5"""
6
7from openpyxl.xml.constants import REL_NS
8from openpyxl.compat import safe_string
9from openpyxl.xml.functions import Element
10
11from . import (
12 MatchPattern,
13 MinMax,
14 Integer,
15 String,
16 Sequence,
17)
18from .serialisable import Serialisable
19
20
21class HexBinary(MatchPattern):
22
23 pattern = "[0-9a-fA-F]+$"
24
25
26class UniversalMeasure(MatchPattern):
27
28 pattern = r"[0-9]+(\.[0-9]+)?(mm|cm|in|pt|pc|pi)"
29
30
31class TextPoint(MinMax):
32 """
33 Size in hundredths of points.
34 In theory other units of measurement can be used but these are unbounded
35 """
36 expected_type = int
37
38 min = -400000
39 max = 400000
40
41
42Coordinate = Integer
43
44
45class Percentage(MinMax):
46
47 pattern = r"((100)|([0-9][0-9]?))(\.[0-9][0-9]?)?%" # strict
48 min = -1000000
49 max = 1000000
50
51 def __set__(self, instance, value):
52 if isinstance(value, str) and "%" in value:
53 value = value.replace("%", "")
54 value = int(float(value) * 1000)
55 super().__set__(instance, value)
56
57
58class Extension(Serialisable):
59
60 uri = String()
61
62 def __init__(self,
63 uri=None,
64 ):
65 self.uri = uri
66
67
68class ExtensionList(Serialisable):
69
70 ext = Sequence(expected_type=Extension)
71
72 def __init__(self,
73 ext=(),
74 ):
75 self.ext = ext
76
77
78class Relation(String):
79
80 namespace = REL_NS
81 allow_none = True
82
83
84class Base64Binary(MatchPattern):
85 # http://www.w3.org/TR/xmlschema11-2/#nt-Base64Binary
86 pattern = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$"
87
88
89class Guid(MatchPattern):
90 # https://msdn.microsoft.com/en-us/library/dd946381(v=office.12).aspx
91 pattern = r"{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}"
92
93
94class CellRange(MatchPattern):
95
96 pattern = r"^[$]?([A-Za-z]{1,3})[$]?(\d+)(:[$]?([A-Za-z]{1,3})[$]?(\d+)?)?$|^[A-Za-z]{1,3}:[A-Za-z]{1,3}$"
97 allow_none = True
98
99 def __set__(self, instance, value):
100
101 if value is not None:
102 value = value.upper()
103 super().__set__(instance, value)
104
105
106def _explicit_none(tagname, value, namespace=None):
107 """
108 Override serialisation because explicit none required
109 """
110 if namespace is not None:
111 tagname = "{%s}%s" % (namespace, tagname)
112 return Element(tagname, val=safe_string(value))