js.documents 2.1.0 → 2.3.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 +72 -33
- package/dist/codecs/registry.cjs +7 -0
- package/dist/codecs/registry.js +7 -0
- package/dist/convert/capability.cjs +5 -0
- package/dist/convert/capability.js +5 -0
- package/dist/convert/codec.cjs +10 -0
- package/dist/convert/codec.d.cts +3 -1
- package/dist/convert/codec.d.ts +3 -1
- package/dist/convert/codec.js +11 -3
- package/dist/convert/composition.cjs +17 -0
- package/dist/convert/composition.d.cts +5 -2
- package/dist/convert/composition.d.ts +5 -2
- package/dist/convert/composition.js +17 -0
- package/dist/convert/convert.cjs +16 -0
- package/dist/convert/convert.d.cts +13 -1
- package/dist/convert/convert.d.ts +13 -1
- package/dist/convert/convert.js +13 -1
- package/dist/convert/local.cjs +2 -1
- package/dist/convert/local.js +2 -1
- package/dist/convert/port.cjs +1 -0
- package/dist/convert/port.d.cts +2 -0
- package/dist/convert/port.d.ts +2 -0
- package/dist/convert/port.js +1 -0
- package/dist/diagnostics-C5-bG09J.d.cts +18 -0
- package/dist/diagnostics-C5-bG09J.d.ts +18 -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 +31 -0
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -4
- package/dist/latex/diagnostics.cjs +21 -0
- package/dist/latex/diagnostics.d.cts +2 -0
- package/dist/latex/diagnostics.d.ts +2 -0
- package/dist/latex/diagnostics.js +19 -0
- package/dist/latex/lint.cjs +82 -0
- package/dist/latex/lint.d.cts +6 -0
- package/dist/latex/lint.d.ts +6 -0
- package/dist/latex/lint.js +81 -0
- package/dist/latex/lower.cjs +550 -0
- package/dist/latex/lower.d.cts +24 -0
- package/dist/latex/lower.d.ts +24 -0
- package/dist/latex/lower.js +548 -0
- package/dist/latex/rational.cjs +29 -0
- package/dist/latex/rational.d.cts +11 -0
- package/dist/latex/rational.d.ts +11 -0
- package/dist/latex/rational.js +27 -0
- package/dist/latex/symbols.cjs +128 -0
- package/dist/latex/symbols.d.cts +16 -0
- package/dist/latex/symbols.d.ts +16 -0
- package/dist/latex/symbols.js +124 -0
- package/dist/latex/temml.cjs +121 -0
- package/dist/latex/temml.d.cts +25 -0
- package/dist/latex/temml.d.ts +25 -0
- package/dist/latex/temml.js +93 -0
- package/dist/markdown/math.cjs +110 -0
- package/dist/markdown/math.d.cts +9 -0
- package/dist/markdown/math.d.ts +9 -0
- package/dist/markdown/math.js +109 -0
- package/dist/markdown/read.cjs +3 -2
- package/dist/markdown/read.d.cts +2 -1
- package/dist/markdown/read.d.ts +2 -1
- package/dist/markdown/read.js +3 -2
- package/dist/markdown/write.cjs +23 -4
- package/dist/markdown/write.js +23 -4
- package/dist/metadata/write.cjs +1 -0
- package/dist/metadata/write.js +1 -0
- package/dist/model/bytes.cjs +5 -0
- package/dist/model/bytes.d.cts +2 -1
- package/dist/model/bytes.d.ts +2 -1
- package/dist/model/bytes.js +5 -1
- package/dist/model/formula.cjs +1 -1
- package/dist/model/formula.js +1 -1
- 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 +2 -1
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_svg_paint = require("./paint.cjs");
|
|
3
|
+
const require_svg_path = require("./path.cjs");
|
|
4
|
+
const require_svg_units = require("./units.cjs");
|
|
5
|
+
const require_svg_transform = require("./transform.cjs");
|
|
6
|
+
let ooxml_js = require("ooxml.js");
|
|
7
|
+
let document_schema_js = require("document-schema.js");
|
|
8
|
+
let odf_js = require("odf.js");
|
|
9
|
+
//#region src/svg/read.ts
|
|
10
|
+
var SvgMissingRootElementError = class extends Error {
|
|
11
|
+
constructor() {
|
|
12
|
+
super("svg text must contain an <svg> root element");
|
|
13
|
+
this.name = "SvgMissingRootElementError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_WIDTH_PT = 225;
|
|
17
|
+
const DEFAULT_HEIGHT_PT = 112.5;
|
|
18
|
+
const KAPPA = 4 / 3 * (Math.SQRT2 - 1);
|
|
19
|
+
function localName(tag) {
|
|
20
|
+
const colon = tag.indexOf(":");
|
|
21
|
+
return colon === -1 ? tag : tag.slice(colon + 1);
|
|
22
|
+
}
|
|
23
|
+
function findAttribute(element, name) {
|
|
24
|
+
for (const attribute of element.attributes) if (localName(attribute.name) === name) return (0, ooxml_js.decodeEntities)(attribute.value);
|
|
25
|
+
}
|
|
26
|
+
function elementChildren(node) {
|
|
27
|
+
return node.type === "element" ? node.children.filter((child) => child.type === "element") : [];
|
|
28
|
+
}
|
|
29
|
+
function textOf(element) {
|
|
30
|
+
let text = "";
|
|
31
|
+
for (const child of element.children) if (child.type === "text" || child.type === "cdata") text += (0, ooxml_js.decodeEntities)(child.value);
|
|
32
|
+
return text;
|
|
33
|
+
}
|
|
34
|
+
function childPaintState(element, parent) {
|
|
35
|
+
return {
|
|
36
|
+
fillSpec: findAttribute(element, "fill") ?? parent.fillSpec,
|
|
37
|
+
strokeSpec: findAttribute(element, "stroke") ?? parent.strokeSpec,
|
|
38
|
+
strokeWidthSpec: findAttribute(element, "stroke-width") ?? parent.strokeWidthSpec,
|
|
39
|
+
fillRuleSpec: findAttribute(element, "fill-rule") ?? parent.fillRuleSpec,
|
|
40
|
+
dashSpec: findAttribute(element, "stroke-dasharray") ?? parent.dashSpec
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function report(state, code, detail) {
|
|
44
|
+
state.sink?.(detail === void 0 ? { code } : {
|
|
45
|
+
code,
|
|
46
|
+
detail
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function resolveFillPaint(state, spec, isFill) {
|
|
50
|
+
if (spec === void 0) return isFill ? {
|
|
51
|
+
r: 0,
|
|
52
|
+
g: 0,
|
|
53
|
+
b: 0
|
|
54
|
+
} : void 0;
|
|
55
|
+
const paint = require_svg_paint.parseSvgPaint(spec);
|
|
56
|
+
if (paint === void 0) {
|
|
57
|
+
report(state, "svg/paint-unsupported", spec);
|
|
58
|
+
return isFill ? {
|
|
59
|
+
r: 0,
|
|
60
|
+
g: 0,
|
|
61
|
+
b: 0
|
|
62
|
+
} : void 0;
|
|
63
|
+
}
|
|
64
|
+
if (paint.kind === "none") return;
|
|
65
|
+
if (paint.kind === "url") {
|
|
66
|
+
report(state, "svg/gradient-unsupported", `#${paint.fragment}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (paint.kind === "currentColor") {
|
|
70
|
+
report(state, "svg/paint-unsupported", "currentColor renders as black: the CSS color property is out of scope");
|
|
71
|
+
return {
|
|
72
|
+
r: 0,
|
|
73
|
+
g: 0,
|
|
74
|
+
b: 0
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return paint.color;
|
|
78
|
+
}
|
|
79
|
+
function resolveStroke(state, paint, ctm) {
|
|
80
|
+
const color = resolveFillPaint(state, paint.strokeSpec, false);
|
|
81
|
+
if (color === void 0) return;
|
|
82
|
+
const widthPt = (require_svg_units.parseSvgUserUnits(paint.strokeWidthSpec) ?? 1) * require_svg_transform.meanScaleFactor(ctm);
|
|
83
|
+
if (!(widthPt > 0)) return;
|
|
84
|
+
const style = require_svg_paint.parseSvgDashStyle(paint.dashSpec);
|
|
85
|
+
return style === void 0 ? {
|
|
86
|
+
color,
|
|
87
|
+
widthPt
|
|
88
|
+
} : {
|
|
89
|
+
color,
|
|
90
|
+
widthPt,
|
|
91
|
+
style
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function resolvePaint(state, paint, ctm) {
|
|
95
|
+
return {
|
|
96
|
+
fill: resolveFillPaint(state, paint.fillSpec, true),
|
|
97
|
+
stroke: resolveStroke(state, paint, ctm)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function boxOfPoints(points) {
|
|
101
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
102
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
103
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
104
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
105
|
+
for (const point of points) {
|
|
106
|
+
minX = Math.min(minX, point.x);
|
|
107
|
+
minY = Math.min(minY, point.y);
|
|
108
|
+
maxX = Math.max(maxX, point.x);
|
|
109
|
+
maxY = Math.max(maxY, point.y);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
xPt: minX,
|
|
113
|
+
yPt: minY,
|
|
114
|
+
widthPt: maxX - minX,
|
|
115
|
+
heightPt: maxY - minY
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function axisAlignedFrame(ctm, x, y, width, height) {
|
|
119
|
+
return boxOfPoints([
|
|
120
|
+
require_svg_transform.applyMatrix(ctm, x, y),
|
|
121
|
+
require_svg_transform.applyMatrix(ctm, x + width, y),
|
|
122
|
+
require_svg_transform.applyMatrix(ctm, x + width, y + height),
|
|
123
|
+
require_svg_transform.applyMatrix(ctm, x, y + height)
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
function similarityFrame(ctm, x, y, width, height) {
|
|
127
|
+
const centre = require_svg_transform.applyMatrix(ctm, x + width / 2, y + height / 2);
|
|
128
|
+
const scale = Math.hypot(ctm.a, ctm.b);
|
|
129
|
+
return {
|
|
130
|
+
xPt: centre.x - scale * width / 2,
|
|
131
|
+
yPt: centre.y - scale * height / 2,
|
|
132
|
+
widthPt: scale * width,
|
|
133
|
+
heightPt: scale * height
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function buildPathVector(state, subpaths, ctm, paint, fillRule) {
|
|
137
|
+
const placed = subpaths.map((subpath) => ({
|
|
138
|
+
start: require_svg_transform.applyMatrix(ctm, subpath.start.x, subpath.start.y),
|
|
139
|
+
closed: subpath.closed,
|
|
140
|
+
segments: subpath.segments.map((segment) => segment.kind === "line" ? {
|
|
141
|
+
kind: "line",
|
|
142
|
+
to: require_svg_transform.applyMatrix(ctm, segment.to.x, segment.to.y)
|
|
143
|
+
} : {
|
|
144
|
+
kind: "cubic",
|
|
145
|
+
control1: require_svg_transform.applyMatrix(ctm, segment.control1.x, segment.control1.y),
|
|
146
|
+
control2: require_svg_transform.applyMatrix(ctm, segment.control2.x, segment.control2.y),
|
|
147
|
+
to: require_svg_transform.applyMatrix(ctm, segment.to.x, segment.to.y)
|
|
148
|
+
})
|
|
149
|
+
}));
|
|
150
|
+
const frame = boxOfPoints(placed.flatMap((subpath) => [subpath.start, ...subpath.segments.flatMap((segment) => segment.kind === "line" ? [segment.to] : [
|
|
151
|
+
segment.control1,
|
|
152
|
+
segment.control2,
|
|
153
|
+
segment.to
|
|
154
|
+
])]));
|
|
155
|
+
if (frame.widthPt === 0 && frame.heightPt === 0) return;
|
|
156
|
+
const localSubpaths = placed.map((subpath) => ({
|
|
157
|
+
start: {
|
|
158
|
+
xPt: subpath.start.x - frame.xPt,
|
|
159
|
+
yPt: subpath.start.y - frame.yPt
|
|
160
|
+
},
|
|
161
|
+
closed: subpath.closed,
|
|
162
|
+
segments: subpath.segments.map((segment) => segment.kind === "line" ? {
|
|
163
|
+
kind: "line",
|
|
164
|
+
to: {
|
|
165
|
+
xPt: segment.to.x - frame.xPt,
|
|
166
|
+
yPt: segment.to.y - frame.yPt
|
|
167
|
+
}
|
|
168
|
+
} : {
|
|
169
|
+
kind: "cubic",
|
|
170
|
+
control1: {
|
|
171
|
+
xPt: segment.control1.x - frame.xPt,
|
|
172
|
+
yPt: segment.control1.y - frame.yPt
|
|
173
|
+
},
|
|
174
|
+
control2: {
|
|
175
|
+
xPt: segment.control2.x - frame.xPt,
|
|
176
|
+
yPt: segment.control2.y - frame.yPt
|
|
177
|
+
},
|
|
178
|
+
to: {
|
|
179
|
+
xPt: segment.to.x - frame.xPt,
|
|
180
|
+
yPt: segment.to.y - frame.yPt
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
}));
|
|
184
|
+
const sourceIndex = state.vectors.length;
|
|
185
|
+
return {
|
|
186
|
+
kind: "path",
|
|
187
|
+
frame,
|
|
188
|
+
subpaths: localSubpaths,
|
|
189
|
+
...paint.fill !== void 0 ? { fill: paint.fill } : {},
|
|
190
|
+
...fillRule !== void 0 ? { fillRule } : {},
|
|
191
|
+
...paint.stroke !== void 0 ? { stroke: paint.stroke } : {},
|
|
192
|
+
paintOrder: state.paintOrder++,
|
|
193
|
+
sourcePath: `svg/vector[${sourceIndex}]`
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function roundedRectSubpaths(x, y, width, height, rx, ry) {
|
|
197
|
+
const radiusX = Math.min(rx, width / 2);
|
|
198
|
+
const radiusY = Math.min(ry, height / 2);
|
|
199
|
+
const kx = radiusX * KAPPA;
|
|
200
|
+
const ky = radiusY * KAPPA;
|
|
201
|
+
return [{
|
|
202
|
+
start: {
|
|
203
|
+
x: x + radiusX,
|
|
204
|
+
y
|
|
205
|
+
},
|
|
206
|
+
closed: true,
|
|
207
|
+
segments: [
|
|
208
|
+
{
|
|
209
|
+
kind: "line",
|
|
210
|
+
to: {
|
|
211
|
+
x: x + width - radiusX,
|
|
212
|
+
y
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
kind: "cubic",
|
|
217
|
+
control1: {
|
|
218
|
+
x: x + width - radiusX + kx,
|
|
219
|
+
y
|
|
220
|
+
},
|
|
221
|
+
control2: {
|
|
222
|
+
x: x + width,
|
|
223
|
+
y: y + radiusY - ky
|
|
224
|
+
},
|
|
225
|
+
to: {
|
|
226
|
+
x: x + width,
|
|
227
|
+
y: y + radiusY
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
kind: "line",
|
|
232
|
+
to: {
|
|
233
|
+
x: x + width,
|
|
234
|
+
y: y + height - radiusY
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
kind: "cubic",
|
|
239
|
+
control1: {
|
|
240
|
+
x: x + width,
|
|
241
|
+
y: y + height - radiusY + ky
|
|
242
|
+
},
|
|
243
|
+
control2: {
|
|
244
|
+
x: x + width - radiusX + kx,
|
|
245
|
+
y: y + height
|
|
246
|
+
},
|
|
247
|
+
to: {
|
|
248
|
+
x: x + width - radiusX,
|
|
249
|
+
y: y + height
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
kind: "line",
|
|
254
|
+
to: {
|
|
255
|
+
x: x + radiusX,
|
|
256
|
+
y: y + height
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
kind: "cubic",
|
|
261
|
+
control1: {
|
|
262
|
+
x: x + radiusX - kx,
|
|
263
|
+
y: y + height
|
|
264
|
+
},
|
|
265
|
+
control2: {
|
|
266
|
+
x,
|
|
267
|
+
y: y + height - radiusY + ky
|
|
268
|
+
},
|
|
269
|
+
to: {
|
|
270
|
+
x,
|
|
271
|
+
y: y + height - radiusY
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
kind: "line",
|
|
276
|
+
to: {
|
|
277
|
+
x,
|
|
278
|
+
y: y + radiusY
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
kind: "cubic",
|
|
283
|
+
control1: {
|
|
284
|
+
x,
|
|
285
|
+
y: y + radiusY - ky
|
|
286
|
+
},
|
|
287
|
+
control2: {
|
|
288
|
+
x: x + radiusX - kx,
|
|
289
|
+
y
|
|
290
|
+
},
|
|
291
|
+
to: {
|
|
292
|
+
x: x + radiusX,
|
|
293
|
+
y
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}];
|
|
298
|
+
}
|
|
299
|
+
function ellipseSubpaths(cx, cy, rx, ry) {
|
|
300
|
+
const kx = rx * KAPPA;
|
|
301
|
+
const ky = ry * KAPPA;
|
|
302
|
+
return [{
|
|
303
|
+
start: {
|
|
304
|
+
x: cx + rx,
|
|
305
|
+
y: cy
|
|
306
|
+
},
|
|
307
|
+
closed: true,
|
|
308
|
+
segments: [
|
|
309
|
+
{
|
|
310
|
+
kind: "cubic",
|
|
311
|
+
control1: {
|
|
312
|
+
x: cx + rx,
|
|
313
|
+
y: cy + ky
|
|
314
|
+
},
|
|
315
|
+
control2: {
|
|
316
|
+
x: cx + kx,
|
|
317
|
+
y: cy + ry
|
|
318
|
+
},
|
|
319
|
+
to: {
|
|
320
|
+
x: cx,
|
|
321
|
+
y: cy + ry
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
kind: "cubic",
|
|
326
|
+
control1: {
|
|
327
|
+
x: cx - kx,
|
|
328
|
+
y: cy + ry
|
|
329
|
+
},
|
|
330
|
+
control2: {
|
|
331
|
+
x: cx - rx,
|
|
332
|
+
y: cy + ky
|
|
333
|
+
},
|
|
334
|
+
to: {
|
|
335
|
+
x: cx - rx,
|
|
336
|
+
y: cy
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
kind: "cubic",
|
|
341
|
+
control1: {
|
|
342
|
+
x: cx - rx,
|
|
343
|
+
y: cy - ky
|
|
344
|
+
},
|
|
345
|
+
control2: {
|
|
346
|
+
x: cx - kx,
|
|
347
|
+
y: cy - ry
|
|
348
|
+
},
|
|
349
|
+
to: {
|
|
350
|
+
x: cx,
|
|
351
|
+
y: cy - ry
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
kind: "cubic",
|
|
356
|
+
control1: {
|
|
357
|
+
x: cx + kx,
|
|
358
|
+
y: cy - ry
|
|
359
|
+
},
|
|
360
|
+
control2: {
|
|
361
|
+
x: cx + rx,
|
|
362
|
+
y: cy - ky
|
|
363
|
+
},
|
|
364
|
+
to: {
|
|
365
|
+
x: cx + rx,
|
|
366
|
+
y: cy
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
}];
|
|
371
|
+
}
|
|
372
|
+
function userUnits(element, attr) {
|
|
373
|
+
return require_svg_units.parseSvgUserUnits(findAttribute(element, attr)) ?? 0;
|
|
374
|
+
}
|
|
375
|
+
function readShape(state, element, ctm, paint) {
|
|
376
|
+
const name = localName(element.tag);
|
|
377
|
+
const id = findAttribute(element, "id");
|
|
378
|
+
const detail = id === void 0 ? name : `${name}#${id}`;
|
|
379
|
+
let fillRule;
|
|
380
|
+
const fillRuleSpec = paint.fillRuleSpec;
|
|
381
|
+
if (fillRuleSpec !== void 0 && fillRuleSpec !== "nonzero") if (fillRuleSpec === "evenodd") fillRule = "evenodd";
|
|
382
|
+
else report(state, "svg/paint-unsupported", fillRuleSpec);
|
|
383
|
+
const resolved = resolvePaint(state, paint, ctm);
|
|
384
|
+
if (resolved.fill === void 0 && resolved.stroke === void 0) {
|
|
385
|
+
report(state, "svg/element-skipped", `${detail}: nothing painted (fill and stroke both absent or none)`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (name === "rect") {
|
|
389
|
+
const x = userUnits(element, "x");
|
|
390
|
+
const y = userUnits(element, "y");
|
|
391
|
+
const width = userUnits(element, "width");
|
|
392
|
+
const height = userUnits(element, "height");
|
|
393
|
+
if (width <= 0 || height <= 0) {
|
|
394
|
+
report(state, "svg/element-skipped", `${detail}: zero or negative size`);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const rxAttr = require_svg_units.parseSvgUserUnits(findAttribute(element, "rx"));
|
|
398
|
+
const ryAttr = require_svg_units.parseSvgUserUnits(findAttribute(element, "ry"));
|
|
399
|
+
const rx = rxAttr ?? ryAttr ?? 0;
|
|
400
|
+
const ry = ryAttr ?? rxAttr ?? 0;
|
|
401
|
+
if (rx > 0 || ry > 0) {
|
|
402
|
+
const vector = buildPathVector(state, roundedRectSubpaths(x, y, width, height, rx, ry), ctm, resolved, fillRule);
|
|
403
|
+
if (vector !== void 0) state.vectors.push(vector);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const rotated = !require_svg_transform.isAxisAligned(ctm) && require_svg_transform.isNonReflectingSimilarity(ctm);
|
|
407
|
+
const frame = rotated ? similarityFrame(ctm, x, y, width, height) : axisAlignedFrame(ctm, x, y, width, height);
|
|
408
|
+
if (frame.widthPt <= 0 || frame.heightPt <= 0) {
|
|
409
|
+
report(state, "svg/element-skipped", `${detail}: collapses to zero size under transform`);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
state.vectors.push({
|
|
413
|
+
kind: "rect",
|
|
414
|
+
frame,
|
|
415
|
+
...rotated ? { rotationDeg: require_svg_transform.similarityRotationDeg(ctm) } : {},
|
|
416
|
+
...resolved.fill !== void 0 ? { fill: resolved.fill } : {},
|
|
417
|
+
...resolved.stroke !== void 0 ? { stroke: resolved.stroke } : {},
|
|
418
|
+
paintOrder: state.paintOrder++,
|
|
419
|
+
sourcePath: `svg/vector[${state.vectors.length}]`
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (name === "circle" || name === "ellipse") {
|
|
424
|
+
const cx = userUnits(element, "cx");
|
|
425
|
+
const cy = userUnits(element, "cy");
|
|
426
|
+
const rx = name === "circle" ? userUnits(element, "r") : userUnits(element, "rx");
|
|
427
|
+
const ry = name === "circle" ? userUnits(element, "r") : userUnits(element, "ry");
|
|
428
|
+
if (rx <= 0 || ry <= 0) {
|
|
429
|
+
report(state, "svg/element-skipped", `${detail}: zero or negative radius`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (!require_svg_transform.isAxisAligned(ctm) && !require_svg_transform.isNonReflectingSimilarity(ctm)) {
|
|
433
|
+
const vector = buildPathVector(state, ellipseSubpaths(cx, cy, rx, ry), ctm, resolved, fillRule);
|
|
434
|
+
if (vector !== void 0) state.vectors.push(vector);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const rotated = !require_svg_transform.isAxisAligned(ctm);
|
|
438
|
+
const frame = rotated ? similarityFrame(ctm, cx - rx, cy - ry, rx * 2, ry * 2) : axisAlignedFrame(ctm, cx - rx, cy - ry, rx * 2, ry * 2);
|
|
439
|
+
if (frame.widthPt <= 0 || frame.heightPt <= 0) {
|
|
440
|
+
report(state, "svg/element-skipped", `${detail}: collapses to zero size under transform`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
state.vectors.push({
|
|
444
|
+
kind: "ellipse",
|
|
445
|
+
frame,
|
|
446
|
+
...rotated ? { rotationDeg: require_svg_transform.similarityRotationDeg(ctm) } : {},
|
|
447
|
+
...resolved.fill !== void 0 ? { fill: resolved.fill } : {},
|
|
448
|
+
...resolved.stroke !== void 0 ? { stroke: resolved.stroke } : {},
|
|
449
|
+
paintOrder: state.paintOrder++,
|
|
450
|
+
sourcePath: `svg/vector[${state.vectors.length}]`
|
|
451
|
+
});
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (name === "line") {
|
|
455
|
+
if (resolved.stroke === void 0) {
|
|
456
|
+
report(state, "svg/element-skipped", `${detail}: a line paints only through its stroke, which is absent or none`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const from = require_svg_transform.applyMatrix(ctm, userUnits(element, "x1"), userUnits(element, "y1"));
|
|
460
|
+
const to = require_svg_transform.applyMatrix(ctm, userUnits(element, "x2"), userUnits(element, "y2"));
|
|
461
|
+
if (from.x === to.x && from.y === to.y) {
|
|
462
|
+
report(state, "svg/element-skipped", `${detail}: zero-length line`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
state.vectors.push({
|
|
466
|
+
kind: "line",
|
|
467
|
+
from: {
|
|
468
|
+
xPt: from.x,
|
|
469
|
+
yPt: from.y
|
|
470
|
+
},
|
|
471
|
+
to: {
|
|
472
|
+
xPt: to.x,
|
|
473
|
+
yPt: to.y
|
|
474
|
+
},
|
|
475
|
+
stroke: resolved.stroke,
|
|
476
|
+
paintOrder: state.paintOrder++,
|
|
477
|
+
sourcePath: `svg/vector[${state.vectors.length}]`
|
|
478
|
+
});
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (name === "polyline" || name === "polygon") {
|
|
482
|
+
const pointsRaw = findAttribute(element, "points");
|
|
483
|
+
if (pointsRaw === void 0) {
|
|
484
|
+
report(state, "svg/element-skipped", `${detail}: no points attribute`);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const numbers = pointsRaw.trim().split(/[\s,]+/).filter((part) => part !== "").map(Number);
|
|
488
|
+
if (numbers.length === 0 || numbers.length % 2 !== 0 || !numbers.every((value) => Number.isFinite(value))) {
|
|
489
|
+
report(state, "svg/element-unsupported", `${detail}: malformed points attribute`);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (numbers.length < 4) {
|
|
493
|
+
report(state, "svg/element-skipped", `${detail}: fewer than two points`);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const segments = [];
|
|
497
|
+
for (let i = 2; i < numbers.length; i += 2) segments.push({
|
|
498
|
+
kind: "line",
|
|
499
|
+
to: {
|
|
500
|
+
x: numbers[i],
|
|
501
|
+
y: numbers[i + 1]
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
const vector = buildPathVector(state, [{
|
|
505
|
+
start: {
|
|
506
|
+
x: numbers[0],
|
|
507
|
+
y: numbers[1]
|
|
508
|
+
},
|
|
509
|
+
closed: name === "polygon",
|
|
510
|
+
segments
|
|
511
|
+
}], ctm, resolved, fillRule);
|
|
512
|
+
if (vector === void 0) report(state, "svg/element-skipped", `${detail}: all points coincide`);
|
|
513
|
+
else state.vectors.push(vector);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (name === "path") {
|
|
517
|
+
const d = findAttribute(element, "d");
|
|
518
|
+
if (d === void 0 || d.trim() === "") {
|
|
519
|
+
report(state, "svg/element-skipped", `${detail}: no d attribute`);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const parsed = require_svg_path.parseSvgPathData(d);
|
|
523
|
+
if (parsed === void 0 || parsed.length === 0) {
|
|
524
|
+
report(state, "svg/element-unsupported", `${detail}: malformed or empty path data`);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const vector = buildPathVector(state, parsed, ctm, resolved, fillRule);
|
|
528
|
+
if (vector === void 0) report(state, "svg/element-skipped", `${detail}: path collapses to a single point`);
|
|
529
|
+
else state.vectors.push(vector);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const NON_RENDERING_ELEMENTS = /* @__PURE__ */ new Set([
|
|
534
|
+
"defs",
|
|
535
|
+
"title",
|
|
536
|
+
"desc",
|
|
537
|
+
"metadata",
|
|
538
|
+
"linearGradient",
|
|
539
|
+
"radialGradient",
|
|
540
|
+
"pattern",
|
|
541
|
+
"clipPath",
|
|
542
|
+
"mask",
|
|
543
|
+
"marker",
|
|
544
|
+
"symbol",
|
|
545
|
+
"script"
|
|
546
|
+
]);
|
|
547
|
+
function walkElement(state, element, ctm, paint) {
|
|
548
|
+
const name = localName(element.tag);
|
|
549
|
+
const id = findAttribute(element, "id");
|
|
550
|
+
const detail = id === void 0 ? name : `${name}#${id}`;
|
|
551
|
+
if (findAttribute(element, "style") !== void 0) report(state, "svg/css-style-ignored", detail);
|
|
552
|
+
for (const opacityAttr of [
|
|
553
|
+
"opacity",
|
|
554
|
+
"fill-opacity",
|
|
555
|
+
"stroke-opacity"
|
|
556
|
+
]) {
|
|
557
|
+
const raw = findAttribute(element, opacityAttr);
|
|
558
|
+
if (raw !== void 0) {
|
|
559
|
+
const value = Number(raw);
|
|
560
|
+
if (Number.isFinite(value) && value < 1) report(state, "svg/opacity-ignored", `${detail}: ${opacityAttr}=${raw}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (name === "g" || name === "a") {
|
|
564
|
+
const own = require_svg_transform.parseSvgTransform(findAttribute(element, "transform"));
|
|
565
|
+
walkChildren(state, element, own === void 0 ? ctm : require_svg_transform.composeMatrices(ctm, own), childPaintState(element, paint));
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (NON_RENDERING_ELEMENTS.has(name)) return;
|
|
569
|
+
if (name === "style") {
|
|
570
|
+
report(state, "svg/css-style-ignored", detail);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (name === "text" || name === "tspan" || name === "textPath" || name === "tref") {
|
|
574
|
+
report(state, "svg/text-unsupported", detail);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (name === "image") {
|
|
578
|
+
report(state, "svg/image-unsupported", detail);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (name === "use") {
|
|
582
|
+
report(state, "svg/use-unsupported", detail);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (name === "rect" || name === "circle" || name === "ellipse" || name === "line" || name === "polyline" || name === "polygon" || name === "path") {
|
|
586
|
+
const own = require_svg_transform.parseSvgTransform(findAttribute(element, "transform"));
|
|
587
|
+
readShape(state, element, own === void 0 ? ctm : require_svg_transform.composeMatrices(ctm, own), childPaintState(element, paint));
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
report(state, "svg/element-unsupported", detail);
|
|
591
|
+
}
|
|
592
|
+
function walkChildren(state, element, ctm, paint) {
|
|
593
|
+
for (const child of elementChildren(element)) walkElement(state, child, ctm, paint);
|
|
594
|
+
}
|
|
595
|
+
function resolveRootGeometry(root, state) {
|
|
596
|
+
const attrWidth = require_svg_units.parseSvgLengthPt(findAttribute(root, "width"));
|
|
597
|
+
const attrHeight = require_svg_units.parseSvgLengthPt(findAttribute(root, "height"));
|
|
598
|
+
const parsedViewBox = require_svg_units.parseSvgViewBox(findAttribute(root, "viewBox"));
|
|
599
|
+
const viewBox = parsedViewBox !== void 0 && parsedViewBox.width > 0 && parsedViewBox.height > 0 ? parsedViewBox : void 0;
|
|
600
|
+
let widthPt = attrWidth !== void 0 && attrWidth > 0 ? attrWidth : void 0;
|
|
601
|
+
let heightPt = attrHeight !== void 0 && attrHeight > 0 ? attrHeight : void 0;
|
|
602
|
+
if (widthPt === void 0 !== (heightPt === void 0)) {
|
|
603
|
+
widthPt = void 0;
|
|
604
|
+
heightPt = void 0;
|
|
605
|
+
}
|
|
606
|
+
if (widthPt === void 0 || heightPt === void 0) if (viewBox !== void 0) {
|
|
607
|
+
widthPt = viewBox.width;
|
|
608
|
+
heightPt = viewBox.height;
|
|
609
|
+
} else {
|
|
610
|
+
widthPt = DEFAULT_WIDTH_PT;
|
|
611
|
+
heightPt = DEFAULT_HEIGHT_PT;
|
|
612
|
+
report(state, "svg/default-size-assumed", "neither width/height nor a usable viewBox was present; assuming the CSS default replaced-element size of 300x150 px");
|
|
613
|
+
}
|
|
614
|
+
if (viewBox === void 0) return {
|
|
615
|
+
widthPt,
|
|
616
|
+
heightPt,
|
|
617
|
+
map: {
|
|
618
|
+
a: .75,
|
|
619
|
+
b: 0,
|
|
620
|
+
c: 0,
|
|
621
|
+
d: .75,
|
|
622
|
+
e: 0,
|
|
623
|
+
f: 0
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
const preserveAspectRatio = findAttribute(root, "preserveAspectRatio") ?? "xMidYMid meet";
|
|
627
|
+
if (preserveAspectRatio.trim() !== "none") {
|
|
628
|
+
const viewBoxAspect = viewBox.width / viewBox.height;
|
|
629
|
+
const pageAspect = widthPt / heightPt;
|
|
630
|
+
if (Math.abs(viewBoxAspect - pageAspect) > 1e-6 * Math.max(viewBoxAspect, pageAspect)) report(state, "svg/preserve-aspect-ratio-stretched", `viewBox aspect ${viewBoxAspect.toFixed(4)} stretched onto page aspect ${pageAspect.toFixed(4)} under preserveAspectRatio="${preserveAspectRatio.trim()}" (letterboxing is out of scope)`);
|
|
631
|
+
}
|
|
632
|
+
const sx = widthPt / viewBox.width;
|
|
633
|
+
const sy = heightPt / viewBox.height;
|
|
634
|
+
return {
|
|
635
|
+
widthPt,
|
|
636
|
+
heightPt,
|
|
637
|
+
map: {
|
|
638
|
+
a: sx,
|
|
639
|
+
b: 0,
|
|
640
|
+
c: 0,
|
|
641
|
+
d: sy,
|
|
642
|
+
e: -viewBox.minX * sx,
|
|
643
|
+
f: -viewBox.minY * sy
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function readSvgContent(text, options) {
|
|
648
|
+
const root = (0, odf_js.parseXml)(text).find((node) => node.type === "element" && localName(node.tag) === "svg");
|
|
649
|
+
if (root === void 0) throw new SvgMissingRootElementError();
|
|
650
|
+
const state = {
|
|
651
|
+
sink: options?.onSvgDiagnostic,
|
|
652
|
+
vectors: [],
|
|
653
|
+
paintOrder: 0
|
|
654
|
+
};
|
|
655
|
+
const rootGeometry = resolveRootGeometry(root, state);
|
|
656
|
+
const titleElement = elementChildren(root).find((child) => localName(child.tag) === "title");
|
|
657
|
+
const title = titleElement === void 0 ? void 0 : textOf(titleElement).trim();
|
|
658
|
+
const metadata = title === void 0 || title === "" ? {} : { title };
|
|
659
|
+
walkChildren(state, root, rootGeometry.map, childPaintState(root, {}));
|
|
660
|
+
const page = {
|
|
661
|
+
size: {
|
|
662
|
+
widthPt: rootGeometry.widthPt,
|
|
663
|
+
heightPt: rootGeometry.heightPt
|
|
664
|
+
},
|
|
665
|
+
shapes: [],
|
|
666
|
+
vectors: state.vectors
|
|
667
|
+
};
|
|
668
|
+
return {
|
|
669
|
+
kind: "drawing",
|
|
670
|
+
formatVersion: document_schema_js.CONTENT_FORMAT_VERSION,
|
|
671
|
+
metadata,
|
|
672
|
+
pages: [page]
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
exports.SvgMissingRootElementError = SvgMissingRootElementError;
|
|
677
|
+
exports.readSvgContent = readSvgContent;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SvgDiagnosticSink } from "./diagnostics.cjs";
|
|
2
|
+
import { ContentDocument } from "document-schema.js";
|
|
3
|
+
//#region src/svg/read.d.ts
|
|
4
|
+
interface ReadSvgContentOptions {
|
|
5
|
+
readonly onSvgDiagnostic?: SvgDiagnosticSink;
|
|
6
|
+
}
|
|
7
|
+
declare class SvgMissingRootElementError extends Error {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
declare function readSvgContent(text: string, options?: ReadSvgContentOptions): ContentDocument;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { ReadSvgContentOptions, SvgMissingRootElementError, readSvgContent };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SvgDiagnosticSink } from "./diagnostics.js";
|
|
2
|
+
import { ContentDocument } from "document-schema.js";
|
|
3
|
+
//#region src/svg/read.d.ts
|
|
4
|
+
interface ReadSvgContentOptions {
|
|
5
|
+
readonly onSvgDiagnostic?: SvgDiagnosticSink;
|
|
6
|
+
}
|
|
7
|
+
declare class SvgMissingRootElementError extends Error {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
declare function readSvgContent(text: string, options?: ReadSvgContentOptions): ContentDocument;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { ReadSvgContentOptions, SvgMissingRootElementError, readSvgContent };
|