svgo icon indicating copy to clipboard operation
svgo copied to clipboard

Add replaceUse plugin

Open tshedor opened this issue 8 years ago • 12 comments

This plugin replaces all <use> elements with the nodes they clone and removes the top-level xlink attribute. While this doesn't "optimize" the SVG, it allows the contents to be used in SVG sprites within <symbol> elements. This plugin is best used in conjunction with removeUselessDefs.

tshedor avatar Oct 12 '17 04:10 tshedor

This is a good plugins! I probably will use it in my project!

But Im suggesting to make this plugin being executed before the removeUselessDefs plugins, otherwise it will just duplicate the path after removing the <use> tag

bbqaaq avatar Oct 15 '17 16:10 bbqaaq

@bbqaaq Great call. I almost always run with multipass enabled, so I didn't notice. I've moved it before removeUselessDefs.

tshedor avatar Oct 15 '17 19:10 tshedor

Hi, I was testing this plugin and I found several bugs:

  • It is removing necessary IDs (remove line 22)
  • The tag use can have some attributes, unfortunately we cannot copy it inside the object being moved, perhaps it is best to use a new group and some transformations
  • It ignores all defined object outside defs (yes, it is possible to do so with a svg)

TiagoDinisFonseca avatar Oct 18 '17 09:10 TiagoDinisFonseca

@TiagoDinisFonseca I've addressed those bugs. Thanks for reporting them.

Regarding the <use> attributes, I changed the <use> tag into a group as per your suggestion, and my tests show equivalent behavior between <use> and <g>. However, please verify that your tests work as expected.

These fixes mean the plugin is no longer complimented by removeUselessDefs as it achieves the desired effect on its own.

tshedor avatar Oct 18 '17 18:10 tshedor

Unfortunately, this is not true. The tag has some attributes: x, y, height and width that g has not. (see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use) x and y should be equivalent to a translate height and width are still mysterious for me here in all my experiences height and width do nothing

TiagoDinisFonseca avatar Oct 19 '17 13:10 TiagoDinisFonseca

As a user I do support adding this plugin as a optional one as I am useful with this. In my project I got some SVGs with <use> and <defs> which is annoying to me

I have gone through this plugin and it works quite fine with the SVGs.

bbqaaq avatar Oct 29 '17 12:10 bbqaaq

@TiagoDinisFonseca @tshedor any updates here? I'd really like to use this plugin.

lifeiscontent avatar Apr 07 '18 05:04 lifeiscontent

I'd guess you should move the attributes from use to the path and not the tag.

lifeiscontent avatar Apr 07 '18 05:04 lifeiscontent

@GreLI can we prioritize this?

what would you need in order to get this merged?

lifeiscontent avatar Apr 07 '18 05:04 lifeiscontent

@lifeiscontent At the time I was interested on this because it could be useful for me. It turns out, I was wrong. Therefore, I did not care to solve its problems. Sorry for that, eventually I can do it when I have the time to run this on my personal PC.

But you can solve it: 1- Your solution is not good enough, you never know what is inside, it could be a second group. 2- I guess that attributes x and y can be transformed into some translation (x,y) => transform="translation(x, y)" 3- What about width and height??? I feel bad about erasing some fields just because I do not understand them. 3.1 - There is a solution for this: we erase those fields... if this is a problem, this will be a bug in the future (I don't like this attitude, but perhaps it is ok)

TiagoDinisFonseca avatar Apr 11 '18 09:04 TiagoDinisFonseca

Does this need to be absolutely perfect to be considered for addition? This would be very useful for me in it's current state

tommoor avatar Jun 22 '19 00:06 tommoor

Rewritten to use the latest csstree api:

exports.fn = () => {
  const defs = new Map;
  const used = new Set;

  return {
    element: {
      enter(node, parentNode) {
        if (node.name === 'svg') {
          delete node.attributes['xmlns:xlink'];
          return
        }

        if (parentNode.name === 'defs') {
          defs.set('#' + node.attributes.id, node);
          return;
        }

        if (node.name === 'use') {
          const id = node.attributes.href || node.attributes['xlink:href'];
          const def = defs.get(id);

          if (!def) {
            console.warn(`Could not find definition for ${id}`);
            return;
          }

          delete node.attributes['xlink:href'];
          delete node.attributes['href'];

          node.name = 'g';
          node.children = [...def.children];

          used.add(def);
        }

      },
    },
    root: {
      exit(root) {
        const defs = querySelector(root, 'defs');
        if (defs) {
          defs.children = defs.children.filter(node => !used.has(node));
          if (!defs.children.length) {
            defs.parentNode.children = defs.parentNode.children.filter(node => node !== defs);
          }
        }
      }
    }
  }
}

akre54 avatar Nov 28 '22 16:11 akre54

@tshedor, @bbqaaq, @TiagoDinisFonseca, @lifeiscontent, @tommoor, @akre54, please share an example of icons, the sprite of which breaks without this transformation. Tried to add a valid description of the path of the curve to the test files, but maybe I'm missing something.

And with what tool do you collect the sprite?

firefoxic avatar Dec 13 '22 19:12 firefoxic