Browse Source

Merge branch 'bug_plastex' of git://git.spalax.fr.eu.org/songbook into next

Conflicts:
	songbook.py
	tools.py
	utils/__init__.py
remotes/origin/next
Romain Goffe 12 years ago
parent
commit
bf61393182
  1. 4
      index.py
  2. 126
      songbook.py
  3. 7
      templates/ancient.tmpl
  4. 7
      templates/minimal.tmpl
  5. 8
      templates/patacrep.tmpl
  6. 51
      utils/patchedbabel.py
  7. 54
      utils/plastex.py
  8. 34
      utils/songs.py

4
index.py

@ -104,13 +104,13 @@ class index:
self.prefix_patterns = [] self.prefix_patterns = []
if 'prefix' in self.keywords: if 'prefix' in self.keywords:
for prefix in self.keywords['prefix']: for prefix in self.keywords['prefix']:
self.prefix_patterns.append(re.compile(r"^(%s)\b\s*(.*)$" % prefix)) self.prefix_patterns.append(re.compile(r"^(%s)(\b|\\)(\s*.*)$" % prefix))
def add(self, key, number, link): def add(self, key, number, link):
for pattern in self.prefix_patterns: for pattern in self.prefix_patterns:
match = pattern.match(key) match = pattern.match(key)
if match: if match:
key = "%s (%s)" % (match.group(2), match.group(1)) key = "%s (%s)" % (match.group(2) + match.group(3), match.group(1))
break # Only one match per key break # Only one match per key
(first, key) = self.filter(key) (first, key) = self.filter(key)
if not self.data.has_key(first): if not self.data.has_key(first):

126
songbook.py

