Browse Source

Mise en place du moteur de templates.

songbook fonctionne a nouveau !
pull/15/head
Luthaf 11 years ago
parent
commit
000eeccf9c
  1. 2
      setup.py
  2. 209
      songbook_core/build.py
  3. 123
      songbook_core/data/templates/default.tex
  4. 152
      songbook_core/data/templates/default.tmpl
  5. 58
      songbook_core/data/templates/layout.tex
  6. 8
      songbook_core/songs.py
  7. 59
      songbook_core/templates.py

2
setup.py

@ -19,7 +19,7 @@ setup(name='songbook-core',
requires=[ requires=[
"argparse", "codecs", "distutils", "fnmatch", "glob", "json", "argparse", "codecs", "distutils", "fnmatch", "glob", "json",
"locale", "logging", "os", "plasTeX", "re", "subprocess", "sys", "locale", "logging", "os", "plasTeX", "re", "subprocess", "sys",
"textwrap", "unidecode" "textwrap", "unidecode", "jinja2"
], ],
packages=['songbook_core'], packages=['songbook_core'],
package_data={'songbook_core': ['data/latex/*', package_data={'songbook_core': ['data/latex/*',

209
songbook_core/build.py

@ -16,12 +16,18 @@ from songbook_core import errors
from songbook_core.files import recursive_find from songbook_core.files import recursive_find
from songbook_core.index import process_sxd from songbook_core.index import process_sxd
from songbook_core.songs import Song, SongsList from songbook_core.songs import Song, SongsList
from songbook_core.templates import render_tex
EOL = "\n" EOL = "\n"
DEFAULT_AUTHWORDS = {
"after": ["by"],
"ignore": ["unknown"],
"sep": ["and"],
}
def parse_template(template): def parse_template(template):
"""Return the list of parameters defined in the template.""" """Return the list of default parameters defined in the template."""
embedded_json_pattern = re.compile(r"^%%:") embedded_json_pattern = re.compile(r"^%%:")
with open(template) as template_file: with open(template) as template_file:
code = [ code = [
@ -34,93 +40,15 @@ def parse_template(template):
data = json.loads(''.join(code)) data = json.loads(''.join(code))
parameters = dict() parameters = dict()
for param in data: for param in data:
parameters[param["name"]] = param
return parameters
# pylint: disable=too-many-return-statements
def to_value(parameter, data):
"""Convert 'data' to a LaTeX string.
Conversion is done according to the template parameter it corresponds to.
"""
if "type" not in parameter:
return data
elif parameter["type"] == "stringlist":
if "join" in parameter:
join_text = parameter["join"]
else:
join_text = ''
return join_text.join(data)
elif parameter["type"] == "color":
return data[1:]
elif parameter["type"] == "font":
return data + 'pt'
elif parameter["type"] == "enum":
return data
elif parameter["type"] == "file":
return data
elif parameter["type"] == "flag":
if "join" in parameter:
join_text = parameter["join"]
else:
join_text = ''
return join_text.join(data)
def format_declaration(name, parameter):
"""Return LaTeX code to declare variale 'name'.
'parameter' is the corresponding line of the template (which can provide
default value).
"""
value = ""
if "default" in parameter:
value = parameter["default"]
return (
r'\def\set@{name}#1{{\def\get{name}{{#1}}}}'.format(name=name)
+ EOL
+ format_definition(name, to_value(parameter, value))
)
def format_definition(name, value):
"""Write LaTeX code to set a value to a variable"""
return r'\set@{name}{{{value}}}'.format(name=name, value=value) + EOL
def clean(basename):
"""Clean (some) temporary files used during compilation.
Depending of the LaTeX modules used in the template, there may be others
that are note deleted by this function."""
generated_extensions = [
"_auth.sbx",
"_auth.sxd",
".aux",
".log",
".out",
".sxc",
".tex",
"_title.sbx",
"_title.sxd",
]
for ext in generated_extensions:
try: try:
os.unlink(basename + ext) parameters[param["name"]] = param["default"]
except Exception as exception: except KeyError:
raise errors.CleaningError(basename + ext, exception) parameters[param["name"]] = None
return parameters
DEFAULT_AUTHWORDS = {
"after": ["by"],
"ignore": ["unknown"],
"sep": ["and"],
}
# pylint: disable=too-few-public-methods
class Songbook(object): class Songbook(object):
"""Reprensent a songbook (.sb) file. """Represent a songbook (.sb) file.
- Low level: provide a Python representation of the values stored in the - Low level: provide a Python representation of the values stored in the
'.sb' file. '.sb' file.
@ -132,11 +60,7 @@ class Songbook(object):
self.basename = basename self.basename = basename
# Default values: will be updated while parsing raw_songbook # Default values: will be updated while parsing raw_songbook
self.config = { self.config = {
'template': os.path.join( 'template': "default.tex",
__SHAREDIR__,
"templates",
"default.tmpl",
),
'titleprefixwords': [], 'titleprefixwords': [],
'authwords': {}, 'authwords': {},
'lang': 'french', 'lang': 'french',
@ -168,7 +92,6 @@ class Songbook(object):
] + [',']) ] + [','])
] ]
def _parse(self, raw_songbook): def _parse(self, raw_songbook):
"""Parse raw_songbook. """Parse raw_songbook.
@ -176,7 +99,7 @@ class Songbook(object):
are stored verbatim in self.config. are stored verbatim in self.config.
""" """
self.config.update(raw_songbook) self.config.update(raw_songbook)
self.config['datadir'] = os.path.abspath(self.config['datadir'])
### Some post-processing ### Some post-processing
# Compute song list # Compute song list
if self.config['songs'] is None: if self.config['songs'] is None:
@ -200,88 +123,46 @@ class Songbook(object):
self.config['authwords'][key] = value self.config['authwords'][key] = value
def write_tex(self, output): def write_tex(self, output):
r"""Build the '.tex' file corresponding to self. """Build the '.tex' file corresponding to self.
Arguments: Arguments:
- output: a file object, in which the file will be written. - output: a file object, in which the file will be written.
TODO: Update this.
Quelques notes concernant le rendu
- self.config['titleprefixwords']
> return EOL.join([
> r"\titleprefixwords{%s}" % prefix
> for prefix
> in self.config['titleprefixwords']
> ])
- self.config['authwords']:
> # Processing authwords values
> tex = []
> for key in ["after", "sep", "ignore"]:
> for word in self.config['authwords'][key]:
> if key == "after":
> tex.append(r"\auth%sword{%s}" % ("by", word))
> else:
> tex.append(r"\auth%sword{%s}" % (key, word))
> return EOL.join(tex)
- Liste des langues utilisées (pour chargement par babel):
self.songlist.languages(). Donc la commande pour Babel peut
ressembler à :
> r"\PassOptionsToPackage{%s}{babel}" % ",".join(
> self.songslist.languages()
> )
""" """
parameters = parse_template(os.path.join( context = parse_template(os.path.join(
self.config['datadir'], self.config['datadir'],
'templates', 'templates',
self.config['template'] self.config['template']
)) ))
output.write((
'%% This file has been automatically '
'generated, do not edit!\n'
))
output.write(r'\makeatletter' + EOL)
# output automatic parameters
output.write(format_declaration("name", {"default": self.basename}))
output.write(format_declaration("songslist", {"type": "stringlist"}))
# output template parameter command
for name, parameter in parameters.iteritems():
output.write(format_declaration(name, parameter))
# output template parameter values
for name, value in self.config.iteritems():
if name in parameters:
output.write(format_definition(
name,
to_value(parameters[name], value),
))
if len(self.config['songs']) > 0: context.update(self.config)
output.write(format_definition('songslist', self.songslist.latex())) context['titleprefixkeys'] = ["after", "sep", "ignore"]
output.write(r'\makeatother' + EOL) context['songlist'] = self.songslist
context['filename'] = output.name[:-4]
render_tex(output, context, self.config['datadir'])
# output template
comment_pattern = re.compile(r"^\s*%")
with codecs.open(
os.path.join(
self.config['datadir'],
'templates',
self.config['template']
),
'r',
'utf-8',
) as template_file:
content = [
line
for line
in template_file
if not comment_pattern.match(line)
]
output.write(u''.join(content)) def clean(basename):
"""Clean (some) temporary files used during compilation.
Depending of the LaTeX modules used in the template, there may be others
that are not deleted by this function."""
generated_extensions = [
"_auth.sbx",
"_auth.sxd",
".aux",
".log",
".out",
".sxc",
".tex",
"_title.sbx",
"_title.sxd",
]
for ext in generated_extensions:
try:
os.unlink(basename + ext)
except Exception as exception:
raise errors.CleaningError(basename + ext, exception)
def buildsongbook( def buildsongbook(
@ -302,6 +183,12 @@ def buildsongbook(
songbook = Songbook(raw_songbook, basename) songbook = Songbook(raw_songbook, basename)
with codecs.open("{}.tex".format(basename), 'w', 'utf-8') as output: with codecs.open("{}.tex".format(basename), 'w', 'utf-8') as output:
songbook.write_tex(output) songbook.write_tex(output)
##############################################################################
##############################################################################
# import sys
# sys.exit(0)
##############################################################################
##############################################################################
if not 'TEXINPUTS' in os.environ.keys(): if not 'TEXINPUTS' in os.environ.keys():
os.environ['TEXINPUTS'] = '' os.environ['TEXINPUTS'] = ''

123
songbook_core/data/templates/default.tex

@ -0,0 +1,123 @@
(% comment %)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Template parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%:[
%%: {"name":"title", "description":"Title", "default":"Recueil de chansons pour guitare", "mandatory":true},
%%: {"name":"author", "description":"Author", "default":"The Songbook Team", "mandatory":true},
%%: {"name":"booktype", "description":"Type", "type":"enum", "values":["chorded","lyric"], "default":"chorded", "mandatory":true},
%%: {"name":"lang", "description":"Language", "default":"english"},
%%: {"name":"instruments", "description":"Instruments", "type":"flag", "values":["guitar","ukulele"], "join":",", "mandatory":true, "default":["guitar"]},
%%: {"name":"bookoptions", "description":"Options", "type":"flag", "values":["diagram","importantdiagramonly","lilypond","pictures","tabs","repeatchords","onesongperpage"], "join":",", "mandatory":true, "default":["diagram","pictures"]},
%%: {"name":"version", "description":"Version", "default":"unknown"},
%%: {"name":"subtitle", "description":"Subtitle"},
%%: {"name":"web", "description":"Web", "default":"http://www.patacrep.com"},
%%: {"name":"mail", "description":"Email", "default":"crep@team-on-fire.com"},
%%: {"name":"picture", "description":"Picture", "type":"file", "default":"treble_a"},
%%: {"name":"picturecopyright", "description":"Copyright", "default":"Dbolton \\url{http://commons.wikimedia.org/wiki/User:Dbolton}"},
%%: {"name":"footer", "description":"Footer", "default":"\\begin{flushright}Generated using Songbook (\\url{http://www.patacrep.com})\\end{flushright}"},
%%: {"name":"mainfontsize", "description":"Font Size", "type":"font", "default":"10"},
%%: {"name":"songnumberbgcolor", "description":"Number Shade", "type":"color", "default":"D1E4AE"},
%%: {"name":"notebgcolor", "description":"Note Shade", "type":"color", "default":"D1E4AE"},
%%: {"name":"indexbgcolor", "description":"Index Shade", "type":"color", "default":"D1E4AE"},
%%: {"name":"titleprefixwords", "description":"Ignore some words in the beginning of song titles"},
%%: {"name":"authwords", "descriptipn":"Set of options to process author string (LaTeX commands authsepword, authignoreword, authbyword)"},
%%: {"name":"languages", "description":"List of languages used by songs", "default":""}
%%:]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(% endcomment %)
(* extends "layout.tex" *)
(* block extrapackages *)
\usepackage[
a4paper % paper size
,includeheadfoot % include header and footer into text size
,hmarginratio=1:1 % ratio between inner and outer margin (default)
,outer=1.8cm % outer margin (right)
,vmarginratio=1:1 % ratio between top and bottom margin
,bmargin=1.3cm % bottom margin
]{geometry}
\usepackage{chords}
(* endblock extrapackages *)
(* block header *)
\title{((title))}
\author{((author))}
\subtitle{((subtitle))}
(* if version!="unknown" *)
\version{((version))}
(* endif *)
\mail{((mail))}
\web{((web))}
\picture{((picture))}
\picturecopyright{((picturecopyright))}
\footer{((footer))}
\lang{((mainlang))}
\newindex{titleidx}{((filename))_title}
\newauthorindex{authidx}{((filename))_auth}
(* for prefix in titleprefixwords *)
\titleprefixwords{((prefix))}
(* endfor*)
(* for key in titleprefixkeys *)
(* for word in authwords.key *)
(* if key=="after" *)
\authbyword{((word))}
(* else *)
\auth((key))word{((word))}
(* endif *)
(* endfor *)
(* endfor*)
\graphicspath{{((datadir))/img/}}
\pagestyle{empty}
(* endblock header *)
(* block preface *)
\definecolor{SongNumberBgColor}{HTML}{((songnumberbgcolor))}
\definecolor{NoteBgColor}{HTML}{((notebgcolor))}
\definecolor{IndexBgColor}{HTML}{((indexbgcolor))}
\renewcommand{\snumbgcolor}{SongNumberBgColor}
\renewcommand{\notebgcolor}{NoteBgColor}
\renewcommand{\idxbgcolor}{IndexBgColor}
(* if mainlang==english *)
\showindex{Songs Index}{titleidx}
\showindex{Authors Index}{authidx}
(* else *)
\showindex{Index des chansons}{titleidx}
\showindex{Index des auteurs}{authidx}
(* endif *)
(* if mainlang==french *)
\notenamesin{A}{B}{C}{D}{E}{F}{G}
\notenamesout{La}{Si}{Do}{}{Mi}{Fa}{Sol}
(* endif *)
% list of chords
\ifchorded
\phantomsection
(* if mainlang==english *)
\addcontentsline{toc}{section}{Chords list}
(* else *)
\addcontentsline{toc}{section}{Liste des accords}
(* endif *)
% \chords
\fi
(* endblock *)
(* block content *)
\phantomsection
(* if mainlang==english *)
\addcontentsline{toc}{section}{Songs list}
(* else *)
\addcontentsline{toc}{section}{Liste des chansons}
(* endif *)
\begin{songs}{titleidx,authidx}
(* for song in songlist.songs *)
\input{((song.path))}
(* endfor *)
\end{songs}
(* endblock *)

152
songbook_core/data/templates/default.tmpl

@ -1,152 +0,0 @@
% Copyright (C) 2009-2010 Romain Goffe, Alexandre Dupas
% Copyright (C) 2008 Kevin W. Hamlen
%
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 2
% of the License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
% MA 02110-1301, USA.
%
% The latest version of this program can be obtained from
% http://songs.sourceforge.net.
%
% Modified to serve personnal purposes. Newer versions can be
% obtained from http://www.lohrun.net.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Template parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%:[
%%: {"name":"title", "description":"Title", "default":"Recueil de chansons pour guitare", "mandatory":true},
%%: {"name":"author", "description":"Author", "default":"The Songbook Team", "mandatory":true},
%%: {"name":"booktype", "description":"Type", "type":"enum", "values":["chorded","lyric"], "default":"chorded", "mandatory":true},
%%: {"name":"lang", "description":"Language", "default":"english"},
%%: {"name":"instruments", "description":"Instruments", "type":"flag", "values":["guitar","ukulele"], "join":",", "mandatory":true, "default":["guitar"]},
%%: {"name":"bookoptions", "description":"Options", "type":"flag", "values":["diagram","importantdiagramonly","lilypond","pictures","tabs","repeatchords","onesongperpage"], "join":",", "mandatory":true, "default":["diagram","pictures"]},
%%: {"name":"version", "description":"Version", "default":"unknown"},
%%: {"name":"subtitle", "description":"Subtitle"},
%%: {"name":"web", "description":"Web", "default":"http://www.patacrep.com"},
%%: {"name":"mail", "description":"Email", "default":"crep@team-on-fire.com"},
%%: {"name":"picture", "description":"Picture", "type":"file", "default":"treble_a"},
%%: {"name":"picturecopyright", "description":"Copyright", "default":"Dbolton \\url{http://commons.wikimedia.org/wiki/User:Dbolton}"},
%%: {"name":"footer", "description":"Footer", "default":"\\begin{flushright}Generated using Songbook (\\url{http://www.patacrep.com})\\end{flushright}"},
%%: {"name":"mainfontsize", "description":"Font Size", "type":"font", "default":"10"},
%%: {"name":"songnumberbgcolor", "description":"Number Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"notebgcolor", "description":"Note Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"indexbgcolor", "description":"Index Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"titleprefixwords", "description":"Ignore some words in the beginning of song titles"},
%%: {"name":"authwords", "descriptipn":"Set of options to process author string (LaTeX commands authsepword, authignoreword, authbyword)"},
%%: {"name":"languages", "description":"List of languages used by songs", "default":""}
%%:]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% begin document
\makeatletter\def\input@path{{tex/}}
\documentclass[\getbooktype,\getinstruments,\getbookoptions,\getmainfontsize]{crepbook}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\PassOptionsToPackage{\getlanguages}{babel}
\PassOptionsToPackage{\getlang}{babel}
\usepackage{babel}
\title{\gettitle}
\author{\getauthor}
\subtitle{\getsubtitle}
\version{\getversion}
\mail{\getmail}
\web{\getweb}
\picture{\getpicture}
\picturecopyright{\getpicturecopyright}
\footer{\getfooter}
\lang{\getlang}
\newindex{titleidx}{\getname_title}
\newauthorindex{authidx}{\getname_auth}
\definecolor{SongNumberBgColor}{HTML}{\getsongnumberbgcolor}
\definecolor{NoteBgColor}{HTML}{\getnotebgcolor}
\definecolor{IndexBgColor}{HTML}{\getindexbgcolor}
\renewcommand{\snumbgcolor}{SongNumberBgColor}
\renewcommand{\notebgcolor}{NoteBgColor}
\renewcommand{\idxbgcolor}{IndexBgColor}
\gettitleprefixwords
\getauthwords
\pagestyle{empty}
% Customization of the page appearance
\usepackage[
a4paper % paper size
,includeheadfoot % include header and footer into text size
,hmarginratio=1:1 % ratio between inner and outer margin (default)
,outer=1.8cm % outer margin (right)
,vmarginratio=1:1 % ratio between top and bottom margin
,bmargin=1.3cm % bottom margin
% ,bindingoffset=1.7cm % space reserved to bound pages together
]{geometry}
\usepackage{chords}
\begin{document}
% translate default title
\IfStrEq{\gettitle}{Recueil de chansons pour guitare}{
\IfStrEq{\getlang}{english}{\title{Patacrep songbook}}{}
}{}
\maketitle
% indexes
\IfStrEq{\getlang}{english}{
\showindex{Songs Index}{titleidx}
}{
\showindex{Index des chansons}{titleidx}
}
\IfStrEq{\getlang}{english}{
\showindex{Authors Index}{authidx}
}{
\showindex{Index des auteurs}{authidx}
}
% chords notation
\IfStrEq{\getlang}{french}{
\notenamesin{A}{B}{C}{D}{E}{F}{G}
\notenamesout{La}{Si}{Do}{Ré}{Mi}{Fa}{Sol}
}{}
% list of chords
\ifchorded
\phantomsection
\IfStrEq{\getlang}{english}{
\addcontentsline{toc}{section}{Chords list}
}{
\addcontentsline{toc}{section}{Liste des accords}
}
\chords
\fi
% songs
\phantomsection
\IfStrEq{\getlang}{english}{
\addcontentsline{toc}{section}{Songs list}
}{
\addcontentsline{toc}{section}{Liste des chansons}
}
\begin{songs}{titleidx,authidx}
\getsongslist
\end{songs}
\end{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% end document

58
songbook_core/data/templates/layout.tex

@ -0,0 +1,58 @@
%% Automaticly generated document.
%% You may edit this file but all changes will be overwritten.
%% If you want to change this document, have a look at
%% the templating system.
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 2
% of the License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
% MA 02110-1301, USA.
%
% The latest version of this program can be obtained from
% https://github.com/patacrep/
% Copyright (C) 2014 The Songbook team (www.patacrep.com)
\documentclass[((booktype)),
(* for option in bookoptions *)((option)),
(* endfor *)
(* for instrument in instruments *)((instrument)),
(* endfor *)
((mainfontsize))pt]{crepbook}
(* block packages *)
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
(* for lang in songlist.languages() *)
\PassOptionsToPackage{((lang))}{babel}
(* endfor *)
\usepackage{babel}
(* block extrapackages *)
(* endblock extrapackages *)
(* endblock packages *)
(* block header *)
(* endblock header *)
\begin{document}
\maketitle
(* block preface *)
(* endblock *)
(* block content *)
(* endblock *)
(* block postface *)
(* endblock *)
\end{document}
% End of file

8
songbook_core/songs.py

@ -126,14 +126,6 @@ class SongsList(object):
for filename in glob.iglob(os.path.join(self._songdir, regexp)): for filename in glob.iglob(os.path.join(self._songdir, regexp)):
self.append(filename) self.append(filename)
def latex(self):
"""Renvoie le code LaTeX nécessaire pour intégrer la liste de chansons.
"""
result = [r'\input{{{0}}}'.format(song.path.replace("\\", "/").strip())
for song in self.songs]
result.append(r'\selectlanguage{%s}' % self._language)
return '\n'.join(result)
def languages(self): def languages(self):
"""Renvoie la liste des langues utilisées par les chansons""" """Renvoie la liste des langues utilisées par les chansons"""
return set().union(*[set(song.languages) for song in self.songs]) return set().union(*[set(song.languages) for song in self.songs])

59
songbook_core/templates.py

@ -0,0 +1,59 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Template for .tex generation settings and utilities"""
from jinja2 import Environment, FileSystemLoader, ChoiceLoader, PackageLoader
import os
import re
_LATEX_SUBS = (
(re.compile(r'\\'), r'\\textbackslash'),
(re.compile(r'([{}_#%&$])'), r'\\\1'),
(re.compile(r'~'), r'\~{}'),
(re.compile(r'\^'), r'\^{}'),
(re.compile(r'"'), r"''"),
(re.compile(r'\.\.\.+'), r'\\ldots'),
)
def _escape_tex(value):
'''Escape TeX special characters'''
newval = value
for pattern, replacement in _LATEX_SUBS:
newval = pattern.sub(replacement, newval)
return newval
def _init_tex_env(datadir=''):
'''Start a new jinja2 environment for .tex creation'''
loader = ChoiceLoader([
PackageLoader('songbook_core', 'data/templates'),
FileSystemLoader(os.path.join(datadir, 'templates')),
])
texenv = Environment(loader=loader)
texenv.block_start_string = '(*'
texenv.block_end_string = '*)'
texenv.variable_start_string = '(('
texenv.variable_end_string = '))'
texenv.comment_start_string = '(% comment %)'
texenv.comment_end_string = '(% endcomment %)'
texenv.filters['escape_tex'] = _escape_tex
texenv.trim_blocks = True
texenv.lstrip_blocks = True
return texenv
def render_tex(output, context, datadir=''):
'''Render a template into a .tex file
Arguments:
- output: a file object to write the result
- context: all the data to populate the template
- datadir: location of the user-defined templates
'''
env = _init_tex_env(datadir=datadir)
template = env.get_template(context['template'])
content = template.render(**context)
output.write(content)
return None # TODO: gestion des erreurs
Loading…
Cancel
Save