]> git.ipfire.org Git - ipfire.org.git/blob - src/backend/util.py
wiki: Implement embedding images with {{...}} syntax
[ipfire.org.git] / src / backend / util.py
1 #!/usr/bin/python3
2
3 import PIL.Image
4 import PIL.ImageFilter
5 import PIL.ImageOps
6 import io
7 import logging
8 import random
9 import re
10 import string
11 import unicodedata
12
13 def parse_search_query(query):
14 q = []
15 for word in query.split():
16 # Is this lexeme negated?
17 negated = word.startswith("!")
18
19 # Remove any special characters
20 word = re.sub(r"\W+", "", word, flags=re.UNICODE)
21 if not word:
22 continue
23
24 # Restore negation
25 if negated:
26 word = "!%s" % word
27
28 q.append(word)
29
30 return " & ".join(q)
31
32 def format_size(s, max_unit=None):
33 units = ("B", "kB", "MB", "GB", "TB")
34
35 i = 0
36 while s >= 1024 and i < len(units) - 1:
37 s /= 1024
38 i += 1
39
40 if max_unit and units[i] == max_unit:
41 break
42
43 return "%.0f%s" % (s, units[i])
44
45 def format_time(s, shorter=True):
46 #_ = handler.locale.translate
47 _ = lambda x: x
48
49 hrs, s = divmod(s, 3600)
50 min, s = divmod(s, 60)
51
52 if s >= 30:
53 min += 1
54
55 if shorter and not hrs:
56 return _("%(min)d min") % { "min" : min }
57
58 return _("%(hrs)d:%(min)02d hrs") % {"hrs" : hrs, "min" : min}
59
60 def random_string(length=8):
61 input_chars = string.ascii_letters + string.digits
62
63 r = (random.choice(input_chars) for i in range(length))
64
65 return "".join(r)
66
67 def normalize(s):
68 # Remove any non-ASCII characters
69 try:
70 s = unicodedata.normalize("NFKD", s)
71 except TypeError:
72 pass
73
74 # Remove excessive whitespace
75 s = re.sub(r"[^\w]+", " ", s)
76
77 return "-".join(s.split())
78
79 def generate_thumbnail(data, size, square=False, **args):
80 assert data, "No image data received"
81
82 image = PIL.Image.open(io.BytesIO(data))
83
84 # Save image format
85 format = image.format
86
87 # Remove any alpha-channels
88 if image.format == "JPEG" and not image.mode == "RGB":
89 # Make a white background
90 background = PIL.Image.new("RGBA", image.size, (255,255,255))
91
92 # Convert image to RGBA if not in RGBA, yet
93 if not image.mode == "RGBA":
94 image = image.convert("RGBA")
95
96 # Flatten both images together
97 flattened_image = PIL.Image.alpha_composite(background, image)
98
99 # Remove the alpha channel
100 image = flattened_image.convert("RGB")
101
102 # Resize the image to the desired resolution
103 if square:
104 image = PIL.ImageOps.fit(image, (size, size), PIL.Image.LANCZOS)
105 else:
106 image.thumbnail((size, size), PIL.Image.LANCZOS)
107
108 if image.format == "JPEG":
109 # Apply a gaussian blur to make compression easier
110 image = image.filter(PIL.ImageFilter.GaussianBlur(radius=0.05))
111
112 # Arguments to optimise the compression
113 args.update({
114 "subsampling" : "4:2:0",
115 "quality" : 70,
116 })
117
118 with io.BytesIO() as f:
119 # If writing out the image does not work with optimization,
120 # we try to write it out without any optimization.
121 try:
122 image.save(f, format, optimize=True, **args)
123 except:
124 image.save(f, format, **args)
125
126 return f.getvalue()