ChordPro / chordpro

Reference implementation of the ChordPro standard for musical lead sheets.
Other
325 stars 51 forks source link

Custom styles #404

Open gwyndaf opened 3 months ago

gwyndaf commented 3 months ago

Possible enhancement for the longer term.

Rationale

1) It's possible to use custom environments, such as {start_of_bridge} or {start_of_wittyrap} but, I believe, not possible to define how they should look in configs, because existing pdf.spacing and pdf.fonts sections are available only for standard song elements, not custom ones.

2) Used in markup, styles could help make character-level formatting simpler, more consistent and clearer in code. For instance, backing vocals might often be on the same line as lyrics, so must(?) be formatted using markup, not directive.

3) pdf.spacing and pdf.fonts item names aren't always consistent (e.g. lyrics and text). This might be an opportunity to rationalise that.

Possible implementations

1) Basic extension of current functionality. In pdf.fonts, allow arbitrary names to be created, such as pdf.fonts.bridge or pdf.fonts.footnote. When, say, {start_of_bridge} is used, ChordPro uses pdf.fonts.bridge if defined; if not defined, falls back to ? maybe fonts.text? Maybe there's a need to specify line spacing but - as that's relative to font size - it might be unnecessary, and could overcomplicate this as a 'basic' approach. Even if not defineable, it should be clear which spacing setting gets used by customs styles (maybe lyrics?).

2) Independent styles, which aren't hard-wired to existing song elements, but could be linked somehow. Possibly this might be an opportunity to consolidate typography settings in one place, e.g. both line spacing and font spec under the same item name. When referenced in a line-level context (e.g. a directive like {start_of_chorus: style=rousing}), all settings get applied; when referenced in a character-level context (e.g. markup), only font-specific settings are applied and line spacing ignored: for instance, for backing vocals, something like <span style=bv>, <sty=bv> or even <bv> to reference a bv style. In my own usage, I currently use the <i> short tag for BVs (because it's short and clean within the song source), but need to use preprocessor to convert into a more specific format (italic and grey). And anything that needs preprocessor handling seems like a good candidate for an enhancement! :)

I'm conscious that this thinking reflects only PDF usage. If it involves a major enhancement, I guess the ideal approach would be flexible enough to serve other output formats, such as HTML and LaTeX.

I'm sure the second of these in particular would need much more thought and work, so just raising this as a possible item for future consideration.

sciurius commented 3 months ago

I once experimented with section styles. When you open a section e.g. {start_of_bridge} ChordPro would look for a config setting pdf.section.style and use this for font, size, colour.

pdf.section.style {
    bridge {
        font: serif
        size: 12
        color: blue
    }
}

It was stalled for some reason, I don't remember why.

Your proposal sounds nice and logical. Being able to define a style as a set of properties (font/size/color/background/spacing) and then automatically apply the style to each section of the same name, or use a style= property to designate the style. Shouldn't be that hard to implement.

While pdf.fonts could be used for this purpose I wonder whether it is better to separate the font from the style. The term 'font' is already very overloaded.

gwyndaf commented 3 months ago

If it also includes attributes like line spacing, style would certainly be a more accurate term than font and, as you say, font is already doing a lot of work. Treating styles as a slightly separate area of functionality might also make it easier to experiment with in development without affecting existing spacing and fonts functionality.

There's maybe a question of what else might potentially be included in a style. If we consider chorus as a model, we've also got section-related attributes like indent and bar. I don't know whether they should be defined in the style or in the section. If I consider WP/DTP parallels, things like indent are generally included in style definitions (I'm cautious about encouraging users to think ChordPro is a WP/DTP tool but, if there are similar concepts, maybe it's clearer for users if their scope is similar).

And should styles include typographic nuances currently available only through markup but not fonts settings: things like rise or underline_color, for instance? I'm not necessarily advocating their inclusion, but simply that the styles mechanism is flexible enough to accommodate them in future, if necessary. This also makes me wonder whether character-level setting names should be consistent with markup: for instance, using face rather than font (which may also remove some ambiguity around 'font').

That is, it seems like there are potentially three(?) categories of style information, section-level, line-level and character-level, so I suppose the relevant style settings would need to be parsed and 'sent to' the relevant area of processing.

