Support additional markers in the chordpro file, and move to div-based HTML
This commit is contained in:
parent
454c29152d
commit
d3f68f109b
@ -1,7 +1,7 @@
|
||||
from hyphen import Hyphenator
|
||||
|
||||
from chordpro.constants import DIRECTIVE, CHORD_WORD, CHORUS_MARKER, KNOWN_DIRECTIVES, \
|
||||
START_OF, END_OF
|
||||
from chordpro.constants import BRIDGE_MARKER, CHORD_WORD, CHORUS_MARKER, DIRECTIVE, END_OF, \
|
||||
KNOWN_DIRECTIVES, START_OF, VERSE_MARKER
|
||||
|
||||
HYPHEN_CACHE = {}
|
||||
SYLLABLE_EXCEPTIONS = {
|
||||
@ -159,6 +159,18 @@ class Verse(object):
|
||||
return match is not None
|
||||
return match.group(1) == type_
|
||||
|
||||
@staticmethod
|
||||
def is_verse_marker(line):
|
||||
return line.strip().startswith('{verse')
|
||||
|
||||
@staticmethod
|
||||
def get_verse_from_marker(line):
|
||||
match = VERSE_MARKER.match(line)
|
||||
if not match:
|
||||
return None
|
||||
if len(match.groups()) > 1:
|
||||
return match.group(2)
|
||||
|
||||
@staticmethod
|
||||
def is_chorus_marker(line):
|
||||
return line.strip().startswith('{chorus')
|
||||
@ -171,6 +183,18 @@ class Verse(object):
|
||||
if len(match.groups()) > 1:
|
||||
return match.group(2)
|
||||
|
||||
@staticmethod
|
||||
def is_bridge_marker(line):
|
||||
return line.strip().startswith('{bridge')
|
||||
|
||||
@staticmethod
|
||||
def get_bridge_from_marker(line):
|
||||
match = BRIDGE_MARKER.match(line)
|
||||
if not match:
|
||||
return None
|
||||
if len(match.groups()) > 1:
|
||||
return match.group(2)
|
||||
|
||||
|
||||
class Metadata(object):
|
||||
|
||||
@ -214,9 +238,21 @@ class Song(object):
|
||||
current_verse = None
|
||||
elif is_verse:
|
||||
current_verse.add_line(line.strip())
|
||||
elif Verse.is_verse_marker(line):
|
||||
verse_name = Verse.get_verse_from_marker(line)
|
||||
for verse in self.verses[::-1]:
|
||||
if verse.title == verse_name or verse.type_ == "verse":
|
||||
self.verse_order.append(verse)
|
||||
break
|
||||
elif Verse.is_chorus_marker(line):
|
||||
chorus_name = Verse.get_chorus_from_marker(line)
|
||||
for verse in self.verses[::-1]:
|
||||
if verse.title == chorus_name or verse.type_ == "chorus":
|
||||
self.verse_order.append(verse)
|
||||
break
|
||||
elif Verse.is_bridge_marker(line):
|
||||
bridge_name = Verse.get_bridge_from_marker(line)
|
||||
for verse in self.verses[::-1]:
|
||||
if verse.title == bridge_name or verse.type_ == "bridge":
|
||||
self.verse_order.append(verse)
|
||||
break
|
||||
|
@ -35,5 +35,6 @@ START_OF = re.compile(r'\{start_of_(' + '|'.join(KNOWN_VERSE_TYPES) + r')(: *(.*
|
||||
END_OF = re.compile(r'\{end_of_(' + '|'.join(KNOWN_VERSE_TYPES) + r')\}')
|
||||
CHORUS_MARKER = re.compile(r'\{chorus(: *(.*?))?\}')
|
||||
VERSE_MARKER = re.compile(r'\{verse: *(.*?)\}')
|
||||
BRIDGE_MARKER = re.compile(r'\{bridge: *(.*?)\}')
|
||||
CHORD_WORD = re.compile(r'(.*?)\[(.*?)\]')
|
||||
CHORD = re.compile(r'\[(.*?)\]')
|
||||
|
@ -1,9 +1,16 @@
|
||||
import os
|
||||
|
||||
CHORD = '<td class="chord">{}</td>'
|
||||
SYLLB = '<td class="syllable">{}</td>'
|
||||
LINE = '<table class="line" border="0" cellpadding="0" cellspacing="0"><tr class="chords-line">{}</tr><tr ' \
|
||||
'class="lyrics-line">{}</tr></table>'
|
||||
# Tables -- don't work when rendered to PDF
|
||||
# CHORD = '<td class="chord">{}</td>'
|
||||
# SYLLB = '<td class="syllable">{}</td>'
|
||||
# LINE = '<table class="line" border="0" cellpadding="0" cellspacing="0"><tr class="chords-line">{}</tr><tr ' \
|
||||
# 'class="lyrics-line">{}</tr></table>'
|
||||
|
||||
# Divs
|
||||
CHORD = '<div class="chord">{}</div>'
|
||||
SYLLB = '<div class="syllable">{}</div>'
|
||||
WRAPP = '<div class="wrapper">{}</div>'
|
||||
LINE = '<div class="line">{}</div>'
|
||||
STANZA = '''<section class="stanza-section {verse_type}-section">
|
||||
<h4 class="stanza-heading {verse_type}-heading">{verse_name}</h4>
|
||||
<div class="stanza {verse_type}">
|
||||
@ -16,6 +23,9 @@ HTML = '''<html>
|
||||
<title>{title}</title>
|
||||
<style>
|
||||
h1.title, h3.composer, h4.stanza-heading {{ font-weight: normal; }}
|
||||
h4.stanza-heading {{ margin-bottom: 0; }}
|
||||
.stanza-section {{ margin-bottom: 2rem; page-break-inside: avoid; }}
|
||||
.wrapper {{ display: inline-block !important; }}
|
||||
{styles}
|
||||
</style>
|
||||
</head>
|
||||
@ -403,26 +413,30 @@ def generate_option_styles(options):
|
||||
return styles
|
||||
|
||||
|
||||
def render(song, options=None):
|
||||
def render(song, options=None, extra_styles=None):
|
||||
"""Render a song to HTML"""
|
||||
styles = options and generate_option_styles(options) or []
|
||||
if extra_styles:
|
||||
styles.append(extra_styles)
|
||||
rendered_verses = []
|
||||
for verse in song.verse_order:
|
||||
rendered_lines = []
|
||||
for line in verse.lines:
|
||||
rendered_chords = []
|
||||
rendered_syllables = []
|
||||
for word_counter, word in enumerate(line.words):
|
||||
is_last_word = (word_counter + 1) == len(line.words)
|
||||
for syll_counter, syllable in enumerate(word.syllables):
|
||||
is_last_syllable = (syll_counter + 1) == len(word.syllables)
|
||||
rendered_chords.append(CHORD.format(syllable.chord or ' '))
|
||||
rendered_syllables.append(SYLLB.format(
|
||||
syllable.syllable + (' ' if is_last_syllable and not is_last_word else '')))
|
||||
rendered_lines.append(LINE.format(''.join(rendered_chords), ''.join(rendered_syllables)))
|
||||
rendered_chord = CHORD.format(syllable.chord or ' ')
|
||||
rendered_syllable = SYLLB.format(
|
||||
(syllable.syllable or ' ') +
|
||||
(' ' if is_last_syllable and not is_last_word else ''))
|
||||
rendered_syllables.append(WRAPP.format(rendered_chord + rendered_syllable))
|
||||
rendered_lines.append(LINE.format(''.join(rendered_syllables)))
|
||||
rendered_verses.append(STANZA.format(verse_type=verse.type_ or '', verse_name=verse.title,
|
||||
verse_body='\n'.join(rendered_lines)))
|
||||
title = song.metadata.get('title') or 'Song'
|
||||
metadata = TITLE.format(title=title, composer=song.metadata.get('artist') or song.metadata.get('composer') or '')
|
||||
metadata = TITLE.format(title=title, composer=song.metadata.get('artist') or
|
||||
song.metadata.get('composer') or '')
|
||||
body = metadata + '\n' + '\n'.join(rendered_verses)
|
||||
return HTML.format(title=title, body=body, styles=os.linesep.join(styles))
|
||||
|
Loading…
Reference in New Issue
Block a user