mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.09k stars 2.21k forks source link

postcss doesn't encode spaces in svg-load urls for control icons #7896

Open adamkalis opened 5 years ago

adamkalis commented 5 years ago

From commit d141026f3625ba11936e1b47ea784ed7c3cdc9a8, the url value of background-image properties is generated from postcss without the spaces of the url be encoded.

For example: Before this commit, the css property for zoom in icon (+) was: background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%0A%20%20%3Cpath%20style%3D%27fill%3A%23333333%3B%27%20d%3D%27M%2010%206%20C%209.446%206%209%206.4459904%209%207%20L%209%209%20L%207%209%20C%206.446%209%206%209.446%206%2010%20C%206%2010.554%206.446%2011%207%2011%20L%209%2011%20L%209%2013%20C%209%2013.55401%209.446%2014%2010%2014%20C%2010.554%2014%2011%2013.55401%2011%2013%20L%2011%2011%20L%2013%2011%20C%2013.554%2011%2014%2010.554%2014%2010%20C%2014%209.446%2013.554%209%2013%209%20L%2011%209%20L%2011%207%20C%2011%206.4459904%2010.554%206%2010%206%20z%27%20%2F%3E%0A%3C%2Fsvg%3E%0A");

After this commit with postcss, the css property becomes: background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E %3Cpath style='fill:%23333333;' d='M 10 6 C 9.446 6 9 6.4459904 9 7 L 9 9 L 7 9 C 6.446 9 6 9.446 6 10 C 6 10.554 6.446 11 7 11 L 9 11 L 9 13 C 9 13.55401 9.446 14 10 14 C 10.554 14 11 13.55401 11 13 L 11 11 L 13 11 C 13.554 11 14 10.554 14 10 C 14 9.446 13.554 9 13 9 L 11 9 L 11 7 C 11 6.4459904 10.554 6 10 6 z'/%3E %3C/svg%3E");

There are css minifiers like rcssmin, which is used by django compressor, that remove spaces from the urls. Removing the spaces make the urls unreadable for the browsers, so icons are not rendered. There is this issue in rcssmin project about removing spaces that explains in the last two comments why spaces should be encoded to %20 by linking to rfc2397.

One possible solution is postcss-inline-svg plugin that offers an encoding option, however by default it doesn't encode spaces, which means that a custom encode function should be passed as encoding option.

andrewharvey commented 5 years ago

This feels like an issue in https://github.com/TrySound/postcss-inline-svg, if spaces aren't allowed, it shouldn't output them. That said, @adamkalis I agree, it's possible to workaround and provide our own encoding function to postcss-inline-svg. I'd be happy to take a look at a PR if you were able to fix this?

gregholst commented 4 years ago

I am using django-compressor, but the problem for my control svgs rendered only in white seems not related to this tool. Why? Because in my codesandbox the problem is the same! The svg icons zoom out / in, compass, full screen / shrink get displayed in white before changing any css, see here: https://codesandbox.io/s/highcharts-react-demo-d832d

I could work around this for some css classes (changing the fill hex value eg. '000000' in the first one defines black), but not for the shrink. I have not tried for the compass yet. In general this seems rather a bug. Should I open a new issue for this one?

.mapboxgl-ctrl-zoom-in { background-image: url(data:image/svg+xml;charset=utf8,<svg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27>%0A%20%20<path%20style%3D%27fill%3A%23000000%3B%27%20d%3D%27M%2010%206%20C%209.446%206%209%206.4459904%209%207%20L%209%209%20L%207%209%20C%206.446%209%206%209.446%206%2010%20C%206%2010.554%206.446%2011%207%2011%20L%209%2011%20L%209%2013%20C%209%2013.55401%209.446%2014%2010%2014%20C%2010.554%2014%2011%2013.55401%2011%2013%20L%2011%2011%20L%2013%2011%20C%2013.554%2011%2014%2010.554%2014%2010%20C%2014%209.446%2013.554%209%2013%209%20L%2011%209%20L%2011%207%20C%2011%206.4459904%2010.554%206%2010%206%20z%27%20%2F>%0A<%2Fsvg>%0A) !important; }

.mapboxgl-ctrl-zoom-out { background-image: url(data:image/svg+xml;charset=utf8,<svg%20viewBox%3D%270%200%2020%2020%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27>%0A%20%20<path%20style%3D%27fill%3A%23000000%3B%27%20d%3D%27m%207%2C9%20c%20-0.554%2C0%20-1%2C0.446%20-1%2C1%200%2C0.554%200.446%2C1%201%2C1%20l%206%2C0%20c%200.554%2C0%201%2C-0.446%201%2C-1%200%2C-0.554%20-0.446%2C-1%20-1%2C-1%20z%27%20%2F>%0A<%2Fsvg>%0A) !important; }

.mapboxgl-ctrl-fullscreen { background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjkiIGhlaWdodD0iMjkiIHZpZXdCb3g9IjAgMCAyOSAyOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0yNCAxNnY1LjVjMCAxLjc1LS43NSAyLjUtMi41IDIuNUgxNnYtMWwzLTEuNS00LTUuNSAxLTEgNS41IDQgMS41LTNoMXpNNiAxNmwxLjUgMyA1LjUtNCAxIDEtNCA1LjUgMyAxLjV2MUg3LjVDNS43NSAyNCA1IDIzLjI1IDUgMjEuNVYxNmgxem03LTExdjFsLTMgMS41IDQgNS41LTEgMS01LjUtNEw2IDEzSDVWNy41QzUgNS43NSA1Ljc1IDUgNy41IDVIMTN6bTExIDIuNWMwLTEuNzUtLjc1LTIuNS0yLjUtMi41SDE2djFsMyAxLjUtNCA1LjUgMSAxIDUuNS00IDEuNSAzaDFWNy41eiIvPgo8L3N2Zz4=");

andrewharvey commented 4 years ago

as a workaround you can try to replace postcss.config.js with

module.exports = {                                                                                                            
    plugins: [
        require('postcss-inline-svg')({
            encode: function(code) {
                return code
                    .replace(/%/g, "%25")
                    .replace(/ /g, "%20")
                    .replace(/</g, "%3C")
                    .replace(/>/g, "%3E")
                    .replace(/&/g, "%26")
                    .replace(/#/g, "%23")
                    .replace(/{/g, "%7B")
                    .replace(/}/g, "%7D");
            }
        }),
        require('cssnano')({
            preset: ['default', {
                svgo: false
            }],
        }),
    ]
}

This disabled svg optimization, but I can't work out how to enable svgo while still retaining %20 instead of .

gregholst commented 4 years ago

Sorry, forgot to post that updating mapbox css to this version solved my problem:

<link href='https://api.mapbox.com/mapbox-gl-js/v1.7.0/mapbox-gl.css' rel='stylesheet' />
andrewharvey commented 4 years ago

I was testing with 1.9.0 and still noticing an issue with a 3rd party CSS minimizer. According to the links in this thread, spaces shouldn't appear in the data url which although browsers seem to have no issue with, some CSS minimizers do decide to drop them and break the CSS.