Your earlier section style example would certainly make sense for my suggested simple approach 1). However, it ties styles closely to sections, which could be limiting. I wonder if a slight change might leave the door open to a more flexible approach in future, like the more flexible/powerful approach 2). That is, if we change around the relationship, so style is an attribute of each section, for instance:

pdf.section.bridge {
    style {
        font: serif
        size: 12
        color: blue
    }
   indent: 30
}

That might simplify a future shift to something like

pdf.styles {
    bridge {
        font: serif
        size: 12
        color: blue
        spacing: 1.1
        indent: 30
        underline_color: #008800
    }
  }
pdf.section.bridge {
  style: bridge
  }

which could then also be used ad hoc like {start_of_prechorus: style=bridge}.

Possibly that same style could also be applied to specific characters (i.e. not section- or -line-level attributes like indent or spacing):

{start_of_chorus}
Some [C] regular chorus [G]stuff
Some <sty=bridge>special stuff</sty>
{end_of_chorus}

I'm probably inventing some strange and hypothetical scenarios, not necessarily as actual use cases, but simply to help conceive an initial approach to styles that allows maximum flexibility for future refinement.

sciurius commented 3 months ago

We're moving towards CSS, aren't we? Well, why not...

A ChordPro section corresponds to a <div> (display:block), and a textual span to a <span> (display:inline).

Properties for <span>:

Properties for <div>: all <span> properties, plus

A default chorus would be something like margin-left:-8; padding-left:8; border-left: 1px solid foreground.

The default class for a section is the section name; the following are equivalent:

{start_of_bridge}
{start_of_bridge class="bridge"}

style is then available for additional properties, e.g.

{start_of_bridge class="fancy" style="font-weight:bold"}

While this may seem a bit overkill at first, it has the advantage that it elegantly separates the appearance from the other configs, and many people will have already some basic experience with CSS.

Actually, when I started the ChordPro rewrite, my intention was to go HTML+CSS. Unfortunately there were (and, AFAIK, are) no decent tools to produce good PDF from HTML+CSS. ('good' meaning handling running headings and footers, toc generation, etc.)

gwyndaf commented 3 months ago

Ah, interesting idea. I hadn't really considered any particular syntax, and just assumed something like the the existing, for consistency. Mainly my thought was the general conceptual model of styles, as used in wordprocessing and DTP applications and, of course, CSS, which I hadn't considered.

Doesn't seem so much overkill as smart adoption of a familiar, mature, comprehensive and rigorous approach to styles, which many minds have already spent years considering, developing, and using. Even if initial ChordPro implementation is limited, as per your suggested <span> and <div> properties, the standards are there for any future growth.

I like your idea of that sections default to the class of the same name (if defined): it helps keep the song source clean, readable and portable.

Separating appearance (into a separate CSS or similar file?) makes sense. In fact, my own multiple configs can be roughly categorised as core, instrument, page layout and typography, which get combined in different ways for different purposes.

Perhaps another advantage/possibility: if, at some point, it seems useful or sensible to build a visual config editor into the GUI, there might be existing tools/code that can do that (or be adapted) for CSS.

Good point that CSS is fundamentally intended for on-screen/continuous output, rather than page-based print/PDF, so concepts like header/footer are alien. My HTML/CSS knowledge is rusty, but I think it at least supports pt sizes (not just px), so would support suitable measurement units for physical/page use (I've dealt with many designers who don't know their px from their pt!).

Might an intermediate approach be to support CSS syntax for body layout and typography, but retain existing page layout functionality like paper size, margins and pdf.formats: that already supports markup, so is there any reason it couldn't also support <span class=... > styles?

That way, perhaps existing PDF layout processes could maybe be retained, and styles (even if in CSS syntax) somehow translated into the equivalent spacing and font values?

sciurius commented 3 months ago

Good points!

CSS is fundamentally intended for on-screen/continuous output, rather than page-based print/PDF.

This is a common misconception. CSS3 has several selectors and property settings to deal with printed content. See e.g. src/ChordPro/lib/ChordPro/res/styles/print.css. It is just that noone seems to feel like supporting these...

sciurius commented 3 months ago

HTML is an relevant target anyway, since it allow for partial processing (e.g. process a section only). This may be interesting for live-updating editors.

Also (not related): A section can contain lyrics lines so we need to be able to specify styles for both the lyrics and the chords.

gwyndaf commented 3 months ago

Interesting point about styles to change chord appearance by section. My thinking arose largely from what's currently possible for chorus, and making similar format control available for other/arbitrary sections, and that's largely about lyrics format/layout.

That said, I use mostly inline chords, so their layout just follows lyrics. With above-line chords, I suppose their layout already does change, e.g. if chorus is indented.

I wonder if this raises questions about how things like inline-chords and (maybe?) chord-formats might be handled in a CSS model, if there was provision for the appearance of chords to vary by section.

For example, I currently have some subtle styling of chord delimiters for inline chords
(with base fonts.chord.color : #008800):

"inline-chords" : "<span weight=regular color=#228822>(</span>%s<span weight=regular color=#228822>)</span>",

So, I imagine each section style might need to allow for inline-chords (or equivalent) functionality. I wonder if this also has implications for 'conditional' chord formatting, for optional or passing chords based on suffixes like [Gm*] (as discussed in #352)

sciurius commented 3 months ago

Thanks for the suggestions. I now remember:

It was stalled for some reason, I don't remember why.

I realised that in the end, a section would become a mini-song (cf. the LaTeX minipage), complete with its own config. That was going a bit too far.

In practice, I think people will want the option for a section to have just a different font or colour. So it is sufficient to support the set of properties enumerated in https://github.com/ChordPro/chordpro/issues/404#issuecomment-2272971571.

div.bridge: {
    font-size: 120%;
}

This will affect all content (i.e, lyrics and fonts). To change only chord properties we can use a selector:

div.bridge chords: {
    font-size: 10;
}
gwyndaf commented 3 months ago

I agree that it's enough just to provide control over just a limited number of chord appearance properties. Your work on chord-formats is really useful, and it seems counter-productive to compromise/break that for implementing styles.

I see a couple of questions following from .chords styles:

1) For inline chords, should bridge.chords affect only the chord (%s value) or the whole inline-chords value?
I think the latter is preferable for immediate practical purposes. For instance, I currently use variations of green for chords and delimiters. If a chord colour is varied to blue in bridge.chords, it's probably more sensible that both chord and delimiters are made blue, rather than blue chords within green delimiters. However, I think it's important to retain the current approach to the default inline-chords, which lets me do some nice things :)

2) Should annotations also follow .chords formatting?
I don't think I have a strong view. I'm guessing that they'd vary from fonts.annotations according to what's defined in div.bridge.chords. So, with the example above, annotations would turn blue, like chords themselves. Maybe an annotations selector could be useful in future, but seems unnecessary complication right now.

Potentially, in future, more subtle colour control could overcome some short-term compromises. For example, my inline chord delimiters are the chord colour but a bit lighter, while annotations are a darker shade of the chord colour. Potentially, those 'lighter' and 'darker' adjustments could be part of the default config (or base style), so they're retained when a different colour hue is applied. This is my thinking behind #340, although I realise that's dependent on PDF::API2 functionality, so not an immediate prospect.

sciurius commented 3 months ago

This may be a good opportunity to remove some of the font overloading.

div.bridge chord affects chords only. div.bridge annotation affects annotations only. In either case, this is the whole formatting string, just as it is now.

div.bridge chord[inline] affects inline chords only. div.bridge annotation[inline] affects inline annotations only.

Hmm. Now it looks as if inline is a property... Well, why not:

div.bridge chord#foo { inline: "{{%s}}" }

with

{start_of_bridge}
[A]Hello, [<span id="foo">B</span>]World
{end_of_bridge}

This would produce

A
Hello {{B}}World

Nope, let's not do that ☺. Maybe just:

div.bridge chord { inline: "{{%s}}" }

with

[A]Hello [B]Hello
{start_of_bridge}
[A]Hello, [B]World
{end_of_bridge}

This would produce

A     B
Hello Hello
{{A}}Hello {{B}}World

But that's for later.

Can you confirm #399? Then I can fix the spreads and roll out 6.060.