Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mergeText plugin #1908

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
31a8bcd
Added mergeText plugin.
johnkenny54 Dec 2, 2023
45a6288
Added mergeText plugin to builtin.js.
johnkenny54 Dec 2, 2023
a9b6d2d
Merge branch 'main' into mergeText
johnkenny54 Dec 24, 2023
c379a5f
Changed to use inheritableAttrs from collections.js.
johnkenny54 Dec 24, 2023
f72ff44
Fixed formatting.
johnkenny54 Dec 25, 2023
2cccb0e
Added mergeText documentation.
johnkenny54 Dec 25, 2023
eac2f56
Merge branch 'main' into mergeText
johnkenny54 Dec 26, 2023
8855681
Fixed formatting.
johnkenny54 Dec 26, 2023
562152a
feat(convertPathData): use the sagitta of arcs to round and convert t…
KTibow Dec 27, 2023
c2cacc7
chore: move health files to .github (#1906)
SethFalco Dec 27, 2023
4768af6
chore: Update @types/csso and correct types (#1910)
jdufresne Dec 27, 2023
967d2f1
deps: update css-tree and clean ts-ignores (#1911)
SethFalco Dec 27, 2023
f238d6a
feat(convertPathData): allow converting q to t in more cases (#1889)
KTibow Dec 27, 2023
433dcef
refactor(convertPaths): clean up plugin (#1913)
SethFalco Dec 28, 2023
db05c57
chore: Use idiomatic Jest patterns to assert exceptions (#1909)
jdufresne Dec 28, 2023
f6a2ca2
chore: improve jest tests with more precise assertions (#1912)
jdufresne Dec 28, 2023
ccadbb1
Added mergeText plugin.
johnkenny54 Dec 2, 2023
a94c45a
Added mergeText plugin to builtin.js.
johnkenny54 Dec 2, 2023
65c4d7f
Changed to use inheritableAttrs from collections.js.
johnkenny54 Dec 24, 2023
5de4997
Fixed formatting.
johnkenny54 Dec 25, 2023
8cb03da
Added mergeText documentation.
johnkenny54 Dec 25, 2023
0145589
Fixed formatting.
johnkenny54 Dec 26, 2023
d7aa272
Merge branch 'mergeText' of github.com:johnkenny54/svgo into mergeText
johnkenny54 Dec 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/FUNDING.yml

This file was deleted.

5 changes: 0 additions & 5 deletions SECURITY.md

This file was deleted.

3 changes: 3 additions & 0 deletions docs/03-plugins/convert-path-data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ svgo:
transformPrecision:
description: Number of decimal places to round to, using conventional rounding rules.
default: 5
smartArcRounding:
description: Round the radius of circular arcs when the effective change is under the error. The effective change is determined using the <a href="https://wikipedia.org/wiki/Sagitta_(geometry)" target="_blank">sagitta</a> of the arc.
default: true
removeUseless:
description: Remove redundant path commands that don't draw anything.
default: true
Expand Down
15 changes: 15 additions & 0 deletions docs/03-plugins/merge-text.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: Merge Text
svgo:
pluginId: mergeText
---

Merge adjacent `<text>` elements and remove `<tspan>` when it is the only child of a `<text>` element.

If the SVG contains any `<style>` elements, it will not be modified.

`<text>` or `<tspan>` elements which have non-inheritable attributes other than `x` or `y` will not be modified.

## Implementation

- https://github.com/svg/svgo/blob/main/plugins/mergeText.js
1 change: 1 addition & 0 deletions lib/builtin.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ exports.builtin = [
require('../plugins/mergeStyles.js'),
require('../plugins/inlineStyles.js'),
require('../plugins/mergePaths.js'),
require('../plugins/mergeText.js'),
require('../plugins/minifyStyles.js'),
require('../plugins/moveElemsAttrsToGroup.js'),
require('../plugins/moveGroupAttrsToElems.js'),
Expand Down
2 changes: 1 addition & 1 deletion lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const parseSvg = (data, from) => {
*/
let current = root;
/**
* @type {Array<XastParent>}
* @type {XastParent[]}
*/
const stack = [root];

Expand Down
6 changes: 3 additions & 3 deletions lib/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ const readNumber = (string, cursor) => {
};

/**
* @type {(string: string) => Array<PathDataItem>}
* @type {(string: string) => PathDataItem[]}
*/
const parsePathData = (string) => {
/**
* @type {Array<PathDataItem>}
* @type {PathDataItem[]}
*/
const pathData = [];
/**
Expand Down Expand Up @@ -307,7 +307,7 @@ const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => {

/**
* @typedef {{
* pathData: Array<PathDataItem>;
* pathData: PathDataItem[];
* precision?: number;
* disableSpaceAfterFlags?: boolean;
* }} StringifyPathDataOptions
Expand Down
48 changes: 24 additions & 24 deletions lib/path.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,55 @@ const { parsePathData, stringifyPathData } = require('./path.js');

describe('parse path data', () => {
it('should allow spaces between commands', () => {
expect(parsePathData('M0 10 L \n\r\t20 30')).toEqual([
expect(parsePathData('M0 10 L \n\r\t20 30')).toStrictEqual([
{ command: 'M', args: [0, 10] },
{ command: 'L', args: [20, 30] },
]);
});
it('should allow spaces and commas between arguments', () => {
expect(parsePathData('M0 , 10 L 20 \n\r\t30,40,50')).toEqual([
expect(parsePathData('M0 , 10 L 20 \n\r\t30,40,50')).toStrictEqual([
{ command: 'M', args: [0, 10] },
{ command: 'L', args: [20, 30] },
{ command: 'L', args: [40, 50] },
]);
});
it('should forbid commas before commands', () => {
expect(parsePathData(', M0 10')).toEqual([]);
expect(parsePathData(', M0 10')).toStrictEqual([]);
});
it('should forbid commas between commands', () => {
expect(parsePathData('M0,10 , L 20,30')).toEqual([
expect(parsePathData('M0,10 , L 20,30')).toStrictEqual([
{ command: 'M', args: [0, 10] },
]);
});
it('should forbid commas between command name and argument', () => {
expect(parsePathData('M0,10 L,20,30')).toEqual([
expect(parsePathData('M0,10 L,20,30')).toStrictEqual([
{ command: 'M', args: [0, 10] },
]);
});
it('should forbid multiple commas in a row', () => {
expect(parsePathData('M0 , , 10')).toEqual([]);
expect(parsePathData('M0 , , 10')).toStrictEqual([]);
});
it('should stop when unknown char appears', () => {
expect(parsePathData('M0 10 , L 20 #40')).toEqual([
expect(parsePathData('M0 10 , L 20 #40')).toStrictEqual([
{ command: 'M', args: [0, 10] },
]);
});
it('should stop when not enough arguments', () => {
expect(parsePathData('M0 10 L 20 L 30 40')).toEqual([
expect(parsePathData('M0 10 L 20 L 30 40')).toStrictEqual([
{ command: 'M', args: [0, 10] },
]);
});
it('should stop if moveto not the first command', () => {
expect(parsePathData('L 10 20')).toEqual([]);
expect(parsePathData('10 20')).toEqual([]);
expect(parsePathData('L 10 20')).toStrictEqual([]);
expect(parsePathData('10 20')).toStrictEqual([]);
});
it('should stop on invalid scientific notation', () => {
expect(parsePathData('M 0 5e++1 L 0 0')).toEqual([
expect(parsePathData('M 0 5e++1 L 0 0')).toStrictEqual([
{ command: 'M', args: [0, 5] },
]);
});
it('should stop on invalid numbers', () => {
expect(parsePathData('M ...')).toEqual([]);
expect(parsePathData('M ...')).toStrictEqual([]);
});
it('should handle arcs', () => {
expect(
Expand All @@ -71,7 +71,7 @@ describe('parse path data', () => {
l 50,-25
`,
),
).toEqual([
).toStrictEqual([
{ command: 'M', args: [600, 350] },
{ command: 'l', args: [50, -25] },
{ command: 'a', args: [25, 25, -30, 0, 1, 50, -25] },
Expand All @@ -96,7 +96,7 @@ describe('stringify path data', () => {
{ command: 'H', args: [50] },
],
}),
).toEqual('M0 0h10 20 30H40 50');
).toBe('M0 0h10 20 30H40 50');
});
it('should not combine sequence of moveto', () => {
expect(
Expand All @@ -108,7 +108,7 @@ describe('stringify path data', () => {
{ command: 'm', args: [40, 50] },
],
}),
).toEqual('M0 0M10 10m20 30m40 50');
).toBe('M0 0M10 10m20 30m40 50');
});
it('should combine moveto and sequence of lineto', () => {
expect(
Expand All @@ -122,15 +122,15 @@ describe('stringify path data', () => {
{ command: 'L', args: [10, 10] },
],
}),
).toEqual('m0 0 10 10M0 0l10 10M0 0 10 10');
).toBe('m0 0 10 10M0 0l10 10M0 0 10 10');
expect(
stringifyPathData({
pathData: [
{ command: 'm', args: [0, 0] },
{ command: 'L', args: [10, 10] },
],
}),
).toEqual('M0 0 10 10');
).toBe('M0 0 10 10');
});
it('should avoid space before first, negative and decimals', () => {
expect(
Expand All @@ -142,11 +142,11 @@ describe('stringify path data', () => {
{ command: 'L', args: [7, 0.8] },
],
}),
).toEqual('M0-1.2.3 4 5-.6 7 .8');
).toBe('M0-1.2.3 4 5-.6 7 .8');
});
it('should configure precision', () => {
/**
* @type {Array<PathDataItem>}
* @type {PathDataItem[]}
*/
const pathData = [
{ command: 'M', args: [0, -1.9876] },
Expand All @@ -159,17 +159,17 @@ describe('stringify path data', () => {
pathData,
precision: 3,
}),
).toEqual('M0-1.988.3 3.142-.3-3.142 100 200');
).toBe('M0-1.988.3 3.142-.3-3.142 100 200');
expect(
stringifyPathData({
pathData,
precision: 0,
}),
).toEqual('M0-2 0 3 0-3 100 200');
).toBe('M0-2 0 3 0-3 100 200');
});
it('allows to avoid spaces after arc flags', () => {
/**
* @type {Array<PathDataItem>}
* @type {PathDataItem[]}
*/
const pathData = [
{ command: 'M', args: [0, 0] },
Expand All @@ -182,12 +182,12 @@ describe('stringify path data', () => {
pathData,
disableSpaceAfterFlags: false,
}),
).toEqual('M0 0A50 50 10 1 0 .2 20a50 50 10 1 0 .2 20 50 50 10 1 0 .2 20');
).toBe('M0 0A50 50 10 1 0 .2 20a50 50 10 1 0 .2 20 50 50 10 1 0 .2 20');
expect(
stringifyPathData({
pathData,
disableSpaceAfterFlags: true,
}),
).toEqual('M0 0A50 50 10 10.2 20a50 50 10 10.2 20 50 50 10 10.2 20');
).toBe('M0 0A50 50 10 10.2 20a50 50 10 10.2 20 50 50 10 10.2 20');
});
});
11 changes: 5 additions & 6 deletions lib/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
const csstree = require('css-tree');
const csswhat = require('css-what');
const {
// @ts-ignore internal api
syntax: { specificity },
} = require('csso');
const { visit, matches } = require('./xast.js');
Expand All @@ -34,7 +33,7 @@ const csstreeWalkSkip = csstree.walk.skip;
*/
const parseRule = (ruleNode, dynamic) => {
/**
* @type {Array<StylesheetDeclaration>}
* @type {StylesheetDeclaration[]}
*/
const declarations = [];
// collect declarations
Expand Down Expand Up @@ -74,10 +73,10 @@ const parseRule = (ruleNode, dynamic) => {
};

/**
* @type {(css: string, dynamic: boolean) => Array<StylesheetRule>}
* @type {(css: string, dynamic: boolean) => StylesheetRule[]}
*/
const parseStylesheet = (css, dynamic) => {
/** @type {Array<StylesheetRule>} */
/** @type {StylesheetRule[]} */
const rules = [];
const ast = csstree.parse(css, {
parseValue: false,
Expand Down Expand Up @@ -108,10 +107,10 @@ const parseStylesheet = (css, dynamic) => {
};

/**
* @type {(css: string) => Array<StylesheetDeclaration>}
* @type {(css: string) => StylesheetDeclaration[]}
*/
const parseStyleDeclarations = (css) => {
/** @type {Array<StylesheetDeclaration>} */
/** @type {StylesheetDeclaration[]} */
const declarations = [];
const ast = csstree.parse(css, {
context: 'declarationList',
Expand Down