UNPKG

6.49 kBJavaScriptView Raw
1/**
2 * @license
3 * The MIT License
4 *
5 * Copyright © 2012–2016 Kir Belevich
6 *
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use,
11 * copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following
14 * conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 * OTHER DEALINGS IN THE SOFTWARE.
27 *
28 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29 *
30 * Лицензия MIT
31 *
32 * Copyright © 2012–2016 Кир Белевич
33 *
34 * Данная лицензия разрешает лицам, получившим копию
35 * данного
36 * программного обеспечения и сопутствующей
37 * документации
38 * (в дальнейшем именуемыми «Программное Обеспечение»),
39 * безвозмездно
40 * использовать Программное Обеспечение без
41 * ограничений, включая
42 * неограниченное право на использование, копирование,
43 * изменение,
44 * добавление, публикацию, распространение,
45 * сублицензирование
46 * и/или продажу копий Программного Обеспечения, также
47 * как и лицам,
48 * которым предоставляется данное Программное
49 * Обеспечение,
50 * при соблюдении следующих условий:
51 *
52 * Указанное выше уведомление об авторском праве и
53 * данные условия
54 * должны быть включены во все копии или значимые части
55 * данного
56 * Программного Обеспечения.
57 *
58 * ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК
59 * ЕСТЬ»,
60 * БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ
61 * ПОДРАЗУМЕВАЕМЫХ,
62 * ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ
63 * ПРИГОДНОСТИ,
64 * СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И
65 * ОТСУТСТВИЯ НАРУШЕНИЙ
66 * ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ
67 * НЕСУТ
68 * ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ
69 * ИЛИ ДРУГИХ
70 * ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ
71 * ИНОМУ,
72 * ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С
73 * ПРОГРАММНЫМ
74 * ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО
75 * ОБЕСПЕЧЕНИЯ
76 * ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
77 */
78
79'use strict';
80
81var JSAPI = require('../lib/svgo/jsAPI');
82
83exports.type = 'full';
84
85exports.active = false;
86
87exports.description = 'Finds <path> elements with the same d, fill, and ' +
88 'stroke, and converts them to <use> elements ' +
89 'referencing a single <path> def.';
90
91/**
92 * Finds <path> elements with the same d, fill, and stroke, and converts them to
93 * <use> elements referencing a single <path> def.
94 *
95 * @author Jacob Howcroft
96 */
97exports.fn = function(data) {
98 const seen = new Map();
99 let count = 0;
100 const defs = [];
101 traverse(data, item => {
102 if (!item.isElem('path') || !item.hasAttr('d')) {
103 return;
104 }
105 const d = item.attr('d').value;
106 const fill = (item.hasAttr('fill') && item.attr('fill').value) || '';
107 const stroke = (item.hasAttr('stroke') && item.attr('stroke').value) || '';
108 const key = d + ';s:' + stroke + ';f:' + fill;
109 const hasSeen = seen.get(key);
110 if (!hasSeen) {
111 seen.set(key, {elem: item, reused: false});
112 return;
113 }
114 if (!hasSeen.reused) {
115 hasSeen.reused = true;
116 if (!hasSeen.elem.hasAttr('id')) {
117 hasSeen.elem.addAttr({name: 'id', local: 'id',
118 prefix: '', value: 'reuse-' + (count++)});
119 }
120 defs.push(hasSeen.elem);
121 }
122 item = convertToUse(item, hasSeen.elem.attr('id').value);
123 });
124 const defsTag = new JSAPI({
125 elem: 'defs', prefix: '', local: 'defs', content: [], attrs: []}, data);
126 data.content[0].spliceContent(0, 0, defsTag);
127 for (let def of defs) {
128 // Remove class and style before copying to avoid circular refs in
129 // JSON.stringify. This is fine because we don't actually want class or
130 // style information to be copied.
131 const style = def.style;
132 const defClass = def.class;
133 delete def.style;
134 delete def.class;
135 const defClone = def.clone();
136 def.style = style;
137 def.class = defClass;
138 defClone.removeAttr('transform');
139 defsTag.spliceContent(0, 0, defClone);
140 // Convert the original def to a use so the first usage isn't duplicated.
141 def = convertToUse(def, defClone.attr('id').value);
142 def.removeAttr('id');
143 }
144 return data;
145};
146
147/** */
148function convertToUse(item, href) {
149 item.renameElem('use');
150 item.removeAttr('d');
151 item.removeAttr('stroke');
152 item.removeAttr('fill');
153 item.addAttr({name: 'xlink:href', local: 'xlink:href',
154 prefix: 'none', value: '#' + href});
155 delete item.pathJS;
156 return item;
157}
158
159/** */
160function traverse(parent, callback) {
161 if (parent.isEmpty()) {
162 return;
163 }
164 for (let child of parent.content) {
165 callback(child);
166 traverse(child, callback);
167 }
168}