Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scope the CSS included in the SVG #3953

Merged
merged 5 commits into from
Feb 17, 2025
Merged

Scope the CSS included in the SVG #3953

merged 5 commits into from
Feb 17, 2025

Conversation

lpugin
Copy link
Contributor

@lpugin lpugin commented Feb 13, 2025

Limit the scope of CSS style in the SVG through svg@id, namely with

<svg id="de1cn3h">

and then

#de1cn3h g.syl {
   opacity: 0.0;
}

Also remove xmlns:mei from the svg, which is not necessary but make the svg invalid.

No change expected

@samuelbradshaw
Copy link
Contributor

samuelbradshaw commented Feb 13, 2025

Will this make it harder to override CSS styles in sheet music? Adding the ID will increase the specificity of the CSS rule, making it harder to create a general CSS rule for all rendered sheet music in a website's stylesheet. !important is always an option for overriding pre-set styles, but best practice is to avoid !important unless necessary. Maybe a class or data attribute could be used to scope the CSS rule instead of an ID?

@craigsapp
Copy link
Contributor

I don't think it will be a problem. If there is a single SVG on the page, it definitely will not be a problem if your CSS does not use the svg ID in the selectors. Even if you have multiple SVG images, you would not give the ID in your external stylesheet so that it would apply to all SVG images (i.e., as any current implementation would handle it):

g.syl {
   opacity: 0.0;
}

Internal CSS is added using verovio's --svg-css option, in which case it is useful to prevent other SVG images from seeing the CSS.

If you use <img> to load an SVG, then the internal CSS of the SVG cannot be seen by other SVG images on the page. But external CSS cannot affect the SVG inside in img element, so no dynamic highlighting, for example.

For inserting the SVG image directly into the HTML code, the internal CSS for the svg can otherwise be seen by all other SVG images on the page, which in some cases may cause conflicts between styles in different images (which is what this enhancement is for).

The use of the ID in the CSS selector would be useful if there is internal CSS in the SVG image that should apply to only a single SVG image. I am wondering how the SVG ID could be coordinated with the internal CSS in that case? Or would the IDREF for CSS in the SVG be automatically applied (which I suspect that this commit does not do?). The easiest way would probably be a parameter input to verovio when rendering to set the svg ID, such as --svg-id de1cn3h. If no --svg-id is given, then either there would be no ID added to the svg image, or a random one could be applied as currently.

@lpugin
Copy link
Contributor Author

lpugin commented Feb 13, 2025

