SVG Export - clarifications and efficiencies

• Nov 20, 2015 - 18:22

This new topic is an attempt to consolidate some concepts and information regarding SVG export.
Background: MuseScore v1 used fonts in SVG. v2 changed that, so now the raw vectors (paths) are exported to the .svg file. This eliminates the need for specific fonts to be installed on the user's machine, which is good. It also bloats the .svg file because those raw paths are a lot more SVG text than a reference to a font glyph. This is especially evident with lyrics and other text in a score. MuseScore puts an entire segment's lyric's text into one SVG path element. I've seen a score title produce a path of Element::Type::TEXT with 37,000 characters in it, one long line of text in the .svg file.

Efficiencies: Recent pull requests by Heslil Inc. and myself make changes to the SVG Export code to offset the increased file size in v2. So far, we have eliminated group elements that needlessly wrap other elements, and excluded default value settings from the file. But the files are still large, and those paths are still long.
Two further efficiencies can be achieved:
1) Set scaling (magnification, "mag" variable) to 1:1 and change SVG transform="matrix(a,b,c,d,e,f)" to transform="translate(e,f)". Less text and generally simpler. The "S" in SVG stands for "Scalable", so generalized scaling of all the elements internally is unnecessary - this is not a printed page.
2) Remove transforms entirely for all polyline elements and images. This goes back to the way v1 exported SVG. If you're going to export glyphs, fixed vectors, then the basic way to go about it is to translate() to your location, then draw the glyph. But if you're drawing lines from x1/y1 to x2/y2, it makes more sense to simply add the translation coordinates to the line's x/y coordinates (the SVG polyline element's points attribute). Less text and generally simpler, also conceptually more in line with the no-font paradigm.
In my personal version of MuseScore master, I have coded both these changes and they work well so far.

CSS: Another possible efficiency is in the use of external CSS. In my own project I am doing this, and I've been able to reduce my .svg file sizes by an additional 10-15% with a 755 byte .css file. CSS mostly affects the polylines, not the paths, but it enables the elimination of many repeated attribute settings in the SVG file. Unfortunately it's not appropriate for the basic use of SVG files as self-contained images, clip art, so it's outside the scope of MuseScore for the foreseeable future.

<path> Elements: MuseScore uses SVG paths for two distinct sets of elements:
a) graphically fixed elements such as rests, note heads, accidentals, etc.
b) graphically variable elements such as slurs, beams, text, etc.
Nothing you can do about the variable-sized elements. The fixed elements used to be font glyphs in v1. There are two other ways to use references instead of the actual path text in SVG:
1. The <defs> element combined with the <use> element
2. The <font> element
Both these methods have one basic problem: defining all the paths in the defs or a font element will take up a fixed amount of space that might be very large compared to how many of those paths are actually used in the file, like a bulky header. For example, you define all the different types of note heads, but only use 3. The SVG export code might need to be intelligent enough to only define the paths that are used by the file.
Method #1 also suffers from the problem that use elements cannot be animated, which affects my current project (hopefully there are others who care about animation too). I am currently playing with method #2 to see if it might be viable and worth the trouble. Yes, it's a step back into using fonts, but without requiring any fonts to be installed. It's a win-win if it's plausible.

But what about text? It's not music, but there can be a lot of it in a score. MuseScore v2's no-font policy for SVG affects lyrics and other score text, the graphics over which MuseScore has the least control, because these are external fonts. It's an interesting feature in that it also bypasses font copyrights via SVG exports. I don't have a solution or a recommendation here. I need to investigate further with my own scores and see how it goes. It seems that text in a score might be best implemented as text in an SVG document. But a reference to an external font is inappropriate for standalone image files, so it's in the same category as CSS here.


Comments

I am not an expert in this area, so I I cannot really comment on this; I have a doubt, though:

As far I understand, some widely used SVG editors like InkScape (which is the default SVG editor in Linux distros) do not support CSS, or do not support it completely.

Would the use of CSS in SVG files by MuseScore affect the possibility to correctly read and edit them by those SVG editors?

In reply to by Miwarre

The feature/code described here is part of my pending pull request.
The use of external CSS in MuseScore is facilitated by the setting of a custom attribute, "data-type"; but that might possibly change to the "class" attribute, which happens to be super CSS-friendly.
Does Inkscape use the "class" attribute for any SVG elements? If it does, then there might be a conflict if MuseScore adopts the "class" attribute to capture the element type in SVG. It's unlikely that Inkscape has its own custom "data-type" attribute, but it is possible.
Other than that there is extremely low probability of any conflict. You can see below that I've attached my latest MuseScore test .svg file in pre and post Inkscape states, and they both render just fine. (Inkscape on Windows 64bit). Plus, no one has so far suggested that MuseScore start automatically using an external CSS file. I am using it and I am pleased with the results. I put it in the post because I was trying to cover all the available ways to make the files more efficient.

