| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 |
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
9x
9x
9x
4x
5x
9x
9x
1x
72x
36x
36x
36x
36x
36x
36x
36x
36x
36x
36x
36x
36x
36x
36x
36x
1x
| // Plugin code based off of svgo v1.0.3.
import { JsApi } from './jsapi';
import { Plugin } from '../plugins/_types';
import { bakeGroupTransforms } from '../plugins/bakeGroupTransforms';
import { collapseGroups } from '../plugins/collapseGroups';
import { convertPathData } from '../plugins/convertPathData';
import { js2xml } from './js2xml';
import { mergePaths } from '../plugins/mergePaths';
import { processPlugins } from '../plugins/_plugins';
import { removeComments } from '../plugins/removeComments';
import { removeDefaults } from '../plugins/removeDefaults';
import { removeEmptyGroups } from '../plugins/removeEmptyGroups';
import { removeHiddenElems } from '../plugins/removeHiddenElems';
import { removeXMLProcInst } from '../plugins/removeXMLProcInst';
import { xml2js } from './xml2js';
// The order is from https://github.com/svg/svgo/blob/master/.svgo.yml
export const plugins: { [name: string]: Plugin } = {
removeXMLProcInst,
removeComments,
// cleanupAttrs,
// cleanupIDs,
// cleanupNumericValues,
// convertColors,
removeDefaults,
// removeUselessStrokeAndFill,
removeHiddenElems,
bakeGroupTransforms,
collapseGroups,
convertPathData,
// convertTransform,
removeEmptyGroups,
mergePaths,
// removeUnusedNS,
// sortAttrs,
};
const batchedPlugins = (function(ps: Plugin[]) {
return ps.map(item => [item]).reduce(
(arr, item) => {
const last = arr[arr.length - 1];
if (last && item[0].type === last[0].type) {
last.push(item[0]);
} else {
arr.push(item);
}
return arr;
},
[] as Plugin[][]
);
})(Object.keys(plugins).map(k => plugins[k]));
// TODO: make it possible to configure indentation too?
export interface Options {
plugins: Plugin[][];
multipass?: boolean;
pretty?: boolean;
}
export class Avdo {
constructor(
private readonly Ioptions: Options = {
plugins: batchedPlugins,
multipass: true,
pretty: true,
},
) {}
optimize(xml: string) {
return new Promise<string>((resolve, reject) => {
const maxPassCount = this.options.multipass ? 10 : 1;
let numPasses = 0;
let prevResultSize = Number.POSITIVE_INFINITY;
const onFail = (error: string) => reject(error);
const onSuccess = (result: string) => {
numPasses++;
Iif (numPasses < maxPassCount && result.length < prevResultSize) {
prevResultSize = result.length;
this.optimizeOnce(result, onSuccess, onFail);
} else {
resolve(result);
}
};
this.optimizeOnce(xml, onSuccess, onFail);
});
}
private optimizeOnce(
xml: string,
onSuccess: (result: string) => void,
onFail: (error: string) => void,
) {
xml2js(
xml,
jsApi => {
jsApi = processPlugins(jsApi, this.options.plugins);
onSuccess(js2xml(jsApi, { pretty: this.options.pretty }));
},
error => onFail(error),
);
}
}
|