diff --git a/python/index.py b/python/index.py new file mode 100644 index 00000000..965bdad9 --- /dev/null +++ b/python/index.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Generate indexes files for the Crep's chordbook compilation. This is +# a replacement for the original makeindex program written in C that +# produces an index file (.sbx) from a file generated by the latex +# compilation of the songbook (.sxd). +# +# Usage : songbook-makeindex.py src +# src is the .sxd file generated by latex +# + +import sys +import re +import locale +import warnings + +# Pattern set to ignore latex command in title prefix +keywordPattern = re.compile(r"^%(\w+)\s?(.*)$") +firstLetterPattern = re.compile(r"^(?:\{?\\\w+\}?)*[^\w]*(\w)") +iecPattern = re.compile(r"\IeC {\\(.*?)}") +replacePattern = { + '`A': 'À', + '`a': 'à', + '^a': 'â', + "'a": 'á', + "~a": 'ã', + 'oe': 'œ', + "'e" : 'é', + "`e" : 'è', + "^e" : 'ê', + '"e' : 'ë', + "'E" : 'É', + "`E" : 'È', + "'o" : 'ó', + "^o" : 'ô', + r'"\i' : 'i', + r'^\i' : 'i', + '"u' : 'ü', + '`u' : 'ù', + '`u' : 'ù', + '~n' : 'ñ', + "c C" : 'Ç', + "c c" : 'ç', + "textquoteright" : "'", +} + +def sortkey(value): + ''' + From a title, return something usable for sorting. It handles locale (but + don't forget to call locale.setlocale(locale.LC_ALL, '')). It also try to + handle the sort with crappy latex escape sequences. Some chars may not be + handled by this function, so add them to *replacePattern* dictionnary. + ''' + def repl(match): + try: + return replacePattern[match.group(1).strip()] + except KeyError: + warnings.warn("Error, no match to replace %s in %s. You should add it in the coresponding table in title_sort.py" % (match.group(0), match.group(1))) + + return locale.strxfrm(iecPattern.sub(repl, value).replace(' ', 'A')) + +def processSXDEntry(tab): + return (tab[0], tab[1], tab[2]) + +def processSXD(filename): + file = open(filename) + data = [] + for line in file: + data.append(line.strip()) + file.close() + + type = data[0] + i = 1 + idx = index() + + if len(data) > 1: + while data[i].startswith('%'): + keywords = keywordPattern.match(data[i]).groups() + idx.keyword(keywords[0],keywords[1]) + i += 1 + + idx.compileKeywords() + for i in range(i,len(data),3): + entry = processSXDEntry(data[i:i+3]) + idx.add(entry[0],entry[1],entry[2]) + return idx + +class index: + data = dict() + keywords = dict() + + def filter(self, key): + letter = firstLetterPattern.match(key).group(1) + if re.match('\d',letter): + letter = '0-9' + return (letter.upper(), key) + + def keyword(self, key, word): + if not self.keywords.has_key(key): + self.keywords[key] = [] + self.keywords[key].append(word) + + def compileKeywords(self): + self.prefix_patterns = [] + if 'prefix' in self.keywords: + for prefix in self.keywords['prefix']: + self.prefix_patterns.append(re.compile(r"^(%s)\b\s*(.*)$" % prefix)) + + def add(self, key, number, link): + for pattern in self.prefix_patterns: + match = pattern.match(key) + if match: + key = "%s (%s)" % (match.group(2), match.group(1)) + break # Only one match per key + (first, key) = self.filter(key) + if not self.data.has_key(first): + self.data[first] = dict() + if not self.data[first].has_key(key): + self.data[first][key] = [] + self.data[first][key].append({'num':number, 'link':link}) + + def refToStr(self, ref): + if sys.version_info >= (2,6): + return '\\hyperlink{{{0[link]}}}{{{0[num]}}}'.format(ref) + else: + return '\\hyperlink{%(link)s}{%(num)s}' % ref + + def entryToStr(self, key, entry): + if sys.version_info >= (2,6): + return '\\idxentry{{{0}}}{{{1}}}\n'.format(key, '\\\\'.join(map(self.refToStr, entry))) + else: + return '\\idxentry{%s}{%s}\n' % (key, '\\\\'.join(map(self.refToStr, entry))) + + def idxBlockToStr(self, letter, entries): + str = '\\begin{idxblock}{'+letter+'}'+'\n' + for key in sorted(entries.keys(), key=sortkey): + str += self.entryToStr(key, entries[key]) + str += '\\end{idxblock}'+'\n' + return str + + def entriesToStr(self): + str = "" + for letter in sorted(self.data.keys()): + str += self.idxBlockToStr(letter, self.data[letter]) + return str diff --git a/python/song.py b/python/song.py new file mode 100644 index 00000000..5f65cf3e --- /dev/null +++ b/python/song.py @@ -0,0 +1,21 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# + +import re + +reTitle = re.compile('(?<=beginsong\\{)(.(? 0: + out.write(formatDefinition('songslist', songslist(library, songs, prefixes))) + out.write('\\makeatother\n') + + # output template + commentPattern = re.compile(r"^\s*%") + f = open("templates/"+template) + content = [ line for line in f if not commentPattern.match(line) ] + + for index, line in enumerate(content): + if re.compile("getCacheDirectory").search(line): + line = line.replace("\\getCacheDirectory", cachePath.replace("\\","/") + "/") + content[index] = line + if re.compile("getLibraryImgDirectory").search(line): + line = line.replace("\\getLibraryImgDirectory", library + "img/") + content[index] = line + if re.compile("getLibraryLilypondDirectory").search(line): + line = line.replace("\\getLibraryLilypondDirectory", library + "lilypond/") + content[index] = line + + f.close() + out.write(''.join(content)) + + out.close() + +def makeDepend(sb, library, output): + name = output[:-2] + + indexPattern = re.compile(r"^[^%]*\\(?:newauthor|new)index\{.*\}\{(.*?)\}") + lilypondPattern = re.compile(r"^[^%]*\\(?:lilypond)\{(.*?)\}") + + # check for deps (in sb data) + deps = []; + if sb["songs"] == "all": + deps += recursiveFind(os.path.join(library, 'songs'), '*.sg') + else: + deps += map(lambda x: library + "songs/" + x, sb["songs"]) + + # check for lilypond deps (in songs data) if necessary + lilypond = [] + if "bookoptions" in sb and "lilypond" in sb["bookoptions"]: + for filename in deps: + tmpl = open(filename) + lilypond += matchRegexp(lilypondPattern, tmpl) + tmpl.close() + + # check for index (in template file) + if "template" in sb: + filename = sb["template"] + else: + filename = "patacrep.tmpl" + tmpl = open("templates/"+filename) + idx = map(lambda x: x.replace("\getname", name), matchRegexp(indexPattern, tmpl)) + tmpl.close() + + # write .d file + out = open(output, 'w') + out.write('{0} {1} : {2}\n'.format(output, name+".tex", ' '.join(deps))) + out.write('{0} : {1}\n'.format(name+".pdf", ' '.join(map(lambda x: x+".sbx",idx)+map(lambda x: library+"lilypond/"+x+".pdf", lilypond)))) + out.write('\t$(LATEX) {0}\n'.format(name+".tex")) + out.write('{0} : {1}\n'.format(' '.join(map(lambda x: x+".sxd",idx)), name+".aux")) + out.close() + +def usage(): + print "No usage information yet." + +def main(): + locale.setlocale(locale.LC_ALL, '') # set script locale to match user's + try: + opts, args = getopt.getopt(sys.argv[1:], + "hs:o:l:d", + ["help","songbook=","output=","depend","library="]) + except getopt.GetoptError, err: + # print help and exit + print str(err) + usage() + sys.exit(2) + + songbook = None + depend = False + output = None + library = './' + + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-s", "--songbook"): + songbook = a + elif o in ("-d", "--depend"): + depend = True + elif o in ("-o", "--output"): + output = a + elif o in ("-l", "--library"): + if not a.endswith('/'): + a += '/' + library = a + else: + assert False, "unhandled option" + + makeCoverCache(library) + if songbook and output: + f = open(songbook) + sb = json.load(f) + f.close() + + if depend: + makeDepend(sb, library, output) + else: + makeTexFile(sb, library, output) + +if __name__ == '__main__': + main() diff --git a/utils/utils.py b/python/tools.py similarity index 100% rename from utils/utils.py rename to python/tools.py diff --git a/songbook-makeindex.py b/songbook-makeindex.py index 494b9889..8e54dda2 100755 --- a/songbook-makeindex.py +++ b/songbook-makeindex.py @@ -9,10 +9,10 @@ # src is the .sxd file generated by latex # -import sys import os.path import glob import re +import sys from optparse import OptionParser import sortindex import locale diff --git a/songbook.py b/songbook.py index 6d3ec838..43a7e1c1 100755 --- a/songbook.py +++ b/songbook.py @@ -4,33 +4,22 @@ import getopt, sys import os.path -import re -import json -import locale -import shutil import locale import platform +import shutil +import json +import re +from subprocess import call -from utils.utils import recursiveFind - -reTitle = re.compile('(?<=beginsong\\{)(.(? 1: + while data[i].startswith('%'): + keywords = keywordPattern.match(data[i]).groups() + idx.keyword(keywords[0],keywords[1]) + i += 1 + + idx.compileKeywords() + for i in range(i,len(data),3): + entry = processSXDEntry(data[i:i+3]) + idx.add(entry[0],entry[1],entry[2]) + return idx + +class index: + data = dict() + keywords = dict() + + def filter(self, key): + letter = firstLetterPattern.match(key).group(1) + if re.match('\d',letter): + letter = '0-9' + return (letter.upper(), key) + + def keyword(self, key, word): + if not self.keywords.has_key(key): + self.keywords[key] = [] + self.keywords[key].append(word) + + def compileKeywords(self): + self.prefix_patterns = [] + if 'prefix' in self.keywords: + for prefix in self.keywords['prefix']: + self.prefix_patterns.append(re.compile(r"^(%s)\b\s*(.*)$" % prefix)) + + def add(self, key, number, link): + for pattern in self.prefix_patterns: + match = pattern.match(key) + if match: + key = "%s (%s)" % (match.group(2), match.group(1)) + break # Only one match per key + (first, key) = self.filter(key) + if not self.data.has_key(first): + self.data[first] = dict() + if not self.data[first].has_key(key): + self.data[first][key] = [] + self.data[first][key].append({'num':number, 'link':link}) + + def refToStr(self, ref): + if sys.version_info >= (2,6): + return '\\hyperlink{{{0[link]}}}{{{0[num]}}}'.format(ref) + else: + return '\\hyperlink{%(link)s}{%(num)s}' % ref + + def entryToStr(self, key, entry): + if sys.version_info >= (2,6): + return '\\idxentry{{{0}}}{{{1}}}\n'.format(key, '\\\\'.join(map(self.refToStr, entry))) + else: + return '\\idxentry{%s}{%s}\n' % (key, '\\\\'.join(map(self.refToStr, entry))) + + def idxBlockToStr(self, letter, entries): + str = '\\begin{idxblock}{'+letter+'}'+'\n' + for key in sorted(entries.keys(), key=sortkey): + str += self.entryToStr(key, entries[key]) + str += '\\end{idxblock}'+'\n' + return str + + def entriesToStr(self): + str = "" + for letter in sorted(self.data.keys()): + str += self.idxBlockToStr(letter, self.data[letter]) + return str diff --git a/utils/__init__.py b/utils/__init__.py deleted file mode 100644 index e69de29b..00000000