Here is some SVG generated with --svg-css 'g.syl{ opacity: 0.0; }'. As you can see, the #dr3gobx css rule is added to both the common css and the custom css. I would not expect this to be a problem, but it might be good to test it.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="2100px" height="2970px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" overflow="visible" id="dr3gobx">
   <desc>Engraved by Verovio 5.1.0-dev-da5157a</desc>
   <defs>
      <symbol id="E990-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M0 -97v147c0 35 37 52 76 52c41 0 84 -19 84 -54v-149c-6 6 -11 10 -11 10c-12 8 -28 14 -64 14h-5c-32 0 -43 -2 -58 -10c-11 -6 -22 -14 -22 -14v4z" />
      </symbol>
      <symbol id="E995-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M36 -58c-18 0 -30 -6 -36 -18v162c0 20 10 30 30 30h1c34 0 92 -14 108 -52v-180c-12 44 -67 58 -103 58z" />
      </symbol>
      <symbol id="E996-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M4 -93c0 0 -4 6 -4 10v140c0 42 45 45 72 45h9c19 0 63 -4 63 -47v-448c0 -3 -2 -8 -6 -8h-5c-3 0 -6 5 -6 8v311c-11 7 -29 12 -51 12h-4c-34 -1 -63 -17 -68 -23z" />
      </symbol>
      <symbol id="E997-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M6 -401c-4 0 -6 5 -6 8v448c0 43 44 47 63 47h9c27 0 72 -3 72 -45v-140c0 -4 -4 -10 -4 -10c-5 6 -34 22 -68 23h-4c-22 0 -40 -5 -51 -12v-311c0 -3 -3 -8 -6 -8h-5z" />
      </symbol>
      <symbol id="E9B5-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M0 300h13v-450h-13v450z" />
      </symbol>
      <symbol id="E9BA-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M0 104c100 -149 297 -328 467 -355h10c22 0 37 15 44 45v-176v-18h-24c-70 0 -154 35 -251 103c-110 78 -192 151 -246 219v182z" />
      </symbol>
      <symbol id="E9BE-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M0 300h13v-450h-13v450z" />
      </symbol>
      <symbol id="E906-6lq4mu" viewBox="0 0 1000 1000" overflow="inherit">
         <path transform="scale(1,-1)" d="M181 -131c0 -21 -3 -36 -10 -46c-11 -17 -33 -25 -64 -25c-47 0 -77 9 -91 28c-11 15 -17 47 -17 94v43c0 13 0 26 1 37v41c0 13 0 26 -1 39c0 48 5 79 16 94c14 19 45 28 92 28c32 0 53 -8 64 -24c7 -10 10 -26 10 -47c0 -28 -9 -47 -26 -58c-13 -8 -33 -12 -58 -12h-23 c-7 0 -14 0 -21 1c-17 0 -26 -4 -26 -11v-51v-51c0 -8 8 -12 24 -12c4 0 11 0 22 1s19 1 25 1c25 0 44 -4 56 -11c18 -11 27 -30 27 -59z" />
      </symbol>
   </defs>
   <style type="text/css">#dr3gobx g.page-margin{font-family:Times,serif;} g.ending, g.fing, g.reh, g.tempo{font-weight:bold;} g.dir, g.dynam, g.mNum{font-style:italic;} g.label{font-weight:normal;} path{stroke:currentColor}</style>
   <style type="text/css">#dr3gobx g.syl{ opacity: 0.0; }</style>
   <svg class="definition-scale" color="black" viewBox="0 0 21000 29700">
      <g class="page-margin" transform="translate(500, 500)">
         <g id="mzpdn2s" class="mdiv pageMilestone" />
         <g id="sybk5z9" class="score pageMilestone" />
         <g id="s12c860x" class="system">
            <g id="seyf69s" class="section systemMilestone" />
            <g id="s1f5awmz" class="staff">
               <path d="M0 540 L852 540" stroke-width="13" />
               <path d="M0 720 L852 720" stroke-width="13" />
               <path d="M0 900 L852 900" stroke-width="13" />
               <path d="M0 1080 L852 1080" stroke-width="13" />
               <path d="M0 1260 L852 1260" stroke-width="13" />
               <g id="kb2t54j" class="keySig" />
               <g id="l7g0txr" class="layer">
                  <g id="c1k09lm9" class="clef">
                     <use xlink:href="#E906-6lq4mu" x="90" y="720" height="720px" width="720px" />
                  </g>
                  <g id="spd1yhz" class="syllable">
                     <g id="s1tfjgxb" class="syl">
                        <text x="311" y="1752" font-size="0px">
                           <tspan id="t1htylje" class="text">
                              <tspan font-size="405px">Be</tspan>
                           </tspan>
                        </text>
                     </g>
                     <g id="n1aeg39u" class="neume">
                        <g id="n1tt2x7e" class="nc">
                           <use xlink:href="#E990-6lq4mu" x="311" y="1170" height="720px" width="720px" />
                        </g>
                        <g id="n1cnpfi6" class="nc">
                           <use xlink:href="#E990-6lq4mu" x="426" y="1170" height="720px" width="720px" />
                        </g>
                        <g id="nb59jm4" class="nc">
                           <use xlink:href="#E996-6lq4mu" x="532" y="1080" height="720px" width="720px" />
                        </g>
                     </g>
                  </g>
               </g>
            </g>
            <g id="scmu1ik" class="staff">
               <path d="M852 540 L1325 540" stroke-width="13" />
               <path d="M852 720 L1325 720" stroke-width="13" />
               <path d="M852 900 L1325 900" stroke-width="13" />
               <path d="M852 1080 L1325 1080" stroke-width="13" />
               <path d="M852 1260 L1325 1260" stroke-width="13" />
               <g id="l1k1vi1t" class="layer">
                  <g id="skb7ysu" class="syllable">
                     <g id="sclc1qi" class="syl">
                        <text x="852" y="1752" font-size="0px">
                           <tspan id="t1e6n56j" class="text">
                              <tspan font-size="405px">ne</tspan>
                           </tspan>
                        </text>
                     </g>
                  </g>
               </g>
            </g>
            <g id="s1kz1z5h" class="staff">
               <path d="M1325 540 L1775 540" stroke-width="13" />
               <path d="M1325 720 L1775 720" stroke-width="13" />
               <path d="M1325 900 L1775 900" stroke-width="13" />
               <path d="M1325 1080 L1775 1080" stroke-width="13" />
               <path d="M1325 1260 L1775 1260" stroke-width="13" />
               <g id="lf4xa3e" class="layer">
                  <g id="s1leipow" class="syllable">
                     <g id="sz6i8m7" class="syl">
                        <text x="1325" y="1752" font-size="0px">
                           <tspan id="t1drdv8s" class="text">
                              <tspan font-size="405px">di</tspan>
                           </tspan>
                        </text>
                     </g>
                     <g id="nz3kco4" class="neume">
                        <g id="n1qxhlnm" class="nc">
                           <use xlink:href="#E990-6lq4mu" x="1325" y="990" height="720px" width="720px" />
                        </g>
                        <g id="n183o28n" class="nc">
                           <use xlink:href="#E996-6lq4mu" x="1431" y="900" height="720px" width="720px" />
                        </g>
                     </g>
                  </g>
               </g>
            </g>
            <g id="s7odwrx" class="staff">
               <path d="M1775 540 L2225 540" stroke-width="13" />
               <path d="M1775 720 L2225 720" stroke-width="13" />
               <path d="M1775 900 L2225 900" stroke-width="13" />
               <path d="M1775 1080 L2225 1080" stroke-width="13" />
               <path d="M1775 1260 L2225 1260" stroke-width="13" />
               <g id="l4w3w0w" class="layer">
                  <g id="s8t3kb7" class="syllable">
                     <g id="s14jvcsi" class="syl">
                        <text x="1775" y="1752" font-size="0px">
                           <tspan id="t69pgs3" class="text">
                              <tspan font-size="405px">ci</tspan>
                           </tspan>
                        </text>
                     </g>
                     <g id="n1uoj2zd" class="neume">
                        <g id="nilw7t8" class="nc">
                           <use xlink:href="#E997-6lq4mu" x="1775" y="990" height="720px" width="720px" />
                        </g>
                        <g id="ndxtacy" class="nc">
                           <use xlink:href="#E990-6lq4mu" x="1869" y="1170" height="720px" width="720px" />
                        </g>
                     </g>
                  </g>
               </g>
            </g>
            <g id="s1xawr38" class="staff">
               <path d="M2225 540 L3202 540" stroke-width="13" />
               <path d="M2225 720 L3202 720" stroke-width="13" />
               <path d="M2225 900 L3202 900" stroke-width="13" />
               <path d="M2225 1080 L3202 1080" stroke-width="13" />
               <path d="M2225 1260 L3202 1260" stroke-width="13" />
               <g id="l133gn09" class="layer">
                  <g id="s15d3qt9" class="syllable">
                     <g id="smkczen" class="syl">
                        <text x="2225" y="1752" font-size="0px">
                           <tspan id="t13z7rpf" class="text">
                              <tspan font-size="405px">te</tspan>
                           </tspan>
                        </text>
                     </g>
                     <g id="n5benki" class="neume">
                        <g id="nm4c1p9" class="nc">
                           <use xlink:href="#E995-6lq4mu" x="2225" y="1080" height="720px" width="720px" />
                           <use xlink:href="#E9BE-6lq4mu" x="2225" y="1238" height="720px" width="720px" />
                           <use xlink:href="#E9BE-6lq4mu" x="2316" y="1251" height="720px" width="720px" />
                           <g id="lnfzerc" class="liquescent" />
                        </g>
                     </g>
                     <g id="n8k2b0e" class="neume">
                        <g id="n8pf5rc" class="nc">
                           <use xlink:href="#E9B5-6lq4mu" x="2525" y="1260" height="720px" width="720px" />
                        </g>
                        <g id="n19xd4m3" class="nc">
                           <use xlink:href="#E9BA-6lq4mu" x="2525" y="1080" height="720px" width="720px" />
                        </g>
                        <g id="npng4r9" class="nc">
                           <use xlink:href="#E990-6lq4mu" x="2891" y="1170" height="720px" width="720px" />
                        </g>
                        <g id="n1713vny" class="nc">
                           <use xlink:href="#E990-6lq4mu" x="2997" y="1260" height="720px" width="720px" />
                        </g>
                     </g>
                  </g>
               </g>
            </g>
            <g id="s1kcvza2" class="systemMilestoneEnd seyf69s" />
         </g>
         <g id="px1looh" class="pageMilestoneEnd sybk5z9" />
         <g id="p1oepedl" class="pageMilestoneEnd mzpdn2s" />
         <g id="pyvcout" class="pgHead autogenerated" />
         <g id="pctzpbc" class="pgFoot autogenerated">
            <g id="fkeiut" class="fig">
               <g class="svg" transform="translate(7750, 28099) scale(10.000000, 10.000000)">
                  <g>
                     <path fill="#00000" d="M 17.11278,49.26451 V 10.367554 h 12.696511 c 3.288815,7.336676 6.71267,14.610456 10.127281,21.888976 3.419998,-7.484684 6.255515,-14.204887 9.58887,-21.909915 2.436207,0.103726 4.316952,0.08453 6.484294,-0.05808 V 49.264423 H 50.274275 C 50.145222,38.35639 50.016136,27.448368 49.887066,16.540329 45.003488,27.315459 39.967448,38.433564 35.083854,49.208695 34.678996,49.343717 34.426583,49.135619 34.021726,49.270644 29.46588,39.330716 24.910049,29.390805 20.354201,19.450876 20.224341,29.388756 20.09461,39.326619 19.964815,49.264497 H 17.11278 Z M 36.754144,39.324695 C 32.721221,30.512695 28.6883,21.700679 24.655394,12.888662 22.530527,12.750737 22.656775,12.71031 20.714356,12.813974 v 0.966497 c 4.612938,9.99784 9.609439,20.748709 13.87298,30.081745 0.722248,-1.512523 1.444528,-3.025014 2.166808,-4.537521 z m 25.738417,9.939815 c 0,-12.965651 0,-25.931305 0,-38.896956 10.084388,0 20.168791,0 30.253178,0 0,0.960416 0,1.920849 0,2.881265 -6.482825,0 -12.965652,0 -19.448478,0 0,4.802097 0,9.604178 0,14.406277 5.52241,0 11.044819,0 16.567229,0 0,0.960415 0,1.920832 0,2.881248 -5.52241,0 -11.044819,0 -16.567229,0 0,5.282306 0,10.564611 0,15.846917 6.963035,0 13.926068,0 20.889103,0 0,0.960416 0,1.920832 0,2.881249 -10.564597,0 -21.129207,0 -31.693803,0 z m 4.321874,-2.160937 c 0,-11.765131 0,-23.530263 0,-35.295394 -0.720312,0 -1.440625,0 -2.160937,0 0,11.765131 0,23.530263 0,35.295394 0.720312,0 1.440625,0 2.160937,0 z m 32.41413,2.160937 c 0,-12.965651 0,-25.931305 0,-38.896956 2.401045,0 4.802095,0 7.203135,0 0,12.965651 0,25.931305 0,38.896956 -2.40104,0 -4.80209,0 -7.203135,0 z m 2.881265,-2.160937 c 0,-11.765131 0,-23.530263 0,-35.295394 -0.72031,0 -1.44064,0 -2.160953,0 0,11.765131 0,23.530263 0,35.295394 0.720313,0 1.440643,0 2.160953,0 z" />
                     <path fill="#00000" d="m 346.3,6.7 c -3.1,0.5 -4.9,4.4 -2.7,6.8 1.2,1.4 2.7,2.4 4.1,3.5 -2.4,5.3 -7.7,8.3 -11.9,12.1 -3.7,2.8 -7,6.2 -9.7,9.9 -2.2,-0.8 -0.3,-6.8 -0.6,-9.5 0.1,-3.1 0.7,-6.4 2.9,-8.7 1.3,-1.9 3,-3.5 4.5,-5.2 1.2,-3.4 -3.2,-4.9 -5.8,-4.6 -7.3,-0.5 -14.6,2 -20,6.8 -5,4.6 -10,10.1 -11.5,17 -0.9,3.5 -0.2,7.5 2.8,9.9 2.5,2.8 7.4,2.9 9.8,-0.2 1.7,-2.3 3.2,-4.8 3.8,-7.6 -0.1,-2.6 -4.4,-2.3 -4.3,0.2 1.2,2.4 -0.9,4.7 -2.7,6.2 -1.7,0.9 -4.7,0.7 -4.8,-1.7 -1,-5.8 2.1,-11.3 5.2,-16.1 4,-5.8 9.8,-11.2 17.2,-11.8 1.6,0 8.1,-1.2 6.8,1.4 -2.8,2.8 -5.6,6.1 -6,10.3 -1.6,7.7 0.1,15.7 -2.3,23.2 -1.1,3.9 2,2.6 2.5,0.2 3.3,-7.4 8.9,-13.4 15.1,-18.5 4.2,-3.9 9.5,-7.1 11.9,-12.6 1.3,-3.6 1.2,-9 -2.7,-10.9 -0.6,-0.2 -1.2,-0.3 -1.8,-0.3 z m 66,14.5 c -2,0.8 -1.8,4.9 0.8,4.4 3.4,0.4 2.9,-6.2 -0.8,-4.4 z m 0.6,7.3 c -1.3,0.5 -7.6,0.5 -5.4,2.2 4,-0.3 0.7,4.5 0.6,6.7 -0.4,2.9 -2.4,5.9 -1.2,8.8 2.7,1.7 5.6,-0.8 6.8,-3.2 1.6,-1.4 -0.5,-3 -1.2,-0.9 -0.4,0.8 -2.4,3.5 -2.6,1.8 1.1,-5 2.7,-9.9 3.9,-14.9 l -0.1,-0.5 h -0.9 z m -65.1,0.2 c -6.5,1.6 -10.5,9.5 -8.2,15.7 2.1,3.4 7.4,2.9 10.1,0.4 2,-0.2 3.6,-5.3 1,-3.4 -1.4,2 -4,4.2 -6.5,2.8 -2.2,-1.9 -1.9,-5.7 1.7,-5.3 3.1,-0.9 6.9,-2.1 8.1,-5.5 0.9,-2.7 -1.6,-5.2 -4.3,-4.8 h -1.1 -0.7 z m 13.6,0 c -2.2,0 -8.5,1.8 -2.9,2.3 0.9,1.9 -1,4.9 -1.1,7.2 -0.5,2.9 -1.4,5.7 -1.7,8.7 3.7,0.7 3.8,-2.6 4.4,-5.3 0.9,-3.3 1.6,-7 4.1,-9.5 2.4,1.6 6.8,-0.7 3.7,-3.4 -2.9,-0.5 -4.8,2.4 -6.2,4.3 0,-1.2 2.4,-5 -0.2,-4.3 z m 15.8,0 c -6.4,1.3 -9.6,9.1 -7.7,14.9 1.9,4 7.7,3.9 11,1.7 4.1,-3 6,-8.9 4.4,-13.7 -1.3,-3 -4.8,-3.2 -7.6,-3 z m 17,0 c -2.3,0 -8.8,1.9 -3,2.4 0.4,2.5 -1,5.7 -1.4,8.4 -0.7,2.4 -1.8,7.5 2.1,7.2 3.8,-0.1 6.5,-3.5 8.5,-6.3 2,-3.2 4.5,-7 3.3,-10.9 -3.7,-3 -3.6,2.6 -3.1,5.1 -0.7,3.8 -2.7,7.9 -6.2,9.8 -3.6,0.6 -1,-4.3 -0.8,-6.1 0.6,-3.2 1.6,-6.3 1.9,-9.5 -0.4,0 -0.8,0 -1.1,0 z m 30.6,0 c -6.3,1.2 -9.5,8.9 -7.8,14.7 1.5,4 7.2,4.2 10.5,2.4 4.6,-2.8 6.6,-9.3 4.8,-14.3 -1.4,-2.8 -4.8,-3 -7.5,-2.8 z m -75.4,1.7 c 2.1,1.5 0.2,4.7 -1.6,5.7 -1.1,0.4 -5.5,3.4 -4.6,0.7 0.8,-2.7 2.2,-6.2 5.3,-6.6 l 0.9,0.1 z m 30.8,0 c 2.6,1.5 1.3,5.2 0.8,7.6 -0.8,2.7 -1.9,5.9 -4.7,7.1 -5.1,0.8 -4,-5.8 -2.8,-8.8 1,-2.8 3,-6.8 6.7,-5.8 z m 47.1,-0.1 c 2.9,0.7 2,4.7 1.5,6.8 -0.7,3 -1.8,6.7 -5,8 -4.7,0.8 -4,-5.1 -3,-7.9 0.9,-3 2.7,-7.3 6.5,-6.8 z m -88.1,5.9 c -0.3,1.3 0.9,-0.6 0,0 z m -1.1,2.4 c -0.2,0.5 0.5,-0.1 0,0 z m -1.3,4.5 c -0.3,1.3 0.8,-0.2 0,0 z" />
                     <text y="41" x="201" fill="#00000" style="font-style:italic;font-weight:normal;font-size:30px;line-height:125%;font-family:Times;text-anchor:middle" xml:space="preserve">engraved with</text>
                  </g>
               </g>
            </g>
         </g>
      </g>
   </svg>