On a side note (warning: the following contains negative opinions of Inkscape): Have you ever peeked inside an Inkscape-generated .svg file with a text editor? It's a maze of stuff, much of which is labeled with the precursor to Inkscape's name (I can't remember it off the top of my head). Anyway, the files are full of all kinds of things that are unnecessary for rendering MuseScore and many other things in SVG, but they cover all the bases of possible things you might do in Inkscape. This bloats the file and renders it much more difficult to read in a text editor. I don't know how it might affect render-time performance. Try opening a MuseScore .svg file in Inkscape, then saving it with a new name from Inkscape. You will see the file change dramatically, and not for the better. I have attached my current testing file in pre and post Inkscape states. You'll see that the difference is dramatic on the inside. The file size is around 8% bigger, but the number of lines in the files goes from 122 to 839, a 588% increase. though now that I look at the file, most of the new lines are due to a more generous way of formatting the SVG elements - something that might be preferable to many people - so my complaint is probably overstated. A better comparison is a file generated by Inkscape from scratch, a file that uses all its definitions and has all its quirks.
The newer SVG generated by the latest pull request versus an InkScape file is an excellent comparison of two entirely different approaches to generating the contents of the .svg file: Maximalism (Inkscape) vs. Minimalism (MuseScore). IMO Minimalism is the obvious choice: smaller, easier to read files cannot be worse than larger files with all kinds of unnecessary definitions and legacy throwbacks.
I'm sure most of the major SVG editors do similar things to Inkscape. I imagine, for example, that Illustrator is even worse in this regard, but I don't have any direct experience there.

as an even greater aside: InkScape has a reverse coordinates system for the Y dimension vs. SVG, bottom-up. Doesn't that mess with your head? It messed with mine when trying to translate an Inkscape SVG into something more basic a few months ago.

Attachment Size
mm72.svg 80.09 KB
mm72-Inkscape.svg 86.66 KB

In reply to by sideways

Thank you for your explanations. Which however I am not sure to understand.

The generated file you supply opens correctly in Inkscape; I do not know the original score, but anything looks reasonable, so I assume it is read correctly (of course, also the Inkscape version opens correctly in Inkscape!).

However, it does not seem to me that this file uses any CSS in any way; it references an external MuseScore.svg.css file, which it is not there, and includes some references to classes, but also contains the drawing attributes explicitly in each path.

I assumed that using a CSS implied factoring out all these common attributes to a style sheet and removing them from the SVG. This would indeed significantly reduce the file size.

Whence my question: how would SVG editors not (completely?) supporting CSS deal with those "style-sheeted" SVG's?

Thanks, M.

P.S.: I never experienced any upside down drawing to/from Inkscape; so I tend to assume that it reads and writes SVG coordinates correctly. It uses indeed a bottom left corner coordinate origin in the user interface, which I find convenient (the y axis usually goes up in mathematics...).

P.P.S.: The Inkscape version of the file includes additional Inkscape-specific info to reconstruct user interface elements like window size, grid settings and so on. It has been probably saved as "Inkscape SVG"; by saving as "Plain SVG" all these additional info are stripped and the file size is smaller.

In reply to by Miwarre

Whence my question: how would SVG editors not (completely?) supporting CSS
deal with those "style-sheeted" SVG's?

I do not know. It would depend on whether they could at least read the CSS, even if they don't write it.
re: reverse coordinates - the SVG coordinate system is top-down for the Y axis. Inkscape is bottom-up. It resolves this in the file somehow, I forget the mechanism. You may like the bottom-up arrangement, but it is the reverse of SVG and very odd for an editor that is focused specifically on SVG.
re: "Plain SVG", that's interesting. I didn't know about it. My experience with InkScape was very mixed. The Inkscape SVG I dealt with was all tied in with a bunch of definitions that I'm not sure how the "plain" feature would remove. But that's really neither here nor there relative to MuseScore.

A quick comment to set a frame to the whole project.

1/ By default, MuseScore should create SVG files that are highly compatible with any browser or editor and any use case. If the size is a bit larger, so be it. In 2.0.2 we don't have class or data-type. To me, the addition of this feature would be enough even if we wouldn't have optimized the size of the SVG.

2/ We do have options for PNG export (resolution mainly) and I'm not opposed to have options for SVG export if they make sense. If @sideways, you coded SVG+CSS (or SVG+VTT) export, why not put it in MuseScore. Even if it's only accessible via command line (mscore file.mscz -o file.svgc would create file.svg+file.css for example), it's better to have the code out there for maintainability reason. Idem for font vs path.

3/ We also need to keep in mind that SVG export is also used for part of the score via the Image capture tool.

One last thing The SVG export code might need to be intelligent enough to only define the paths that are used by the file. Unfortunately, that it would mean it's not possible to edit the SVG with the same font if you need other characters.

In reply to by [DELETED] 5

1/ By minimizing the content in the SVG file we are increasing the combatibility. Less content = less compatibility problems.

2/ I'm not opposed to options either. I am fine with putting this stuff in MuseScore. It's not complete yet, so I'm not suggesting it now. I'm also not familiar with the command line in MuseScore, so I'm not yet thinking along those lines.

- re: "One last thing": You would simply need to add those additional paths to the <font> element in the file. If you're going to get your hands dirty messing around with the SVG text, you might as well go all the way. And I'm only thinking of doing this for the music symbols, note heads and such, not text. Text is a whole other ballgame.

In reply to by [DELETED] 5

"Unfortunately, that it would mean it's not possible to edit the SVG with the same font if you need other characters.". This applies to any SVG, from whatever source, once font glyphs have been converted to paths, which is a common operation.

If peoples editing a generated SVG need other glyphs in addition to the ones already used (and included) in the SVG, Emmentaler can also be provided as an SVG "font" as an extra; it is one more maintenance operation, but with, for instance, FontForge it is rather quick and simple.

For other fonts, not provided by MuseScore, I think it is not MuseScore responsibility.

In reply to by Miwarre

I also thought about an external SVG font file. It has promise, but like CSS it doesn't fit the standalone image paradigm, so it would be unavailable for that common usage of SVG files. And if you're going to have an external font file, why not the actual TTF or PostScript file? If the font file is referenced in the SVG you can use any font, whether or not it's installed in the OS. That's the tactic I'm going to pursue with SVG text.

I don't understand your first paragraph. Do you mean once the SVG is loaded into a browser, during run-time? Or are you talking about updates to the source font which require changes to the downstream SVG font? You can always regenerate the SVG from MuseScore to get the latest glyph updates translated into paths. And you can always copy/paste new paths into the <fonts> element in the SVG file and re-render it. What am I missing?

In reply to by sideways

I'm starting to get the sense that there are two basic paradigms for SVG Export in MuseScore:
1) Standalone images
2) Files with external references to CSS and/or Fonts.
It's based on the intended use of the file as a standalone image versus a full-blown, web-based score/part.

So an options dialog might look something like this:
[radio button] Embed fonts and styles
[radio button] Use external fonts and styles

In reply to by sideways

You might be able to get away with just programming the export for "greedy" SVGs (i.e. ones that include all information anyone could ever wish for for later editing of the SVG) and then use a standard SVG optimization library to make the file standalone and strip out everything that is unnecessary for regular users who just want a scalable image. The stripped SVG would be the default export option, but there would be an option to skip the optimisation process to preserve the extra information.

