pdf converter, still missing motifs
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 22 Oct 2010 13:59:50 +0000 (15:59 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 22 Oct 2010 13:59:50 +0000 (15:59 +0200)
librarian/functions.py
librarian/pdf.py
librarian/pdf/wl-logo.png [new file with mode: 0644]
librarian/pdf/wl.sty [new file with mode: 0644]
librarian/xslt/wl2tex.xslt

index 6d52b84..8427ba8 100644 (file)
@@ -41,6 +41,26 @@ def reg_strip():
     _register_function(strip)
 
 
     _register_function(strip)
 
 
+def reg_starts_white():
+    def starts_white(context, text):
+        if isinstance(text, list):
+            text = ''.join(text)
+        if not text:
+            return False
+        return text[0].isspace()
+    _register_function(starts_white)
+
+
+def reg_ends_white():
+    def ends_white(context, text):
+        if isinstance(text, list):
+            text = ''.join(text)
+        if not text:
+            return False
+        return text[-1].isspace()
+    _register_function(ends_white)
+
+
 def reg_wrap_words():
     def wrap_words(context, text, wrapping):
         """XPath extension function automatically wrapping words in passed text"""
 def reg_wrap_words():
     def wrap_words(context, text, wrapping):
         """XPath extension function automatically wrapping words in passed text"""
@@ -63,3 +83,13 @@ def reg_wrap_words():
         return '\n'.join(' '.join(line) for line in lines)
     _register_function(wrap_words)
 
         return '\n'.join(' '.join(line) for line in lines)
     _register_function(wrap_words)
 
+
+def reg_person_name():
+    def person_name(context, text):
+        """ Converts "Name, Forename" to "Forename Name" """
+        if isinstance(text, list):
+            text = ''.join(text)
+        return ' '.join([t.strip() for t in text.split(',', 1)[::-1]])
+    _register_function(person_name)
+
+
index 169d661..659390d 100644 (file)
@@ -3,6 +3,7 @@
 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+from __future__ import with_statement
 import os
 import os.path
 import shutil
 import os
 import os.path
 import shutil
@@ -10,6 +11,9 @@ from StringIO import StringIO
 from tempfile import mkdtemp
 import re
 
 from tempfile import mkdtemp
 import re
 
+import sys
+sys.path.append('..') # for running from working copy
+
 from Texml.processor import process
 from lxml import etree
 from lxml.etree import XMLSyntaxError, XSLTApplyError
 from Texml.processor import process
 from lxml import etree
 from lxml.etree import XMLSyntaxError, XSLTApplyError
@@ -18,7 +22,13 @@ from librarian.parser import WLDocument
 from librarian import ParseError
 from librarian import functions
 
 from librarian import ParseError
 from librarian import functions
 
+
+
 functions.reg_substitute_entities()
 functions.reg_substitute_entities()
+functions.reg_person_name()
+functions.reg_strip()
+functions.reg_starts_white()
+functions.reg_ends_white()
 
 STYLESHEETS = {
     'wl2tex': 'xslt/wl2tex.xslt',
 
 STYLESHEETS = {
     'wl2tex': 'xslt/wl2tex.xslt',
@@ -26,7 +36,14 @@ STYLESHEETS = {
 
 
 def insert_tags(doc, split_re, tagname):
 
 
 def insert_tags(doc, split_re, tagname):
-    print tagname
+    """ inserts <tagname> for every occurence of `split_re' in text nodes in the `doc' tree 
+
+    >>> t = etree.fromstring('<a><b>A-B-C</b>X-Y-Z</a>');
+    >>> insert_tags(t, re.compile('-'), 'd');
+    >>> print etree.tostring(t)
+    <a><b>A<d/>B<d/>C</b>X<d/>Y<d/>Z</a>
+    """
+
     for elem in doc.iter():
         if elem.text:
             chunks = split_re.split(elem.text)
     for elem in doc.iter():
         if elem.text:
             chunks = split_re.split(elem.text)
@@ -42,7 +59,7 @@ def insert_tags(doc, split_re, tagname):
             elem.tail = chunks.pop(0)
             while chunks:
                 ins = etree.Element(tagname)
             elem.tail = chunks.pop(0)
             while chunks:
                 ins = etree.Element(tagname)
-                ins.tail = chunks.pop(0)
+                ins.tail = chunks.pop()
                 parent.insert(ins_index, ins)
 
 
                 parent.insert(ins_index, ins)
 
 
@@ -58,8 +75,11 @@ def fix_hanging(doc):
                 "nbsp")
 
 
                 "nbsp")
 
 
+def get_resource(path):
+    return os.path.join(os.path.dirname(__file__), path)
+
 def get_stylesheet(name):
 def get_stylesheet(name):
-    return os.path.join(os.path.dirname(__file__), STYLESHEETS[name])
+    return get_resource(STYLESHEETS[name])
 
 def transform(provider, slug, output_file=None, output_dir=None):
     """ produces a pdf file
 
 def transform(provider, slug, output_file=None, output_dir=None):
     """ produces a pdf file
@@ -78,8 +98,6 @@ def transform(provider, slug, output_file=None, output_dir=None):
 
         substitute_hyphens(document.edoc)
         fix_hanging(document.edoc)
 
         substitute_hyphens(document.edoc)
         fix_hanging(document.edoc)
-        
-        print etree.tostring(document.edoc)
 
         # if output to dir, create the file
         if output_dir is not None:
 
         # if output to dir, create the file
         if output_dir is not None:
@@ -96,6 +114,8 @@ def transform(provider, slug, output_file=None, output_dir=None):
         fout.close()
         del texml
 
         fout.close()
         del texml
 
+        shutil.copy(get_resource('pdf/wl.sty'), temp)
+        shutil.copy(get_resource('pdf/wl-logo.png'), temp)
         print "pdflatex -output-directory %s %s" % (temp, os.path.join(temp, 'doc.tex'))
         if os.system("pdflatex -output-directory %s %s" % (temp, os.path.join(temp, 'doc.tex'))):
             raise ParseError("Error parsing .tex file")
         print "pdflatex -output-directory %s %s" % (temp, os.path.join(temp, 'doc.tex'))
         if os.system("pdflatex -output-directory %s %s" % (temp, os.path.join(temp, 'doc.tex'))):
             raise ParseError("Error parsing .tex file")
diff --git a/librarian/pdf/wl-logo.png b/librarian/pdf/wl-logo.png
new file mode 100644 (file)
index 0000000..03b46b5
Binary files /dev/null and b/librarian/pdf/wl-logo.png differ
diff --git a/librarian/pdf/wl.sty b/librarian/pdf/wl.sty
new file mode 100644 (file)
index 0000000..f904646
--- /dev/null
@@ -0,0 +1,72 @@
+\usepackage[MeX]{polski}
+\usepackage[utf8]{inputenc}
+\pagestyle{plain}
+%\usepackage{antpolt}
+\usepackage{graphicx}
+\usepackage{fancyhdr}
+
+\makeatletter
+
+% bottom figure below footnotes
+\usepackage{fnpos}
+\makeFNabove
+
+\usepackage{color}
+\definecolor{theme-gray}{gray}{.3}
+
+\setlength{\marginparsep}{2em}
+\setlength{\marginparwidth}{8.5em}
+\setlength{\oddsidemargin}{0pt}
+\setlength{\voffset}{0pt}
+\setlength{\topmargin}{0pt}
+\setlength{\headheight}{0pt}
+\setlength{\headsep}{0pt}
+\setlength{\textheight}{24cm}
+
+\pagestyle{fancy}
+\renewcommand{\headrulewidth}{0pt}
+\renewcommand{\footrulewidth}{0.4pt}
+\lfoot{\footnotesize \@author, \emph{\@title}}
+\cfoot{}
+\rfoot{\footnotesize \thepage}
+
+\clubpenalty=100000
+\widowpenalty=100000
+%\interlinepenalty=8000
+
+
+\renewcommand{\maketitle}{
+    \thispagestyle{empty}
+    \footnotesize
+    \color{theme-gray}
+
+    \noindent \begin{minipage}[t]{.35\textwidth}\vspace{0pt}
+        \includegraphics[width=\textwidth]{pdf/wl-logo.png}
+    \end{minipage}
+    \begin{minipage}[t]{.65\textwidth}\vspace{0pt}
+        Wejdź na stronę http://wolnelektury.pl/ i~zobacz, jak wiele możliwości daje interaktywna wersja szkolnej biblioteki
+        internetowej Wolne Lektury.
+        \vspace{.5em}
+
+        Ten utwór nie jest chroniony prawem autorskim i~znajduje się w~domenie publicznej, co oznacza, że możesz go swobodnie wykorzystywać, publikować i~rozpowszechniać.
+
+        \vspace{.6em}
+
+    \end{minipage}
+    \noindent \rule{\linewidth}{0.4pt}
+
+    \vspace{.6em}
+    \color{black}
+
+    \begin{figure}[b!]
+        \footnotesize
+        \color{theme-gray}
+        \noindent \rule{\linewidth}{0.4pt}
+
+        Szkolna biblioteka internetowa Wolne Lektury tworzona jest dzięki pracy Wolontariuszy, ale to osoby
+        zatrudnione w~FNP będą musiały wymyślić, co powinno zostać napisane w~tym miejscu. Można zupełnie usunąć
+        tę sekcję, ale autor konwertera chciał się popisać, że umie coś takiego zrobić.
+        \color{black}
+    \end{figure}
+}
+
index 74292a7..5de6d85 100644 (file)
 
 <xsl:output encoding="utf-8" indent="yes" version="2.0" />
 
 
 <xsl:output encoding="utf-8" indent="yes" version="2.0" />
 
-
 <xsl:template match="utwor">
     <TeXML xmlns="http://getfo.sourceforge.net/texml/ns1">
         <TeXML escape="0">
         \documentclass[a4paper, oneside, 11pt]{book}
 <xsl:template match="utwor">
     <TeXML xmlns="http://getfo.sourceforge.net/texml/ns1">
         <TeXML escape="0">
         \documentclass[a4paper, oneside, 11pt]{book}
-        \usepackage[MeX]{polski}
-        \usepackage[utf8]{inputenc}
-        \pagestyle{plain}
-        \usepackage{antpolt}
-        \usepackage[bottom]{footmisc}
-
-        \usepackage{color}
-        \definecolor{theme-gray}{gray}{.3}
-
-
-        \setlength{\marginparsep}{2em}
-        \setlength{\marginparwidth}{8.5em}
-        \setlength{\oddsidemargin}{0pt}
-        \clubpenalty=10000
-        \widowpenalty=10000 
+        \usepackage{wl}
         </TeXML>
 
         </TeXML>
 
-        <xsl:apply-templates select="rdf:RDF" mode='title' />
-        <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode='title' />
+        <xsl:apply-templates select="rdf:RDF" mode="titlepage" />
+        <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode='titlepage' />
 
         <env name="document">
             <cmd name="maketitle" />
 
         <env name="document">
             <cmd name="maketitle" />
+
+            <xsl:choose>
+                <xsl:when test="(powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny)/nazwa_utworu">
+                    <xsl:apply-templates select="(powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny)/autor_utworu" mode="title" />
+                    <!-- title in master -->
+                </xsl:when>
+                <xsl:otherwise>
+                    <!-- look for author title in dc -->
+                    <xsl:apply-templates select="rdf:RDF" mode="firstdctitle" />
+                    <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode='firstdctitle' />
+                </xsl:otherwise>
+            </xsl:choose>
             <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" />
             <xsl:apply-templates select="utwor" mode="part" />
         </env>
             <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" />
             <xsl:apply-templates select="utwor" mode="part" />
         </env>
 </xsl:template>
 
 <xsl:template match="utwor" mode="part">
 </xsl:template>
 
 <xsl:template match="utwor" mode="part">
-    <xsl:if test="utwor">
-        <xsl:apply-templates select="rdf:RDF" mode='subtitle' />
-        <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode='subtitle' />
-    </xsl:if>
+    <!-- title for empty dc -->
+    <xsl:choose>
+        <xsl:when test="(powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny)/nazwa_utworu">
+            <!-- title in master -->
+        </xsl:when>
+        <xsl:otherwise>
+            <!-- look for title in dc -->
+            <xsl:apply-templates select="rdf:RDF" mode="dctitle" />
+            <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode='dctitle' />
+        </xsl:otherwise>
+    </xsl:choose>
+
     <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" />
     <xsl:apply-templates select="utwor" mode="part" />
 </xsl:template>
 
     <xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" />
     <xsl:apply-templates select="utwor" mode="part" />
 </xsl:template>
 
+<!-- =================== -->
+<!-- = MAIN TITLE PAGE = -->
+<!-- = (from DC)       = -->
+<!-- =================== -->
 
 
-<!-- ============================================================================== -->
-<!-- = MASTER TAG                                                                 = -->
-<!-- = (can contain block tags, paragraph tags, standalone tags and special tags) = -->
-<!-- ============================================================================== -->
+<xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode="titlepage">
+    <xsl:apply-templates select="rdf:RDF" mode="titlepage" />
+</xsl:template>
 
 
-<xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode="title">
-    <xsl:apply-templates select="rdf:RDF" mode='title' />
+<xsl:template match="rdf:RDF" mode="titlepage">
+    <cmd name='title'><parm>
+        <xsl:value-of select=".//dc:title/text()" />
+    </parm></cmd>
+    <cmd name='author'><parm>
+        <xsl:value-of select="wl:person_name(.//dc:creator/text())" />
+    </parm></cmd>
 </xsl:template>
 
 </xsl:template>
 
-<xsl:template match="rdf:RDF" mode="title">
 
 
-<!-- TODO!
-            <cmd name='title'><parm>
-                <xsl:apply-templates select="dzielo_nadrzedne|podtytul" mode="header" />
-            </parm></cmd>
-            czytanie z tagów nazwa_utworu, autor_utworu
--->
+<!-- ============== -->
+<!-- = BOOK TITLE = -->
+<!-- = (from DC)  = -->
+<!-- ============== -->
 
 
-                <cmd name='title'><parm>
-                    <xsl:apply-templates select=".//dc:title" mode="header" />
-                </parm></cmd>
-                <cmd name='author'><parm>
-                    <xsl:apply-templates select=".//dc:author" mode="header" />
-                </parm></cmd>
+<xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode="dctitle">
+    <xsl:apply-templates select="rdf:RDF" mode="dctitle" />
 </xsl:template>
 
 </xsl:template>
 
-<xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode="subtitle">
-    <xsl:apply-templates select="rdf:RDF" mode='subtitle' />
+<xsl:template match="rdf:RDF" mode="dctitle">
+    <cmd name="section*"><parm>
+        <xsl:value-of select=".//dc:title/text()" />
+    </parm></cmd>
 </xsl:template>
 
 </xsl:template>
 
-<xsl:template match="rdf:RDF" mode="subtitle">
-                <cmd name='part*'><parm>
-                    <xsl:apply-templates select=".//dc:title" mode="header" />
-                </parm></cmd>
+
+<xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode="firstdctitle">
+    <xsl:apply-templates select="rdf:RDF" mode="firstdctitle" />
+</xsl:template>
+
+<xsl:template match="rdf:RDF" mode="firstdctitle">
+    <cmd name="subsection*"><parm>
+        <xsl:value-of select="wl:person_name(.//dc:creator/text())" />
+    </parm></cmd>
+    <cmd name="section*"><parm>
+        <xsl:value-of select=".//dc:title/text()" />
+    </parm></cmd>
 </xsl:template>
 
 
 </xsl:template>
 
 
+<!-- ============================================================================== -->
+<!-- = MASTER TAG                                                                 = -->
+<!-- = (can contain block tags, paragraph tags, standalone tags and special tags) = -->
+<!-- ============================================================================== -->
 
 <xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny">
     <xsl:apply-templates />
 
 <xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny">
     <xsl:apply-templates />
 </xsl:template>
 
 <xsl:template match="lista_osob">
 </xsl:template>
 
 <xsl:template match="lista_osob">
-    <cmd name="par">
+    <cmd name="par"><parm>
         <cmd name="textbf">
             <parm><xsl:value-of select="naglowek_listy" /></parm>
         </cmd>
         <env name="itemize">
             <xsl:apply-templates select="lista_osoba" />
         </env>
         <cmd name="textbf">
             <parm><xsl:value-of select="naglowek_listy" /></parm>
         </cmd>
         <env name="itemize">
             <xsl:apply-templates select="lista_osoba" />
         </env>
-    </cmd>
+    </parm></cmd>
 </xsl:template>
 
 <xsl:template match="dedykacja">
 </xsl:template>
 
 <xsl:template match="dedykacja">
-    <cmd name="raggedleft"><parm>
+    <cmd name="hspace"><parm>0.4<cmd name='linewidth' /></parm></cmd>
+    <env name="minipage"><parm>0.6<cmd name='linewidth' /></parm>
         <env name="em">
             <xsl:apply-templates />
         </env>
         <env name="em">
             <xsl:apply-templates />
         </env>
-    </parm></cmd>
+    </env>
 </xsl:template>
 
 <xsl:template match="kwestia">
 </xsl:template>
 
 <xsl:template match="kwestia">
 <!-- = PARAGRAPH TAGS                         = -->
 <!-- = (can contain inline and special tags)  = -->
 <!-- ========================================== -->
 <!-- = PARAGRAPH TAGS                         = -->
 <!-- = (can contain inline and special tags)  = -->
 <!-- ========================================== -->
-<!-- Title page -->
-<xsl:template match="autor_utworu" mode="header">
-    <xsl:apply-templates mode="inline" />
-</xsl:template>
-
-<xsl:template match="nazwa_utworu" mode="header">
-    <xsl:apply-templates mode="inline" />
-</xsl:template>
-
-<xsl:template match="dzielo_nadrzedne" mode="header">
-    <xsl:apply-templates mode="inline" />
-</xsl:template>
 
 
-<xsl:template match="podtytul" mode="header">
-    <xsl:apply-templates mode="inline" />
+<!-- only in root -->
+<xsl:template match="autor_utworu" mode="title">X
+    <cmd name="subsection*"><parm>
+        <xsl:apply-templates mode="inline" />
+    </parm></cmd>
 </xsl:template>
 
 </xsl:template>
 
-
 <xsl:template match="nazwa_utworu">
 <xsl:template match="nazwa_utworu">
-    <cmd name="pagebreak" />
     <cmd name="section*"><parm>
         <xsl:apply-templates mode="inline" />
     </parm></cmd>
     <cmd name="section*"><parm>
         <xsl:apply-templates mode="inline" />
     </parm></cmd>
 </xsl:template>
 
 <xsl:template match="motto_podpis">
 </xsl:template>
 
 <xsl:template match="motto_podpis">
-    <cmd name="raggedleft"><parm>
-        <xsl:apply-templates mode="inline" />
-    </parm></cmd>
+    <cmd name="hspace"><parm>0.4<cmd name='linewidth' /></parm></cmd>
+    <env name="minipage"><parm>0.6<cmd name='linewidth' /></parm>
+        <env name="em">
+            <xsl:apply-templates mode="inline" />
+        </env>
+    </env>
 </xsl:template>
 
 </xsl:template>
 
-
 <!-- ================================================ -->
 <!-- = INLINE TAGS                                  = -->
 <!-- = (contain other inline tags and special tags) = -->
 <!-- ================================================ -->
 <!-- ================================================ -->
 <!-- = INLINE TAGS                                  = -->
 <!-- = (contain other inline tags and special tags) = -->
 <!-- ================================================ -->
+
 <!-- Annotations -->
 <xsl:template match="pa|pe|pr|pt" mode="inline">
     <cmd name="footnote"><parm><xsl:apply-templates mode="inline" /></parm></cmd>
 <!-- Annotations -->
 <xsl:template match="pa|pe|pr|pt" mode="inline">
     <cmd name="footnote"><parm><xsl:apply-templates mode="inline" /></parm></cmd>
 </xsl:template>
 
 <xsl:template match="wyroznienie" mode="inline">
 </xsl:template>
 
 <xsl:template match="wyroznienie" mode="inline">
-    <!-- TODO: letterspacing 1pt -->
     <cmd name="emph"><parm><xsl:apply-templates mode="inline" /></parm></cmd>
 </xsl:template>
 
     <cmd name="emph"><parm><xsl:apply-templates mode="inline" /></parm></cmd>
 </xsl:template>
 
 
 <xsl:template match="begin" mode="inline">
     <xsl:variable name="mnum" select="concat('m', substring(@id, 2))" />
 
 <xsl:template match="begin" mode="inline">
     <xsl:variable name="mnum" select="concat('m', substring(@id, 2))" />
-    <cmd name="mbox" />
+    <!--cmd name="mbox" />
     <cmd name="marginpar">
         <parm><cmd name="raggedright"><parm>
             <cmd name="hspace"><parm>0pt</parm></cmd>
     <cmd name="marginpar">
         <parm><cmd name="raggedright"><parm>
             <cmd name="hspace"><parm>0pt</parm></cmd>
                 </parm></cmd>
             </parm></cmd>
         </parm></cmd></parm>
                 </parm></cmd>
             </parm></cmd>
         </parm></cmd></parm>
-    </cmd>
+    </cmd-->
 </xsl:template>
 
 <xsl:template match="begin|end">
 </xsl:template>
 
 <xsl:template match="begin|end">
 <!-- ======== -->
 <xsl:template match="text()" />
 <xsl:template match="text()" mode="inline">
 <!-- ======== -->
 <xsl:template match="text()" />
 <xsl:template match="text()" mode="inline">
-    <xsl:value-of select="wl:substitute_entities(.)" />
+    <xsl:if test="preceding-sibling::node() and wl:starts_white(.)">
+      <xsl:text> </xsl:text>
+    </xsl:if>
+
+    <xsl:value-of select="wl:substitute_entities(wl:strip(.))" />
+
+    <xsl:if test="following-sibling::node() and wl:ends_white(.)">
+      <xsl:text> </xsl:text>
+    </xsl:if>
 </xsl:template>
 
 
 </xsl:template>