</svg>

@craigsapp
Copy link
Contributor

Will --svg-css allow multiple styling rules with the ID prefix added automatically to each? Or should there be one rule for each --svg-css option?

--svg-css "g.note {fill: red} g.rest {fill:red}"

I am also wondering about multiple selectors for a style rule 😜 :

--svg-css "g.note, g.rest {fill: red}"

It might be nice to prefix the ID with the text "svg" to make it clearer what the ID refers to. Such as svg#de1cn3h.

With such use, adding --svg-id is not necessary since it is handled/coordinated automatically.

@lpugin
Copy link
Contributor Author

lpugin commented Feb 14, 2025

I hadn't thought about the multiple rules case. I would be easy to prefix all the rules in the common css, but having to parse the custom css to add it is not something we would want to do.

Maybe one solution would be to use the mei root @xml:id, and to add it to the common css only when it is given. Then users need to also use it for the custom css. So the option would to be

--svg-css "#123 g.note, #123 g.rest {fill: red}"

for a document witn <mei xml:id="123">

@craigsapp
Copy link
Contributor

Here is a demo to handle most CSS cases other than @media:

#include <iostream>
#include <regex>
#include <string>
#include <vector>

// Function prototype
std::string transformCssRule(const std::string& rule, const std::string& refid);

int main() {
    std::vector<std::string> cssInput = {
        "g.note {fill: red;}",
        "g.note, g.rest {fill: red;}",
        "g.note {fill: red} g.rest {fill: blue}"
    };

    std::string refid = "abc"; // REFID to prepend

    for (const auto& cssRule : cssInput) {
        std::cout << "Original: " << cssRule << std::endl;
        std::cout << "Transformed: " << transformCssRule(cssRule, refid) << std::endl;
        std::cout << std::endl;
    }

    return 0;
}