@ -10,10 +10,46 @@ import shutil
import json import json
import re import re
from subprocess import call from subprocess import call
from tools import recursiveFind from tools import recursiveFind
from song import *
from index import * from index import *
from unidecode import unidecode
from utils.plastex import parsetex
class Song:
#: Ordre de tri
sort = []
#: Préfixes à ignorer pour le tri
prefixes = []
def __init__(self, path, languages, titles, args):
self.titles = titles
self.normalized_titles = [locale.strxfrm(unprefixed(unidecode(unicode(title, "utf-8")), self.prefixes)) for title in titles]
self.args = args
self.path = path
self.languages = languages
def __repr__(self):
return repr((self.titles, self.args, self.path))
def __cmp__(self, other):
if not isinstance(other, Song):
return NotImplemented
for key in self.sort:
if key == "@title":
self_key = self.normalized_titles
other_key = other.normalized_titles
elif key == "@path":
self.key = locale.strxfrm(self.path)
other_key = locale.strxfrm(other.path)
else:
self_key = locale.strxfrm(self.args.get(key, ""))
other_key = locale.strxfrm(other.args.get(key, ""))
if self_key < other_key:
return -1
elif self_key > other_key:
return 1
return 0
def matchRegexp(reg, iterable): def matchRegexp(reg, iterable):
return [ m.group(1) for m in (reg.match(l) for l in iterable) if m ] return [ m.group(1) for m in (reg.match(l) for l in iterable) if m ]
@ -27,29 +63,57 @@ def unprefixed(title, prefixes):
return match.group(2) return match.group(2)
return title return title
def songslist(library, songs, prefixes): class SongsList:
song_objects = [] """Manipulation et traitement de liste de chansons"""
for s in songs:
path = library + 'songs/' + s def __init__(self, library, language):
with open(path, 'r+') as f: self._library = library
data = f.read() self._language = language
title = reTitle.search(data).group(0)
artist = reArtist.search(data.replace("{","")).group(0) # Liste triée des chansons
match = reAlbum.search(data.replace("{","")) self.songs = []
lilypond = False
if match:
album = match.group(0) def append(self, filename):
"""Ajout d'une chanson à la liste
Effets de bord : analyse syntaxique plus ou moins sommaire du fichier
pour en extraire et traiter certaines information (titre, langue,
album, etc.).
"""
path = os.path.join(self._library, 'songs', filename)
# Exécution de PlasTeX
data = parsetex(path)
song = Song(path, data['languages'], data['titles'], data['args'])
low, high = 0, len(self.songs)
while low != high:
middle = (low + high) / 2
if song < self.songs[middle]:
high = middle
else: else:
album = '' low = middle + 1
song_objects.append(Song(title, artist, album, path, lilypond)) self.songs.insert(low, song)
def append_list(self, filelist):
"""Ajoute une liste de chansons à la liste
song_objects = sorted(song_objects, key=lambda x: locale.strxfrm(unprefixed(x.title, prefixes))) L'argument est une liste de chaînes, représentant des noms de fichiers.
song_objects = sorted(song_objects, key=lambda x: locale.strxfrm(x.album)) """
song_objects = sorted(song_objects, key=lambda x: locale.strxfrm(x.artist)) for filename in filelist:
self.append(filename)
result = [ '\\input{{{0}}}'.format(song.path.replace("\\","/").strip()) for song in song_objects ] def latex(self):
"""Renvoie le code LaTeX nécessaire pour intégrer la liste de chansons.
"""
result = [ '\\input{{{0}}}'.format(song.path.replace("\\","/").strip()) for song in self.songs]
result.append('\\selectlanguage{%s}' % self._language)
return '\n'.join(result) return '\n'.join(result)
def languages(self):
"""Renvoie la liste des langues utilisées par les chansons"""
return set().union(*[set(song.languages) for song in self.songs])
def parseTemplate(template): def parseTemplate(template):
embeddedJsonPattern = re.compile(r"^%%:") embeddedJsonPattern = re.compile(r"^%%:")
f = open(template) f = open(template)
@ -115,9 +179,26 @@ def makeTexFile(sb, library, output):
for prefix in sb["titleprefixwords"]: for prefix in sb["titleprefixwords"]:
titleprefixwords += "\\titleprefixword{%s}\n" % prefix titleprefixwords += "\\titleprefixword{%s}\n" % prefix
sb["titleprefixwords"] = titleprefixwords sb["titleprefixwords"] = titleprefixwords
if "lang" not in sb:
sb["lang"] = "french"
if "sort" in sb:
sort = sb["sort"]
del sb["sort"]
else:
sort = [u"by", u"album", u"@title"]
Song.sort = sort
Song.prefixes = prefixes
parameters = parseTemplate("templates/"+template) parameters = parseTemplate("templates/"+template)
# compute songslist
if songs == "all":
songs = map(lambda x: x[len(library) + 6:], recursiveFind(os.path.join(library, 'songs'), '*.sg'))
songslist = SongsList(library, sb["lang"])
songslist.append_list(songs)
sb["languages"] = ",".join(songslist.languages())
# output relevant fields # output relevant fields
out = open(output, 'w') out = open(output, 'w')
out.write('%% This file has been automatically generated, do not edit!\n') out.write('%% This file has been automatically generated, do not edit!\n')
@ -132,12 +213,9 @@ def makeTexFile(sb, library, output):
for name, value in sb.iteritems(): for name, value in sb.iteritems():
if name in parameters: if name in parameters:
out.write(formatDefinition(name, toValue(parameters[name],value))) out.write(formatDefinition(name, toValue(parameters[name],value)))
# output songslist
if songs == "all":
songs = map(lambda x: x[len(library) + 6:], recursiveFind(os.path.join(library, 'songs'), '*.sg'))
if len(songs) > 0: if len(songs) > 0:
out.write(formatDefinition('songslist', songslist(library, songs, prefixes))) out.write(formatDefinition('songslist', songslist.latex()))
out.write('\\makeatother\n') out.write('\\makeatother\n')
# output template # output template

7
templates/ancient.tmpl

