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 | ;
|
80 |
|
81 | var JSAPI = require('../lib/svgo/jsAPI');
|
82 |
|
83 | exports.type = 'full';
|
84 |
|
85 | exports.active = false;
|
86 |
|
87 | exports.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 | */
|
97 | exports.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 | /** */
|
148 | function 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 | /** */
|
160 | function 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 | }
|