In reply to by sideways

I'll try to be short, as these are details.

I brought the SVG "font" example for not as a dependency of the SVG generated by MuseScore, to be associated to it in any case. But just as a collateral, optional, tool for peoples who want to edit an existing SVG generated by MuseScore and need some additional glyphs not already present in the SVG as a shape because not used in that particular (fragment of) score.

By text-to-path I intended the (often manual) operation of editing an SVG by replacing font glyphs with their outlines coded as paths, often precisely to remove the dependency from an external font, among other reasons.

Regarding CSS - I ran into an interesting, but not uncommon situation: rehearsal marks with a frame around them. Other types of text can have frames too, and there are other situations, like double-barlines, where a single MuseScore element has two or more graphic elements in SVG. In the case of framed text, SVG text has no concept of a border, and neither does the path element used to render it in today's MuseScore, so it must be a separate element.

In today's code these framed rehearsal marks are each 2 path elements in SVG, one for the text, the other for the frame. This relates to CSS styling as follows:

>The path element for the text is a shape, with fill=color and stroke=none
>The path element for the frame is a line, with fill=none and stroke=color

If you style by class="RehearsalMark", you cannot distinguish between the text and the frame because they're both paths, so using the fill and stroke attributes will screw up your graphics. fill="blue" will fill both the inside of the frame and the text with blue. stroke="blue" will put an extra blue outline around the text.
And there's no sub-type in MuseScore to distinguish between the two, though class Text does encapsulate the frame information, so it might be possible to sub-type it in SVG.

This is where I yearn for the old days of font-based text, where the text would be an SVG text element and the frame would be a path element (though it might be better as a rect or a circle - SVG rects have rounded corner attributes, just like MuseScore). It just so happens that I like to use apply both color and frames to my rehearsal marks.

So where can I find the v1.3 source? The MuseScore repository only has v2.x branches available, and musescore-old is all v0.x branches. Where is the v1 source? Is there at least a zip file somewhere? Or did svggenerator.cpp not exist prior to v2? But still, where is the code that decides whether to call SvgPaingEngine::drawPath() or drawPolygon()? Was there ever a drawText() function? I have v1.3 installed on my machine, and I generated an SVG file with it just now. It does use <text> elements for note heads, for example. Did Qt handle all of that automagically in v1?
If I could find the v1.3 source I wouldn't have so many questions... thx

In reply to by Marc Sabatella

You might be right. I had a similar thought, but I couldn't be sure, and I saw that the directory tree for the project was reduced to just a single mscore folder and that was too radical a change for me to trust as only one version back. Now that I have the 1.3 source I can see that I was wrong on that one - 1.3 has the same single directory.

But if master is 1.3, then where are v1.0, 1.1, and 1.2?

Anyway, problem solved, kind of. I have the 1.3 source, I see that there is no svggenerator.cpp, I see saveSVG() in file.cpp (it has the same for loop twice in a row at the end which is strange). But I still don't see how v2 changed from fonts/text to paths? Is it the text-to-path that @Miwarre refers to? I tried searching the MuseScore source for variations of text-to-path and found nothing. Is there a more proper spelling?

I see in text.cpp that Text::draw() has changed, but that seems to be about the frames. In the end, the new code calls TextFragment::draw(), which calls QPainter::drawText(). How does that call to drawText() end up calling SvgPaintEngine::drawPath(), where I have code that is definitely executing. Where is that change? What is that change?

Is this the answer to all my questions?
void QPaintEngine::drawTextItem(const QPointF & p, const QTextItem & textItem)
This function draws the text item textItem at position p. The default implementation of this function converts the text to a QPainterPath and paints the resulting path.
So somehow, by creating SvgPaintEngine as a subclass of QPaintEngine, Qt is now directing all it's QPainter::drawText() calls to QPaintEngine::drawTextItem(), which has this default behavior of converting the text to a path then drawing it via drawPath(). Why wasn't that the default behavior happening in v1.3? It's hard to tell from the Qt docs if QPainter::drawText() converts to a path or not, but I would assume that it uses QPaintEngine.
Regardless, I see now that I can override drawTextItem() and go from there. Unless v1.3 was doing it a better way that wasn't just a Qt default behavior...

In reply to by shoogle

What advantage would that provide? What attributes would the elements inside the group inherit? The only one I can see is the transform=translate(). The stroke and fill are definitely not shared by the text and the frame, so none of the fill-xxx or stroke-xxx attributes would apply either.

I believe that there might be various legitimate uses of groups that could exist inside MuseScore SVG. But coding those elements to be inside groups while other elements remain group-less is not necessarily straightforward. And wrapping everything in a group is excessive, as the vast majority of elements, when previously wrapped in groups, were alone in those groups. >99% of the old group wrappers were around single elements, Staff lines being the primary exception.

An interesting example is the SlurSegment. All the SlurSegments in a system have the same translate() coordinates. They are unique in this way, though Beams are similar, but only for the Y axis. The translate() X coordinate is constant, the start of the system, in all the SlurSegments. This is the case even in my piano-roll-style scores with one long system: all the slurs in a single system are drawn relative to the same x/y coordinate position. So it would be ideal to stick all those slurs in a group with a single transform=translate() attribute. But to do so would require knowing which system a slur is in, which is not straightforward in the code.

In reply to by sideways

For rehearsal marks it would seem to make sense that the border should move around with the letter if I open it in an SVG editor. The letter wouldn't be alone in the group because the border of the rehearsal mark would also be in the group.

However, there are probably many elements that could be considered part of a larger whole (e.g. if I move a staff should the notes move too?), so if grouping them all is too difficult/unnecessary then don't bother.

In reply to by shoogle

Ah, the other frontier I can't seem to avoid: editing the MuseScore-generated files in an SVG editor (as opposed to a text editor). I think basic compatibility with SVG editors, as defined by being able to open the MuseScore-generated file and edit it, is about as far as we can expect to go in terms of friendliness towards these editors. MuseScore is a one-way SVG street: write-only. You can always group the items once you have the file open inside your SVG editor, they all have that feature: problem solved.

