Browse Source

[chordpro] More generic song renderer

pull/79/head
Louis 9 years ago
parent
commit
eba5d404d1
  1. 2
      patacrep/build.py
  2. 2
      patacrep/content/__init__.py
  3. 2
      patacrep/content/song.py
  4. 2
      patacrep/data/templates/songs.tex
  5. 15
      patacrep/songs/__init__.py
  6. 28
      patacrep/songs/chordpro/__init__.py
  7. 4
      patacrep/songs/chordpro/ast.py
  8. 0
      patacrep/songs/chordpro/data/latex/content_chord
  9. 0
      patacrep/songs/chordpro/data/latex/content_chordlist
  10. 0
      patacrep/songs/chordpro/data/latex/content_comment
  11. 0
      patacrep/songs/chordpro/data/latex/content_define
  12. 0
      patacrep/songs/chordpro/data/latex/content_error
  13. 0
      patacrep/songs/chordpro/data/latex/content_guitar_comment
  14. 0
      patacrep/songs/chordpro/data/latex/content_image
  15. 0
      patacrep/songs/chordpro/data/latex/content_line
  16. 0
      patacrep/songs/chordpro/data/latex/content_newline
  17. 0
      patacrep/songs/chordpro/data/latex/content_partition
  18. 0
      patacrep/songs/chordpro/data/latex/content_space
  19. 0
      patacrep/songs/chordpro/data/latex/content_verse
  20. 0
      patacrep/songs/chordpro/data/latex/content_word
  21. 0
      patacrep/songs/chordpro/data/latex/song
  22. 4
      patacrep/songs/latex/__init__.py
  23. 46
      patacrep/templates.py

2
patacrep/build.py