std::string transformCssRule(const std::string& rule, const std::string& refid) {
    std::regex selectorRegex(R"(([\w\s.,]+)\s*\{([^}]*)\})");
    std::sregex_iterator it(rule.begin(), rule.end(), selectorRegex);
    std::sregex_iterator end;
    
    std::string result;

    while (it != end) {
        std::string selectors = (*it)[1].str();
        std::string properties = (*it)[2].str();

        // Trim trailing spaces from selectors to prevent extra spaces before `{`
        selectors = std::regex_replace(selectors, std::regex(R"(\s+$)"), "");

        // Prepend `#refid` to each selector
        std::regex multiSelectorRegex(R"((\b\w+\.[\w-]+\b))");
        selectors = std::regex_replace(selectors, multiSelectorRegex, "#" + refid + " $1");

        // Keep contents inside `{}` unchanged
        result += selectors + " {" + properties + "}";

        ++it;
        if (it != end) {
            result += " ";
        }
    }

    return result;
}

Results:

Original: g.note {fill: red;}
Transformed: #abc g.note {fill: red;}

Original: g.note, g.rest {fill: red;}
Transformed: #abc g.note, #abc g.rest {fill: red;}

Original: g.note {fill: red} g.rest {fill: blue}
Transformed: #abc g.note {fill: red}  #abc g.rest {fill: blue}