btw - your idea would, of course, apply to any text with a frame, and I think every text element in MuseScore has a potential frame - the Text Properties dialog is the same for all of them

also btw - I meant Staff, not System in my post above: all slurs within a staff (not a system) have the same translate() coordinates.

also, to be crystal clear: SlurSegments are not grouped by staff in v2.0.2, each SlurSegment is wrapped in its own group like everybody else. The grouping is done by Qt via SvgPaintEngine::UpdateState(). The types of MuseScore elements that generate groups with more than one path/polyline inside are MuseScore elements represented by 2+ graphic elements. Double bar lines, for example. Otherwise it's a 1:1 relationship, SVG groups to MuseScore elements.

In reply to by sideways

After further coding, I've found some additional details:
Double bar lines are not grouped together via UpdateState, barline.cpp, BarLine::draw() actually calls QPainter::drawLine() multiple times, thus the "state" changes for each polyline within the BarLine.
Beams (paths) and Tuplet lines (not text) are grouped with the same "state", as are staff lines. Other than that I have not come across other examples of multiple items grouped together by Qt, though I my sample scores do not have all the element types present.

In reply to by sideways

I have succeeded in reverting to fonts for text and notes/rests/etc. One "improvement" over v1.3 SVG text elements using the MScore font: v1.3 exported the actual character, which no text editor knows how to display, and which might be invisible in some editors. I am now exporting those private-use-range unicode characters as XML entities, like &\#xE050; the Treble Clef character. (ignore the backslash between & and #, it helps me to display the entity text almost correctly here).

I wanted to attach a full set of files to demo a MuseScore SVG with external CSS and fonts, but I can't upload a .css file here. I can post a zip if anyone is interested

I have completed this work, except for one item which I am still editing manually in the SVG text. I am attaching some example files for your perusal. This site won't let me attach a .css file or font files, so I'm attaching it as a zip. Some things to note:

- This score exported by 2.0.2 is 1,473,788 bytes. My new .svg file is 175,015 bytes. That's 88.5% smaller, 1/9th of the original size. The .css file is 1,461 bytes, and it serves as a common style sheet for all my scores.

- If you do not have the FreeSerif font installed on your machine the text will not display properly. That's by design for the various tests I'm performing. I have included the mscore.ttf and .eot files so that font does not need to be installed. I have found that these are the two types of files most required by the major browsers. I created the .eot file using an app called eotfast.exe, which converts .ttf to .eot.

- One of the improvements I did not mention in my first post on this topic is the reduction of the quantity of staff line polyline elements in the file. Staff lines are drawn by measure, so there are 5 polylines per measure, not per staff. Many of my scores are piano-roll style, one long system, and that could be 5 polylines, but instead it's 5 x MeasureCount. Now that the scaling is out of the picture and the code is only translating, the math for this change is butt simple: first measure's x2 = last measures x2. That's it. So this is a change I can make easily manually at the moment, and it eliminates 315 polylines from the file. I think I can code this, and will give it a try soon, but for now it's an easy manual change to a score.

- The "data-cue" attribute and the SMAWS 2.1 reference are my sheet music animation project, you can ignore them. The cues are for WebVTT synchronization.

- You may think I'm nuts, but I spent a bunch of time building applications for financial analysts, and I like to see numbers line up vertically on the page/screen. It really wasn't much work doing the fixed formats you see inside the .svg file, and if I need to debug an SVG file while working on this animation, all this formatting saves me time/headache finding problems. Some of you are concerned with compatibility with SVG graphical editors, I'm concerned with how it formats in a text editor.

Attachment Size
BWV_988_IV.zip 130.14 KB

In reply to by Jojo-Schmitz

Ok, thanks! I'll, check that out. IE is the one that likes .eot files, I believe. I always test first in Firefox and Chrome - Chrome is the SVG-friendliest of all the browsers. IE is generally the weirdest.

...after some testing/investigating: This is definitely an issue with the .eot file. If I use ASCII text in those note head text elements I see the characters in a default serif font. And if I use the same CSS with HTML, not SVG, I have the same issue. So it's considering the MScore font not found or not usable in some way. It might be that EOT doesn't deal properly with the Private Use Area in unicode, which is where the MScore font's characters are.
I have used this same CSS markup to include fonts elsewhere, and it does work in IE11 with other fonts, so this is not an SVG-specific issue, it's an EOT/font issue. I just downloaded Microsoft's own WEFT web font embedding tool, but it's some wizard-based thing that wants to analyze my websites and change my html for me. It doesn't seem to simply convert font files or provide more transparent help on this issue.
Damn, it's always something obscure... I'll post a fix when/if I find one. Thanks for testing it!
Here's someone else having a similar problem, though 3.5 years ago, and no real solution provided: http://stackoverflow.com/questions/10747144/getting-ie-to-use-private-c…
Here is a page that purports to test the PUA (private use area) support in your browser, but it looks worse in Firefox than Chrome or IE11 (on my machine), and it generally looks kind of empty: http://www.fileformat.info/info/unicode/block/private_use_area/utf8test…
I'm wondering if for IE this font needs the characters moved down (visually up in FontForge) in the unicode character set. But that would also mean a different SVG file for IE, because the character values there would have to change too.

Does anyone else have experience displaying the MScore font in Internet Explorer?

In reply to by Jojo-Schmitz

I have tried two things to solve this, one of which works:
1) The bad news: I tried moving the characters down by 0xC000 and 0xD000 in the character set, putting them in more normal ranges. Worked in Chrome/Firefox, same problem in IE.

2) The good news: I created a brand new font in FontForge, set encoding to Full Unicode (which is way huger than I imagined), and copied/pasted glyphs from mscore.sfd to my new mscore-web.sfd in exactly the same slot in the Private Use Area. I then generated a .ttf file and the .eot file, and ¡Abracadabra! the font displays in IE.

