114 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| /**
 | |
|  * @typedef {import('../lib/types').XastElement} XastElement
 | |
|  * @typedef {import('../lib/types').XastParent} XastParent
 | |
|  * @typedef {import('../lib/types').XastNode} XastNode
 | |
|  */
 | |
| 
 | |
| const JSAPI = require('../lib/svgo/jsAPI.js');
 | |
| 
 | |
| exports.type = 'visitor';
 | |
| exports.name = 'reusePaths';
 | |
| exports.active = false;
 | |
| exports.description =
 | |
|   'Finds <path> elements with the same d, fill, and ' +
 | |
|   'stroke, and converts them to <use> elements ' +
 | |
|   'referencing a single <path> def.';
 | |
| 
 | |
| /**
 | |
|  * Finds <path> elements with the same d, fill, and stroke, and converts them to
 | |
|  * <use> elements referencing a single <path> def.
 | |
|  *
 | |
|  * @author Jacob Howcroft
 | |
|  *
 | |
|  * @type {import('../lib/types').Plugin<void>}
 | |
|  */
 | |
| exports.fn = () => {
 | |
|   /**
 | |
|    * @type {Map<string, Array<XastElement>>}
 | |
|    */
 | |
|   const paths = new Map();
 | |
| 
 | |
|   return {
 | |
|     element: {
 | |
|       enter: (node) => {
 | |
|         if (node.name === 'path' && node.attributes.d != null) {
 | |
|           const d = node.attributes.d;
 | |
|           const fill = node.attributes.fill || '';
 | |
|           const stroke = node.attributes.stroke || '';
 | |
|           const key = d + ';s:' + stroke + ';f:' + fill;
 | |
|           let list = paths.get(key);
 | |
|           if (list == null) {
 | |
|             list = [];
 | |
|             paths.set(key, list);
 | |
|           }
 | |
|           list.push(node);
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       exit: (node, parentNode) => {
 | |
|         if (node.name === 'svg' && parentNode.type === 'root') {
 | |
|           /**
 | |
|            * @type {XastElement}
 | |
|            */
 | |
|           const rawDefs = {
 | |
|             type: 'element',
 | |
|             name: 'defs',
 | |
|             attributes: {},
 | |
|             children: [],
 | |
|           };
 | |
|           /**
 | |
|            * @type {XastElement}
 | |
|            */
 | |
|           const defsTag = new JSAPI(rawDefs, node);
 | |
|           let index = 0;
 | |
|           for (const list of paths.values()) {
 | |
|             if (list.length > 1) {
 | |
|               // add reusable path to defs
 | |
|               /**
 | |
|                * @type {XastElement}
 | |
|                */
 | |
|               const rawPath = {
 | |
|                 type: 'element',
 | |
|                 name: 'path',
 | |
|                 attributes: { ...list[0].attributes },
 | |
|                 children: [],
 | |
|               };
 | |
|               delete rawPath.attributes.transform;
 | |
|               let id;
 | |
|               if (rawPath.attributes.id == null) {
 | |
|                 id = 'reuse-' + index;
 | |
|                 index += 1;
 | |
|                 rawPath.attributes.id = id;
 | |
|               } else {
 | |
|                 id = rawPath.attributes.id;
 | |
|                 delete list[0].attributes.id;
 | |
|               }
 | |
|               /**
 | |
|                * @type {XastElement}
 | |
|                */
 | |
|               const reusablePath = new JSAPI(rawPath, defsTag);
 | |
|               defsTag.children.push(reusablePath);
 | |
|               // convert paths to <use>
 | |
|               for (const pathNode of list) {
 | |
|                 pathNode.name = 'use';
 | |
|                 pathNode.attributes['xlink:href'] = '#' + id;
 | |
|                 delete pathNode.attributes.d;
 | |
|                 delete pathNode.attributes.stroke;
 | |
|                 delete pathNode.attributes.fill;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           if (defsTag.children.length !== 0) {
 | |
|             if (node.attributes['xmlns:xlink'] == null) {
 | |
|               node.attributes['xmlns:xlink'] = 'http://www.w3.org/1999/xlink';
 | |
|             }
 | |
|             node.children.unshift(defsTag);
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|     },
 | |
|   };
 | |
| };
 |