Compiling command:

g++ --std=c++20 -O3 -Wall -Wextra -Werror -o cssid cssid.cpp 

@samuelbradshaw
Copy link
Contributor

samuelbradshaw commented Feb 14, 2025

Something else you might need to worry about is that CSS has stricter rules than HTML, SVG, or XML on what constitutes a valid ID. See https://developer.mozilla.org/en-US/docs/Web/CSS/ident

In particular, CSS identifiers can't start with a number. So if your SVG looks like this:
<svg id="1a2b3c">

This CSS will not work:
#1a2b3c g.note { fill: red; }

You have to escape the identifier, like this:
#\31 a2b3c g.note {fill: red; } or #\000031a2b3c g.note {fill: red; }

Or you could use an attribute selector as a workaround:
[id="1a2b3c"] g.note { fill: red; }

@craigsapp
Copy link
Contributor

Note that IDs should not start with a number in XML as well (for example, in SVG data), so when generating an ID, automatically that should be avoided (that is why I used #abc in my demo code).

@craigsapp
Copy link
Contributor

I hadn't thought about the multiple rules case. I would be easy to prefix all the rules in the common css, but having to parse the custom css to add it is not something we would want to do.

Maybe one solution would be to use the mei root @xml:id, and to add it to the common css only when it is given. Then users need to also use it for the custom css. So the option would to be

--svg-css "#123 g.note, #123 g.rest {fill: red}"

for a document with <mei xml:id="123">

One thought on this is that perhaps the MEI and the SVG are somehow accessible in the same HTML page. Most websites where that currently happens has MEI in a text editor such as ACE, so it should not be a problem.

Example with MoVI:

Screenshot 2025-02-15 at 6 38 35 AM

@lpugin
Copy link
Contributor Author

lpugin commented Feb 16, 2025

The default SVG is now going to be looking like:

#d1qjy7a g.page-margin {
   font-family: Times, serif;
}
#d1qjy7a g.ending,
#d1qjy7a g.fing,
#d1qjy7a g.reh,
#d1qjy7a g.tempo {
   font-weight: bold;
}
#d1qjy7a g.dir,
#d1qjy7a g.dynam,
#d1qjy7a g.mNum {
   font-style: italic;
}
#d1qjy7a g.label {
   font-weight: normal;
}
#d1qjy7a path {
   stroke: currentColor;
}