So to get the Mscore font to work in IE I copied all of the glyphs into this new file and it works. Attached is a zip file with the updated font files/css file (I didn't want to use the same font file name, though I am using the same font-family name in CSS).

I don't know what the difference is with the new .sfd file. Maybe there's a setting that can be changed in the existing font to make it compatible with IE, I do not know. Maybe it's because mscore.sfd doesn't define the full Unicode character set, or it's "encoding" is incomplete in some way. My new font does not include the ".notedef" or other 3 final characters in the original file that are in the 0x10000 range. I only copied from 0xE000 to 0xEBB0, the triple-dot character.

btw - I believe this is why LilyPond uses .png files to display their fonts on the web.
See here: https://fonts.openlilylib.org/#font-catalog
and here: https://fonts.openlilylib.org/font-specimen-viewer.html

Attachment Size
BWV_988_IV.zip 129.91 KB

In reply to by Miwarre

Yep, because it doesn't know about the font, it just sees a character code.
So you have to be cautious using external references in your SVG files if you want to open them directly in Inkscape and have them be editable. Even if you have the font installed, the reference to the font family is only in the CSS, not in the SVG. SVG is just a class=value to define the formatting.

excuse the edit- I just saw the measure number 5 looks OK. That's because it's Element::Type::Text, which is so generic that I can't style it in CSS (yet). So the font-family and font-size are specified in the SVG file. It would appear that you have the FreeSerif font installed on your machine, either that or it's not FreeSerif, but the fact that it's an alphanumeric ASCII character renders it legible.

I never said external references were for everyone. For me, they're a bonanza.
Thanks for continuing to test this!

In reply to by Miwarre

I tested it against Scribus, and a similar result. I do not know of an SVG editor that specifically handles CSS, but I'm more of into text than graphics. Have you tried the google svg-edit? It's the most basic of all of the SVG graphical editors I've tried, but I didn't like the way it ran inside the browser. It might have CSS capabilities - chrome certainly does.
Depending on the rest of your deployment situation, you could, for example probably take advantage of external font files, the way MuseScore v1.3 did; but not external CSS. Maybe internal CSS, that could be an option. You could try that with a manual edit to these SVG files if you like. I'm not coding anything inside MuseScore to generate the CSS, that's all manual, and it's really a one-time thing, not an every-time-you-export thing.
I don't know if Inkscape would deal with CSS embedded in the SVG. But if you're really into the smaller file size, then give it a try. It seems to me like the best hope you've got of getting something to work.

In reply to by sideways

While I certainly would not be against any significant reduction in SVG file size, this is not my major concern in the area. I use MuseScore-generated SVG only rarely and, in all occasions so far, to embed musical examples in the LibreOffice text files I use for the front matters of my scores, sometimes with an intermediate editing step in Inkscape. I believe not to be the only MuseScore user with such a work-flow.

So, I am glad to learn that this .CSS research is primarily an exploration and there is no plan to incorporate it in MuseScore, because at the moment it seems it would limit the usability of MuseScore-generated SVG files in several contexts.

Thanks for the effort you spend on this! M.

In reply to by sideways

What problems might occur by drawing/painting StaffLines first, prior to Symbols, Text, InstrumentNames, and SlurSegments?

I just completed the coding of the staff line element reduction. I did it as a separate first pass, placing StaffLines first in the draw order. To me this makes sense, as even "blank" music paper has the staff lines printed on it. But MuseScore has these 4 items before staff lines in the normal draw order, so I would like to know what problems might arise from doing this in the SVG.

btw, the reduction in the BWV_988_IV file was 630 lines, not 315, for 2 staves in the system. I have coded this to function properly on multi-system and multi-page scores, and it all added up to only 14 lines of code. For my piano-roll-style scores the savings are the most extreme, but it doesn't hurt to optimize it everywhere, especially when it's easy to implement.

In the continued spirit of eliminating the unnecessary from these SVG files, I have now completed the total removal of all transforms. The only elements still using them were the paths, such as Beams and SlurSegments. The solution was the same as previously, adding the translate coordinates to the x/y values in the SVG element, in this case all the Move, Line, and Circle coordinates inside the path data (d="path data" attribute).

Attached is that same Bach piano variation, now weighing in at 158,778 bytes, an 89.2% reduction from the 2.0.2 file size. But more than the file size, I can now fully see every line of text in the file except the 8 rehearsal mark paths, without having to scroll horizontally. The max width of one of those lines is 152 characters. Finally, a truly human-readable .svg file of sheet music; I'm very pleased.

Regarding the rehearsal marks, or more generally, frames around text: These are long path definitions just to draw a circle when SVG has a circle element where all you specify is the center x/y and the radius. And SVG rect elements have rounded corners too. I focus on this because I've noticed that my rehearsal marker text is never well centered in the frame. I can understand why this happens, font characters have spacing definitions too, and the character may not be centered in the glyph canvas. The worst problem is that my rehearsal markers generally use ALL CAPS, and the fonts all have extra space below the letter to account for lower-case y and j.
So I'm going to specify text-anchor="middle" for these text elements and position them in the center of the circle or rectangle, instead of positioning the circle or rectangle around the text. I will also endeavor to convert these path elements to circles or rects, but that's the lesser priority. I will only be modifying svggenerator.cpp for this, as I don't want to screw up the way these are drawn elsewhere in MuseScore.

Attachment Size
BWV_988_IV.zip 131.39 KB

In reply to by sideways

Rehearsal Marks & their Frames: Is this one of the reasons people import their scores into Inkscape or LibreOffice, to fix and/or add rehearsal markers?. I've never been happy with the way MuseScore frames my rehearsal marker text, especially if that text is a single capital letter. The frame rectangle is too tall and not wide enough. If I set the Text Margin, the frame simply scales in the same proportions. This in addition to the lack of proper centering of the character within the frame.

So I did an awful, but effective, thing: I hardcoded my frame height, set a minimum frame width, and just for grins, set the frame y coordinate to be constant (this only works for scores with a single system per page, so I'm going to have to change that later). Then I coded the rehearsal marker text to be text-anchor:middle in my CSS and set the code to center the text horizontally. The text y coordinate is fixed relative to the frame.

I've attached the results, which look really good and 100% consistent. A final solution requires eliminating the hard coding of the frame's y coordinate, and setting the frame height relative to the font size (in pixels). I'll get to that soon enough. But for me it's a major improvement over the MuseScore positioning and sizing of both the text and the frame. It's unfortunate that it's such an element-type-specific tweak, but I'm fussy about the way things look. And I know I'm not alone in that fussiness, which is why I ask if Rehearsal Markers are one of the things people do in outside editors.
The updated zip file includes the bold version of the Linux Biolinum font, which is unfortunately a large-ish OTF file, and it's large-ish EOT. Please excuse the inflated size of the zip file

Attachment Size
BWV_988_IV.zip 650.45 KB

In reply to by sideways

At least in my case, no, rehearsal markers are not a reason (I very rarely use them); I import SVG's from MuseScore in text documents when I need a few music excerpts (typically one or two measures of one part) in a prevalently textual context, usually editorial / critical notes in the front matters of a score. Occasionally this requires a passage through Inkscape to 'fine tune' the image to suit the typographic context / layout.

In reply to by sideways

I've never really used rehearsal marks in MuseScore, but if I did I would probably just stick with how they look by default and wouldn't bother changing them.

The SVGs are looking fantastic BTW! I opened them with a text editor and the improvement is enormous! I have just a couple of points:

1. Strange spacing/indentation of tags

There is some funny spacing/indentation for certain elements that doesn't seem to achieve anything (not even in terms of readability). For example, there are lots of spaces before pretty much every "x" tag:

<text class="Text"          x= "3902.8944" y=  "16.3464">60</text>
                  ^^^^^^^^^^ why the spaces here?

2. Embedding fonts and styles for a standalone file

You probably know this already, but to make the file standalone you can simply embed the font inside the CSS stylesheet, and embed the CSS inside the SVG. Here's your example file with the CSS and fonts embedded: BWV_988_IV_embed.svg (note the size is now 258 KB)

Embedding fonts in CSS

To embed the font files (or any file) inside the CSS simply replace references to file paths with a small preamble (which includes the file's mime type - e.g. "application/x-font-ttf") followed by the raw bytes of the file in base64 format:

In the CSS, replace

"mscore-web.ttf"

with

"data:application/x-font-ttf;base64,<<BASE64_RAW_BYTES>>"

Where "<<BASE64_RAW_BYTES>>" is the bytes of "mscore-web.ttf" encoded in base64 format (it will look like a seemingly random string of letters, numbers and a few symbols). There are converters online and also available by default in the command line on all platforms, including Windows!.

Common mime types for fonts:

Format Mimetype
.woff  application/font-woff
.ttf   application/font-ttf
.eot   application/vnd.ms-fontobject
.otf   application/font-otf
.svg   image/svg+xml

I noticed that you are using TTF and EOT formats, but perhaps you should consider using the compressed WOFF format for a smaller file size and better cross-platform compatibility.

Embedding CSS in SVG

Just like in HTML, simply put the CSS inside <style></style> tags anywhere in the SVG between the opening and closing <SVG> tags. Be aware that any text inside the CSS needs to be SVG safe (i.e. HTML/XML safe). E.g. use "&lt;" instead of "<".

The resulting file is standalone (so safe for distribution) but benefits from all of your optimizations and so is many times smaller than the SVGs produced by MuseScore 2.0.2.

Sizes for comparison:

  • Your SVG: 157kB (236kB with necessary fonts and CSS file)
  • Your SVG with fonts embedded (a.k.a "my SVG"): 258kB (standalone)
  • MuseScore 2.0.2 SVG: 1.44MB (standalone)
Attachment Size
BWV_988_IV_embed.svg 257.97 KB

In reply to by shoogle

Interestingly it appears that Inkscape supports embedded CSS even though it doesn't support linked CSS from a separate file.

Unfortunately it doesn't support fonts unless they are properly installed on the user's system (i.e. not linked or embedded but actually installed).

How they look in Inkscape:
linked.png
embedded.png
paths2.0.2.png

In reply to by shoogle

The extra spaces in text elements prior to the "x" attribute are to align all the text elements' attribures. Scroll down to the bottom of the file, where all the animated text elements are, and you'll see how things line up.

Yes, regarding embedded fonts/css, I have mentioned this possibility previously. I'm not interested in it, but it might be useful for standalone files.

I use TTF/EOT because I've read various articles about how to declare fonts in CSS and this is how I've narrowed it down to the bare minimum. This should work on all the major browsers. WOFF support is patchy. IE requires EOT.

Now that I check it again it appears that WOFF is better supported than I remembered. It looks like even IE supports it. I'll have to give it a try, thx

In reply to by sideways

You're welcome.
My point about the embedded fonts and CSS is that it would probably be welcome for inclusion in the main MuseScore code, even as the default option for all users. It would enable you to use the same code for standalone SVGs as you are already using for non-standalone files with just a very slight modification to save the fonts and CSS internally instead of externally.

The spacing of the "x" attributes makes sense at the bottom of the file where the length of the previous attribute makes it necessary for alignment, but it's not as important at the top of the file because there are a whole load of other elements in between that don't have "x" attributes. But if you want them to like up anyway then I guess that's up to you. The size difference would probably be negligible if the SVGs were compressed.

In reply to by shoogle

It's too much trouble to format some SVG text elements one way and others another way, so the indentation is everywhere. The addition to the file size is negligible even without compression. It's effectively a fixed-width field for class= in all SVG text elements (and the RehearsalMark rect elements that I've hacked together). No, this is not a feature I would include in MuseScore, it's for the animation I'm doing, where I like to be able to examine the animated elements in particular. They are all grouped together at the end of the file and sorted chronologically. In case you care, the data-cue (WebVTT cue ID) is formed by start tick + "_" + end tick for that element The ticks are converted to start and end times in milliseconds in the VTT file.

Embedded CSS in MuseScore SVG Export poses one major problem: How do you know what CSS to generate? I have handwritten my CSS. Generating it programmatically would require pulling data from the MuseScore text styles and lines (stroke-width is one of the main properties I use in CSS, they are always the same, unless you're scaling...). Not impossible, but a solid bit of work.

I think you'll notice that without the embedded CSS, if you use default colors like black for most things, the file-size reduction with all the efficiencies I've achieved is close to your embedded CSS doc.

Embedded fonts is easier, since the MuseScore fonts are fully available and basically static constants. Too bad your editor doesn't support the feature. External fonts' characters would still be drawn by path elements.

In reply to by Marc Sabatella

1) Easier said than done, especially if you care about how the font actually looks
2) MuseScore uses the full bounding box for the glyph, which for most fonts includes extra height for special characters and accents on capital letters, e.g. ÀÉÏÑ, stuff like that. Add to that the extra space below for p and y.
3) It's not the character size so much as it's position within that bounding box. Even in monospace fonts the letters are different widths, it's the bounding box/spacing that's the same. You're essentially recommending that I use a monospace font, but that doesn't solve the problem, even if I could find one I liked for this purpose.

To summarize: it's easier and better looking if I pick a font I like and center it using a little CSS and a little hardcoding of margins. But more than anything, there is no font in existence which solves the problem, even monospace fonts. It's a function of the way fonts are constructed, combined with the way MuseScore measures the size of the text.

In reply to by sideways

Fair enough. You just asked if rehearsal marks were a driving force behiond why people might import graphic versions of their scores into other programs, and at least the answer for me is, certainly not. There are many reasons I might create graphics of my scores, but rehearsal marks don't make the cut - the way the boxes look just doesn't seem a big enough issue to even consider going to all that trouble.

That said, again, *if* I cared, another option would be to add *two* rehearsal marks at each point. One a regular letter, the other a simple fixed size box character. Or a graphic if I couldn't find one I liked. Or use Inkscape to design my own set of pre-composed rehearsal marks, then add them to my score as graphics. One could even pre-populate a custom palette with these. Or use FontForge to create a customized version of my chosen font, with boxes around the letters. In fact, I suspect that's probably the best way to go; then you can take full advantage of the built-in semantics of rehearsal marks (appearing in all parts, breaking mmrests, automatic sequencing, the Find facility, etc).

In reply to by Marc Sabatella

Thanks for your interest and your input. I can still take advantage of all the functionality of rehearsal marks. All I do differently is export them to SVG using a semi-hardcoded format. I use MuseScore's horizontal position, then fabricate the rest myself.
I'm trying to produce an album of animated SVG scores that look consistent, and I want to be able to produce the content files automatically. This is the price I'm paying for consistency and customization. But it's all SVG, none of it affects any other aspect of MuseScore. Generally I edit a score in a stable version of MuseScore, then if I want to export it to SVG I open it in this version of MuseScore. In that sense I use two editors too, but both of them are MuseScore.

fyi - This is an album of songs, and I use rehearsal marks to indicate song structure. So, for example, I'll have a sequence of markers like this: A1 B1 A2 C etc. I rarely get past the letter C. Sometimes, for analytical purposes, I'll sub-divide the sections like this: A1.0 A1.1 A1.2 B1 A2.0 A2.1, etc. Sometimes I deviate from my scheme and use lower-case names, like "intro". A custom framed font wouldn't handle all of that.
My vertical position is fixed in case I screw that up in MuseScore, my height is adjusted around my font size, I use CSS to center the text, and I set a minimum width - it's really not that complicated :-) As you might have gathered from my preference for editing SVG in a text editor, I prefer reading/writing code to editing graphic objects graphically. I feel I can be more precise that way (more trustworthy than my eyes), and I can automate it so that I don't have to think about it ever again.
In the animation player I have timeline navigation rulers that show bars and markers (I am used to the Pro Tools name for them, not "rehearsal marks"). I am building a navigation similar to that used in DAWs like Pro Tools, one not based on min:sec, but more related to the musical structure. Rehearsal marks in MuseScore are the equivalent functionality to markers in ProTools, at least in my mind.

I am in the process of creating a new pull request to replace the old one that I just closed. It will include 6 improvements to SVG Export. I want to run them all by people before I commit them:
- No more group wrappers for anything
- Default values not written to the file
- class="TypeName" (it seems that @lasconic and I both like it this way. If you prefer data-type="TypeName", speak now or forever hold your peace)
- No more transforms
- Staff lines drawn once per system, not once per measure
- 1:1 Scaling - In today's code, the scaling in file.cpp/saveSVG() is to scale to 300dpi, which is based on the SvgGenerator being a "printer" and this being a pdf-style print job. SVG is scalable, it doesn't require pre-scaling.

That sums it up. Please let me know if you foresee a problem with implementing any of these.

NOT INCLUDED: CSS, fonts, cue IDs, extra-fancy text field alignment. If/when these get included in MuseScore it will be as options in the UI or command line.

In reply to by sideways

Here are two more details to consider regarding the class attribute before I create a pull request with all 6 of these changes:

1) Element sub-types will not be included in this release. I think they need more thought and implementation experience prior to choosing a scheme to implement in MuseScore. The scheme I am currently using may or may not be what I end up using. I don't have experience with enough different scores yet. And the other proposals raise issues for me, on top of the fact that I have no specific use-cases beyond my own today. So for this release all BarLine elements will appear as class="BarLine" in the SVG. It's a solid step in the right direction, and sub-types will figure themselves out as specific needs to use them arise. The code to get the class value is encapsulated in a separate function, getClass(), in svggenerator.cpp, so sub-typing is ready to be encapsulated inside there.

2) Regarding the preference for lower case expressed by @lasconic: I prefer to leave the MuseScore type names unchanged, whatever case they're in. It's simpler and more consistent. Unless the case of those names is subject to change inside MuseScore, in which case it would be best to be case-insensitive.

