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

Unbound namespace prefix: "xlink" #1730

Closed
JoKalliauer opened this issue Dec 20, 2022 · 11 comments
Closed

Unbound namespace prefix: "xlink" #1730

JoKalliauer opened this issue Dec 20, 2022 · 11 comments
Assignees
Labels

Comments

@JoKalliauer
Copy link

Describe the bug
svgo does not create an output of https://camo.githubusercontent.com/48fff2a348143da7155275645da575b81276b9810d1e9f8b38547e34c9aaacb8/68747470733a2f2f7376672e6769746875622e696f2f7376676f2d6c6f676f2e737667 with multipass: true

To Reproduce
svgo -i input.svg -o output.svg
with input.svg and svgo.config.js.txt

Expected behavior
Don't delete xmlns:xlink="http://www.w3.org/1999/xlink" in <svg if some child-elements use xlink:href="

Screenshots
2022-12-20

Desktop:

  • SVGO Version 3.0.2
  • NodeJs Version 14.21.1
  • OS: Ubuntu 22.04

copyright
source: #1336 (comment)

author: unkown, maybe <!-- ! https://github.com/svg/svgo --><!-- ! SVGO project logo by Yegor Bolshakov (http://xizzzy.ru/) --> or André Castillo

license: assuming MIT License

@SethFalco SethFalco self-assigned this Sep 23, 2023
@SethFalco
Copy link
Member

SethFalco commented Sep 23, 2023

The solution to this is probably to ignore unknown namespaces in this case. It appears there's a use-case for it, and if right now SVGO is just refusing to optimize the SVG at all, we could update it, so it just does what it can.

The motivation behind the removeXMLNS plugin is for use in HTML5, where XML namespaces are not required. The browser knows how to handle common ones, and ignores unknown ones.

In other words, the goal is to intentionally create a malformed standalone SVG, but that will parse and render correctly with an HTML parser.

When using the HTML syntax, the namespace is provided automatically by the HTML parser.

<html>
<svg viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="50" fill="green">
</svg>
</html>

As the example shows there's no need to have an ‘xmlns’ attribute declaring that the element is in the SVG namespace when using the HTML parser.

https://www.w3.org/TR/SVG2/struct.html#Namespace

Additionally, SVG allows inclusion of attributes from foreign namespaces on any SVG element. The SVG user agent must include unknown attributes in the DOM but should otherwise ignore unknown attributes.

https://www.w3.org/TR/SVG2/struct.html#ForeignNamespaces

I'll give this more thought later, but the solution probably won't be to keep used namespaces. If you need them, users just shouldn't use this plugin. (It's disabled by default!)

@JoKalliauer
Copy link
Author

The motivation behind the removeXMLNS plugin is for use in HTML5, where XML namespaces are not required. The browser knows how to handle common ones, and ignores unknown ones.

Why is the error occurring with svgo.config.js: (With svgo 3.0.2)

module.exports = {
  multipass: true,
  plugins: ([
    {
      name: 'removeXMLNS',
      active: false
    },
	{
      name: 'reusePaths',
      active: false
    },
  ])
}

@GreLI
Copy link
Member

GreLI commented Nov 14, 2023

Actually for HTML it's enough just “href” without “xlink:” prefix, which is now considered outdated. (But still probably needed for not updated software.)

@JoKalliauer
Copy link
Author

SVG 2.0 is ofiically still a draft and all common SVG 2.0-renderes work with xlink:href=" as well with href="

SVG 1.1 spec does only allow xlink:href=" (not with href=")

As long as svgo does not declare to be a SVG2.0-only tool, which breaks SVG1.1, it should imho use xlink:href=", or make a setting to switch between SVG2.0 and SVG1.1 (even though many tools only work with one or the other)

@SethFalco
Copy link
Member

SethFalco commented Nov 17, 2023

Ahh, I misunderstood the issue.

You're using the SVGO v2 config syntax in SVGO v3. Note that when the major version increments, that means there are breaking or interface changes, so it's best to read the changelog and README/documentation.

The active property is being ignored in v3, so by defining the plugin the way you are, you're actually enabling them.

SVG 1.1 spec does only allow xlink:href=" (not with href=")

GreLI is correct on the basis that you were enabling the removeXMLNS plugin where SVG 1.1 or SVG 2 compatibility no longer applies because the SVG is expected to be inlined to an HTML document which doesn't require namespaces, refer to what I described in #1730 (comment).

See: https://svgo.dev/docs/plugins/remove-xmlns/

Actually for HTML it's enough just “href” without “xlink:” prefix, which is now considered outdated. (But still probably needed for not updated software.)

Good idea, this is a much better fix.

I'll address this by finishing off TrySound's PR for creating a plugin to migrate from xlink:href to href. Then we can either remove the xlink:href part of the removeXMLNS plugin and recommend users use removeXlink too, or have it internally call removeXlink with a parameter to disable it.

@SethFalco
Copy link
Member

SethFalco commented Nov 18, 2023

While the primary issue seems to be the SVGO v2 vs v3 config syntax, the issue you ran into here been resolved and will be released in SVGO v3.0.4.

  • removeXMLNS will no longer remove the XLink namespace at all.
  • A new plugin, removeXlink can be used to migrate from the XLink namespace to the SVG 2 equivalent when only SVG 2 or inline HTML compatibility is required. (disabled by default)

@silverwind
Copy link

silverwind commented Dec 8, 2023

I'm seeing the same error with this SVG using svgo@3.0.5:

<svg width="59" height="59" viewBox="0 0 59 59"><style id="style562">.B{stroke-linejoin:miter}.C{stroke-width:.788}.D{stroke:none}.E{stroke-linecap:square}.F{stroke-width:3.15}</style><g id="g621" class="D" transform="translate(.56 -7.6)"><path id="use619" d="M57.88 29.93c0 5.9-13 11.03-29.14 11.03C13 40.96 0 35.84 0 29.93v16.15c0 6.3 13 11.41 28.74 11.41 16.15 0 29.14-5.11 29.14-11.41z"/></g><path id="use625" stroke="none" d="M29.3 9.34C13.55 9.34.56 14.46.56 20.76c0 5.9 13 11.02 28.74 11.02 16.15 0 29.14-5.12 29.14-11.02 0-6.3-13-11.42-29.14-11.42zm11.03 2.35v3.94l-2.36-.4-4.33 3.55-4.33-.4 4.72-3.54-2.75-.79zM7.65 15.24l13.39 2.36L23 16.03l2.36 3.93-9.06 1.97 1.97-1.57-14.17-2.37zm34.26 4.33-1.18 1.18 13.38 2.36-3.15 2.76-13.38-2.76-1.97 1.97-1.58-3.94zm-16.54 2.75 4.33.8-5.12 4.32 2.76.79-9.06 1.58v-3.94l2.37.4z"/><symbol id="A" overflow="visible"><g id="g570" fill="none" class="B E"><path id="path566" d="M47.65 26.38V.78" class="C"/><path id="path568" d="M47.65 25.99V8.27" class="F"/></g><path id="path572" d="M48.44 3.15h-1.58V0h1.58v3.15z" class="D"/><g id="g578" fill="none" class="B E"><path id="path574" d="M9.46 26.38V.78" class="C"/><path id="path576" d="M9.46 25.99V8.27" class="F"/></g><g id="g584" class="D"><path id="path580" d="M10.24 3.15H8.67V0h1.57v3.15z"/><use xlink:href="#C" id="use582"/></g><use xlink:href="#C" id="use586" fill="none" class="B C"/><use xlink:href="#D" id="use588" class="D"/><use xlink:href="#D" id="use590" fill="none" class="B C"/><path id="path592" d="m22.45 25.2 2.36 3.94-9.05 1.97 1.96-1.58-14.17-2.36 3.54-2.76 13.4 2.37 1.96-1.58zm12.6 9.06-1.58-3.94 7.88-1.58-1.18 1.19 13.38 2.36-3.14 2.75L37 32.3l-1.96 1.97zm-4.33-11.03 9.05-2.36v3.94l-2.36-.4-4.33 3.55-4.33-.4 4.72-3.54-2.75-.79zm-3.94 14.18-9.06 1.57v-3.94l2.37.4 4.72-3.94 4.33.79-5.12 4.33 2.76.79z" class="D"/></symbol><defs id="defs597"><path id="C" d="M57.88 29.93c0 5.9-13 11.03-29.14 11.03C13 40.96 0 35.84 0 29.93v16.15c0 6.3 13 11.41 28.74 11.41 16.15 0 29.14-5.11 29.14-11.41z"/><path id="D" d="M28.74 40.96c16.15 0 29.14-5.12 29.14-11.03 0-6.3-13-11.42-29.14-11.42C13 18.51 0 23.63 0 29.93c0 5.9 13 11.03 28.74 11.03z"/></defs></svg>

Adding the removeXlink plugin does not make the error go away. This is my usage:

    const {data} = optimize(str, {
      plugins: [
        {name: "preset-default"},
        {name: "removeXMLNS"},
        {name: "removeDimensions"},
      ],
    });

I think the whole <use> tag could likely be removed here.

@SethFalco
Copy link
Member

SethFalco commented Dec 8, 2023

@silverwind Hey! The error you're encountering is different from this issue.

The bug reported in this issue was because the error was caused by SVGO, we broke the SVG ourselves. In the SVG you've posted, it's already malformed before SVGO can touch it.

SVGO shouldn't touch malformed SVGs. We have no way of knowing what the user wants. It's better if they're given an error message that their SVG is malformed so they can fix it before going through SVGO, which is exactly what it's doing now.

  • The SVG wouldn't load in most clients anyway.
  • When inlined in HTML, the <use> nodes would still work, since the browser assumes xlink:href refers to href. If SVGO removed them, that'd break browser usage.

I recommend you fix the SVG by either adding the XLink namespace, or removing references to XLink yourself, depending on whether they should be rendered or not.

@silverwind
Copy link

@SethFalco thanks. I've replaced that SVG with a different, valid one now. Though, it does render in all browsers that I've tested (Firefox, Chrome, Safari), so I think they are just more lenient towards this error than svgo.

@SethFalco
Copy link
Member

SethFalco commented Dec 11, 2023

No actually! Browsers just ignore explicit namespaces and prefixes because it's not supported in the HTML spec. Browsers have to infer the namespace by usage, which is why it works.

For example, xlink:href is interpreted as href in browsers.

Meanwhile, the SVG should fail to parse correctly by non HTML clients, like SVG editors and desktop applications.

When using the HTML syntax, the namespace is provided automatically by the HTML parser.

https://www.w3.org/TR/SVG2/struct.html#Namespace

@JoKalliauer
Copy link
Author

While the primary issue seems to be the SVGO v2 vs v3 config syntax, the issue you ran into here been resolved and will be released in SVGO v3.0.4.

* `removeXMLNS` will no longer remove the XLink namespace at all.

* A new plugin, [`removeXlink`](https://svgo.dev/docs/plugins/remove-xlink/) can be used to migrate from the XLink namespace to the SVG 2 equivalent when only SVG 2 or inline HTML compatibility is required. (disabled by default)

Confirm fix with 3.2.0.
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants