rism-digital / verovio

🎵 Music notation engraving library for MEI with MusicXML and Humdrum support and various toolkits (JavaScript, Python)
https://www.verovio.org
GNU Lesser General Public License v3.0
683 stars 185 forks source link

Change text font #722

Closed vitto32 closed 5 years ago

vitto32 commented 7 years ago

I'm trying to change the default font (Times.xml) but I have no clue where to start from. I've tried using saxon9ee on an svg font but i get and empty xml.

craigsapp commented 7 years ago

I change the font after typesetting via CSS:

svg tspan {
   font-family: simonetta, VerovioText !important;
}

For example:

http://www.tassomusic.org/work/?id=Trm1049a&k=e

(Notice that the text of the page and the music matches).

The size of the font is close to Times, but it would be nice to be able to typeset with a font other than Times (and when generating PDF with the PDF toolkit, the resulting font substitution has a smaller font size).

vitto32 commented 7 years ago

Thank you @craigsapp . That's the approach I'm already using (through CSS) and I changed the font size in style.h (DEFAULT_LYRIC_SIZE) to get smaller lyrics.

What I'd like to do now is to set the font metrics to match a less condensed font. I think Times.xml in data folder is the key but I don't know how to generate a similar file with a different font. I've tried the tools in the font folder but they seems more targeted to notation fonts (Bravura etc..).

lpugin commented 7 years ago

The Times.xml (and related files) where generated offline and include the bounding boxes of some text characters. The font itself is not included and is always the one provided by the SVG viewer. For this reason it makes sense to do this only with a standard fonts, such as Times. (Otherwise, the font is quite likely to be missing and will be replaced by something else, which will cause layout problems. Already with Times this is not exactly the same in all plateform / browsers). We can look at including other standard fonts in the same way (e.g., Georgia, Arial) but this will make sense to have a styling mechanism in place.

Another option is to include the font itself (or a font url) but this raises other issues.

sonovice commented 5 years ago

@lpugin I am forced to use a specific font for a project. Could you please consider adding your script for calculating the Times.xml to the repo? That would be super helpful!

sonovice commented 5 years ago

I guess, the script was/is somewhat similar to the generate-bbox.js in the fonts folder. Unfortunately, it relies on the abandoned "phantomjs". I'll try to find some time in the near future to come up with something more general.

lpugin commented 5 years ago

This is a general feature request

th-we commented 4 years ago

To summarize what has to be done here:

First step would be to support just one alternative text and "music text" font, respectively.

@lpugin Can we re-open for discussion? We want to work on this.

sonovice commented 2 months ago

I recently had to use a custom font for the text so I came up with my own python script to generate the bounding boxes needed by Verovio. Maybe it is of use for others.

I also had to replace all occurrences of the font name Times with VerovioText (random choice) in the code base and load my custom font with that name via CSS when using the SVG output in the browser.

The script only depends on fonttools - No PhantomJS or other browser-based rendering needed.

from pathlib import Path

from fontTools.ttLib import TTFont
from fontTools.pens.boundsPen import BoundsPen

def main(args):
    output_dir = args.output_dir
    output_dir.mkdir(exist_ok=True)

    with open(output_dir / "VerovioText-Regular.xml", "w") as f:
        f.write(extract_glyphs(args.regular_path))

    with open(output_dir / "VerovioText-Bold.xml", "w") as f:
        f.write(extract_glyphs(args.bold_path))

    with open(output_dir / "VerovioText-Italic.xml", "w") as f:
        f.write(extract_glyphs(args.italic_path))

    with open(output_dir / "VerovioText-BoldItalic.xml", "w") as f:
        f.write(extract_glyphs(args.bold_italic_path))

def extract_glyphs(font_path: Path):
    font = TTFont(str(font_path))
    glyph_set = font.getGlyphSet()
    glyph_order = font.getGlyphOrder()
    name_to_unicode = font["cmap"].buildReversed()
    bp = BoundsPen(glyph_set)

    result = '<?xml version="1.0" encoding="UTF-8"?>\n<bounding-boxes font-family="VerovioText" units-per-em="2048">\n'

    for glyph_name in glyph_order:
        if glyph_name not in name_to_unicode:
            continue

        bp.init()
        glyph = glyph_set[glyph_name]
        glyph.draw(bp)
        bbox = bp.bounds
        if bbox is None:
            continue

        x_min, y_min, x_max, y_max = bbox
        width = x_max - x_min
        height = y_max - y_min
        if width == 0 or height == 0:
            continue

        for unicode in name_to_unicode[glyph_name]:
            c = hex(unicode)[2:].upper()

            result += f'  <g c="{c}" x="{float(x_min)}" y="{(float(y_min))}" w="{float(width)}" h="{float(height)}" h-a-x="{float(glyph.width)}" />\n'

    result += "</bounding-boxes>"

    return result

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="Extract glyph bounding boxes from font files.")
    parser.add_argument("output_dir",                type=Path, help="Path to output directory. Usually <verovio-root>/data/text/")
    parser.add_argument("-r",  "--regular_path",     type=Path, help="Path to regular font file.",     required=True)
    parser.add_argument("-b",  "--bold_path",        type=Path, help="Path to bold font file.",        required=True)
    parser.add_argument("-i",  "--italic_path",      type=Path, help="Path to italic font file.",      required=True)
    parser.add_argument("-bi", "--bold_italic_path", type=Path, help="Path to bold italic font file.", required=True)

    args = parser.parse_args()
    main(args)
sonovice commented 1 month ago

There are still some details to be covered when using a custom text font. The most apparent one is "incorrect" placement of dashes in lyrics: image

In order to move them up to be roughly in the middle of the font's x-height the (for now constant) multiplier has to be adjusted in https://github.com/rism-digital/verovio/blob/fd201a9635f207ba34e38746d0d7359a0bbdc219/src/view_control.cpp#L1330 Example:

- y += (m_options->m_lyricSize.GetValue() * m_doc->GetDrawingUnit(staff->m_drawingStaffSize) / 5);
+ y += (m_options->m_lyricSize.GetValue() * m_doc->GetDrawingUnit(staff->m_drawingStaffSize) / 3.7);  
lpugin commented 1 month ago

Thanks! Would you be able to make a PR?

sonovice commented 1 month ago

Thanks! Would you be able to make a PR?

This is too hacky to be a comprehensive solution for now. A few things are still missing:

If the fonts are not included, there are some other issues:

I could help with the Python stuff and make it a proper script that outputs all needed data. However, I am not really familiar with Verovio's actual codebase (or C in general), so I am a bit lost here. Also, this would introduce a hard dependency on Python with the fonttools package.

EDIT Fonts could potentially be included by encoding them in base64 and including them in Verovio's CSS style block:

@font-face {
  font-family: 'VerovioText';
  font-style: normal;
  font-weight: 400;
  src: url(data:font/woff2;charset=utf-8;base64,<base64_encoded>) format('woff2');
}