Unless there is some disagreement about this, I will go ahead with the pull request in the next day or two. Thanks!

In reply to by sideways

Apologies for the trickle of information, but as I integrate these changes into the MuseScore master I continue to run into changes that may or may not suit MuseScore:

As part of the elimination of pre-scaling the SVG, I have changed the units in the svg element's width and height attributes from millimeters to pixels. If you look at the top of a 2.0.2 SVG file, you'll see the <svg> element. Inside there you'll see width="123mm" height="45mm". My code changes that to width="678px" height="90px".

I don't think this should cause anyone any problems. I also think it is better this way, especially in relation to the viewBox attribute, which is specified in the same code and the same line of SVG text. In pixels, the width and height of the svg element and its viewBox are the same. The only difference is that internally, the viewBox uses floating point, while the document's width and height are integers. In my code I use the viewBox numbers because they're more precise, so why not?

Here is the difference (float vs. int) in the MuseScore code (file.cpp):
printer.setSize(QSize(w * mag, h * mag));
printer.setViewBox(QRectF(0.0, 0.0, w * mag, h * mag));
mag = 1 now, because there's no more scaling.

If anyone foresees a problem with switching from mm to px here, please let me know. Thanks.

In reply to by sideways

Attached is a sample file generated by the pending pull request code. It's one of my two-bar examples.