@ -42,14 +42,17 @@
%%: {"name":"mainfontsize", "description":"Font Size", "type":"font", "default":"10"}, %%: {"name":"mainfontsize", "description":"Font Size", "type":"font", "default":"10"},
%%: {"name":"songnumberbgcolor", "description":"Number Shade", "type":"color", "default":"#D1E4AE"}, %%: {"name":"songnumberbgcolor", "description":"Number Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"notebgcolor", "description":"Note Shade", "type":"color", "default":"#FFFDB3"}, %%: {"name":"notebgcolor", "description":"Note Shade", "type":"color", "default":"#FFFDB3"},
%%: {"name":"indexbgcolor", "description":"Index Shade", "type":"color", "default":"#D1E4AE"} %%: {"name":"indexbgcolor", "description":"Index Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"languages", "description":"List of languages used by songs", "default":""}
%%:] %%:]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% begin document % begin document
\makeatletter\def\input@path{{tex/}} \makeatletter\def\input@path{{tex/}}
\documentclass[\getbooktype,\getinstruments,\getbookoptions,\getmainfontsize]{crepbook} \documentclass[\getbooktype,\getinstruments,\getbookoptions,\getmainfontsize]{crepbook}
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\usepackage[italian,portuguese,english,spanish,french]{babel} \PassOptionsToPackage{\getlanguages}{babel}
\PassOptionsToPackage{\getlang}{babel}
\usepackage{babel}
\usepackage[T1]{fontenc} \usepackage[T1]{fontenc}
\usepackage{venturisold} \usepackage{venturisold}

7
templates/minimal.tmpl

@ -30,7 +30,8 @@
%%: {"name":"instruments", "description":"Instruments", "type":"flag", "values":["guitar","ukulele"], "join":",", "mandatory":true, "default":["guitar"]}, %%: {"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":["pictures"]}, %%: {"name":"bookoptions", "description":"Options", "type":"flag", "values":["diagram","importantdiagramonly","lilypond","pictures","tabs","repeatchords","onesongperpage"], "join":",", "mandatory":true, "default":["pictures"]},
%%: {"name":"mainfontsize", "description":"Font Size", "type":"font", "default":"10"}, %%: {"name":"mainfontsize", "description":"Font Size", "type":"font", "default":"10"},
%%: {"name":"titleprefixwords", "description":"Ignore some words in the beginning of song titles"} %%: {"name":"titleprefixwords", "description":"Ignore some words in the beginning of song titles"},
%%: {"name":"languages", "description":"List of languages used by songs", "default":""}
%%:] %%:]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% begin document % begin document
@ -39,7 +40,9 @@
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc} \usepackage[T1]{fontenc}
\usepackage{lmodern} \usepackage{lmodern}
\usepackage[italian,portuguese,spanish,english,french]{babel} \PassOptionsToPackage{\getlanguages}{babel}
\PassOptionsToPackage{\getlang}{babel}
\usepackage{babel}
\lang{\getlang} \lang{\getlang}
\graphicspath{ {img/}, {\getLibraryImgDirectory}, {\getLibraryLilypondDirectory} } \graphicspath{ {img/}, {\getLibraryImgDirectory}, {\getLibraryLilypondDirectory} }

8
templates/patacrep.tmpl

@ -43,7 +43,8 @@
%%: {"name":"songnumberbgcolor", "description":"Number Shade", "type":"color", "default":"#D1E4AE"}, %%: {"name":"songnumberbgcolor", "description":"Number Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"notebgcolor", "description":"Note Shade", "type":"color", "default":"#D1E4AE"}, %%: {"name":"notebgcolor", "description":"Note Shade", "type":"color", "default":"#D1E4AE"},
%%: {"name":"indexbgcolor", "description":"Index 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":"titleprefixwords", "description":"Ignore some words in the beginning of song titles"},
%%: {"name":"languages", "description":"List of languages used by songs", "default":""}
%%:] %%:]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% begin document % begin document
@ -52,7 +53,10 @@
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc} \usepackage[T1]{fontenc}
\usepackage{lmodern} \usepackage{lmodern}
\usepackage[italian,portuguese,spanish,english,french]{babel}
\PassOptionsToPackage{\getlanguages}{babel}
\PassOptionsToPackage{\getlang}{babel}
\usepackage{babel}
\title{\gettitle} \title{\gettitle}
\author{\getauthor} \author{\getauthor}

51
utils/patchedbabel.py

