New EPUB builder, other minor changes.
[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 c.tag not in ('pe', 'pa', 'pt', 'pr', 'motyw'):
71                 t += c.raw_printable_text()
72             t += self.normalize_text(c.tail)
73         return t
74     
75     def normalize_text(self, text):
76         text = text or ''
77         for e, s in self.text_substitutions:
78             text = text.replace(e, s)
79             # FIXME: TEmporary turnoff
80 #        text = re.sub(r'\s+', ' ', text)
81 ### TODO: Added now for epub
82         text = re.sub(r'(?<=\s\w)\s+', u'\u00A0', text)
83
84         return text
85
86     def _build_inner(self, builder, build_method):
87         child_count = len(self)
88         if self.CAN_HAVE_TEXT and self.text:
89             text = self.normalize_text(self.text)
90             if self.STRIP:
91                 text = text.lstrip()
92                 if not child_count:
93                     text = text.rstrip()
94             builder.push_text(text)
95         for i, child in enumerate(self):
96             if isinstance(child, WLElement):
97                 getattr(child, build_method)(builder)
98             if self.CAN_HAVE_TEXT and child.tail:
99                 text = self.normalize_text(child.tail)
100                 if self.STRIP and i == child_count - 1:
101                     text = text.rstrip()
102                 builder.push_text(text)
103
104     def _txt_build_inner(self, builder):
105         self._build_inner(builder, 'txt_build')
106
107     def txt_build(self, builder):
108         if hasattr(self, 'TXT_LEGACY_TOP_MARGIN'):
109             builder.push_legacy_margin(self.TXT_LEGACY_TOP_MARGIN)
110         else:
111             builder.push_margin(self.TXT_TOP_MARGIN)
112         builder.push_text(self.TXT_PREFIX, True)
113         self._txt_build_inner(builder)
114         builder.push_text(self.TXT_SUFFIX, True)
115         if hasattr(self, 'TXT_LEGACY_BOTTOM_MARGIN'):
116             builder.push_legacy_margin(self.TXT_LEGACY_BOTTOM_MARGIN)
117         else:
118             builder.push_margin(self.TXT_BOTTOM_MARGIN)
119
120     def _html_build_inner(self, builder):
121         self._build_inner(builder, 'html_build')
122
123     def get_html_attr(self, builder):
124         attr = self.HTML_ATTR.copy()
125         if self.HTML_CLASS:
126             attr['class'] = self.HTML_CLASS
127         # always copy the id attribute (?)
128         if self.attrib.get('id'):
129             attr['id'] = self.attrib['id']
130         elif '_compat_section_id' in self.attrib:
131             attr['id'] = self.attrib['_compat_section_id']
132         return attr
133
134     def html_build(self, builder):
135         if self.HTML_TAG:
136             builder.start_element(
137                 self.HTML_TAG,
138                 self.get_html_attr(builder),
139             )
140
141         self._html_build_inner(builder)
142         if self.HTML_TAG:
143             builder.end_element()
144
145     def _epub_build_inner(self, builder):
146         self._build_inner(builder, 'epub_build')
147
148     def get_epub_attr(self, builder):
149         attr = self.EPUB_ATTR.copy()
150         if self.EPUB_CLASS:
151             attr['class'] = self.EPUB_CLASS
152         return attr
153
154     def epub_build(self, builder):
155         # TEMPORARY
156         self.CAN_HAVE_TEXT = True
157         self.STRIP = False
158         
159         if self.EPUB_START_CHUNK:
160             builder.start_chunk()
161
162         fragment = None
163         if self.SECTION_PRECEDENCE:
164             if not self.EPUB_START_CHUNK:
165                 fragment = 'sub%d' % builder.assign_section_number()
166                 self.attrib['id'] = fragment
167
168             builder.add_toc_entry(
169                 fragment,
170                 self.raw_printable_text(),
171                 self.SECTION_PRECEDENCE
172             )
173             
174         if self.EPUB_TAG:
175             attr = self.get_epub_attr(builder)
176             if fragment:
177                 attr['id'] = fragment
178             builder.start_element(
179                 self.EPUB_TAG,
180                 attr
181             )
182
183         self._epub_build_inner(builder)
184         if self.EPUB_TAG:
185             builder.end_element()
186             
187     def sanitize(self):
188         # TODO: Remove insanity here.
189         for e in self:
190             if isinstance(e, WLElement):
191                 e.sanitize()