If any of you have specific scores that you would like to see converted it could provide me with some test cases. Feel free to post a link to a score on musescore.org and I'll endeavor to download it and convert it.

One more question, which is not crucial to this pull request, but is something to consider moving forward:
The file headers all include these two lines:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg ... xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny">

The entire first line is optional, and all the values being set are the same as the defaults. I'm not so sure about the second line, but I'm wondering if it's necessary or 100% correct. Does anyone have any insight into these settings? I'm thinking that they are simply Qt values that MuseScore inherited, but it would be good to know.

Attachment Size
mm72.svg 77.84 KB

In reply to by sideways

regarding the second line of header quoted above:

xmlns="http://www.w3.org/2000/svg" This is absolutely necessary for the browser to render the SVG.

xmlns:xlink="http://www.w3.org/1999/xlink" This is for XLink and links to external resources from within this file. By default, a MuseScore SVG has no reason for this. Even if gradients do get implemented, and they use internal urls, this is not required in the header. If someone wants to muck around in their MuseScore-generated SVG file and add external links, they should feel free, and they can add this to the header in the process. Unless there is some other form of external resource that I do not foresee, this is unnecessary for MuseScore.

version="1.2" baseProfile="tiny" This ties the document to SVG Tiny 1.2, which is oriented specifically to mobile devices with small screens. Do we really want to do that? From what I'm reading it seems that ican do more harm than good, including compatibility with IE and limited feature sets elsewhere. It seems to me that if MuseScore seeks to be as compatible as possible, these settings should simply be omitted. I can't even find "Tiny" on caniuse.com and a google search for "svg tiny 1.2 browser support" yields little useful information - for example.

