1d3edd6da0b8d7059124c487b329fff2d30c73b3
[librarian.git] / src / librarian / elements / base.py
1 # -*- coding: utf-8
2
3 import re
4 from lxml import etree
5 from librarian import dcparser, RDFNS
6 from librarian.html import raw_printable_text
7 from librarian.util import get_translation
8
9
10 class WLElement(etree.ElementBase):
11     SECTION_PRECEDENCE = None
12
13     TXT_TOP_MARGIN = 0
14     TXT_BOTTOM_MARGIN = 0
15     TXT_PREFIX = ""
16     TXT_SUFFIX = ""
17
18     HTML_TAG = None
19     HTML_ATTR = {}
20     HTML_CLASS = None
21
22     EPUB_TAG = None
23     EPUB_ATTR = {}
24     EPUB_CLASS = None
25     EPUB_START_CHUNK = False
26    
27     CAN_HAVE_TEXT = True
28     STRIP = False
29
30     text_substitutions = [
31         (u'---', u'—'),
32         (u'--', u'–'),
33         #(u'...', u'…'),  # Temporary turnoff for epub
34         (u',,', u'„'),
35         (u'"', u'”'),
36         ('\ufeff', ''),
37
38         ("'", "\u2019"),    # This was enabled for epub.
39     ]
40
41     @property
42     def meta_object(self):
43         if not hasattr(self, '_meta_object'):
44             elem = self.find(RDFNS('RDF'))
45             if elem is not None:
46                 self._meta_object = dcparser.BookInfo.from_element(elem)
47             else:
48                 self._meta_object = None
49         return self._meta_object
50
51     @property
52     def meta(self):
53         if self.meta_object is not None:
54             return self.meta_object
55         else:
56             if self.getparent() is not None:
57                 return self.getparent().meta
58             else:
59                 return self.document.base_meta
60
61     @property
62     def gettext(self):
63         return get_translation(self.meta.language).gettext
64
65     def raw_printable_text(self):
66         # TODO: podtagi, wyroznienia, etc
67         t = ''
68         t += self.normalize_text(self.text)
69         for c in self:
70             if not isinstance(c, WLElement):
71                 continue
72             if c.tag not in ('pe', 'pa', 'pt', 'pr', 'motyw'):
73                 t += c.raw_printable_text()
74             t += self.normalize_text(c.tail)
75         return t
76     
77     def normalize_text(self, text):
78         text = text or ''
79         for e, s in self.text_substitutions:
80             text = text.replace(e, s)
81             # FIXME: TEmporary turnoff
82 #        text = re.sub(r'\s+', ' ', text)
83 ### TODO: Added now for epub
84         text = re.sub(r'(?<=\s\w)\s+', u'\u00A0', text)
85
86         return text
87
88     def _build_inner(self, builder, build_method):
89         child_count = len(self)
90         if self.CAN_HAVE_TEXT and self.text:
91             text = self.normalize_text(self.text)
92             if self.STRIP:
93                 text = text.lstrip()
94                 if not child_count:
95                     text = text.rstrip()
96             builder.push_text(text)
97         for i, child in enumerate(self):
98             if isinstance(child, WLElement):
99                 getattr(child, build_method)(builder)
100             if self.CAN_HAVE_TEXT and child.tail:
101                 text = self.normalize_text(child.tail)
102                 if self.STRIP and i == child_count - 1:
103                     text = text.rstrip()
104                 builder.push_text(text)
105
106     def _txt_build_inner(self, builder):
107         self._build_inner(builder, 'txt_build')
108
109     def txt_build(self, builder):
110         if hasattr(self, 'TXT_LEGACY_TOP_MARGIN'):
111             builder.push_legacy_margin(self.TXT_LEGACY_TOP_MARGIN)
112         else:
113             builder.push_margin(self.TXT_TOP_MARGIN)
114         builder.push_text(self.TXT_PREFIX, True)
115         self._txt_build_inner(builder)
116         builder.push_text(self.TXT_SUFFIX, True)
117         if hasattr(self, 'TXT_LEGACY_BOTTOM_MARGIN'):
118             builder.push_legacy_margin(self.TXT_LEGACY_BOTTOM_MARGIN)
119         else:
120             builder.push_margin(self.TXT_BOTTOM_MARGIN)
121
122     def _html_build_inner(self, builder):
123         self._build_inner(builder, 'html_build')
124
125     def get_html_attr(self, builder):
126         attr = self.HTML_ATTR.copy()
127         if self.HTML_CLASS:
128             attr['class'] = self.HTML_CLASS
129         # always copy the id attribute (?)
130         if self.attrib.get('id'):
131             attr['id'] = self.attrib['id']
132         elif '_compat_section_id' in self.attrib:
133             attr['id'] = self.attrib['_compat_section_id']
134         return attr
135
136     def html_build(self, builder):
137         if self.HTML_TAG:
138             builder.start_element(
139                 self.HTML_TAG,
140                 self.get_html_attr(builder),
141             )
142
143         self._html_build_inner(builder)
144         if self.HTML_TAG:
145             builder.end_element()
146
147     def _epub_build_inner(self, builder):
148         self._build_inner(builder, 'epub_build')
149
150     def get_epub_attr(self, builder):
151         attr = self.EPUB_ATTR.copy()
152         if self.EPUB_CLASS:
153             attr['class'] = self.EPUB_CLASS
154         return attr
155
156     def epub_build(self, builder):
157         from librarian.elements.masters import Master
158
159         # TEMPORARY
160         self.CAN_HAVE_TEXT = True
161         self.STRIP = False
162        
163         start_chunk = self.EPUB_START_CHUNK and isinstance(self.getparent(), Master)
164
165         if start_chunk:
166             builder.start_chunk()
167
168         fragment = None
169         if self.SECTION_PRECEDENCE:
170             if not start_chunk:
171                 fragment = 'sub%d' % builder.assign_section_number()
172                 self.attrib['id'] = fragment
173
174             builder.add_toc_entry(
175                 fragment,
176                 self.raw_printable_text(),
177                 self.SECTION_PRECEDENCE
178             )
179             
180         if self.EPUB_TAG:
181             attr = self.get_epub_attr(builder)
182             if fragment:
183                 attr['id'] = fragment
184             builder.start_element(
185                 self.EPUB_TAG,
186                 attr
187             )
188
189         self._epub_build_inner(builder)
190         if self.EPUB_TAG:
191             builder.end_element()
192
193     def validate(self):
194         from librarian.elements.masters import Master
195         from librarian.elements.blocks import DlugiCytat, PoezjaCyt
196
197         if self.SECTION_PRECEDENCE:
198             assert isinstance(self.getparent(), (Master, DlugiCytat, PoezjaCyt)), \
199                     'Header {} inside a <{}> instead of a master.'.format(
200                             etree.tostring(self), self.getparent().tag)
201
202         for c in self:
203             if isinstance(c, WLElement):
204                 c.validate()
205
206
207     def sanitize(self):
208         # TODO: Remove insanity here.
209         for e in self:
210             if isinstance(e, WLElement):
211                 e.sanitize()