@iconify/tools 4.0.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ const utils = require('@iconify/utils');
4
+ const svg_cleanup_inlineStyle = require('../svg/cleanup/inline-style.cjs');
3
5
  const svg_data_tags = require('../svg/data/tags.cjs');
6
+ const svg_parse = require('../svg/parse.cjs');
4
7
  const optimise_unwrap = require('./unwrap.cjs');
8
+ require('../css/parse.cjs');
9
+ require('../css/parser/tokens.cjs');
10
+ require('../css/parser/error.cjs');
11
+ require('../css/parser/strings.cjs');
12
+ require('../css/parser/text.cjs');
13
+ require('../svg/data/attributes.cjs');
5
14
 
6
15
  function isTinyNumber(value, limit) {
7
16
  const num = parseInt(value);
@@ -25,8 +34,10 @@ function checkClipPathNode(clipNode, expectedWidth, expectedHeight) {
25
34
  ...childNode.attribs
26
35
  };
27
36
  delete attribs["fill"];
28
- const fill = childNode.attribs["fill"]?.toLowerCase();
29
- if (fill !== "white" && fill !== "#fff" && fill !== "#ffffff" && fill !== void 0) {
37
+ const fill = childNode.attribs["fill"];
38
+ const colorValue = fill ? utils.stringToColor(fill) : null;
39
+ const colorString = colorValue ? utils.colorToString(colorValue) : null;
40
+ if (fill && colorString !== "#fff") {
30
41
  console.warn(
31
42
  "Unxepected fill on clip path:",
32
43
  childNode.attribs["fill"]
@@ -43,24 +54,6 @@ function checkClipPathNode(clipNode, expectedWidth, expectedHeight) {
43
54
  }
44
55
  delete attribs["width"];
45
56
  delete attribs["height"];
46
- for (const attr in childNode.attribs) {
47
- const value = childNode.attribs[attr];
48
- switch (attr) {
49
- case "rx":
50
- case "ry":
51
- case "x":
52
- case "y":
53
- if (value === "0") {
54
- delete attribs[attr];
55
- }
56
- break;
57
- case "transform":
58
- if (value === "") {
59
- delete attribs[attr];
60
- }
61
- break;
62
- }
63
- }
64
57
  break;
65
58
  }
66
59
  default:
@@ -97,6 +90,58 @@ function remove(svg) {
97
90
  optimise_unwrap.unwrapEmptyGroup(svg);
98
91
  let content = svg.toString();
99
92
  const backup = content;
93
+ const isPenpot = content.includes("frame-clip-def");
94
+ if (isPenpot) {
95
+ svg_cleanup_inlineStyle.cleanupInlineStyle(svg);
96
+ svg_parse.parseSVG(svg, (item) => {
97
+ const tagName = item.tagName;
98
+ for (const attr in item.element.attribs) {
99
+ const value = item.element.attribs[attr];
100
+ switch (attr) {
101
+ case "id":
102
+ if (!svg_data_tags.maskTags.has(tagName) && !svg_data_tags.symbolTag.has(tagName)) {
103
+ item.$element.removeAttr(attr);
104
+ }
105
+ break;
106
+ case "class":
107
+ case "xmlns:xlink":
108
+ case "version":
109
+ item.$element.removeAttr(attr);
110
+ break;
111
+ case "transform": {
112
+ const trimmed = value.replace(/\s+/g, "").replace(/\.0+/g, "");
113
+ if (!trimmed || trimmed === "matrix(1,0,0,1,0,0)") {
114
+ item.$element.removeAttr(attr);
115
+ }
116
+ break;
117
+ }
118
+ case "rx":
119
+ case "ry":
120
+ case "x":
121
+ case "y":
122
+ if (value === "0") {
123
+ item.$element.removeAttr(attr);
124
+ }
125
+ break;
126
+ case "fill-opacity":
127
+ case "stroke-opacity":
128
+ case "opacity":
129
+ if (value === "1") {
130
+ item.$element.removeAttr(attr);
131
+ }
132
+ break;
133
+ case "fill":
134
+ case "stroke": {
135
+ const colorValue = utils.stringToColor(value);
136
+ if (colorValue?.type === "rgb") {
137
+ item.$element.attr(attr, utils.colorToString(colorValue));
138
+ }
139
+ }
140
+ }
141
+ }
142
+ });
143
+ content = svg.toString();
144
+ }
100
145
  const clipPathBlocks = content.match(
101
146
  /<clipPath[^>]*>[\s\S]+?<\/clipPath>/g
102
147
  );
@@ -105,7 +150,6 @@ function remove(svg) {
105
150
  const lines = content.split(split);
106
151
  content = lines.shift() + split + lines.join("");
107
152
  }
108
- content = content.replaceAll('class="frame-clip-def frame-clip"', "");
109
153
  if (content.includes("<defs>")) {
110
154
  content = content.replace(/<\/?defs>/g, "");
111
155
  }
@@ -138,30 +182,21 @@ function remove(svg) {
138
182
  if (typeof clipID !== "string") {
139
183
  return false;
140
184
  }
141
- const checkClipPath = (node) => {
142
- const id = node.attribs["id"];
143
- if (id !== clipID) {
144
- return;
145
- }
146
- const result = checkClipPathNode(
147
- node,
148
- svg.viewBox.width,
149
- svg.viewBox.height
150
- );
151
- cheerio(node).remove();
152
- return result;
153
- };
154
185
  const findClipPath = () => {
155
186
  for (let i = 0; i < children.length; i++) {
156
187
  const node = children[i];
157
- if (node.type === "tag") {
158
- const tagName = node.tagName;
159
- if (tagName === "clipPath") {
160
- const result = checkClipPath(node);
161
- if (result !== void 0) {
162
- return result;
163
- }
188
+ if (node.type === "tag" && node.tagName === "clipPath") {
189
+ const id = node.attribs["id"];
190
+ if (id === clipID) {
191
+ const result = checkClipPathNode(
192
+ node,
193
+ svg.viewBox.width,
194
+ svg.viewBox.height
195
+ );
196
+ cheerio(node).remove();
197
+ return result;
164
198
  }
199
+ return;
165
200
  }
166
201
  }
167
202
  };
@@ -4,7 +4,8 @@ import '@iconify/types';
4
4
  import '@iconify/utils/lib/customisations/defaults';
5
5
 
6
6
  /**
7
- * Removes clip path from SVG, which Figma and Penpot add to icons that might have overflowing elements
7
+ * Removes clip path from SVG, which Figma and Penpot add to icons that might have overflowing elements.
8
+ * Also removes mess generated by Penpot
8
9
  *
9
10
  * Function was originally designed for Figma only, but later added support for Penpot
10
11
  */
@@ -4,7 +4,8 @@ import '@iconify/types';
4
4
  import '@iconify/utils/lib/customisations/defaults';
5
5
 
6
6
  /**
7
- * Removes clip path from SVG, which Figma and Penpot add to icons that might have overflowing elements
7
+ * Removes clip path from SVG, which Figma and Penpot add to icons that might have overflowing elements.
8
+ * Also removes mess generated by Penpot
8
9
  *
9
10
  * Function was originally designed for Figma only, but later added support for Penpot
10
11
  */
@@ -4,7 +4,8 @@ import '@iconify/types';
4
4
  import '@iconify/utils/lib/customisations/defaults';
5
5
 
6
6
  /**
7
- * Removes clip path from SVG, which Figma and Penpot add to icons that might have overflowing elements
7
+ * Removes clip path from SVG, which Figma and Penpot add to icons that might have overflowing elements.
8
+ * Also removes mess generated by Penpot
8
9
  *
9
10
  * Function was originally designed for Figma only, but later added support for Penpot
10
11
  */
@@ -1,5 +1,14 @@
1
- import { defsTag, maskTags, symbolTag } from '../svg/data/tags.mjs';
1
+ import { stringToColor, colorToString } from '@iconify/utils';
2
+ import { cleanupInlineStyle } from '../svg/cleanup/inline-style.mjs';
3
+ import { maskTags, symbolTag, defsTag } from '../svg/data/tags.mjs';
4
+ import { parseSVG } from '../svg/parse.mjs';
2
5
  import { unwrapEmptyGroup } from './unwrap.mjs';
6
+ import '../css/parse.mjs';
7
+ import '../css/parser/tokens.mjs';
8
+ import '../css/parser/error.mjs';
9
+ import '../css/parser/strings.mjs';
10
+ import '../css/parser/text.mjs';
11
+ import '../svg/data/attributes.mjs';
3
12
 
4
13
  function isTinyNumber(value, limit) {
5
14
  const num = parseInt(value);
@@ -23,8 +32,10 @@ function checkClipPathNode(clipNode, expectedWidth, expectedHeight) {
23
32
  ...childNode.attribs
24
33
  };
25
34
  delete attribs["fill"];
26
- const fill = childNode.attribs["fill"]?.toLowerCase();
27
- if (fill !== "white" && fill !== "#fff" && fill !== "#ffffff" && fill !== void 0) {
35
+ const fill = childNode.attribs["fill"];
36
+ const colorValue = fill ? stringToColor(fill) : null;
37
+ const colorString = colorValue ? colorToString(colorValue) : null;
38
+ if (fill && colorString !== "#fff") {
28
39
  console.warn(
29
40
  "Unxepected fill on clip path:",
30
41
  childNode.attribs["fill"]
@@ -41,24 +52,6 @@ function checkClipPathNode(clipNode, expectedWidth, expectedHeight) {
41
52
  }
42
53
  delete attribs["width"];
43
54
  delete attribs["height"];
44
- for (const attr in childNode.attribs) {
45
- const value = childNode.attribs[attr];
46
- switch (attr) {
47
- case "rx":
48
- case "ry":
49
- case "x":
50
- case "y":
51
- if (value === "0") {
52
- delete attribs[attr];
53
- }
54
- break;
55
- case "transform":
56
- if (value === "") {
57
- delete attribs[attr];
58
- }
59
- break;
60
- }
61
- }
62
55
  break;
63
56
  }
64
57
  default:
@@ -95,6 +88,58 @@ function remove(svg) {
95
88
  unwrapEmptyGroup(svg);
96
89
  let content = svg.toString();
97
90
  const backup = content;
91
+ const isPenpot = content.includes("frame-clip-def");
92
+ if (isPenpot) {
93
+ cleanupInlineStyle(svg);
94
+ parseSVG(svg, (item) => {
95
+ const tagName = item.tagName;
96
+ for (const attr in item.element.attribs) {
97
+ const value = item.element.attribs[attr];
98
+ switch (attr) {
99
+ case "id":
100
+ if (!maskTags.has(tagName) && !symbolTag.has(tagName)) {
101
+ item.$element.removeAttr(attr);
102
+ }
103
+ break;
104
+ case "class":
105
+ case "xmlns:xlink":
106
+ case "version":
107
+ item.$element.removeAttr(attr);
108
+ break;
109
+ case "transform": {
110
+ const trimmed = value.replace(/\s+/g, "").replace(/\.0+/g, "");
111
+ if (!trimmed || trimmed === "matrix(1,0,0,1,0,0)") {
112
+ item.$element.removeAttr(attr);
113
+ }
114
+ break;
115
+ }
116
+ case "rx":
117
+ case "ry":
118
+ case "x":
119
+ case "y":
120
+ if (value === "0") {
121
+ item.$element.removeAttr(attr);
122
+ }
123
+ break;
124
+ case "fill-opacity":
125
+ case "stroke-opacity":
126
+ case "opacity":
127
+ if (value === "1") {
128
+ item.$element.removeAttr(attr);
129
+ }
130
+ break;
131
+ case "fill":
132
+ case "stroke": {
133
+ const colorValue = stringToColor(value);
134
+ if (colorValue?.type === "rgb") {
135
+ item.$element.attr(attr, colorToString(colorValue));
136
+ }
137
+ }
138
+ }
139
+ }
140
+ });
141
+ content = svg.toString();
142
+ }
98
143
  const clipPathBlocks = content.match(
99
144
  /<clipPath[^>]*>[\s\S]+?<\/clipPath>/g
100
145
  );
@@ -103,7 +148,6 @@ function remove(svg) {
103
148
  const lines = content.split(split);
104
149
  content = lines.shift() + split + lines.join("");
105
150
  }
106
- content = content.replaceAll('class="frame-clip-def frame-clip"', "");
107
151
  if (content.includes("<defs>")) {
108
152
  content = content.replace(/<\/?defs>/g, "");
109
153
  }
@@ -136,30 +180,21 @@ function remove(svg) {
136
180
  if (typeof clipID !== "string") {
137
181
  return false;
138
182
  }
139
- const checkClipPath = (node) => {
140
- const id = node.attribs["id"];
141
- if (id !== clipID) {
142
- return;
143
- }
144
- const result = checkClipPathNode(
145
- node,
146
- svg.viewBox.width,
147
- svg.viewBox.height
148
- );
149
- cheerio(node).remove();
150
- return result;
151
- };
152
183
  const findClipPath = () => {
153
184
  for (let i = 0; i < children.length; i++) {
154
185
  const node = children[i];
155
- if (node.type === "tag") {
156
- const tagName = node.tagName;
157
- if (tagName === "clipPath") {
158
- const result = checkClipPath(node);
159
- if (result !== void 0) {
160
- return result;
161
- }
186
+ if (node.type === "tag" && node.tagName === "clipPath") {
187
+ const id = node.attribs["id"];
188
+ if (id === clipID) {
189
+ const result = checkClipPathNode(
190
+ node,
191
+ svg.viewBox.width,
192
+ svg.viewBox.height
193
+ );
194
+ cheerio(node).remove();
195
+ return result;
162
196
  }
197
+ return;
163
198
  }
164
199
  }
165
200
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "description": "Collection of functions for cleaning up and parsing SVG for Iconify project",
5
5
  "author": "Vjacheslav Trushkin",
6
- "version": "4.0.1",
6
+ "version": "4.0.2",
7
7
  "license": "MIT",
8
8
  "bugs": "https://github.com/iconify/tools/issues",
9
9
  "homepage": "https://github.com/iconify/tools",