In reply to by sideways

In the process of cleaning up my own headers I've bumped into various bits of code in file.cpp/saveSVG() and svggenerator.cpp that should be updated. I will include them in the pull request. They are:

• svggenerator.cpp - Initialization of the SvgPaintEnginePrivate class, resolution = 72;. Now that element.h is included, the Ms namespace is available, and in line with the 72dpi changes this should be: resolution = Ms::DPI;.

• file.cpp/saveSVG() - 2nd line of code sets this very same property to its default value: printer.setResolution(DPI); This is unnecessary.

• svggenerator.cpp/SvgGenerator::metric() - I don't know who or what might call this function, but it is using a literal 25.4 value in a couple of places and that should be replace by the new constant DPMM. For example, this line of code:
return qRound(d->engine->size().height() * 25.4 / d->engine->resolution());
should change to this:
return qRound(d->engine->size().height() / Ms::DPMM);

These are not in the pull request, they are recommendations for a future clean-up of the file:

• svggenerator.cpp - struct _attributes{} - it's only really used for the title and description, but there's a bunch of code to deal with all the other structure members. And given that it's only two members, it hardly merits a separate struct. title and description could easily lose the struct wrapper.

• file.cpp and svggenerator.cpp - size and viewBox are the same, why do we need to set both? I don't know if there's some Qt process underneath the covers that needs them both, but if there isn't, then we should eliminate size, which is the one that is no longer used in the pull request code.

• svggenerator.cpp/qfontToSvg() - This is not used in MuseScore or my font-based code. I'm using an override of drawTextItem(), in the same group of functions as drawPath() and drawPolygon(). Many of unused members of the attributes structure (mentioned above) appear here. This function is a prime candidate for deletion, as it is generally outdated and has no matching drawTextItem() implementation to deliver actual results.

• svggenerator.cpp - Text Codecs. SvgPaintEngine::end() has this code:
#ifndef QT_NO_TEXTCODEC
d->stream->setCodec(QTextCodec::codecForName("UTF-8"));
#endif
The first line of the current SVG header (that I'm lobbying to remove) is:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
If a Text Codec is actually defined, this might cause problems. In my code, I'm removing that first line of SVG header, as mentioned previously, and I'm removing the #ifndef around the call to QTextStream::setCodec, so that it always uses the SVG default, UTF-8. I recommend doing the same thing here. If you want to integrate other text codecs, that can be done later, but right now MuseScore only supports UTF-8 regardless.

In reply to by sideways

The pull request has been made. See issue #86686 for details:
https://musescore.org/en/node/86686#comment-398701
--
One last thing I have had to change to make my piano-roll-style scores work: Increase the limit on the width of the page from 2,000mm to 200,000mm. An semi-arbitrary increase, because there's no reason to limit it at all in the paperless world. 2,000 was definitely too small, and I can't imagine getting near 200,000, so that's where I stuck it. I could have switched to inches, then it would be a 2,000 inch limit, but I didn't want to deal with the rounding errors discussed previously in this thread.

This change is in mscore/pagesettings.ui. I'm not going to include it in the pending pull request, but I wanted to note it as an issue and a simple fix. There are large format printers that can print from a roll of paper/plastic and their horizontal limit is a lot more than 2 meters too, FWIW.

Do you still have an unanswered question? Please log in first to post your question.