@craigsapp
Copy link
Contributor

craigsapp commented Feb 16, 2025

How are the defaults overridden? I.e. if --svg-css "path { stroke: red; }", does that replace the entire default CSS or is it being appended in a way such that path { stroke: currentColor; } is overridden (probably the best way), or is the path selector being replaced in the default CSS with the new style?

@craigsapp
Copy link
Contributor

craigsapp commented Feb 16, 2025

#d1qjy7a g.page-margin {
   font-family: Times, serif;
}

I am also wondering how changing fonts in the CSS will (not) affect music layout 😜 And related to that, how to give non-Times font metrics to verovio... (For Tasso in Music Project, I am using a different font, Simonetta, which is pretty close to Times, so I can apply the font after rendering without much problem).

@lpugin
Copy link
Contributor Author

lpugin commented Feb 16, 2025

How are the defaults overridden?

This hasn't change. They are overridden with an additional <style> appended after the default one.

@lpugin lpugin requested a review from ahankinson February 17, 2025 10:48
src/svgdevicecontext.cpp Outdated Show resolved Hide resolved
@lpugin lpugin requested a review from ahankinson February 17, 2025 11:29
@ahankinson
Copy link
Contributor

should I merge?

@lpugin
Copy link
Contributor Author

lpugin commented Feb 17, 2025

Yes

@ahankinson ahankinson merged commit 82a88b2 into develop Feb 17, 2025
9 checks passed
@lpugin lpugin deleted the develop-scope-css branch February 17, 2025 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants