1 # -*- coding: utf-8 -*-
3 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
6 """PDF creation library.
8 Creates one big XML from the book and its children, converts it to LaTeX
9 with TeXML, then runs it by XeLaTeX.
12 from __future__ import with_statement
16 from StringIO import StringIO
17 from tempfile import mkdtemp, NamedTemporaryFile
19 from copy import deepcopy
20 from subprocess import call, PIPE
22 from Texml.processor import process
23 from lxml import etree
24 from lxml.etree import XMLSyntaxError, XSLTApplyError
26 from xmlutils import Xmill, tag, tagged, ifoption
27 from librarian.dcparser import Person
28 from librarian.parser import WLDocument
29 from librarian import ParseError, DCNS, get_resource, IOFile, Format
30 from librarian import functions
31 from pdf import PDFFormat
36 def _wrap(*args, **kw):
37 value = f(*args, **kw)
39 prefix = (u'<TeXML escape="%d">' % (really and 1 or 0))
41 if isinstance(value, list):
42 import pdb; pdb.set_trace()
43 if isinstance(value, tuple):
44 return prefix + value[0], value[1] + postfix
46 return prefix + value + postfix
51 def cmd(name, pass_text=False):
52 def wrap(self, element):
53 pre = u'<cmd name="%s">' % name
56 pre += "<parm>%s</parm>" % element.text
63 def mark_alien_characters(text):
64 text = re.sub(ur"([\u0400-\u04ff]+)", ur"<alien>\1</alien>", text)
68 class EduModule(Xmill):
69 def __init__(self, options=None):
70 super(EduModule, self).__init__(options)
71 self.activity_counter = 0
72 self.register_text_filter(functions.substitute_entities)
73 self.register_text_filter(mark_alien_characters)
75 def get_dc(self, element, dc_field, single=False):
76 values = map(lambda t: t.text, element.xpath("//dc:%s" % dc_field, namespaces={'dc': DCNS.uri}))
81 def handle_rdf__RDF(self, _):
82 "skip metadata in generation"
86 def get_rightsinfo(self, element):
87 rights_lic = self.get_dc(element, 'rights.license', True)
88 return u'<cmd name="rightsinfostr">' + \
89 (rights_lic and u'<opt>%s</opt>' % rights_lic or '') +\
90 u'<parm>%s</parm>' % self.get_dc(element, 'rights', True) +\
94 def get_authors(self, element):
95 authors = self.get_dc(element, 'creator.expert') + \
96 self.get_dc(element, 'creator.scenario') + \
97 self.get_dc(element, 'creator.textbook')
98 return u', '.join(authors)
101 def get_title(self, element):
102 return self.get_dc(element, 'title', True)
104 def handle_utwor(self, element):
107 <TeXML xmlns="http://getfo.sourceforge.net/texml/ns1">
109 \\documentclass[%s]{wl}
110 \\usepackage{style}''' % self.options['customization_str'],
111 self.options['has_cover'] and '\usepackage{makecover}',
112 (self.options['morefloats'] == 'new' and '\usepackage[maxfloats=64]{morefloats}') or
113 (self.options['morefloats'] == 'old' and '\usepackage{morefloats}') or
114 (self.options['morefloats'] == 'none' and
115 u'''\\IfFileExists{morefloats.sty}{
116 \\usepackage{morefloats}
118 u'''\\def\\authors{%s}''' % self.get_authors(element),
119 u'''\\author{\\authors}''',
120 u'''\\title{%s}''' % self.get_title(element),
121 u'''\\def\\bookurl{%s}''' % self.get_dc(element, 'identifier.url', True),
122 u'''\\def\\rightsinfo{%s}''' % self.get_rightsinfo(element),
125 return u"".join(filter(None, lines)), u'</TeXML>'
128 handle_naglowek_rozdzial = escape(True)(cmd("naglowekrozdzial", True))
129 handle_naglowek_podrozdzial = escape(True)(cmd("naglowekpodrozdzial", True))
132 def handle_powiesc(self, element):
134 <env name="document">
135 <cmd name="maketitle"/>
138 handle_autor_utworu = cmd('autorutworu', True)
139 handle_nazwa_utworu = cmd('nazwautworu', True)
140 handle_dzielo_nadrzedne = cmd('dzielonadrzedne', True)
141 handle_podtytul = cmd('podtytul', True)
143 handle_akap = handle_akap_dialog = handle_akap_cd = lambda s, e: ("\n", "\n")
144 handle_strofa = lambda s, e: ("\n","\n")
146 def handle_aktywnosc(self, element):
147 self.activity_counter += 1
150 'activity_counter': self.activity_counter,
153 submill = EduModule(self.options)
155 opis = submill.generate(element.xpath('opis')[0])
157 n = element.xpath('wskazowki')
158 if n: wskazowki = submill.generate(n[0])
161 n = element.xpath('pomoce')
163 if n: pomoce = submill.generate(n[0])
166 forma = ''.join(element.xpath('forma/text()'))
168 czas = ''.join(element.xpath('czas/text()'))
170 counter = self.activity_counter
177 %(counter)d. %(opis)s
182 handle_opis = ifoption(sub_gen=True)(lambda s, e: ('', ''))
183 handle_wskazowki = ifoption(sub_gen=True)(lambda s, e: ('', ''))
185 @ifoption(sub_gen=True)
186 def handle_pomoce(self, _):
187 return "Pomoce: ", ""
189 def handle_czas(self, *_):
192 def handle_forma(self, *_):
195 # def handle_cwiczenie(self, element):
196 # exercise_handlers = {
198 # 'uporzadkuj': Uporzadkuj,
201 # 'przyporzadkuj': Przyporzadkuj,
202 # 'prawdafalsz': PrawdaFalsz
205 # typ = element.attrib['typ']
206 # handler = exercise_handlers[typ](self.options)
207 # return handler.generate(element)
210 # def handle_lista(self, element, attrs={}):
211 # ltype = element.attrib.get('typ', 'punkt')
212 # if ltype == 'slowniczek':
213 # surl = element.attrib.get('href', None)
216 # sxml = etree.fromstring(self.options['provider'].by_uri(surl).get_string())
217 # self.options = {'slowniczek': True, 'slowniczek_xml': sxml }
218 # return '<div class="slowniczek">', '</div>'
220 # listtag = {'num': 'ol',
223 # 'czytelnia': 'ul'}[ltype]
225 # classes = attrs.get('class', '')
226 # if classes: del attrs['class']
228 # attrs_s = ' '.join(['%s="%s"' % kv for kv in attrs.items()])
229 # if attrs_s: attrs_s = ' ' + attrs_s
231 # return '<%s class="lista %s %s"%s>' % (listtag, ltype, classes, attrs_s), '</%s>' % listtag
233 # def handle_punkt(self, element):
234 # if self.options['slowniczek']:
235 # return '<dl>', '</dl>'
237 # return '<li>', '</li>'
239 # def handle_definiendum(self, element):
240 # nxt = element.getnext()
243 # # let's pull definiens from another document
244 # if self.options['slowniczek_xml'] and (not nxt or nxt.tag != 'definiens'):
245 # sxml = self.options['slowniczek_xml']
246 # assert element.text != ''
247 # defloc = sxml.xpath("//definiendum[text()='%s']" % element.text)
249 # definiens = defloc[0].getnext()
250 # if definiens.tag == 'definiens':
251 # subgen = EduModule(self.options)
252 # definiens_s = subgen.generate(definiens)
254 # return u"<dt>", u"</dt>" + definiens_s
256 # def handle_definiens(self, element):
257 # return u"<dd>", u"</dd>"
260 # def handle_podpis(self, element):
261 # return u"""<div class="caption">""", u"</div>"
263 # def handle_tabela(self, element):
264 # has_frames = int(element.attrib.get("ramki", "0"))
265 # if has_frames: frames_c = "framed"
266 # else: frames_c = ""
267 # return u"""<table class="%s">""" % frames_c, u"</table>"
269 # def handle_wiersz(self, element):
270 # return u"<tr>", u"</tr>"
272 # def handle_kol(self, element):
273 # return u"<td>", u"</td>"
275 # def handle_rdf__RDF(self, _):
276 # # ustal w opcjach rzeczy :D
279 # def handle_link(self, element):
280 # if 'material' in element.attrib:
281 # formats = re.split(r"[, ]+", element.attrib['format'])
284 # fmt_links.append(u'<a href="%s">%s</a>' % (self.options['urlmapper'].url_for_material(element.attrib['material'], f), f.upper()))
286 # return u"", u' (%s)' % u' '.join(fmt_links)
289 # class Exercise(EduModule):
290 # def __init__(self, *args, **kw):
291 # self.question_counter = 0
292 # super(Exercise, self).__init__(*args, **kw)
294 # def handle_rozw_kom(self, element):
295 # return u"""<div style="display:none" class="comment">""", u"""</div>"""
297 # def handle_cwiczenie(self, element):
298 # self.options = {'exercise': element.attrib['typ']}
299 # self.question_counter = 0
300 # self.piece_counter = 0
303 # <div class="exercise %(typ)s" data-type="%(typ)s">
304 # <form action="#" method="POST">
305 # """ % element.attrib
307 # <div class="buttons">
308 # <span class="message"></span>
309 # <input type="button" class="check" value="sprawdź"/>
310 # <input type="button" class="retry" style="display:none" value="spróbuj ponownie"/>
311 # <input type="button" class="solutions" value="pokaż rozwiązanie"/>
312 # <input type="button" class="reset" value="reset"/>
317 # # Add a single <pytanie> tag if it's not there
318 # if not element.xpath(".//pytanie"):
319 # qpre, qpost = self.handle_pytanie(element)
321 # post = qpost + post
324 # def handle_pytanie(self, element):
325 # """This will handle <cwiczenie> element, when there is no <pytanie>
328 # self.question_counter += 1
329 # self.piece_counter = 0
330 # solution = element.attrib.get('rozw', None)
331 # if solution: solution_s = ' data-solution="%s"' % solution
332 # else: solution_s = ''
334 # handles = element.attrib.get('uchwyty', None)
336 # add_class += ' handles handles-%s' % handles
337 # self.options = {'handles': handles}
339 # minimum = element.attrib.get('min', None)
340 # if minimum: minimum_s = ' data-minimum="%d"' % int(minimum)
341 # else: minimum_s = ''
343 # return '<div class="question%s" data-no="%d" %s>' %\
344 # (add_class, self.question_counter, solution_s + minimum_s), \
347 class EduModulePDFFormat(PDFFormat):
350 texml = edumod.generate(self.wldoc.edoc.getroot()).encode('utf-8')
352 open("/tmp/texml.xml", "w").write(texml)