@emasoft/svg-matrix 1.2.1 → 1.3.1
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.
- package/README.md +75 -0
- package/bin/svgfonts.js +1666 -0
- package/bin/svgm.js +1 -1
- package/dist/svg-matrix.global.min.js +8 -0
- package/dist/svg-matrix.min.js +2 -2
- package/dist/svg-toolbox.global.min.js +493 -0
- package/dist/svg-toolbox.min.js +36 -36
- package/dist/svgm.min.js +64 -64
- package/dist/version.json +44 -16
- package/package.json +11 -3
- package/scripts/postinstall.js +10 -4
- package/scripts/version-sync.js +2 -2
- package/src/animation-references.js +2 -1
- package/src/bezier-intersections.js +1 -1
- package/src/browser-verify.js +0 -1
- package/src/clip-path-resolver.js +3 -1
- package/src/flatten-pipeline.js +0 -3
- package/src/font-manager.js +1906 -0
- package/src/index.js +2 -2
- package/src/inkscape-support.js +2 -2
- package/src/mask-resolver.js +14 -6
- package/src/matrix.js +3 -3
- package/src/mesh-gradient.js +43 -2
- package/src/off-canvas-detection.js +14 -22
- package/src/pattern-resolver.js +3 -2
- package/src/svg-boolean-ops.js +0 -5
- package/src/svg-collections.js +11 -0
- package/src/svg-matrix-lib.js +4 -3
- package/src/svg-parser.js +0 -28
- package/src/svg-rendering-context.js +2 -4
- package/src/svg-toolbox-lib.js +2 -2
- package/src/svg-validation-data.js +1 -1
- package/src/svgm-lib.js +2 -2
- package/src/transform-optimization.js +93 -142
- package/src/verification.js +0 -2
- package/templates/svgm_replacement_map.yml +53 -0
|
@@ -196,6 +196,97 @@ export function matricesEqual(m1, m2, tolerance = VERIFICATION_TOLERANCE) {
|
|
|
196
196
|
return matrixMaxDifference(m1, m2).lessThan(D(tolerance));
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Compute the combined matrix from a list of transform objects.
|
|
201
|
+
* Used internally for verification that optimizations preserve the transform result.
|
|
202
|
+
*
|
|
203
|
+
* @param {Array<Object>} transforms - Array of transform objects with type and params
|
|
204
|
+
* @returns {Matrix} Combined 3x3 transformation matrix
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* const transforms = [
|
|
208
|
+
* { type: 'translate', params: { tx: 10, ty: 20 } },
|
|
209
|
+
* { type: 'rotate', params: { angle: 0.5 } }
|
|
210
|
+
* ];
|
|
211
|
+
* const combined = computeCombinedMatrix(transforms);
|
|
212
|
+
*/
|
|
213
|
+
export function computeCombinedMatrix(transforms) {
|
|
214
|
+
let combined = identityMatrix();
|
|
215
|
+
|
|
216
|
+
for (const t of transforms) {
|
|
217
|
+
// Validate transform object structure
|
|
218
|
+
if (
|
|
219
|
+
!t ||
|
|
220
|
+
typeof t !== "object" ||
|
|
221
|
+
!t.type ||
|
|
222
|
+
!t.params ||
|
|
223
|
+
typeof t.params !== "object"
|
|
224
|
+
) {
|
|
225
|
+
continue; // Skip malformed transforms
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let m = null;
|
|
229
|
+
switch (t.type) {
|
|
230
|
+
case "translate":
|
|
231
|
+
if (
|
|
232
|
+
t.params.tx === null ||
|
|
233
|
+
t.params.tx === undefined ||
|
|
234
|
+
t.params.ty === null ||
|
|
235
|
+
t.params.ty === undefined
|
|
236
|
+
) {
|
|
237
|
+
continue; // Skip transforms with missing params
|
|
238
|
+
}
|
|
239
|
+
m = translationMatrix(t.params.tx, t.params.ty);
|
|
240
|
+
break;
|
|
241
|
+
case "rotate":
|
|
242
|
+
if (t.params.angle === null || t.params.angle === undefined) {
|
|
243
|
+
continue; // Skip transforms with missing angle
|
|
244
|
+
}
|
|
245
|
+
if (
|
|
246
|
+
t.params.cx !== undefined &&
|
|
247
|
+
t.params.cx !== null &&
|
|
248
|
+
t.params.cy !== undefined &&
|
|
249
|
+
t.params.cy !== null
|
|
250
|
+
) {
|
|
251
|
+
m = rotationMatrixAroundPoint(
|
|
252
|
+
t.params.angle,
|
|
253
|
+
t.params.cx,
|
|
254
|
+
t.params.cy,
|
|
255
|
+
);
|
|
256
|
+
} else {
|
|
257
|
+
m = rotationMatrix(t.params.angle);
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
case "scale":
|
|
261
|
+
if (
|
|
262
|
+
t.params.sx === null ||
|
|
263
|
+
t.params.sx === undefined ||
|
|
264
|
+
t.params.sy === null ||
|
|
265
|
+
t.params.sy === undefined
|
|
266
|
+
) {
|
|
267
|
+
continue; // Skip transforms with missing params
|
|
268
|
+
}
|
|
269
|
+
m = scaleMatrix(t.params.sx, t.params.sy);
|
|
270
|
+
break;
|
|
271
|
+
case "matrix":
|
|
272
|
+
if (!t.params.matrix) {
|
|
273
|
+
continue; // Skip transforms with missing matrix
|
|
274
|
+
}
|
|
275
|
+
m = t.params.matrix;
|
|
276
|
+
break;
|
|
277
|
+
default:
|
|
278
|
+
// Skip unknown transform types
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (m !== null) {
|
|
283
|
+
combined = combined.mul(m);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return combined;
|
|
288
|
+
}
|
|
289
|
+
|
|
199
290
|
// ============================================================================
|
|
200
291
|
// Transform Merging Functions
|
|
201
292
|
// ============================================================================
|
|
@@ -920,77 +1011,7 @@ export function optimizeTransformList(transforms) {
|
|
|
920
1011
|
}
|
|
921
1012
|
|
|
922
1013
|
// Calculate original combined matrix for verification
|
|
923
|
-
|
|
924
|
-
for (const t of transforms) {
|
|
925
|
-
// Validate transform object structure
|
|
926
|
-
if (
|
|
927
|
-
!t ||
|
|
928
|
-
typeof t !== "object" ||
|
|
929
|
-
!t.type ||
|
|
930
|
-
!t.params ||
|
|
931
|
-
typeof t.params !== "object"
|
|
932
|
-
) {
|
|
933
|
-
continue; // Skip malformed transforms
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
let m = null; // Initialize m to null to catch missing assignments
|
|
937
|
-
switch (t.type) {
|
|
938
|
-
case "translate":
|
|
939
|
-
if (
|
|
940
|
-
t.params.tx === null ||
|
|
941
|
-
t.params.tx === undefined ||
|
|
942
|
-
t.params.ty === null ||
|
|
943
|
-
t.params.ty === undefined
|
|
944
|
-
) {
|
|
945
|
-
continue; // Skip transforms with missing params
|
|
946
|
-
}
|
|
947
|
-
m = translationMatrix(t.params.tx, t.params.ty);
|
|
948
|
-
break;
|
|
949
|
-
case "rotate":
|
|
950
|
-
if (t.params.angle === null || t.params.angle === undefined) {
|
|
951
|
-
continue; // Skip transforms with missing angle
|
|
952
|
-
}
|
|
953
|
-
if (
|
|
954
|
-
t.params.cx !== undefined &&
|
|
955
|
-
t.params.cx !== null &&
|
|
956
|
-
t.params.cy !== undefined &&
|
|
957
|
-
t.params.cy !== null
|
|
958
|
-
) {
|
|
959
|
-
m = rotationMatrixAroundPoint(
|
|
960
|
-
t.params.angle,
|
|
961
|
-
t.params.cx,
|
|
962
|
-
t.params.cy,
|
|
963
|
-
);
|
|
964
|
-
} else {
|
|
965
|
-
m = rotationMatrix(t.params.angle);
|
|
966
|
-
}
|
|
967
|
-
break;
|
|
968
|
-
case "scale":
|
|
969
|
-
if (
|
|
970
|
-
t.params.sx === null ||
|
|
971
|
-
t.params.sx === undefined ||
|
|
972
|
-
t.params.sy === null ||
|
|
973
|
-
t.params.sy === undefined
|
|
974
|
-
) {
|
|
975
|
-
continue; // Skip transforms with missing params
|
|
976
|
-
}
|
|
977
|
-
m = scaleMatrix(t.params.sx, t.params.sy);
|
|
978
|
-
break;
|
|
979
|
-
case "matrix":
|
|
980
|
-
if (!t.params.matrix) {
|
|
981
|
-
continue; // Skip transforms with missing matrix
|
|
982
|
-
}
|
|
983
|
-
m = t.params.matrix;
|
|
984
|
-
break;
|
|
985
|
-
default:
|
|
986
|
-
// Skip unknown transform types, but don't try to multiply null matrix
|
|
987
|
-
continue;
|
|
988
|
-
}
|
|
989
|
-
// Only multiply if m was successfully assigned (prevents undefined matrix multiplication)
|
|
990
|
-
if (m !== null) {
|
|
991
|
-
originalMatrix = originalMatrix.mul(m);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
1014
|
+
const originalMatrix = computeCombinedMatrix(transforms);
|
|
994
1015
|
|
|
995
1016
|
// Step 1: Remove identity transforms
|
|
996
1017
|
const { transforms: step1, removedCount: _removedCount } =
|
|
@@ -1210,77 +1231,7 @@ export function optimizeTransformList(transforms) {
|
|
|
1210
1231
|
const { transforms: final } = removeIdentityTransforms(optimized);
|
|
1211
1232
|
|
|
1212
1233
|
// Calculate optimized combined matrix for verification
|
|
1213
|
-
|
|
1214
|
-
for (const t of final) {
|
|
1215
|
-
// Validate transform object structure
|
|
1216
|
-
if (
|
|
1217
|
-
!t ||
|
|
1218
|
-
typeof t !== "object" ||
|
|
1219
|
-
!t.type ||
|
|
1220
|
-
!t.params ||
|
|
1221
|
-
typeof t.params !== "object"
|
|
1222
|
-
) {
|
|
1223
|
-
continue; // Skip malformed transforms
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
let m = null; // Initialize m to null to catch missing assignments
|
|
1227
|
-
switch (t.type) {
|
|
1228
|
-
case "translate":
|
|
1229
|
-
if (
|
|
1230
|
-
t.params.tx === null ||
|
|
1231
|
-
t.params.tx === undefined ||
|
|
1232
|
-
t.params.ty === null ||
|
|
1233
|
-
t.params.ty === undefined
|
|
1234
|
-
) {
|
|
1235
|
-
continue; // Skip transforms with missing params
|
|
1236
|
-
}
|
|
1237
|
-
m = translationMatrix(t.params.tx, t.params.ty);
|
|
1238
|
-
break;
|
|
1239
|
-
case "rotate":
|
|
1240
|
-
if (t.params.angle === null || t.params.angle === undefined) {
|
|
1241
|
-
continue; // Skip transforms with missing angle
|
|
1242
|
-
}
|
|
1243
|
-
if (
|
|
1244
|
-
t.params.cx !== undefined &&
|
|
1245
|
-
t.params.cx !== null &&
|
|
1246
|
-
t.params.cy !== undefined &&
|
|
1247
|
-
t.params.cy !== null
|
|
1248
|
-
) {
|
|
1249
|
-
m = rotationMatrixAroundPoint(
|
|
1250
|
-
t.params.angle,
|
|
1251
|
-
t.params.cx,
|
|
1252
|
-
t.params.cy,
|
|
1253
|
-
);
|
|
1254
|
-
} else {
|
|
1255
|
-
m = rotationMatrix(t.params.angle);
|
|
1256
|
-
}
|
|
1257
|
-
break;
|
|
1258
|
-
case "scale":
|
|
1259
|
-
if (
|
|
1260
|
-
t.params.sx === null ||
|
|
1261
|
-
t.params.sx === undefined ||
|
|
1262
|
-
t.params.sy === null ||
|
|
1263
|
-
t.params.sy === undefined
|
|
1264
|
-
) {
|
|
1265
|
-
continue; // Skip transforms with missing params
|
|
1266
|
-
}
|
|
1267
|
-
m = scaleMatrix(t.params.sx, t.params.sy);
|
|
1268
|
-
break;
|
|
1269
|
-
case "matrix":
|
|
1270
|
-
if (!t.params.matrix) {
|
|
1271
|
-
continue; // Skip transforms with missing matrix
|
|
1272
|
-
}
|
|
1273
|
-
m = t.params.matrix;
|
|
1274
|
-
break;
|
|
1275
|
-
default:
|
|
1276
|
-
// Skip unknown transform types, but don't try to multiply null matrix
|
|
1277
|
-
continue;
|
|
1278
|
-
}
|
|
1279
|
-
// Only multiply if m was successfully assigned (prevents undefined matrix multiplication)
|
|
1280
|
-
if (m !== null) {
|
|
1281
|
-
optimizedMatrix = optimizedMatrix.mul(m);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1234
|
+
const optimizedMatrix = computeCombinedMatrix(final);
|
|
1284
1235
|
|
|
1285
1236
|
// VERIFICATION: Combined matrices must be equal
|
|
1286
1237
|
const maxError = matrixMaxDifference(originalMatrix, optimizedMatrix);
|
package/src/verification.js
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SVG Font Replacement Map
|
|
2
|
+
# ========================
|
|
3
|
+
# This file defines font replacements for SVG processing.
|
|
4
|
+
#
|
|
5
|
+
# Format:
|
|
6
|
+
# original_font: replacement_font
|
|
7
|
+
#
|
|
8
|
+
# Examples:
|
|
9
|
+
# "Arial": "Inter" # Replace Arial with Inter
|
|
10
|
+
# "Times New Roman": "Noto Serif"
|
|
11
|
+
#
|
|
12
|
+
# Font sources (in priority order):
|
|
13
|
+
# 1. Local system fonts
|
|
14
|
+
# 2. Google Fonts (default, free)
|
|
15
|
+
# 3. FontGet (npm: fontget)
|
|
16
|
+
# 4. fnt (brew: alexmyczko/fnt/fnt)
|
|
17
|
+
#
|
|
18
|
+
# Options per font:
|
|
19
|
+
# embed: true # Embed as base64 (default: true)
|
|
20
|
+
# subset: true # Only include used glyphs (default: true)
|
|
21
|
+
# source: "google" # Force specific source
|
|
22
|
+
# weight: "400,700" # Specific weights to include
|
|
23
|
+
# style: "normal,italic" # Specific styles
|
|
24
|
+
#
|
|
25
|
+
# Advanced format:
|
|
26
|
+
# "Arial":
|
|
27
|
+
# replacement: "Inter"
|
|
28
|
+
# embed: true
|
|
29
|
+
# subset: true
|
|
30
|
+
# source: "google"
|
|
31
|
+
# weights: ["400", "500", "700"]
|
|
32
|
+
|
|
33
|
+
replacements:
|
|
34
|
+
# Common font replacements
|
|
35
|
+
# "Arial": "Inter"
|
|
36
|
+
# "Helvetica": "Inter"
|
|
37
|
+
# "Times New Roman": "Noto Serif"
|
|
38
|
+
# "Times": "Noto Serif"
|
|
39
|
+
# "Courier New": "Fira Code"
|
|
40
|
+
# "Courier": "Fira Code"
|
|
41
|
+
# "Georgia": "Merriweather"
|
|
42
|
+
# "Verdana": "Open Sans"
|
|
43
|
+
# "Comic Sans MS": "Comic Neue"
|
|
44
|
+
|
|
45
|
+
# Icon fonts
|
|
46
|
+
# "Font Awesome": "Font Awesome 6 Free"
|
|
47
|
+
# "Material Icons": "Material Symbols Outlined"
|
|
48
|
+
|
|
49
|
+
options:
|
|
50
|
+
default_embed: true
|
|
51
|
+
default_subset: true
|
|
52
|
+
fallback_source: "google"
|
|
53
|
+
auto_download: true
|