From 17e5d7aa0ef8e285d0c4c0479074812029535b67 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Mon, 2 Aug 2021 13:55:10 -0700 Subject: [PATCH] Expand the renderer options, and make a way to programmatically generate the styles easily. --- src/chordpro/renderers/html.py | 204 ++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 65 deletions(-) diff --git a/src/chordpro/renderers/html.py b/src/chordpro/renderers/html.py index b86e6e6..9f3a5f7 100644 --- a/src/chordpro/renderers/html.py +++ b/src/chordpro/renderers/html.py @@ -4,9 +4,9 @@ CHORD = '{}' SYLLB = '{}' LINE = '{}{}
' -VERSE = '''
-

{verse_name}

-
+VERSE = '''
+

{verse_name}

+
{verse_body}
''' @@ -15,10 +15,7 @@ HTML = ''' {title} @@ -26,263 +23,329 @@ HTML = ''' {body} ''' -STYLES = { - 'verse_name_bold': '.verse-name { font-weight: bold !important; }', - 'verse_upper': '.verse-name { text-transform: uppercase; }', - 'chord_bold': '.chord { font-weight: bold; }' -} +STYLE_KEYS = [ + 'font', + 'size', + 'is_bold', + 'is_centered', + 'is_upper', + 'indent' +] HTML_OPTIONS = { 'default_font': { 'description': 'The default font to use.', 'type': str, - 'default': None + 'default': None, + 'group': 'default' }, 'default_size': { 'description': 'The default size to use.', 'type': int, - 'default': None + 'default': None, + 'group': 'default' }, 'title_font': { 'description': 'The font for the title', 'type': str, - 'default': None + 'default': None, + 'group': 'title' }, 'title_size': { 'description': 'The size of the title in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'title' }, 'title_is_bold': { 'description': 'Set to True to make the title bold', 'type': bool, - 'default': True + 'default': True, + 'group': 'title' + }, + 'title_is_centered': { + 'description': 'Center the title on the page', + 'type': bool, + 'default': None, + 'group': 'title' }, 'title_is_upper': { 'description': 'Force the title to be uppercase', 'type': bool, 'default': None, + 'group': 'title' }, 'composer_font': { 'description': 'The font for the composer', 'type': str, - 'default': None + 'default': None, + 'group': 'composer' }, 'composer_size': { 'description': 'The size of the composer in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'composer' }, 'composer_is_bold': { 'description': 'Set to True to make the composer bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'composer' + }, + 'composer_is_centered': { + 'description': 'Center the composer on the page', + 'type': bool, + 'default': None, + 'group': 'composer' }, 'composer_is_upper': { 'description': 'Force the composer to be uppercase', 'type': bool, 'default': None, + 'group': 'composer' }, 'copyright_font': { 'description': 'The font for the copyright', 'type': str, - 'default': None + 'default': None, + 'group': 'copyright' }, 'copyright_size': { 'description': 'The size of the copyright in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'copyright' }, 'copyright_is_bold': { 'description': 'Set to True to make the copyright bold', 'type': bool, 'default': False, + 'group': 'copyright' }, 'copyright_is_upper': { 'description': 'Force the copyright to be uppercase', 'type': bool, 'default': None, + 'group': 'copyright' }, 'stanza_font': { 'description': 'The font for the stanzas', 'type': str, - 'default': None + 'default': None, + 'group': 'stanza' }, 'stanza_size': { 'description': 'The size of the stanza in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'stanza' }, 'stanza_is_bold': { 'description': 'Set to True to make the stanza bold', 'type': bool, - 'default': False + 'default': False, + 'group': 'stanza' }, 'stanza_indent': { 'description': 'How much to indent the stanza by, in rem', 'type': int, - 'default': 2 + 'default': 4, + 'group': 'stanza' }, 'stanza_heading_font': { 'description': 'The font for the stanza headings', 'type': str, - 'default': None + 'default': None, + 'group': 'stanza_heading' }, 'stanza_heading_size': { 'description': 'The size of the stanza heading in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'stanza_heading' }, 'stanza_heading_is_bold': { 'description': 'Set to True to make the stanza heading bold', 'type': bool, - 'default': True + 'default': True, + 'group': 'stanza_heading' }, 'stanza_heading_is_upper': { 'description': 'Force the stanza heading to be uppercase', 'type': bool, 'default': True, + 'group': 'stanza_heading' }, 'verse_font': { 'description': 'The font for the verses', 'type': str, - 'default': None + 'default': None, + 'group': 'verse' }, 'verse_size': { 'description': 'The size of the verse in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'verse' }, 'verse_is_bold': { 'description': 'Set to True to make the verse bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'verse' }, 'verse_indent': { 'description': 'How much to indent the verse by, in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'verse' }, 'verse_heading_font': { 'description': 'The font for the verse headings', 'type': str, - 'default': None + 'default': None, + 'group': 'verse_heading' }, 'verse_heading_size': { 'description': 'The size of the verse heading in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'verse_heading' }, 'verse_heading_is_bold': { 'description': 'Set to True to make the verse heading bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'verse_heading' }, 'verse_heading_is_upper': { 'description': 'Force the verse heading to be uppercase', 'type': bool, 'default': True, + 'group': 'verse_heading' }, 'chorus_font': { 'description': 'The font for the choruses', 'type': str, - 'default': None + 'default': None, + 'group': 'chorus' }, 'chorus_size': { 'description': 'The size of the chorus in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'chorus' }, 'chorus_is_bold': { 'description': 'Set to True to make the chorus bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'chorus' }, 'chorus_indent': { 'description': 'How much to indent the chorus by, in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'chorus' }, 'chorus_heading_font': { 'description': 'The font for the chorus headings', 'type': str, - 'default': None + 'default': None, + 'group': 'chorus_heading' }, 'chorus_heading_size': { 'description': 'The size of the chorus heading in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'chorus_heading' }, 'chorus_heading_is_bold': { 'description': 'Set to True to make the chorus heading bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'chorus_heading' }, 'chorus_heading_is_upper': { 'description': 'Force the chorus heading to be uppercase', 'type': bool, 'default': True, + 'group': 'chorus_heading' }, 'bridge_font': { 'description': 'The font for the bridge', 'type': str, - 'default': None + 'default': None, + 'group': 'bridge' }, 'bridge_size': { 'description': 'The size of the bridge in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'bridge' }, 'bridge_is_bold': { 'description': 'Set to True to make the bridge bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'bridge' }, 'bridge_indent': { 'description': 'How much to indent the bridge by, in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'bridge' }, 'bridge_heading_font': { 'description': 'The font for the bridge headings', 'type': str, - 'default': None + 'default': None, + 'group': 'bridge_heading' }, 'bridge_heading_size': { 'description': 'The size of the bridge heading in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'bridge_heading' }, 'bridge_heading_is_bold': { 'description': 'Set to True to make the bridge heading bold', 'type': bool, - 'default': None + 'default': None, + 'group': 'bridge_heading' }, 'bridge_heading_is_upper': { 'description': 'Force the bridge heading to be uppercase', 'type': bool, 'default': True, + 'group': 'bridge_heading' }, 'chord_font': { 'description': 'The font for the chord', 'type': str, - 'default': None + 'default': None, + 'group': 'chord' }, 'chord_size': { 'description': 'The size of the chord in rem', 'type': int, - 'default': None + 'default': None, + 'group': 'chord' }, 'chord_is_bold': { 'description': 'Set to True to make the chord bold', 'type': bool, - 'default': True + 'default': True, + 'group': 'chord' }, 'chord_is_centered': { 'description': 'Make chords centered over syllables', 'type': bool, - 'default': True + 'default': True, + 'group': 'chord' } } +OPTION_GROUPS = set([option['group'] for option in HTML_OPTIONS]) def get_options(): @@ -290,7 +353,7 @@ def get_options(): return HTML_OPTIONS -def make_style(name, font=None, size=None, is_bold=None, is_centered=None, is_upper=None): +def make_style(name, font=None, size=None, is_bold=None, is_centered=None, is_upper=None, indent=None): """Build a CSS style""" styles = ['.{name} {{'.format(name=name)] if font: @@ -303,12 +366,30 @@ def make_style(name, font=None, size=None, is_bold=None, is_centered=None, is_up styles.append(' text-align: center;') if is_upper: styles.append(' text-transform: uppercase;') - styles.append('}}') + if indent: + styles.append(' margin-left: {indent}rem;'.format(indent=indent)) + styles.append('}') return os.linesep.join(styles) -def render(song, verse_upper=False, verse_name_bold=False, chord_bold=False): +def generate_option_styles(options): + styles = [] + for group in OPTION_GROUPS: + kwargs = {} + for key in STYLE_KEYS: + name = '{group}_{key}'.format(group=group, key=key) + if name not in HTML_OPTIONS: + continue + option = options.get(name, default=HTML_OPTIONS[name]['default']) + if option: + kwargs[key] = option + styles.append(make_style(group.replace('_', '-'), **kwargs)) + return styles + + +def render(song, options=None): """Render a song to HTML""" + styles = options and generate_option_styles(options) or [] rendered_verses = [] for verse in song.verse_order: rendered_lines = [] @@ -328,11 +409,4 @@ def render(song, verse_upper=False, verse_name_bold=False, chord_bold=False): title = song.metadata.get('title') or 'Song' metadata = TITLE.format(title=title, artist=song.metadata.get('artist') or song.metadata.get('composer') or '') body = metadata + '\n' + '\n'.join(rendered_verses) - styles = '' - if verse_name_bold: - styles += STYLES['verse_name_bold'] + '\n' - if verse_upper: - styles += STYLES['verse_upper'] + '\n' - if chord_bold: - styles += STYLES['chord_bold'] + '\n' - return HTML.format(title=title, body=body, styles=styles) + return HTML.format(title=title, body=body, styles=os.linesep.join(styles))