Drop lots of legacy code. Support Python 3.7-3.11.
[librarian.git] / src / librarian / functions.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 from lxml import etree
5 import re
6 from ebooklib import epub
7
8 from librarian.dcparser import Person
9 from librarian import get_resource
10
11
12 def _register_function(f):
13     """ Register extension function with lxml """
14     ns = etree.FunctionNamespace('http://wolnelektury.pl/functions')
15     ns[f.__name__] = f
16
17
18 def reg_substitute_entities():
19     entity_substitutions = [
20         ('---', '—'),
21         ('--', '–'),
22         ('...', '…'),
23         (',,', '„'),
24         ('"', '”'),
25     ]
26
27     def substitute_entities(context, text):
28         """XPath extension function converting all entites in passed text."""
29         if isinstance(text, list):
30             text = ''.join(text)
31         for entity, substitutution in entity_substitutions:
32             text = text.replace(entity, substitutution)
33         return text
34
35     _register_function(substitute_entities)
36
37
38 def reg_strip():
39     def strip(context, text):
40         """Remove unneeded whitespace from beginning and end"""
41         if isinstance(text, list):
42             text = ''.join(text)
43         return re.sub(r'\s+', ' ', text).strip()
44     _register_function(strip)
45
46
47 def reg_starts_white():
48     def starts_white(context, text):
49         if isinstance(text, list):
50             text = ''.join(text)
51         if not text:
52             return False
53         return text[0].isspace()
54     _register_function(starts_white)
55
56
57 def reg_ends_white():
58     def ends_white(context, text):
59         if isinstance(text, list):
60             text = ''.join(text)
61         if not text:
62             return False
63         return text[-1].isspace()
64     _register_function(ends_white)
65
66
67 def reg_wrap_words():
68     def wrap_words(context, text, wrapping):
69         """
70         XPath extension function automatically wrapping words
71         in passed text.
72         """
73         if isinstance(text, list):
74             text = ''.join(text)
75         if not wrapping:
76             return text
77
78         words = re.split(r'\s', text)
79
80         line_length = 0
81         lines = [[]]
82         for word in words:
83             line_length += len(word) + 1
84             if line_length > wrapping:
85                 # Max line length was exceeded. We create new line
86                 lines.append([])
87                 line_length = len(word)
88             lines[-1].append(word)
89         return '\n'.join(' '.join(line) for line in lines)
90     _register_function(wrap_words)
91
92
93 def reg_person_name():
94     def person_name(context, text):
95         """ Converts "Name, Forename" to "Forename Name" """
96         if isinstance(text, list):
97             text = ''.join(text)
98         return Person.from_text(text).readable()
99     _register_function(person_name)
100
101
102 def reg_texcommand():
103     def texcommand(context, text):
104         """Remove non-letters"""
105         if isinstance(text, list):
106             text = ''.join(text)
107         return re.sub(r'[^a-zA-Z]', '', text).strip()
108     _register_function(texcommand)
109
110
111 def lang_code_3to2(text):
112     """Convert 3-letter language code to 2-letter code"""
113     result = ''
114     text = ''.join(text)
115     with open(get_resource('res/ISO-639-2_8859-1.txt'), 'rb') as f:
116         for line in f.read().decode('latin1').split('\n'):
117             codes = line.strip().split('|')
118             if codes[0] == text:
119                 result = codes[2]
120     if result == '':
121         return text
122     else:
123         return result
124
125
126 def mathml_latex(context, trees):
127     from librarian.embeds.mathml import MathML
128     text = MathML(trees[0]).to_latex().data
129     # Remove invisible multiplications, they produce unwanted spaces.
130     text = text.replace('\u2062', '')
131     return text
132
133
134 def reg_mathml_latex():
135     _register_function(mathml_latex)
136
137
138 def reg_mathml_epub(output):
139     from librarian.embeds.mathml import MathML
140
141     def mathml(context, trees):
142         data = MathML(trees[0]).to_latex().to_png().data
143         name = "math%d.png" % mathml.count
144         mathml.count += 1
145         output.add_item(
146             epub.EpubItem(
147                 uid='math%d' % mathml.count,
148                 file_name=name,
149                 media_type='image/png',
150                 content=data
151             )
152         )
153
154         return name
155     mathml.count = 0
156     _register_function(mathml)