import re
import gws
import gws.types as t
MM_PER_IN = 25.4
PT_PER_IN = 72
# OGC's 1px = 0.28mm
# OGC 06-042, 7.2.4.6.9
OGC_M_PER_PX = 0.00028
OGC_SCREEN_PPI = MM_PER_IN / (OGC_M_PER_PX * 1000) # 90.71
PDF_DPI = 96
[docs]def scale2res(x):
return x * OGC_M_PER_PX
[docs]def res2scale(x):
return x / OGC_M_PER_PX
[docs]def mm2in(x):
return x / MM_PER_IN
[docs]def m2in(x):
return (x / MM_PER_IN) * 1000
[docs]def in2mm(x):
return x * MM_PER_IN
[docs]def in2m(x):
return (x * MM_PER_IN) / 1000
[docs]def in2px(x, ppi):
return x * ppi
[docs]def mm2px(x, ppi):
return int((x * ppi) / MM_PER_IN)
[docs]def mm2px_f(x, ppi):
return (x * ppi) / MM_PER_IN
[docs]def px2mm(x, ppi):
return int((x / ppi) * MM_PER_IN)
[docs]def point_mm2px(xy, ppi):
return mm2px(xy[0], ppi), mm2px(xy[1], ppi)
[docs]def point_mm2px_f(xy, ppi):
return mm2px_f(xy[0], ppi), mm2px_f(xy[1], ppi)
[docs]def point_px2mm(xy, ppi):
return px2mm(xy[0], ppi), px2mm(xy[1], ppi)
[docs]def mm2pt(x):
return (x / MM_PER_IN) * PT_PER_IN
[docs]def pt2mm(x):
return (x / PT_PER_IN) * MM_PER_IN
_unit_re = re.compile(r'''(?x)
^
(?P<number>
-?
(\d+ (\.\d*)? )
|
(\.\d+)
)
(?P<rest> .*)
$
''')
_METRIC = {
'mm': 1,
'cm': 10,
'm': 1e3,
'km': 1e6,
}
[docs]def parse(s: str, units: t.List = [], default=None) -> t.Measurement:
if isinstance(s, (int, float)):
if not default:
raise ValueError(f'invalid unit value: {s!r}')
return s, default
s = gws.as_str(s).strip()
m = _unit_re.match(s)
if not m:
raise ValueError(f'invalid unit value: {s!r}')
n = float(m.group('number'))
u = m.group('rest').strip().lower()
if not units and default:
units = [default]
if not u:
if not default:
raise ValueError(f'invalid unit value: {s!r}')
return n, default
if u in units:
return n, u
# e.g. 1cm given, but only mm allowed
if u in _METRIC:
mm = n * _METRIC[u]
for unit, f in _METRIC.items():
if unit in units:
return mm / f, unit
# @TODO: in, ft etc
raise ValueError(f'invalid unit value: {s!r}')
_durations = {
'w': 3600 * 24 * 7,
'd': 3600 * 24,
'h': 3600,
'm': 60,
's': 1,
}
[docs]def parse_duration(s):
if isinstance(s, int):
return s
p = None
r = 0
for n, v in re.findall(r'(\d+)|(\D+)', str(s).strip()):
if n:
p = int(n)
continue
v = v.strip()
if p is None or v not in _durations:
raise ValueError('invalid duration', s)
r += p * _durations[v]
p = None
if p:
r += p
return r