@ -113,7 +113,7 @@ class Songbook(object):
)
# Configuration set
config['render_content'] = content.render_content
config['render'] = content.render
config['content'] = content.process_content(
config.get('content', []),
config,

2
patacrep/content/__init__.py

@ -131,7 +131,7 @@ class ContentError(SongbookError):
return "Content: {}: {}".format(self.keyword, self.message)
@jinja2.contextfunction
def render_content(context, content):
def render(context, content):
"""Render the content of the songbook as a LaTeX code.
Arguments:

2
patacrep/content/song.py

@ -34,7 +34,7 @@ class SongRenderer(Content):
def render(self, context):
"""Return the string that will render the song."""
return self.song.tex(output=context['filename'])
return self.song.render(output=context['filename'], output_format="latex")
#pylint: disable=unused-argument
def parse(keyword, argument, contentlist, config):

2
patacrep/data/templates/songs.tex

@ -108,6 +108,6 @@
\phantomsection
\addcontentsline{toc}{section}{\songlistname}
((render_content(content) ))
(( render(content) ))
(* endblock *)

15
patacrep/songs/__init__.py

@ -10,7 +10,6 @@ import re
from patacrep.authors import process_listauthors
from patacrep import files, encoding
from patacrep.content import Content
LOGGER = logging.getLogger(__name__)
@ -63,20 +62,20 @@ class DataSubpath(object):
return self
# pylint: disable=too-many-instance-attributes
class Song(Content):
class Song:
"""Song (or song metadata)
This class represents a song, bound to a file.
- It can parse the file given in arguments.
- It can render the song as some LaTeX code.
- It can render the song as some code (LaTeX, chordpro, depending on subclasses implemetation).
- Its content is cached, so that if the file has not been changed, the
file is not parsed again.
This class is inherited by classes implementing song management for
several file formats. Those subclasses must implement:
- `parse()` to parse the file;
- `render()` to render the song as LaTeX code.
- `render()` to render the song as code.
"""
# Version format of cached song. Increment this number if we update
@ -166,12 +165,16 @@ class Song(Content):
def __repr__(self):
return repr((self.titles, self.data, self.fullpath))
def tex(self, output): # pylint: disable=no-self-use, unused-argument
"""Return the LaTeX code rendering this song.
def render(self, output, output_format):
"""Return the code rendering this song.
Arguments:
- output: Name of the output file.
- output_format: Format of the output file (latex, chordpro...)
"""
method = "render_{}".format(output_format)
if hasattr(self, method):
return getattr(self, method)(output)
raise NotImplementedError()
def parse(self, config): # pylint: disable=no-self-use

28
patacrep/songs/chordpro/__init__.py

@ -7,14 +7,14 @@ import os
from patacrep import encoding, files
from patacrep.songs import Song
from patacrep.songs.chordpro.syntax import parse_song
from patacrep.templates import TexRenderer
from patacrep.templates import Renderer
class ChordproSong(Song):
"""Chordpros song parser."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.texenv = None
self.jinjaenv = None
def parse(self, config):
"""Parse content, and return the dictionary of song data."""
@ -28,7 +28,7 @@ class ChordproSong(Song):
'song': song,
}
def tex(self, output):
def render(self, output, output_format):
context = {
'language': self.config.get(
'lang',
@ -38,27 +38,31 @@ class ChordproSong(Song):
"titles": self.titles,
"authors": self.authors,
"metadata": self.data,
"render": self.render_tex,
"render": self._render_ast,
}
self.texenv = Environment(loader=FileSystemLoader(os.path.join(
self.jinjaenv = Environment(loader=FileSystemLoader(os.path.join(
os.path.abspath(pkg_resources.resource_filename(__name__, 'data')),
'latex'
output_format,
)))
return self.render_tex(context, self.cached['song'].content, template="song.tex")
return self._render_ast(
context,
self.cached['song'].content,
template="song",
)
@contextfunction
def render_tex(self, context, content, template=None):
"""Render ``content`` as tex."""
def _render_ast(self, context, content, template=None):
"""Render ``content``."""
if isinstance(context, dict):
context['content'] = content
else:
context.vars['content'] = content
if template is None:
template = content.template('tex')
return TexRenderer(
template = content.template()
return Renderer(
template=template,
encoding='utf8',
texenv=self.texenv,
jinjaenv=self.jinjaenv,
).template.render(context)
SONG_PARSERS = {

4
patacrep/songs/chordpro/ast.py

@ -73,14 +73,14 @@ class AST:
_template = None
inline = False
def template(self, extension):
def template(self):
"""Return the template to be used to render this object."""
if self._template is None:
LOGGER.warning("No template defined for {}.".format(self.__class__))
base = "error"
else:
base = self._template
return "content_{}.{}".format(base, extension)
return "content_{}".format(base)
def chordpro(self):
"""Return the chordpro string corresponding to this object."""

0
patacrep/songs/chordpro/data/latex/content_chord.tex → patacrep/songs/chordpro/data/latex/content_chord

0
patacrep/songs/chordpro/data/latex/content_chordlist.tex → patacrep/songs/chordpro/data/latex/content_chordlist

0
patacrep/songs/chordpro/data/latex/content_comment.tex → patacrep/songs/chordpro/data/latex/content_comment

0
patacrep/songs/chordpro/data/latex/content_define.tex → patacrep/songs/chordpro/data/latex/content_define

0
patacrep/songs/chordpro/data/latex/content_error.tex → patacrep/songs/chordpro/data/latex/content_error

0
patacrep/songs/chordpro/data/latex/content_guitar_comment.tex → patacrep/songs/chordpro/data/latex/content_guitar_comment

0
patacrep/songs/chordpro/data/latex/content_image.tex → patacrep/songs/chordpro/data/latex/content_image

0
patacrep/songs/chordpro/data/latex/content_line.tex → patacrep/songs/chordpro/data/latex/content_line

0
patacrep/songs/chordpro/data/latex/content_newline.tex → patacrep/songs/chordpro/data/latex/content_newline

0
patacrep/songs/chordpro/data/latex/content_partition.tex → patacrep/songs/chordpro/data/latex/content_partition

0
patacrep/songs/chordpro/data/latex/content_space.tex → patacrep/songs/chordpro/data/latex/content_space

0
patacrep/songs/chordpro/data/latex/content_verse.tex → patacrep/songs/chordpro/data/latex/content_verse

0
patacrep/songs/chordpro/data/latex/content_word.tex → patacrep/songs/chordpro/data/latex/content_word

0
patacrep/songs/chordpro/data/latex/song.tex → patacrep/songs/chordpro/data/latex/song

4
patacrep/songs/latex/__init__.py

@ -25,8 +25,8 @@ class LatexSong(Song):
self.authors = [self.data['by']]
del self.data['by']
def tex(self, output):
"""Return the LaTeX code rendering the song."""
def render_latex(self, output):
"""Return the code rendering the song."""
return r'\input{{{}}}'.format(files.path2posix(
files.relpath(
self.fullpath,

46
patacrep/templates.py

@ -66,28 +66,28 @@ def _escape_tex(value):
return newval
class TexRenderer:
class Renderer:
"""Render a template to a LaTeX file."""
# pylint: disable=too-few-public-methods
def __init__(self, template, texenv, encoding=None):
def __init__(self, template, jinjaenv, encoding=None):
self.encoding = encoding
self.texenv = texenv
self.texenv.block_start_string = '(*'
self.texenv.block_end_string = '*)'
self.texenv.variable_start_string = '(('
self.texenv.variable_end_string = '))'
self.texenv.comment_start_string = '(% comment %)'
self.texenv.comment_end_string = '(% endcomment %)'
self.texenv.line_comment_prefix = '%!'
self.texenv.filters['escape_tex'] = _escape_tex
self.texenv.trim_blocks = True
self.texenv.lstrip_blocks = True
self.texenv.globals["path2posix"] = files.path2posix
self.template = self.texenv.get_template(template)
class TexBookRenderer(TexRenderer):
self.jinjaenv = jinjaenv
self.jinjaenv.block_start_string = '(*'
self.jinjaenv.block_end_string = '*)'
self.jinjaenv.variable_start_string = '(('
self.jinjaenv.variable_end_string = '))'
self.jinjaenv.comment_start_string = '(% comment %)'
self.jinjaenv.comment_end_string = '(% endcomment %)'
self.jinjaenv.line_comment_prefix = '%!'
self.jinjaenv.filters['escape_tex'] = _escape_tex
self.jinjaenv.trim_blocks = True
self.jinjaenv.lstrip_blocks = True
self.jinjaenv.globals["path2posix"] = files.path2posix
self.template = self.jinjaenv.get_template(template)
class TexBookRenderer(Renderer):
"""Tex renderer for the whole songbook"""
def __init__(self, template, datadirs, lang, encoding=None):
@ -106,17 +106,17 @@ class TexBookRenderer(TexRenderer):
FileSystemLoader(os.path.join(datadir, 'templates'))
for datadir in datadirs
]
texenv = Environment(
jinjaenv = Environment(
loader=ChoiceLoader(loaders),
extensions=[VariablesExtension],
)
try:
super().__init__(template, texenv, encoding)
super().__init__(template, jinjaenv, encoding)
except TemplateNotFound as exception:
# Only works if all loaders are FileSystemLoader().
paths = [
item
for loader in self.texenv.loader.loaders
for loader in self.jinjaenv.loader.loaders
for item in loader.searchpath
]
raise errors.TemplateError(
@ -199,13 +199,13 @@ class TexBookRenderer(TexRenderer):
"""
subvariables = {}
templatename = self.texenv.get_template(template).filename
templatename = self.jinjaenv.get_template(template).filename
with patacrep.encoding.open_read(
templatename,
encoding=self.encoding
) as template_file:
content = template_file.read()
subtemplates = list(find_templates(self.texenv.parse(content)))
subtemplates = list(find_templates(self.jinjaenv.parse(content)))
match = re.findall(_VARIABLE_REGEXP, content)
if match:
for var in match:

Loading…
Cancel
Save