Engine for LaTeX songbooks http://www.patacrep.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

148 lines
4.8 KiB

"""Chordpro parser"""
from jinja2 import Environment, FileSystemLoader, contextfunction, ChoiceLoader
import jinja2
import logging
import operator
import os
from pkg_resources import resource_filename
from patacrep import encoding, files
from patacrep.songs import Song
from patacrep.songs.chordpro.syntax import parse_song
from patacrep.templates import Renderer
from patacrep.latex import lang2babel
LOGGER = logging.getLogger(__name__)
def sort_directive_argument(directives):
"""Sort directives by their argument."""
return sorted(directives, key=operator.attrgetter("argument"))
class ChordproSong(Song):
"""Chordpro song parser"""
# pylint: disable=abstract-method
output_language = None
def _parse(self, config):
"""Parse content, and return the dictionary of song data."""
with encoding.open_read(self.fullpath, encoding=self.encoding) as song:
song = parse_song(song.read(), self.fullpath)
self.authors = song.authors
self.titles = song.titles
self.lang = song.get_data_argument('language', self.config['lang'])
self.data = song.meta
self.cached = {
'song': song,
}
def render(self, output=None, template="song"): # pylint: disable=arguments-differ
context = {
'lang': self.lang,
"titles": self.titles,
"authors": self.authors,
"metadata": self.data,
"render": self._render_ast,
"config": self.config,
"content": self.cached['song'].content,
}
jinjaenv = Environment(loader=ChoiceLoader([
FileSystemLoader(
self.get_datadirs(os.path.join("templates", self.output_language))
),
FileSystemLoader(
os.path.join(resource_filename(__name__, 'data'), self.output_language)
),
]))
jinjaenv.filters['search_image'] = self.search_image
jinjaenv.filters['search_partition'] = self.search_partition
jinjaenv.filters['lang2babel'] = lang2babel
jinjaenv.filters['sortargs'] = sort_directive_argument
try:
return Renderer(
template=template,
encoding='utf8',
jinjaenv=jinjaenv,
).template.render(context)
except jinja2.exceptions.TemplateNotFound:
raise NotImplementedError("Cannot convert to format '{}'.".format(self.output_language))
@staticmethod
@contextfunction
def _render_ast(context, content):
"""Render ``content``."""
context.vars['content'] = content
return context.environment.get_template(content.template()).render(context)
class Chordpro2HtmlSong(ChordproSong):
"""Render chordpro song to html code"""
output_language = "html"
def search_file(self, filename, extensions=None, *, datadirs=None):
try:
datadir, filename, extension = self.search_datadir_file(filename, extensions, datadirs)
return os.path.join(datadir, filename + extension)
except FileNotFoundError:
LOGGER.warning(
"Song '%s' (datadir '%s'): File '%s' not found.",
self.subpath, self.datadir, filename,
)
return None
class Chordpro2LatexSong(ChordproSong):
"""Render chordpro song to latex code"""
output_language = "latex"
def search_file(self, filename, extensions=None, *, datadirs=None):
_datadir, filename, _extension = self.search_datadir_file(
filename,
extensions,
datadirs,
)
return filename
def search_partition(self, filename):
try:
return os.path.join("scores", super().search_partition(filename))
except FileNotFoundError:
LOGGER.warning(
"Song '%s' (datadir '%s'): Score '%s' not found.",
self.subpath, self.datadir, filename,
)
return None
def search_image(self, filename):
try:
return os.path.join("img", super().search_image(filename))
except FileNotFoundError:
LOGGER.warning(
"Song '%s' (datadir '%s'): Image '%s' not found.",
self.subpath, self.datadir, filename,
)
return None
class Chordpro2ChordproSong(ChordproSong):
"""Render chordpro song to chordpro code"""
output_language = "chordpro"
def search_file(self, filename, extensions=None, *, datadirs=None):
# pylint: disable=unused-variable
return filename
SONG_RENDERERS = {
"latex": {
'sgc': Chordpro2LatexSong,
},
"html": {
'sgc': Chordpro2HtmlSong,
},
"chordpro": {
'sgc': Chordpro2ChordproSong,
},
}