@ -0,0 +1,51 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Patch pour le paquet Babel de PlasTeX
Un bug dans PlasTeX intervient lorsqu'on essaye d'analyser une commande LaTeX
\selectlanguage{}, que nouv voulons utiliser ici. Un patch a été proposé aux
développeurs de plasTeX, et accepté. Mais il faut que cette correction arrive
en production. En attendant, nous utilisons cette version modifiée.
Dés que la correction sera entrée en production, il faudra supprimer ce
fichier, et remplater l'occurence à "patchedbabel" par "babel" dans le fichier
"plastex.py".
La correction à suveiller est la révision 1.3 du fichier babel.py :
http://plastex.cvs.sourceforge.net/viewvc/plastex/plastex/plasTeX/Packages/babel.py?view=log
# Comment vérifier si on peut supprimer ce fichier ?
1) Remplacer l'occurence à patchedbabel par babel dans le fichier plastex.py.
2) Générer un fichier .tex à partir d'un fichier .sb, ce dernier faisant
intervenir des chansons dans lesquelles \selectlanguage est utilisé (par
exemple, "make -B matteo.tex" ou "make -B naheulbeuk.tex" pour des fichiers pas
trop gros.
3) Si l'erreur suivante apparaît, c'est qu'il faut encore attendre.
> Traceback (most recent call last):
> [...]
> File "/usr/lib/pymodules/python2.7/plasTeX/Packages/babel.py", line 18, in invoke
> context.loadLanguage(self.attributes['lang'], self.ownerDocument)
> NameError: global name 'context' is not defined
3 bis) Si elle n'apparait pas : youpi ! Supprimez ce fichier !
# Contact et commentaires
Mercredi 27 mars 2013
Louis <spalax(at)gresille.org>
"""
from plasTeX import Command
class selectlanguage(Command):
args = 'lang:str'
def invoke(self, tex):
res = Command.invoke(self, tex)
self.ownerDocument.context.loadLanguage(self.attributes['lang'], self.ownerDocument)
return res

54
utils/plastex.py

@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from plasTeX.TeX import TeX
import codecs
import copy
import os
import sys
class SongParser:
"""Analyseur syntaxique de fichiers .sg"""
@staticmethod
def _create_TeX():
tex = TeX()
tex.disableLogging()
tex.ownerDocument.context.loadBaseMacros()
sys.path.append(os.path.dirname(__file__))
tex.ownerDocument.context.loadPackage(tex, "patchedbabel")
tex.ownerDocument.context.loadPackage(tex, "songs")
sys.path.pop()
return tex
@classmethod
def parse(cls, filename):
tex = cls._create_TeX()
tex.input(codecs.open(filename, 'r+', 'utf-8', 'replace'))
return tex.parse()
def parsetex(filename):
"""Analyse syntaxique d'un fichier .sg
Renvoie un dictionnaire contenant les métadonnées lues dans le fichier. Les
clefs sont :
- languages: l'ensemble des langages utilisés (recherche des
\selectlanguages{}) ;
- titles: la liste des titres ;
- args: le dictionnaire des paramètres passés à \\beginsong.
"""
# Analyse syntaxique
doc = SongParser.parse(filename)
# Extraction des données
data = {
"languages": set(),
}
for node in doc.allChildNodes:
if node.nodeName == "selectlanguage":
data["languages"].add(node.attributes['lang'])
if node.nodeName == "beginsong":
data["titles"] = node.attributes["titles"]
data["args"] = node.attributes["args"]
return data

34
utils/songs.py

@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import plasTeX
def split_linebreak(texlist):
return_list = []
current = []
for token in texlist:
if token.nodeName == '\\':
return_list.append(current)
current = []
else:
current.append(token.textContent.encode('utf-8'))
if current:
return_list.append(current)
return return_list
class beginsong(plasTeX.Command):
args = '{titles}[ args:dict ]'
def invoke(self, tex):
plasTeX.Command.invoke(self, tex)
# Parsing title
titles = []
for tokens in split_linebreak(self.attributes['titles'].allChildNodes):
titles.append("".join(tokens))
self.attributes['titles'] = titles
# Parsing keyval arguments
args = {}
for (key, val) in self.attributes['args'].iteritems():
args[key] = val.textContent.encode('utf-8')
self.attributes['args'] = args
Loading…
Cancel
Save