js.documents 2.0.0 → 2.2.0
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 +49 -29
- package/dist/codecs/registry.cjs +14 -0
- package/dist/codecs/registry.js +14 -0
- package/dist/convert/capability.cjs +10 -0
- package/dist/convert/capability.js +10 -0
- package/dist/convert/codec.cjs +30 -0
- package/dist/convert/codec.d.cts +7 -1
- package/dist/convert/codec.d.ts +7 -1
- package/dist/convert/codec.js +27 -3
- package/dist/convert/composition.cjs +49 -7
- package/dist/convert/composition.d.cts +12 -5
- package/dist/convert/composition.d.ts +12 -5
- package/dist/convert/composition.js +49 -7
- package/dist/convert/convert.cjs +48 -0
- package/dist/convert/convert.d.cts +30 -1
- package/dist/convert/convert.d.ts +30 -1
- package/dist/convert/convert.js +37 -1
- package/dist/convert/local.cjs +4 -1
- package/dist/convert/local.js +4 -1
- package/dist/convert/port.cjs +2 -0
- package/dist/convert/port.d.cts +5 -0
- package/dist/convert/port.d.ts +5 -0
- package/dist/convert/port.js +2 -0
- package/dist/csv/read.cjs +85 -0
- package/dist/csv/read.d.cts +10 -0
- package/dist/csv/read.d.ts +10 -0
- package/dist/csv/read.js +84 -0
- package/dist/csv/records.cjs +85 -0
- package/dist/csv/records.d.cts +10 -0
- package/dist/csv/records.d.ts +10 -0
- package/dist/csv/records.js +80 -0
- package/dist/csv/text.cjs +22 -0
- package/dist/csv/text.d.cts +8 -0
- package/dist/csv/text.d.ts +8 -0
- package/dist/csv/text.js +19 -0
- package/dist/csv/write.cjs +75 -0
- package/dist/csv/write.d.cts +22 -0
- package/dist/csv/write.d.ts +22 -0
- package/dist/csv/write.js +71 -0
- package/dist/edit/odg/scaffold.js +8 -8
- package/dist/edit/odp/scaffold.js +8 -8
- package/dist/edit/ods/scaffold.js +8 -8
- package/dist/edit/odt/scaffold.js +8 -8
- package/dist/index.cjs +48 -1
- package/dist/index.d.cts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +13 -5
- package/dist/layout/reconstruct.cjs +1 -1
- package/dist/layout/reconstruct.js +1 -1
- package/dist/metadata/write.cjs +2 -0
- package/dist/metadata/write.js +2 -0
- package/dist/model/bytes.cjs +7 -0
- package/dist/model/bytes.d.cts +3 -1
- package/dist/model/bytes.d.ts +3 -1
- package/dist/model/bytes.js +6 -1
- package/dist/odb/csv.cjs +3 -6
- package/dist/odb/csv.js +3 -6
- package/dist/svg/diagnostics.cjs +19 -0
- package/dist/svg/diagnostics.d.cts +10 -0
- package/dist/svg/diagnostics.d.ts +10 -0
- package/dist/svg/diagnostics.js +18 -0
- package/dist/svg/paint.cjs +817 -0
- package/dist/svg/paint.d.cts +19 -0
- package/dist/svg/paint.d.ts +19 -0
- package/dist/svg/paint.js +814 -0
- package/dist/svg/path.cjs +337 -0
- package/dist/svg/path.d.cts +22 -0
- package/dist/svg/path.d.ts +22 -0
- package/dist/svg/path.js +336 -0
- package/dist/svg/read.cjs +677 -0
- package/dist/svg/read.d.cts +12 -0
- package/dist/svg/read.d.ts +12 -0
- package/dist/svg/read.js +675 -0
- package/dist/svg/text.cjs +22 -0
- package/dist/svg/text.d.cts +8 -0
- package/dist/svg/text.d.ts +8 -0
- package/dist/svg/text.js +19 -0
- package/dist/svg/transform.cjs +206 -0
- package/dist/svg/transform.d.cts +23 -0
- package/dist/svg/transform.d.ts +23 -0
- package/dist/svg/transform.js +197 -0
- package/dist/svg/units.cjs +47 -0
- package/dist/svg/units.d.cts +12 -0
- package/dist/svg/units.d.ts +12 -0
- package/dist/svg/units.js +44 -0
- package/dist/svg/write.cjs +127 -0
- package/dist/svg/write.d.cts +23 -0
- package/dist/svg/write.d.ts +23 -0
- package/dist/svg/write.js +123 -0
- package/dist/xml/odf-text.js +2 -2
- package/package.json +1 -1
package/dist/svg/text.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/svg/text.ts
|
|
2
|
+
var SvgInvalidUtf8Error = class extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("svg text must be well-formed UTF-8");
|
|
5
|
+
this.name = "SvgInvalidUtf8Error";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
function decodeSvgText(bytes) {
|
|
9
|
+
try {
|
|
10
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
11
|
+
} catch {
|
|
12
|
+
throw new SvgInvalidUtf8Error();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function encodeSvgText(text) {
|
|
16
|
+
return new TextEncoder().encode(text);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { SvgInvalidUtf8Error, decodeSvgText, encodeSvgText };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/svg/transform.ts
|
|
3
|
+
const IDENTITY_MATRIX = {
|
|
4
|
+
a: 1,
|
|
5
|
+
b: 0,
|
|
6
|
+
c: 0,
|
|
7
|
+
d: 1,
|
|
8
|
+
e: 0,
|
|
9
|
+
f: 0
|
|
10
|
+
};
|
|
11
|
+
function applyMatrix(m, x, y) {
|
|
12
|
+
return {
|
|
13
|
+
x: m.a * x + m.c * y + m.e,
|
|
14
|
+
y: m.b * x + m.d * y + m.f
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function composeMatrices(outer, inner) {
|
|
18
|
+
return {
|
|
19
|
+
a: outer.a * inner.a + outer.c * inner.b,
|
|
20
|
+
b: outer.b * inner.a + outer.d * inner.b,
|
|
21
|
+
c: outer.a * inner.c + outer.c * inner.d,
|
|
22
|
+
d: outer.b * inner.c + outer.d * inner.d,
|
|
23
|
+
e: outer.a * inner.e + outer.c * inner.f + outer.e,
|
|
24
|
+
f: outer.b * inner.e + outer.d * inner.f + outer.f
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function applyScale(m, factor) {
|
|
28
|
+
return {
|
|
29
|
+
a: m.a * factor,
|
|
30
|
+
b: m.b * factor,
|
|
31
|
+
c: m.c * factor,
|
|
32
|
+
d: m.d * factor,
|
|
33
|
+
e: m.e * factor,
|
|
34
|
+
f: m.f * factor
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function meanScaleFactor(m) {
|
|
38
|
+
return (Math.hypot(m.a, m.b) + Math.hypot(m.c, m.d)) / 2;
|
|
39
|
+
}
|
|
40
|
+
function isAxisAligned(m) {
|
|
41
|
+
return m.b === 0 && m.c === 0;
|
|
42
|
+
}
|
|
43
|
+
function isNonReflectingSimilarity(m) {
|
|
44
|
+
if (isAxisAligned(m)) return true;
|
|
45
|
+
if (m.a * m.d - m.b * m.c < 0) return false;
|
|
46
|
+
return Math.abs(m.a * m.a + m.b * m.b - (m.c * m.c + m.d * m.d)) < 1e-9;
|
|
47
|
+
}
|
|
48
|
+
function similarityRotationDeg(m) {
|
|
49
|
+
return Math.atan2(m.b, m.a) * 180 / Math.PI;
|
|
50
|
+
}
|
|
51
|
+
const TRANSFORM_NUMBER = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?/y;
|
|
52
|
+
const TRANSFORM_FUNCTIONS = [
|
|
53
|
+
"translate",
|
|
54
|
+
"scale",
|
|
55
|
+
"rotate",
|
|
56
|
+
"skewX",
|
|
57
|
+
"skewY",
|
|
58
|
+
"matrix"
|
|
59
|
+
];
|
|
60
|
+
function skipTransformSeparators(source, scanner) {
|
|
61
|
+
let comma = false;
|
|
62
|
+
while (scanner.pos < source.length && /[\s,]/.test(source[scanner.pos])) {
|
|
63
|
+
if (source[scanner.pos] === ",") comma = true;
|
|
64
|
+
scanner.pos++;
|
|
65
|
+
}
|
|
66
|
+
return comma;
|
|
67
|
+
}
|
|
68
|
+
function scanTransformNumber(source, scanner) {
|
|
69
|
+
TRANSFORM_NUMBER.lastIndex = scanner.pos;
|
|
70
|
+
const match = TRANSFORM_NUMBER.exec(source);
|
|
71
|
+
if (match === null) return;
|
|
72
|
+
scanner.pos = TRANSFORM_NUMBER.lastIndex;
|
|
73
|
+
const value = Number(match[0]);
|
|
74
|
+
return Number.isFinite(value) ? value : void 0;
|
|
75
|
+
}
|
|
76
|
+
function matrixFromFunction(name, args) {
|
|
77
|
+
switch (name) {
|
|
78
|
+
case "translate":
|
|
79
|
+
if (args.length !== 1 && args.length !== 2) return;
|
|
80
|
+
return {
|
|
81
|
+
a: 1,
|
|
82
|
+
b: 0,
|
|
83
|
+
c: 0,
|
|
84
|
+
d: 1,
|
|
85
|
+
e: args[0],
|
|
86
|
+
f: args[1] ?? 0
|
|
87
|
+
};
|
|
88
|
+
case "scale": {
|
|
89
|
+
if (args.length !== 1 && args.length !== 2) return;
|
|
90
|
+
const sx = args[0];
|
|
91
|
+
return {
|
|
92
|
+
a: sx,
|
|
93
|
+
b: 0,
|
|
94
|
+
c: 0,
|
|
95
|
+
d: args[1] ?? sx,
|
|
96
|
+
e: 0,
|
|
97
|
+
f: 0
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case "rotate": {
|
|
101
|
+
if (args.length !== 1 && args.length !== 3) return;
|
|
102
|
+
const radians = args[0] * Math.PI / 180;
|
|
103
|
+
const cos = Math.cos(radians);
|
|
104
|
+
const sin = Math.sin(radians);
|
|
105
|
+
const rotation = {
|
|
106
|
+
a: cos,
|
|
107
|
+
b: sin,
|
|
108
|
+
c: -sin,
|
|
109
|
+
d: cos,
|
|
110
|
+
e: 0,
|
|
111
|
+
f: 0
|
|
112
|
+
};
|
|
113
|
+
if (args.length === 1) return rotation;
|
|
114
|
+
const cx = args[1];
|
|
115
|
+
const cy = args[2];
|
|
116
|
+
return composeMatrices(composeMatrices({
|
|
117
|
+
a: 1,
|
|
118
|
+
b: 0,
|
|
119
|
+
c: 0,
|
|
120
|
+
d: 1,
|
|
121
|
+
e: cx,
|
|
122
|
+
f: cy
|
|
123
|
+
}, rotation), {
|
|
124
|
+
a: 1,
|
|
125
|
+
b: 0,
|
|
126
|
+
c: 0,
|
|
127
|
+
d: 1,
|
|
128
|
+
e: -cx,
|
|
129
|
+
f: -cy
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
case "skewX":
|
|
133
|
+
if (args.length !== 1) return;
|
|
134
|
+
return {
|
|
135
|
+
a: 1,
|
|
136
|
+
b: 0,
|
|
137
|
+
c: Math.tan(args[0] * Math.PI / 180),
|
|
138
|
+
d: 1,
|
|
139
|
+
e: 0,
|
|
140
|
+
f: 0
|
|
141
|
+
};
|
|
142
|
+
case "skewY":
|
|
143
|
+
if (args.length !== 1) return;
|
|
144
|
+
return {
|
|
145
|
+
a: 1,
|
|
146
|
+
b: Math.tan(args[0] * Math.PI / 180),
|
|
147
|
+
c: 0,
|
|
148
|
+
d: 1,
|
|
149
|
+
e: 0,
|
|
150
|
+
f: 0
|
|
151
|
+
};
|
|
152
|
+
case "matrix":
|
|
153
|
+
if (args.length !== 6) return;
|
|
154
|
+
return {
|
|
155
|
+
a: args[0],
|
|
156
|
+
b: args[1],
|
|
157
|
+
c: args[2],
|
|
158
|
+
d: args[3],
|
|
159
|
+
e: args[4],
|
|
160
|
+
f: args[5]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function parseSvgTransform(raw) {
|
|
165
|
+
if (raw === void 0) return;
|
|
166
|
+
const source = raw.trim();
|
|
167
|
+
if (source === "") return;
|
|
168
|
+
const scanner = { pos: 0 };
|
|
169
|
+
let total;
|
|
170
|
+
for (;;) {
|
|
171
|
+
skipTransformSeparators(source, scanner);
|
|
172
|
+
if (scanner.pos >= source.length) break;
|
|
173
|
+
const name = TRANSFORM_FUNCTIONS.find((candidate) => source.startsWith(candidate, scanner.pos));
|
|
174
|
+
if (name === void 0) return;
|
|
175
|
+
scanner.pos += name.length;
|
|
176
|
+
skipTransformSeparators(source, scanner);
|
|
177
|
+
if (source[scanner.pos] !== "(") return;
|
|
178
|
+
scanner.pos++;
|
|
179
|
+
const args = [];
|
|
180
|
+
for (;;) {
|
|
181
|
+
const hadComma = skipTransformSeparators(source, scanner);
|
|
182
|
+
if (source[scanner.pos] === ")") {
|
|
183
|
+
if (hadComma) return;
|
|
184
|
+
scanner.pos++;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
const value = scanTransformNumber(source, scanner);
|
|
188
|
+
if (value === void 0) return;
|
|
189
|
+
args.push(value);
|
|
190
|
+
}
|
|
191
|
+
const step = matrixFromFunction(name, args);
|
|
192
|
+
if (step === void 0) return;
|
|
193
|
+
total = total === void 0 ? step : composeMatrices(total, step);
|
|
194
|
+
}
|
|
195
|
+
return total;
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
exports.IDENTITY_MATRIX = IDENTITY_MATRIX;
|
|
199
|
+
exports.applyMatrix = applyMatrix;
|
|
200
|
+
exports.applyScale = applyScale;
|
|
201
|
+
exports.composeMatrices = composeMatrices;
|
|
202
|
+
exports.isAxisAligned = isAxisAligned;
|
|
203
|
+
exports.isNonReflectingSimilarity = isNonReflectingSimilarity;
|
|
204
|
+
exports.meanScaleFactor = meanScaleFactor;
|
|
205
|
+
exports.parseSvgTransform = parseSvgTransform;
|
|
206
|
+
exports.similarityRotationDeg = similarityRotationDeg;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/svg/transform.d.ts
|
|
2
|
+
interface AffineMatrix {
|
|
3
|
+
readonly a: number;
|
|
4
|
+
readonly b: number;
|
|
5
|
+
readonly c: number;
|
|
6
|
+
readonly d: number;
|
|
7
|
+
readonly e: number;
|
|
8
|
+
readonly f: number;
|
|
9
|
+
}
|
|
10
|
+
declare const IDENTITY_MATRIX: AffineMatrix;
|
|
11
|
+
declare function applyMatrix(m: AffineMatrix, x: number, y: number): {
|
|
12
|
+
readonly x: number;
|
|
13
|
+
readonly y: number;
|
|
14
|
+
};
|
|
15
|
+
declare function composeMatrices(outer: AffineMatrix, inner: AffineMatrix): AffineMatrix;
|
|
16
|
+
declare function applyScale(m: AffineMatrix, factor: number): AffineMatrix;
|
|
17
|
+
declare function meanScaleFactor(m: AffineMatrix): number;
|
|
18
|
+
declare function isAxisAligned(m: AffineMatrix): boolean;
|
|
19
|
+
declare function isNonReflectingSimilarity(m: AffineMatrix): boolean;
|
|
20
|
+
declare function similarityRotationDeg(m: AffineMatrix): number;
|
|
21
|
+
declare function parseSvgTransform(raw: string | undefined): AffineMatrix | undefined;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { AffineMatrix, IDENTITY_MATRIX, applyMatrix, applyScale, composeMatrices, isAxisAligned, isNonReflectingSimilarity, meanScaleFactor, parseSvgTransform, similarityRotationDeg };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/svg/transform.d.ts
|
|
2
|
+
interface AffineMatrix {
|
|
3
|
+
readonly a: number;
|
|
4
|
+
readonly b: number;
|
|
5
|
+
readonly c: number;
|
|
6
|
+
readonly d: number;
|
|
7
|
+
readonly e: number;
|
|
8
|
+
readonly f: number;
|
|
9
|
+
}
|
|
10
|
+
declare const IDENTITY_MATRIX: AffineMatrix;
|
|
11
|
+
declare function applyMatrix(m: AffineMatrix, x: number, y: number): {
|
|
12
|
+
readonly x: number;
|
|
13
|
+
readonly y: number;
|
|
14
|
+
};
|
|
15
|
+
declare function composeMatrices(outer: AffineMatrix, inner: AffineMatrix): AffineMatrix;
|
|
16
|
+
declare function applyScale(m: AffineMatrix, factor: number): AffineMatrix;
|
|
17
|
+
declare function meanScaleFactor(m: AffineMatrix): number;
|
|
18
|
+
declare function isAxisAligned(m: AffineMatrix): boolean;
|
|
19
|
+
declare function isNonReflectingSimilarity(m: AffineMatrix): boolean;
|
|
20
|
+
declare function similarityRotationDeg(m: AffineMatrix): number;
|
|
21
|
+
declare function parseSvgTransform(raw: string | undefined): AffineMatrix | undefined;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { AffineMatrix, IDENTITY_MATRIX, applyMatrix, applyScale, composeMatrices, isAxisAligned, isNonReflectingSimilarity, meanScaleFactor, parseSvgTransform, similarityRotationDeg };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
//#region src/svg/transform.ts
|
|
2
|
+
const IDENTITY_MATRIX = {
|
|
3
|
+
a: 1,
|
|
4
|
+
b: 0,
|
|
5
|
+
c: 0,
|
|
6
|
+
d: 1,
|
|
7
|
+
e: 0,
|
|
8
|
+
f: 0
|
|
9
|
+
};
|
|
10
|
+
function applyMatrix(m, x, y) {
|
|
11
|
+
return {
|
|
12
|
+
x: m.a * x + m.c * y + m.e,
|
|
13
|
+
y: m.b * x + m.d * y + m.f
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function composeMatrices(outer, inner) {
|
|
17
|
+
return {
|
|
18
|
+
a: outer.a * inner.a + outer.c * inner.b,
|
|
19
|
+
b: outer.b * inner.a + outer.d * inner.b,
|
|
20
|
+
c: outer.a * inner.c + outer.c * inner.d,
|
|
21
|
+
d: outer.b * inner.c + outer.d * inner.d,
|
|
22
|
+
e: outer.a * inner.e + outer.c * inner.f + outer.e,
|
|
23
|
+
f: outer.b * inner.e + outer.d * inner.f + outer.f
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function applyScale(m, factor) {
|
|
27
|
+
return {
|
|
28
|
+
a: m.a * factor,
|
|
29
|
+
b: m.b * factor,
|
|
30
|
+
c: m.c * factor,
|
|
31
|
+
d: m.d * factor,
|
|
32
|
+
e: m.e * factor,
|
|
33
|
+
f: m.f * factor
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function meanScaleFactor(m) {
|
|
37
|
+
return (Math.hypot(m.a, m.b) + Math.hypot(m.c, m.d)) / 2;
|
|
38
|
+
}
|
|
39
|
+
function isAxisAligned(m) {
|
|
40
|
+
return m.b === 0 && m.c === 0;
|
|
41
|
+
}
|
|
42
|
+
function isNonReflectingSimilarity(m) {
|
|
43
|
+
if (isAxisAligned(m)) return true;
|
|
44
|
+
if (m.a * m.d - m.b * m.c < 0) return false;
|
|
45
|
+
return Math.abs(m.a * m.a + m.b * m.b - (m.c * m.c + m.d * m.d)) < 1e-9;
|
|
46
|
+
}
|
|
47
|
+
function similarityRotationDeg(m) {
|
|
48
|
+
return Math.atan2(m.b, m.a) * 180 / Math.PI;
|
|
49
|
+
}
|
|
50
|
+
const TRANSFORM_NUMBER = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?/y;
|
|
51
|
+
const TRANSFORM_FUNCTIONS = [
|
|
52
|
+
"translate",
|
|
53
|
+
"scale",
|
|
54
|
+
"rotate",
|
|
55
|
+
"skewX",
|
|
56
|
+
"skewY",
|
|
57
|
+
"matrix"
|
|
58
|
+
];
|
|
59
|
+
function skipTransformSeparators(source, scanner) {
|
|
60
|
+
let comma = false;
|
|
61
|
+
while (scanner.pos < source.length && /[\s,]/.test(source[scanner.pos])) {
|
|
62
|
+
if (source[scanner.pos] === ",") comma = true;
|
|
63
|
+
scanner.pos++;
|
|
64
|
+
}
|
|
65
|
+
return comma;
|
|
66
|
+
}
|
|
67
|
+
function scanTransformNumber(source, scanner) {
|
|
68
|
+
TRANSFORM_NUMBER.lastIndex = scanner.pos;
|
|
69
|
+
const match = TRANSFORM_NUMBER.exec(source);
|
|
70
|
+
if (match === null) return;
|
|
71
|
+
scanner.pos = TRANSFORM_NUMBER.lastIndex;
|
|
72
|
+
const value = Number(match[0]);
|
|
73
|
+
return Number.isFinite(value) ? value : void 0;
|
|
74
|
+
}
|
|
75
|
+
function matrixFromFunction(name, args) {
|
|
76
|
+
switch (name) {
|
|
77
|
+
case "translate":
|
|
78
|
+
if (args.length !== 1 && args.length !== 2) return;
|
|
79
|
+
return {
|
|
80
|
+
a: 1,
|
|
81
|
+
b: 0,
|
|
82
|
+
c: 0,
|
|
83
|
+
d: 1,
|
|
84
|
+
e: args[0],
|
|
85
|
+
f: args[1] ?? 0
|
|
86
|
+
};
|
|
87
|
+
case "scale": {
|
|
88
|
+
if (args.length !== 1 && args.length !== 2) return;
|
|
89
|
+
const sx = args[0];
|
|
90
|
+
return {
|
|
91
|
+
a: sx,
|
|
92
|
+
b: 0,
|
|
93
|
+
c: 0,
|
|
94
|
+
d: args[1] ?? sx,
|
|
95
|
+
e: 0,
|
|
96
|
+
f: 0
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
case "rotate": {
|
|
100
|
+
if (args.length !== 1 && args.length !== 3) return;
|
|
101
|
+
const radians = args[0] * Math.PI / 180;
|
|
102
|
+
const cos = Math.cos(radians);
|
|
103
|
+
const sin = Math.sin(radians);
|
|
104
|
+
const rotation = {
|
|
105
|
+
a: cos,
|
|
106
|
+
b: sin,
|
|
107
|
+
c: -sin,
|
|
108
|
+
d: cos,
|
|
109
|
+
e: 0,
|
|
110
|
+
f: 0
|
|
111
|
+
};
|
|
112
|
+
if (args.length === 1) return rotation;
|
|
113
|
+
const cx = args[1];
|
|
114
|
+
const cy = args[2];
|
|
115
|
+
return composeMatrices(composeMatrices({
|
|
116
|
+
a: 1,
|
|
117
|
+
b: 0,
|
|
118
|
+
c: 0,
|
|
119
|
+
d: 1,
|
|
120
|
+
e: cx,
|
|
121
|
+
f: cy
|
|
122
|
+
}, rotation), {
|
|
123
|
+
a: 1,
|
|
124
|
+
b: 0,
|
|
125
|
+
c: 0,
|
|
126
|
+
d: 1,
|
|
127
|
+
e: -cx,
|
|
128
|
+
f: -cy
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
case "skewX":
|
|
132
|
+
if (args.length !== 1) return;
|
|
133
|
+
return {
|
|
134
|
+
a: 1,
|
|
135
|
+
b: 0,
|
|
136
|
+
c: Math.tan(args[0] * Math.PI / 180),
|
|
137
|
+
d: 1,
|
|
138
|
+
e: 0,
|
|
139
|
+
f: 0
|
|
140
|
+
};
|
|
141
|
+
case "skewY":
|
|
142
|
+
if (args.length !== 1) return;
|
|
143
|
+
return {
|
|
144
|
+
a: 1,
|
|
145
|
+
b: Math.tan(args[0] * Math.PI / 180),
|
|
146
|
+
c: 0,
|
|
147
|
+
d: 1,
|
|
148
|
+
e: 0,
|
|
149
|
+
f: 0
|
|
150
|
+
};
|
|
151
|
+
case "matrix":
|
|
152
|
+
if (args.length !== 6) return;
|
|
153
|
+
return {
|
|
154
|
+
a: args[0],
|
|
155
|
+
b: args[1],
|
|
156
|
+
c: args[2],
|
|
157
|
+
d: args[3],
|
|
158
|
+
e: args[4],
|
|
159
|
+
f: args[5]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function parseSvgTransform(raw) {
|
|
164
|
+
if (raw === void 0) return;
|
|
165
|
+
const source = raw.trim();
|
|
166
|
+
if (source === "") return;
|
|
167
|
+
const scanner = { pos: 0 };
|
|
168
|
+
let total;
|
|
169
|
+
for (;;) {
|
|
170
|
+
skipTransformSeparators(source, scanner);
|
|
171
|
+
if (scanner.pos >= source.length) break;
|
|
172
|
+
const name = TRANSFORM_FUNCTIONS.find((candidate) => source.startsWith(candidate, scanner.pos));
|
|
173
|
+
if (name === void 0) return;
|
|
174
|
+
scanner.pos += name.length;
|
|
175
|
+
skipTransformSeparators(source, scanner);
|
|
176
|
+
if (source[scanner.pos] !== "(") return;
|
|
177
|
+
scanner.pos++;
|
|
178
|
+
const args = [];
|
|
179
|
+
for (;;) {
|
|
180
|
+
const hadComma = skipTransformSeparators(source, scanner);
|
|
181
|
+
if (source[scanner.pos] === ")") {
|
|
182
|
+
if (hadComma) return;
|
|
183
|
+
scanner.pos++;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
const value = scanTransformNumber(source, scanner);
|
|
187
|
+
if (value === void 0) return;
|
|
188
|
+
args.push(value);
|
|
189
|
+
}
|
|
190
|
+
const step = matrixFromFunction(name, args);
|
|
191
|
+
if (step === void 0) return;
|
|
192
|
+
total = total === void 0 ? step : composeMatrices(total, step);
|
|
193
|
+
}
|
|
194
|
+
return total;
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
export { IDENTITY_MATRIX, applyMatrix, applyScale, composeMatrices, isAxisAligned, isNonReflectingSimilarity, meanScaleFactor, parseSvgTransform, similarityRotationDeg };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/svg/units.ts
|
|
3
|
+
const PT_PER_PX = .75;
|
|
4
|
+
const PT_PER_MM = 72 / 25.4;
|
|
5
|
+
const PT_PER_CM = 720 / 25.4;
|
|
6
|
+
const PT_PER_IN = 72;
|
|
7
|
+
const SVG_NUMBER_PATTERN = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?$/;
|
|
8
|
+
const SVG_LENGTH_PATTERN = /^([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)(px|pt|mm|cm|in|pc|q|em|ex|%)?$/;
|
|
9
|
+
function parseSvgLengthPt(raw) {
|
|
10
|
+
if (raw === void 0) return;
|
|
11
|
+
const match = SVG_LENGTH_PATTERN.exec(raw.trim());
|
|
12
|
+
if (match === null) return;
|
|
13
|
+
const value = Number(match[1]);
|
|
14
|
+
if (!Number.isFinite(value)) return;
|
|
15
|
+
switch (match[2]) {
|
|
16
|
+
case void 0:
|
|
17
|
+
case "px": return value * PT_PER_PX;
|
|
18
|
+
case "pt": return value;
|
|
19
|
+
case "mm": return value * PT_PER_MM;
|
|
20
|
+
case "cm": return value * PT_PER_CM;
|
|
21
|
+
case "in": return value * PT_PER_IN;
|
|
22
|
+
case "pc": return value * 12;
|
|
23
|
+
case "q": return value * PT_PER_CM / 40;
|
|
24
|
+
default: return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function parseSvgUserUnits(raw) {
|
|
28
|
+
const pt = parseSvgLengthPt(raw);
|
|
29
|
+
return pt === void 0 ? void 0 : pt / PT_PER_PX;
|
|
30
|
+
}
|
|
31
|
+
function parseSvgViewBox(raw) {
|
|
32
|
+
if (raw === void 0) return;
|
|
33
|
+
const parts = raw.trim().replace(/,/g, " ").split(/\s+/);
|
|
34
|
+
if (parts.length !== 4) return;
|
|
35
|
+
const numbers = parts.map((part) => SVG_NUMBER_PATTERN.test(part) ? Number(part) : NaN);
|
|
36
|
+
if (!numbers.every((value) => Number.isFinite(value)) || numbers[2] < 0 || numbers[3] < 0) return;
|
|
37
|
+
return {
|
|
38
|
+
minX: numbers[0],
|
|
39
|
+
minY: numbers[1],
|
|
40
|
+
width: numbers[2],
|
|
41
|
+
height: numbers[3]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
exports.parseSvgLengthPt = parseSvgLengthPt;
|
|
46
|
+
exports.parseSvgUserUnits = parseSvgUserUnits;
|
|
47
|
+
exports.parseSvgViewBox = parseSvgViewBox;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/svg/units.d.ts
|
|
2
|
+
declare function parseSvgLengthPt(raw: string | undefined): number | undefined;
|
|
3
|
+
declare function parseSvgUserUnits(raw: string | undefined): number | undefined;
|
|
4
|
+
interface SvgViewBox {
|
|
5
|
+
readonly minX: number;
|
|
6
|
+
readonly minY: number;
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly height: number;
|
|
9
|
+
}
|
|
10
|
+
declare function parseSvgViewBox(raw: string | undefined): SvgViewBox | undefined;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { SvgViewBox, parseSvgLengthPt, parseSvgUserUnits, parseSvgViewBox };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/svg/units.d.ts
|
|
2
|
+
declare function parseSvgLengthPt(raw: string | undefined): number | undefined;
|
|
3
|
+
declare function parseSvgUserUnits(raw: string | undefined): number | undefined;
|
|
4
|
+
interface SvgViewBox {
|
|
5
|
+
readonly minX: number;
|
|
6
|
+
readonly minY: number;
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly height: number;
|
|
9
|
+
}
|
|
10
|
+
declare function parseSvgViewBox(raw: string | undefined): SvgViewBox | undefined;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { SvgViewBox, parseSvgLengthPt, parseSvgUserUnits, parseSvgViewBox };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/svg/units.ts
|
|
2
|
+
const PT_PER_PX = .75;
|
|
3
|
+
const PT_PER_MM = 72 / 25.4;
|
|
4
|
+
const PT_PER_CM = 720 / 25.4;
|
|
5
|
+
const PT_PER_IN = 72;
|
|
6
|
+
const SVG_NUMBER_PATTERN = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?$/;
|
|
7
|
+
const SVG_LENGTH_PATTERN = /^([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)(px|pt|mm|cm|in|pc|q|em|ex|%)?$/;
|
|
8
|
+
function parseSvgLengthPt(raw) {
|
|
9
|
+
if (raw === void 0) return;
|
|
10
|
+
const match = SVG_LENGTH_PATTERN.exec(raw.trim());
|
|
11
|
+
if (match === null) return;
|
|
12
|
+
const value = Number(match[1]);
|
|
13
|
+
if (!Number.isFinite(value)) return;
|
|
14
|
+
switch (match[2]) {
|
|
15
|
+
case void 0:
|
|
16
|
+
case "px": return value * PT_PER_PX;
|
|
17
|
+
case "pt": return value;
|
|
18
|
+
case "mm": return value * PT_PER_MM;
|
|
19
|
+
case "cm": return value * PT_PER_CM;
|
|
20
|
+
case "in": return value * PT_PER_IN;
|
|
21
|
+
case "pc": return value * 12;
|
|
22
|
+
case "q": return value * PT_PER_CM / 40;
|
|
23
|
+
default: return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseSvgUserUnits(raw) {
|
|
27
|
+
const pt = parseSvgLengthPt(raw);
|
|
28
|
+
return pt === void 0 ? void 0 : pt / PT_PER_PX;
|
|
29
|
+
}
|
|
30
|
+
function parseSvgViewBox(raw) {
|
|
31
|
+
if (raw === void 0) return;
|
|
32
|
+
const parts = raw.trim().replace(/,/g, " ").split(/\s+/);
|
|
33
|
+
if (parts.length !== 4) return;
|
|
34
|
+
const numbers = parts.map((part) => SVG_NUMBER_PATTERN.test(part) ? Number(part) : NaN);
|
|
35
|
+
if (!numbers.every((value) => Number.isFinite(value)) || numbers[2] < 0 || numbers[3] < 0) return;
|
|
36
|
+
return {
|
|
37
|
+
minX: numbers[0],
|
|
38
|
+
minY: numbers[1],
|
|
39
|
+
width: numbers[2],
|
|
40
|
+
height: numbers[3]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { parseSvgLengthPt, parseSvgUserUnits, parseSvgViewBox };
|