release
[librarian.git] / src / librarian / elements / figures / ilustr.py
1 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
3 #
4 import io
5 import urllib.parse
6 import urllib.request
7 from PIL import Image
8 from ..base import WLElement
9
10
11 MAX_PNG_WEIGHT = 200000
12
13
14 class Ilustr(WLElement):
15     NUMBERING = 'i'
16
17     EPUB_TAG = HTML_TAG = 'img'
18
19     def get_html_attr(self, builder):
20         cls = 'ilustr'
21         if self.attrib.get('wyrownanie'):
22             cls += ' ' + self.attrib['wyrownanie']
23         if self.attrib.get('oblew'):
24             cls += ' oblew'
25         attr = {
26             'class': cls,
27             'alt': self.attrib.get('alt', ''),
28             'title': self.attrib.get('alt', ''),
29             'src': self.attrib.get('src', ''),
30             }
31         if self.attrib.get('srcset'):
32             attr['srcset'] = self.attrib['srcset']
33             attr['sizes'] = '''
34             (min-width: 718px) 600px,
35             (min-width: 600px) calc(100vw - 118px),
36             (min-width: 320px) calc(100vw - 75px),
37             (min-width: 15em) calc(100wv - 60px),
38             calc(100wv - 40px)
39             '''
40         if self.attrib.get('szer'):
41             attr['style'] = 'width: ' + self.attrib['szer']
42         return attr
43
44     def get_epub_attr(self, builder):
45         url = urllib.parse.urljoin(
46             builder.base_url,
47             self.get('src')
48         )
49         
50         imgfile = urllib.request.urlopen(url)
51         img = Image.open(imgfile)
52         th_format, ext, media_type = {
53             'GIF': ('GIF', 'gif', 'image/gif'),
54             'PNG': ('PNG', 'png', 'image/png'),
55         }.get(img.format, ('JPEG', 'jpg', 'image/jpeg'))
56
57         width = 600
58         if img.size[0] < width:
59             th = img
60         else:
61             th = img.resize((width, round(width * img.size[1] / img.size[0])))
62
63         buffer = io.BytesIO()
64         th.save(buffer, format=th_format)
65
66         # Limit PNG to 200K. If larger, convert to JPEG.
67         if th_format == 'PNG' and buffer.tell() > MAX_PNG_WEIGHT:
68             th_format, ext, media_type = 'JPEG', 'jpg', 'image/jpeg'
69             if th.mode != 'RGB':
70                 buffer = io.BytesIO()
71                 th = Image.alpha_composite(
72                     Image.new('RGBA', th.size, '#fff'),
73                     th.convert('RGBA')
74                 )
75                 th = th.convert('RGB')
76             th.save(buffer, format=th_format)
77
78         imgfile.close()
79         file_name = 'image%d.%s' % (
80             builder.assign_image_number(),
81             ext
82         )
83
84         builder.add_file(
85             content=buffer.getvalue(),
86             file_name=file_name,
87             media_type=media_type,
88         )
89         
90         return {
91             'src': file_name,
92             'alt': self.attrib.get('alt', ''),
93             'title': self.attrib.get('alt', ''),
94         }