Basic PDF support.
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 15 Feb 2013 15:37:22 +0000 (16:37 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 15 Feb 2013 15:37:22 +0000 (16:37 +0100)
librarian/book2anything.py
librarian/cover.py
librarian/parser.py
librarian/pdf.py
librarian/pdf/wl.cls
librarian/pyhtml.py
librarian/pypdf.py
librarian/styles/wolnelektury/cover.py
setup.py

index b8b8d27..b60cd0f 100755 (executable)
@@ -10,7 +10,6 @@ import optparse
 
 from librarian import DirDocProvider, ParseError
 from librarian.parser import WLDocument
-from librarian.cover import WLCover
 
 
 class Option(object):
@@ -88,7 +87,7 @@ class Book2Anything(object):
         for option in cls.parser_options:
             parser_args[option.name()] = option.value(options)
         # Prepare additional args for transform method.
-        transform_args = {}
+        transform_args = {"verbose": options.verbose}
         for option in cls.transform_options:
             transform_args[option.name()] = option.value(options)
         # Add flags to transform_args, if any.
@@ -98,6 +97,7 @@ class Book2Anything(object):
             transform_args['flags'] = transform_flags
         # Add cover support, if any.
         if cls.uses_cover:
+            from librarian.styles.wolnelektury.cover import WLCover
             if options.image_cache:
                 def cover_class(*args, **kwargs):
                     return WLCover(image_cache=options.image_cache, *args, **kwargs)
index b53de30..7343e68 100644 (file)
@@ -4,7 +4,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 import re
-import Image, ImageFont, ImageDraw, ImageFilter
+from PIL import Image, ImageFont, ImageDraw, ImageFilter
 from StringIO import StringIO
 from librarian import get_resource, IOFile
 
index f02b64c..fb2f986 100644 (file)
@@ -204,8 +204,8 @@ class WLDocument(object):
         return epub.transform(self, *args, **kwargs)
 
     def as_pdf(self, *args, **kwargs):
-        from librarian import pdf
-        return pdf.transform(self, *args, **kwargs)
+        from librarian import pypdf
+        return pypdf.EduModulePDFFormat(self).build(*args, **kwargs)
 
     def as_mobi(self, *args, **kwargs):
         from librarian import mobi
@@ -217,7 +217,7 @@ class WLDocument(object):
 
     def as_cover(self, cover_class=None, *args, **kwargs):
         if cover_class is None:
-            from librarian.cover import WLCover
+            from librarian.styles.wolnelektury.cover import WLCover
             cover_class = WLCover
         return cover_class(self.book_info, *args, **kwargs).output_file()
 
index d06a656..2cbc2c0 100644 (file)
@@ -265,8 +265,8 @@ class PDFFormat(Format):
         # Copy style
         shutil.copy(get_resource('pdf/wl.cls'), temp)
         shutil.copy(self.style, os.path.join(temp, 'style.sty'))
-        for sfile in ['wasysym.sty', 'uwasyvar.fd', 'uwasy.fd']:
-            shutil.copy(get_resource(os.path.join('res/wasysym', sfile)), temp)
+        #for sfile in ['wasysym.sty', 'uwasyvar.fd', 'uwasy.fd']:
+        #    shutil.copy(get_resource(os.path.join('res/wasysym', sfile)), temp)
 
         # Save attachments
         if self.cover:
index 9e5e6be..4023934 100755 (executable)
@@ -75,7 +75,7 @@
 \usepackage[overload]{textcase}
 \usepackage{scalefnt}
 % TODO: link color is a style thing
-\usepackage[colorlinks=true,linkcolor=black,setpagesize=false,urlcolor=black,xetex]{hyperref}
+\usepackage[colorlinks=true,linkcolor=black,setpagesize=false,urlcolor=blue,xetex]{hyperref}
 
 \ifenablewlfont
 \setmainfont [
@@ -418,3 +418,11 @@ Letters={Uppercase}
 \newcommand*\checkbox{\item[\Square]}
 \newcommand*\radio{\item[\Circle]}
 
+
+\renewcommand{\naglowekrozdzial}[1]{%
+\subsection*{\typosubsection{#1}}%
+}
+
+\renewcommand{\naglowekpodrozdzial}[1]{%
+\subsubsection*{\typosubsubsection{#1}}%
+}
index d138701..6d1e914 100644 (file)
@@ -9,6 +9,7 @@ from xmlutils import Xmill, tag, tagged, ifoption, tag_open_close
 from librarian import functions
 import re
 import random
+from copy import deepcopy
 
 IMAGE_THUMB_WIDTH = 300
 
@@ -444,12 +445,10 @@ class Luki(Exercise):
         return question.xpath(".//luka")
 
     def solution_html(self, piece):
+        piece = deepcopy(piece)
+        piece.tail = None
         sub = EduModule()
         return sub.generate(piece)
-        # print piece.text
-        # return piece.text + ''.join(
-        #     [etree.tostring(n, encoding=unicode)
-        #      for n in piece])
 
     def handle_pytanie(self, element):
         qpre, qpost = super(Luki, self).handle_pytanie(element)
index 4cc4d1d..68e0bd9 100644 (file)
@@ -10,12 +10,14 @@ with TeXML, then runs it by XeLaTeX.
 
 """
 from __future__ import with_statement
+from copy import deepcopy
 import os
 import os.path
 import shutil
 from StringIO import StringIO
 from tempfile import mkdtemp, NamedTemporaryFile
 import re
+import random
 from copy import deepcopy
 from subprocess import call, PIPE
 
@@ -23,7 +25,7 @@ from Texml.processor import process
 from lxml import etree
 from lxml.etree import XMLSyntaxError, XSLTApplyError
 
-from xmlutils import Xmill, tag, tagged, ifoption
+from xmlutils import Xmill, tag, tagged, ifoption, tag_open_close
 from librarian.dcparser import Person
 from librarian.parser import WLDocument
 from librarian import ParseError, DCNS, get_resource, IOFile, Format
@@ -49,15 +51,18 @@ def escape(really):
     return deco
 
 
-def cmd(name, pass_text=False):
+def cmd(name, parms=None):
     def wrap(self, element):
-        pre = u'<cmd name="%s">' % name
-
-        if pass_text:
-            pre += "<parm>%s</parm>" % element.text
-            return pre + '</cmd>'
-        else:
-            return pre, '</cmd>'
+        pre, post = tag_open_close('cmd', name=name)
+
+        if parms:
+            for parm in parms:
+                e = etree.Element("parm")
+                e.text = parm
+                pre += etree.tostring(e)
+        pre += "<parm>"
+        post = "</parm>" + post
+        return pre, post
     return wrap
 
 
@@ -74,7 +79,7 @@ class EduModule(Xmill):
 
         def swap_endlines(txt):
             if self.options['strofa']:
-                txt = txt.replace("/\n", '<ctrl ch="\"/>')
+                txt = txt.replace("/\n", '<ctrl ch="\\"/>')
             return txt
         self.register_text_filter(functions.substitute_entities)
         self.register_text_filter(mark_alien_characters)
@@ -189,7 +194,7 @@ class EduModule(Xmill):
     handle_wyroznienie = \
     handle_texcommand
 
-    _handle_strofa = cmd("strofa", True)
+    _handle_strofa = cmd("strofa")
 
     def handle_strofa(self, element):
         self.options = {'strofa': True}
@@ -250,6 +255,8 @@ class EduModule(Xmill):
         return
 
     def handle_lista(self, element, attrs={}):
+        if not element.findall("punkt"):
+            return None
         ltype = element.attrib.get('typ', 'punkt')
         if ltype == 'slowniczek':
             surl = element.attrib.get('href', None)
@@ -271,16 +278,19 @@ class EduModule(Xmill):
 
     def handle_cwiczenie(self, element):
         exercise_handlers = {
-            'wybor': Wybor}
-            # 'uporzadkuj': Uporzadkuj,
-            # 'luki': Luki,
-            # 'zastap': Zastap,
-            # 'przyporzadkuj': Przyporzadkuj,
-            # 'prawdafalsz': PrawdaFalsz
+            'wybor': Wybor,
+            'uporzadkuj': Uporzadkuj,
+            'luki': Luki,
+            'zastap': Zastap,
+            'przyporzadkuj': Przyporzadkuj,
+            'prawdafalsz': PrawdaFalsz
+        }
 
         typ = element.attrib['typ']
+        self.exercise_counter += 1
         if not typ in exercise_handlers:
             return '(no handler)'
+        self.options = {'exercise_counter': self.exercise_counter}
         handler = exercise_handlers[typ](self.options)
         return handler.generate(element)
 
@@ -322,7 +332,7 @@ class EduModule(Xmill):
                 #        else: frames_c = ""
                 #        return u"""<table class="%s">""" % frames_c, u"</table>"
         return u'''
-<cmd name="begin"><parm>tabular</parm><opt>%s</opt></cmd>
+<cmd name="begin"><parm>tabular</parm><parm>%s</parm></cmd>
     ''' % ('l' * max_col), \
     u'''<cmd name="end"><parm>tabular</parm></cmd>'''
 
@@ -333,10 +343,14 @@ class EduModule(Xmill):
     @escape(1)
     def handle_kol(self, element):
         if element.getnext() is not None:
-            return u"", u'<spec cat="align">'
+            return u"", u'<spec cat="align" />'
         return u"", u""
 
-    handle_link = cmd('em', True)
+    def handle_link(self, element):
+        if element.attrib.get('url'):
+            return cmd('href', parms=[element.attrib['url']])(self, element)
+        else:
+            return cmd('em')(self, element)
 
 
 class Exercise(EduModule):
@@ -344,14 +358,22 @@ class Exercise(EduModule):
         self.question_counter = 0
         super(Exercise, self).__init__(*args, **kw)
 
-    handle_rozw_kom = ifoption(teacher=True)(cmd('akap', True))
+    handle_rozw_kom = ifoption(teacher=True)(cmd('akap'))
 
     def handle_cwiczenie(self, element):
-        self.options = {'exercise': element.attrib['typ']}
+        self.options = {
+            'exercise': element.attrib['typ'],
+            'sub_gen': True,
+        }
         self.question_counter = 0
         self.piece_counter = 0
 
-        pre = u""
+        header = etree.Element("parm")
+        header_cmd = etree.Element("cmd", name="naglowekpodrozdzial")
+        header_cmd.append(header)
+        header.text = u"Zadanie %d." % self.options['exercise_counter']
+
+        pre = etree.tostring(header_cmd, encoding=unicode)
         post = u""
         # Add a single <pytanie> tag if it's not there
         if not element.xpath(".//pytanie"):
@@ -363,29 +385,37 @@ class Exercise(EduModule):
     def handle_pytanie(self, element):
         """This will handle <cwiczenie> element, when there is no <pytanie>
         """
-        opts = {}
         self.question_counter += 1
         self.piece_counter = 0
-        solution = element.attrib.get('rozw', None)
-        if solution:
-            opts['solution'] = solution
+        pre = post = u""
+        if self.options['teacher'] and element.attrib.get('rozw'):
+            post += u" [rozwiązanie: %s]" % element.attrib.get('rozw')
+        return pre, post
 
-        handles = element.attrib.get('uchwyty', None)
-        if handles:
-            opts['handles'] = handles
+    def handle_punkt(self, element):
+        pre, post = super(Exercise, self).handle_punkt(element)
+        if self.options['teacher'] and element.attrib.get('rozw'):
+            post += u" [rozwiązanie: %s]" % element.attrib.get('rozw')
+        return pre, post
 
-        minimum = element.attrib.get('min', None)
-        if minimum:
-            opts['minimum'] = minimum
+    def solution_header(self):
+        par = etree.Element("cmd", name="par")
+        parm = etree.Element("parm")
+        parm.text = u"Rozwiązanie:"
+        par.append(parm)
+        return etree.tostring(par)
 
-        if opts:
-            self.options = opts
-        return u"", u""
+    def explicit_solution(self):
+        if self.options['solution']:
+            par = etree.Element("cmd", name="par")
+            parm = etree.Element("parm")
+            parm.text = self.options['solution']
+            par.append(parm)
+            return self.solution_header() + etree.tostring(par)
 
 
-class Wybor(Exercise):
-    INSTRUCTION = None
 
+class Wybor(Exercise):
     def handle_cwiczenie(self, element):
         pre, post = super(Wybor, self).handle_cwiczenie(element)
         is_single_choice = True
@@ -415,6 +445,89 @@ class Wybor(Exercise):
             return super(Wybor, self).handle_punkt(element)
 
 
+class Uporzadkuj(Exercise):
+    def handle_pytanie(self, element):
+        order_items = element.xpath(".//punkt/@rozw")
+        return super(Uporzadkuj, self).handle_pytanie(element)
+
+
+class Przyporzadkuj(Exercise):
+    def handle_lista(self, lista):
+        header = etree.Element("parm")
+        header_cmd = etree.Element("cmd", name="par")
+        header_cmd.append(header)
+        if 'nazwa' in lista.attrib:
+            header.text = u"Kategorie:"
+        elif 'cel' in lista.attrib:
+            header.text = u"Elementy do przyporządkowania:"
+        else:
+            header.text = u"Lista:"
+        pre, post = super(Przyporzadkuj, self).handle_lista(lista)
+        pre = etree.tostring(header_cmd, encoding=unicode) + pre
+        return pre, post
+
+
+class Luki(Exercise):
+    def find_pieces(self, question):
+        return question.xpath(".//luka")
+
+    def solution(self, piece):
+        piece = deepcopy(piece)
+        piece.tail = None
+        sub = EduModule()
+        return sub.generate(piece)
+
+    def handle_pytanie(self, element):
+        qpre, qpost = super(Luki, self).handle_pytanie(element)
+
+        luki = self.find_pieces(element)
+        random.shuffle(luki)
+        self.words = u"<env name='itemize'>%s</env>" % (
+            "".join("<cmd name='item'/>%s" % self.solution(luka) for luka in luki)
+        )
+        return qpre, qpost
+
+    def handle_opis(self, element):
+        return '', self.words
+
+    def handle_luka(self, element):
+        luka = "_" * 10
+        if self.options['teacher']:
+            piece = deepcopy(element)
+            piece.tail = None
+            sub = EduModule()
+            text = sub.generate(piece)
+            luka += u" [rozwiązanie: %s]" % text
+        return luka
+
+
+class Zastap(Luki):
+    def find_pieces(self, question):
+        return question.xpath(".//zastap")
+
+    def solution(self, piece):
+        return piece.attrib['rozw']
+
+    def list_header(self):
+        return u"Elementy do wstawienia"
+
+    def handle_zastap(self, element):
+        piece = deepcopy(element)
+        piece.tail = None
+        sub = EduModule()
+        text = sub.generate(piece)
+        if self.options['teacher'] and element.attrib.get('rozw'):
+            text += u" [rozwiązanie: %s]" % element.attrib.get('rozw')
+        return text
+
+
+class PrawdaFalsz(Exercise):
+    def handle_punkt(self, element):
+        pre, post = super(PrawdaFalsz, self).handle_punkt(element)
+        if 'rozw' in element.attrib:
+            post += u" [Prawda/Fałsz]"
+        return pre, post
+
 
 
 def fix_lists(tree):
@@ -435,7 +548,7 @@ def fix_lists(tree):
 
 class EduModulePDFFormat(PDFFormat):
     def get_texml(self):
-        edumod = EduModule()
+        edumod = EduModule({"teacher": True})
         texml = edumod.generate(fix_lists(self.wldoc.edoc.getroot())).encode('utf-8')
 
         open("/tmp/texml.xml", "w").write(texml)
index b417216..8181890 100644 (file)
@@ -3,7 +3,7 @@
 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
-import Image, ImageFont, ImageDraw
+from PIL import Image, ImageFont, ImageDraw
 from StringIO import StringIO
 from librarian import get_resource, URLOpener
 from librarian.cover import Cover, TextBox
index a5f105d..0a3682f 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,12 @@ setup(
     maintainer='Radek Czajka',
     maintainer_email='radoslaw.czajka@nowoczesnapolska.org.pl',
     url='http://github.com/fnp/librarian',
-    packages=['librarian'],
+    packages=[
+        'librarian',
+        'librarian.styles',
+        'librarian.styles.wolnelektury',
+        'librarian.styles.wolnelektury.partners',
+    ],
     package_data={'librarian': ['xslt/*.xslt', 'epub/*', 'mobi/*', 'pdf/*', 'fb2/*', 'fonts/*'] +
                                 whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'font-optimizer') +
                                 whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'res')},