@versatiles/svg-renderer 0.8.0 → 1.1.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 +42 -32
- package/dist/index.cjs +61 -22
- package/dist/index.js +61 -22
- package/dist/{maplibre.d.ts → maplibre-svg-export.d.ts} +2 -0
- package/dist/{maplibre.js → maplibre-svg-export.js} +77 -30
- package/dist/{maplibre.umd.js → maplibre-svg-export.umd.js} +77 -30
- package/package.json +25 -28
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/maplibre.cjs +0 -10984
- package/dist/maplibre.cjs.map +0 -1
- package/dist/maplibre.js.map +0 -1
- package/dist/maplibre.umd.js.map +0 -1
package/README.md
CHANGED
|
@@ -64,13 +64,23 @@ document.body.innerHTML = svg;
|
|
|
64
64
|
|
|
65
65
|
The package includes an `SVGExportControl` that adds an export button to any MapLibre GL JS map.
|
|
66
66
|
|
|
67
|
+
With a bundler, import it from the `/maplibre` subpath:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { SVGExportControl } from '@versatiles/svg-renderer/maplibre';
|
|
71
|
+
|
|
72
|
+
map.addControl(new SVGExportControl(), 'top-right');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or load it directly in the browser via the UMD bundle, which exposes a `VersaTilesSVG` global:
|
|
76
|
+
|
|
67
77
|
```html
|
|
68
78
|
<!DOCTYPE html>
|
|
69
79
|
<html>
|
|
70
80
|
<head>
|
|
71
81
|
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css" />
|
|
72
82
|
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
|
|
73
|
-
<script src="https://unpkg.com/@versatiles/svg-renderer/dist/maplibre.umd.js"></script>
|
|
83
|
+
<script src="https://unpkg.com/@versatiles/svg-renderer/dist/maplibre-svg-export.umd.js"></script>
|
|
74
84
|
</head>
|
|
75
85
|
<body>
|
|
76
86
|
<div id="map"></div>
|
|
@@ -87,7 +97,7 @@ The package includes an `SVGExportControl` that adds an export button to any Map
|
|
|
87
97
|
</html>
|
|
88
98
|
```
|
|
89
99
|
|
|
90
|
-
The control opens a panel where the user can set width
|
|
100
|
+
The control opens a panel where the user can set width and height, preview the SVG, download it, or open it in a new tab. Map interactions are disabled while the panel is open.
|
|
91
101
|
|
|
92
102
|
Options:
|
|
93
103
|
|
|
@@ -95,7 +105,6 @@ Options:
|
|
|
95
105
|
new SVGExportControl({
|
|
96
106
|
defaultWidth: 1024, // default: 1024
|
|
97
107
|
defaultHeight: 1024, // default: 1024
|
|
98
|
-
defaultScale: 1, // default: 1
|
|
99
108
|
});
|
|
100
109
|
```
|
|
101
110
|
|
|
@@ -145,53 +154,54 @@ subgraph 0["src"]
|
|
|
145
154
|
2["index.ts"]
|
|
146
155
|
subgraph 3["pipeline"]
|
|
147
156
|
4["render.ts"]
|
|
148
|
-
|
|
157
|
+
E["style_layer.ts"]
|
|
149
158
|
end
|
|
150
159
|
subgraph 5["sources"]
|
|
151
160
|
6["index.ts"]
|
|
152
161
|
7["geojson.ts"]
|
|
153
162
|
9["raster.ts"]
|
|
154
|
-
A["
|
|
155
|
-
B["
|
|
156
|
-
C["
|
|
157
|
-
|
|
163
|
+
A["base64.ts"]
|
|
164
|
+
B["tiles.ts"]
|
|
165
|
+
C["sprite.ts"]
|
|
166
|
+
D["vector.ts"]
|
|
158
167
|
end
|
|
159
168
|
8["geometry.ts"]
|
|
160
|
-
subgraph
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
subgraph F["renderer"]
|
|
170
|
+
G["svg.ts"]
|
|
171
|
+
H["color.ts"]
|
|
172
|
+
I["svg_path.ts"]
|
|
173
|
+
N["types.ts"]
|
|
165
174
|
end
|
|
166
|
-
subgraph
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
subgraph J["maplibre"]
|
|
176
|
+
K["control.ts"]
|
|
177
|
+
L["panel_css.ts"]
|
|
178
|
+
M["index.ts"]
|
|
170
179
|
end
|
|
171
180
|
end
|
|
172
181
|
1-->2
|
|
173
182
|
2-->4
|
|
174
|
-
2-->
|
|
183
|
+
2-->G
|
|
175
184
|
4-->6
|
|
176
|
-
4-->
|
|
177
|
-
4-->
|
|
185
|
+
4-->C
|
|
186
|
+
4-->E
|
|
178
187
|
6-->7
|
|
179
188
|
6-->9
|
|
180
|
-
6-->B
|
|
181
189
|
6-->C
|
|
190
|
+
6-->D
|
|
182
191
|
7-->8
|
|
183
192
|
9-->A
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
9-->B
|
|
194
|
+
B-->8
|
|
186
195
|
C-->A
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
L
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
D-->8
|
|
197
|
+
D-->B
|
|
198
|
+
G-->H
|
|
199
|
+
G-->I
|
|
200
|
+
K-->2
|
|
201
|
+
K-->L
|
|
202
|
+
M-->2
|
|
203
|
+
M-->K
|
|
204
|
+
|
|
205
|
+
class 0,3,5,F,J subgraphs;
|
|
196
206
|
classDef subgraphs fill-opacity:0.1, fill:#888, color:#888, stroke:#888;
|
|
197
207
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -449,19 +449,28 @@ var v8_default = {
|
|
|
449
449
|
},
|
|
450
450
|
"property-type": "data-constant"
|
|
451
451
|
} },
|
|
452
|
-
"layout_fill-extrusion": {
|
|
453
|
-
"
|
|
454
|
-
|
|
455
|
-
"
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
"
|
|
461
|
-
|
|
452
|
+
"layout_fill-extrusion": {
|
|
453
|
+
"visibility": {
|
|
454
|
+
"type": "enum",
|
|
455
|
+
"values": {
|
|
456
|
+
"visible": {},
|
|
457
|
+
"none": {}
|
|
458
|
+
},
|
|
459
|
+
"default": "visible",
|
|
460
|
+
"expression": {
|
|
461
|
+
"interpolated": false,
|
|
462
|
+
"parameters": ["global-state"]
|
|
463
|
+
},
|
|
464
|
+
"property-type": "data-constant"
|
|
462
465
|
},
|
|
463
|
-
"
|
|
464
|
-
|
|
466
|
+
"fill-extrusion-rounded-corner-distance": {
|
|
467
|
+
"type": "number",
|
|
468
|
+
"default": 0,
|
|
469
|
+
"minimum": 0,
|
|
470
|
+
"units": "meters",
|
|
471
|
+
"property-type": "constant"
|
|
472
|
+
}
|
|
473
|
+
},
|
|
465
474
|
layout_line: {
|
|
466
475
|
"line-cap": {
|
|
467
476
|
"type": "enum",
|
|
@@ -4499,7 +4508,11 @@ var Coercion = class Coercion {
|
|
|
4499
4508
|
}
|
|
4500
4509
|
case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
4501
4510
|
case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
4502
|
-
case "projectionDefinition":
|
|
4511
|
+
case "projectionDefinition": {
|
|
4512
|
+
const input = this.args[0].evaluate(ctx);
|
|
4513
|
+
if (ProjectionDefinition.parse(input)) return input;
|
|
4514
|
+
throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
|
|
4515
|
+
}
|
|
4503
4516
|
default: return valueToString(this.args[0].evaluate(ctx));
|
|
4504
4517
|
}
|
|
4505
4518
|
}
|
|
@@ -4602,7 +4615,11 @@ var ParsingContext = class ParsingContext {
|
|
|
4602
4615
|
const expected = this.expectedType;
|
|
4603
4616
|
const actual = parsed.type;
|
|
4604
4617
|
if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
|
|
4605
|
-
else if ("projectionDefinition" === expected.kind && [
|
|
4618
|
+
else if ("projectionDefinition" === expected.kind && [
|
|
4619
|
+
"string",
|
|
4620
|
+
"array",
|
|
4621
|
+
"value"
|
|
4622
|
+
].includes(actual.kind) || [
|
|
4606
4623
|
"color",
|
|
4607
4624
|
"formatted",
|
|
4608
4625
|
"resolvedImage"
|
|
@@ -10027,6 +10044,34 @@ function loadGeoJSONSource(options) {
|
|
|
10027
10044
|
}
|
|
10028
10045
|
}
|
|
10029
10046
|
|
|
10047
|
+
/**
|
|
10048
|
+
* Base64-encode an ArrayBuffer (standard alphabet, with padding).
|
|
10049
|
+
*
|
|
10050
|
+
* Three paths, all producing identical output:
|
|
10051
|
+
* 1. `Uint8Array.prototype.toBase64` — the native method (Node 22+, modern
|
|
10052
|
+
* browsers). Fastest and simplest; used when present.
|
|
10053
|
+
* 2. Node's `Buffer` — for Node versions without the native method.
|
|
10054
|
+
* 3. Chunked `btoa(String.fromCharCode(...))` — the browser fallback. It must
|
|
10055
|
+
* chunk: spreading a whole tile/sprite PNG at once passes one argument per
|
|
10056
|
+
* byte, overflowing the engine's argument-count limit (Safari ~64K, V8
|
|
10057
|
+
* higher but finite) and throwing `RangeError: Maximum call stack size
|
|
10058
|
+
* exceeded` for buffers above ~64 KB. 32 KB slices stay well under the limit.
|
|
10059
|
+
*/
|
|
10060
|
+
function arrayBufferToBase64(buffer) {
|
|
10061
|
+
const bytes = new Uint8Array(buffer);
|
|
10062
|
+
const toBase64 = bytes.toBase64;
|
|
10063
|
+
if (typeof toBase64 === 'function')
|
|
10064
|
+
return toBase64.call(bytes);
|
|
10065
|
+
if (typeof Buffer !== 'undefined')
|
|
10066
|
+
return Buffer.from(buffer).toString('base64');
|
|
10067
|
+
const CHUNK_SIZE = 0x8000; // 32 KB, well below the argument-count limit
|
|
10068
|
+
let binary = '';
|
|
10069
|
+
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
|
10070
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
|
|
10071
|
+
}
|
|
10072
|
+
return btoa(binary);
|
|
10073
|
+
}
|
|
10074
|
+
|
|
10030
10075
|
async function getRasterTiles(job, sourceName) {
|
|
10031
10076
|
const { width, height } = job.renderer;
|
|
10032
10077
|
const { zoom, center } = job.view;
|
|
@@ -10040,10 +10085,7 @@ async function getRasterTiles(job, sourceName) {
|
|
|
10040
10085
|
const tile = await getTile(sourceUrl, zoomLevel, x, y);
|
|
10041
10086
|
if (!tile)
|
|
10042
10087
|
return null;
|
|
10043
|
-
const
|
|
10044
|
-
? Buffer.from(tile.buffer).toString('base64')
|
|
10045
|
-
: btoa(String.fromCharCode(...new Uint8Array(tile.buffer)));
|
|
10046
|
-
const dataUri = `data:${tile.contentType};base64,${base64}`;
|
|
10088
|
+
const dataUri = `data:${tile.contentType};base64,${arrayBufferToBase64(tile.buffer)}`;
|
|
10047
10089
|
return {
|
|
10048
10090
|
x: offsetX,
|
|
10049
10091
|
y: offsetY,
|
|
@@ -10089,10 +10131,7 @@ async function loadSpriteAtlas(style) {
|
|
|
10089
10131
|
const { jsonResponse, imageResponse } = spritePair;
|
|
10090
10132
|
const json = (await jsonResponse.json());
|
|
10091
10133
|
const imageBuffer = await imageResponse.arrayBuffer();
|
|
10092
|
-
const
|
|
10093
|
-
? Buffer.from(imageBuffer).toString('base64')
|
|
10094
|
-
: btoa(String.fromCharCode(...new Uint8Array(imageBuffer)));
|
|
10095
|
-
const sheetDataUri = `data:image/png;base64,${base64}`;
|
|
10134
|
+
const sheetDataUri = `data:image/png;base64,${arrayBufferToBase64(imageBuffer)}`;
|
|
10096
10135
|
// Estimate sheet dimensions from sprite entries
|
|
10097
10136
|
let sheetWidth = 0;
|
|
10098
10137
|
let sheetHeight = 0;
|
package/dist/index.js
CHANGED
|
@@ -447,19 +447,28 @@ var v8_default = {
|
|
|
447
447
|
},
|
|
448
448
|
"property-type": "data-constant"
|
|
449
449
|
} },
|
|
450
|
-
"layout_fill-extrusion": {
|
|
451
|
-
"
|
|
452
|
-
|
|
453
|
-
"
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
"
|
|
459
|
-
|
|
450
|
+
"layout_fill-extrusion": {
|
|
451
|
+
"visibility": {
|
|
452
|
+
"type": "enum",
|
|
453
|
+
"values": {
|
|
454
|
+
"visible": {},
|
|
455
|
+
"none": {}
|
|
456
|
+
},
|
|
457
|
+
"default": "visible",
|
|
458
|
+
"expression": {
|
|
459
|
+
"interpolated": false,
|
|
460
|
+
"parameters": ["global-state"]
|
|
461
|
+
},
|
|
462
|
+
"property-type": "data-constant"
|
|
460
463
|
},
|
|
461
|
-
"
|
|
462
|
-
|
|
464
|
+
"fill-extrusion-rounded-corner-distance": {
|
|
465
|
+
"type": "number",
|
|
466
|
+
"default": 0,
|
|
467
|
+
"minimum": 0,
|
|
468
|
+
"units": "meters",
|
|
469
|
+
"property-type": "constant"
|
|
470
|
+
}
|
|
471
|
+
},
|
|
463
472
|
layout_line: {
|
|
464
473
|
"line-cap": {
|
|
465
474
|
"type": "enum",
|
|
@@ -4497,7 +4506,11 @@ var Coercion = class Coercion {
|
|
|
4497
4506
|
}
|
|
4498
4507
|
case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
4499
4508
|
case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
4500
|
-
case "projectionDefinition":
|
|
4509
|
+
case "projectionDefinition": {
|
|
4510
|
+
const input = this.args[0].evaluate(ctx);
|
|
4511
|
+
if (ProjectionDefinition.parse(input)) return input;
|
|
4512
|
+
throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
|
|
4513
|
+
}
|
|
4501
4514
|
default: return valueToString(this.args[0].evaluate(ctx));
|
|
4502
4515
|
}
|
|
4503
4516
|
}
|
|
@@ -4600,7 +4613,11 @@ var ParsingContext = class ParsingContext {
|
|
|
4600
4613
|
const expected = this.expectedType;
|
|
4601
4614
|
const actual = parsed.type;
|
|
4602
4615
|
if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
|
|
4603
|
-
else if ("projectionDefinition" === expected.kind && [
|
|
4616
|
+
else if ("projectionDefinition" === expected.kind && [
|
|
4617
|
+
"string",
|
|
4618
|
+
"array",
|
|
4619
|
+
"value"
|
|
4620
|
+
].includes(actual.kind) || [
|
|
4604
4621
|
"color",
|
|
4605
4622
|
"formatted",
|
|
4606
4623
|
"resolvedImage"
|
|
@@ -10025,6 +10042,34 @@ function loadGeoJSONSource(options) {
|
|
|
10025
10042
|
}
|
|
10026
10043
|
}
|
|
10027
10044
|
|
|
10045
|
+
/**
|
|
10046
|
+
* Base64-encode an ArrayBuffer (standard alphabet, with padding).
|
|
10047
|
+
*
|
|
10048
|
+
* Three paths, all producing identical output:
|
|
10049
|
+
* 1. `Uint8Array.prototype.toBase64` — the native method (Node 22+, modern
|
|
10050
|
+
* browsers). Fastest and simplest; used when present.
|
|
10051
|
+
* 2. Node's `Buffer` — for Node versions without the native method.
|
|
10052
|
+
* 3. Chunked `btoa(String.fromCharCode(...))` — the browser fallback. It must
|
|
10053
|
+
* chunk: spreading a whole tile/sprite PNG at once passes one argument per
|
|
10054
|
+
* byte, overflowing the engine's argument-count limit (Safari ~64K, V8
|
|
10055
|
+
* higher but finite) and throwing `RangeError: Maximum call stack size
|
|
10056
|
+
* exceeded` for buffers above ~64 KB. 32 KB slices stay well under the limit.
|
|
10057
|
+
*/
|
|
10058
|
+
function arrayBufferToBase64(buffer) {
|
|
10059
|
+
const bytes = new Uint8Array(buffer);
|
|
10060
|
+
const toBase64 = bytes.toBase64;
|
|
10061
|
+
if (typeof toBase64 === 'function')
|
|
10062
|
+
return toBase64.call(bytes);
|
|
10063
|
+
if (typeof Buffer !== 'undefined')
|
|
10064
|
+
return Buffer.from(buffer).toString('base64');
|
|
10065
|
+
const CHUNK_SIZE = 0x8000; // 32 KB, well below the argument-count limit
|
|
10066
|
+
let binary = '';
|
|
10067
|
+
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
|
10068
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
|
|
10069
|
+
}
|
|
10070
|
+
return btoa(binary);
|
|
10071
|
+
}
|
|
10072
|
+
|
|
10028
10073
|
async function getRasterTiles(job, sourceName) {
|
|
10029
10074
|
const { width, height } = job.renderer;
|
|
10030
10075
|
const { zoom, center } = job.view;
|
|
@@ -10038,10 +10083,7 @@ async function getRasterTiles(job, sourceName) {
|
|
|
10038
10083
|
const tile = await getTile(sourceUrl, zoomLevel, x, y);
|
|
10039
10084
|
if (!tile)
|
|
10040
10085
|
return null;
|
|
10041
|
-
const
|
|
10042
|
-
? Buffer.from(tile.buffer).toString('base64')
|
|
10043
|
-
: btoa(String.fromCharCode(...new Uint8Array(tile.buffer)));
|
|
10044
|
-
const dataUri = `data:${tile.contentType};base64,${base64}`;
|
|
10086
|
+
const dataUri = `data:${tile.contentType};base64,${arrayBufferToBase64(tile.buffer)}`;
|
|
10045
10087
|
return {
|
|
10046
10088
|
x: offsetX,
|
|
10047
10089
|
y: offsetY,
|
|
@@ -10087,10 +10129,7 @@ async function loadSpriteAtlas(style) {
|
|
|
10087
10129
|
const { jsonResponse, imageResponse } = spritePair;
|
|
10088
10130
|
const json = (await jsonResponse.json());
|
|
10089
10131
|
const imageBuffer = await imageResponse.arrayBuffer();
|
|
10090
|
-
const
|
|
10091
|
-
? Buffer.from(imageBuffer).toString('base64')
|
|
10092
|
-
: btoa(String.fromCharCode(...new Uint8Array(imageBuffer)));
|
|
10093
|
-
const sheetDataUri = `data:image/png;base64,${base64}`;
|
|
10132
|
+
const sheetDataUri = `data:image/png;base64,${arrayBufferToBase64(imageBuffer)}`;
|
|
10094
10133
|
// Estimate sheet dimensions from sprite entries
|
|
10095
10134
|
let sheetWidth = 0;
|
|
10096
10135
|
let sheetHeight = 0;
|
|
@@ -2,7 +2,9 @@ import { IControl, Map } from 'maplibre-gl';
|
|
|
2
2
|
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
|
|
3
3
|
|
|
4
4
|
interface SVGExportControlOptions {
|
|
5
|
+
/** Initial export width in px. Defaults to the current map viewport width. */
|
|
5
6
|
defaultWidth?: number;
|
|
7
|
+
/** Initial export height in px. Defaults to the current map viewport height. */
|
|
6
8
|
defaultHeight?: number;
|
|
7
9
|
}
|
|
8
10
|
declare class SVGExportControl implements IControl {
|
|
@@ -628,19 +628,28 @@ var v8_default = {
|
|
|
628
628
|
},
|
|
629
629
|
"property-type": "data-constant"
|
|
630
630
|
} },
|
|
631
|
-
"layout_fill-extrusion": {
|
|
632
|
-
"
|
|
633
|
-
|
|
634
|
-
"
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
"
|
|
640
|
-
|
|
631
|
+
"layout_fill-extrusion": {
|
|
632
|
+
"visibility": {
|
|
633
|
+
"type": "enum",
|
|
634
|
+
"values": {
|
|
635
|
+
"visible": {},
|
|
636
|
+
"none": {}
|
|
637
|
+
},
|
|
638
|
+
"default": "visible",
|
|
639
|
+
"expression": {
|
|
640
|
+
"interpolated": false,
|
|
641
|
+
"parameters": ["global-state"]
|
|
642
|
+
},
|
|
643
|
+
"property-type": "data-constant"
|
|
641
644
|
},
|
|
642
|
-
"
|
|
643
|
-
|
|
645
|
+
"fill-extrusion-rounded-corner-distance": {
|
|
646
|
+
"type": "number",
|
|
647
|
+
"default": 0,
|
|
648
|
+
"minimum": 0,
|
|
649
|
+
"units": "meters",
|
|
650
|
+
"property-type": "constant"
|
|
651
|
+
}
|
|
652
|
+
},
|
|
644
653
|
layout_line: {
|
|
645
654
|
"line-cap": {
|
|
646
655
|
"type": "enum",
|
|
@@ -4678,7 +4687,11 @@ var Coercion = class Coercion {
|
|
|
4678
4687
|
}
|
|
4679
4688
|
case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
4680
4689
|
case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
4681
|
-
case "projectionDefinition":
|
|
4690
|
+
case "projectionDefinition": {
|
|
4691
|
+
const input = this.args[0].evaluate(ctx);
|
|
4692
|
+
if (ProjectionDefinition.parse(input)) return input;
|
|
4693
|
+
throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
|
|
4694
|
+
}
|
|
4682
4695
|
default: return valueToString(this.args[0].evaluate(ctx));
|
|
4683
4696
|
}
|
|
4684
4697
|
}
|
|
@@ -4781,7 +4794,11 @@ var ParsingContext = class ParsingContext {
|
|
|
4781
4794
|
const expected = this.expectedType;
|
|
4782
4795
|
const actual = parsed.type;
|
|
4783
4796
|
if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
|
|
4784
|
-
else if ("projectionDefinition" === expected.kind && [
|
|
4797
|
+
else if ("projectionDefinition" === expected.kind && [
|
|
4798
|
+
"string",
|
|
4799
|
+
"array",
|
|
4800
|
+
"value"
|
|
4801
|
+
].includes(actual.kind) || [
|
|
4785
4802
|
"color",
|
|
4786
4803
|
"formatted",
|
|
4787
4804
|
"resolvedImage"
|
|
@@ -10206,6 +10223,34 @@ function loadGeoJSONSource(options) {
|
|
|
10206
10223
|
}
|
|
10207
10224
|
}
|
|
10208
10225
|
|
|
10226
|
+
/**
|
|
10227
|
+
* Base64-encode an ArrayBuffer (standard alphabet, with padding).
|
|
10228
|
+
*
|
|
10229
|
+
* Three paths, all producing identical output:
|
|
10230
|
+
* 1. `Uint8Array.prototype.toBase64` — the native method (Node 22+, modern
|
|
10231
|
+
* browsers). Fastest and simplest; used when present.
|
|
10232
|
+
* 2. Node's `Buffer` — for Node versions without the native method.
|
|
10233
|
+
* 3. Chunked `btoa(String.fromCharCode(...))` — the browser fallback. It must
|
|
10234
|
+
* chunk: spreading a whole tile/sprite PNG at once passes one argument per
|
|
10235
|
+
* byte, overflowing the engine's argument-count limit (Safari ~64K, V8
|
|
10236
|
+
* higher but finite) and throwing `RangeError: Maximum call stack size
|
|
10237
|
+
* exceeded` for buffers above ~64 KB. 32 KB slices stay well under the limit.
|
|
10238
|
+
*/
|
|
10239
|
+
function arrayBufferToBase64(buffer) {
|
|
10240
|
+
const bytes = new Uint8Array(buffer);
|
|
10241
|
+
const toBase64 = bytes.toBase64;
|
|
10242
|
+
if (typeof toBase64 === 'function')
|
|
10243
|
+
return toBase64.call(bytes);
|
|
10244
|
+
if (typeof Buffer !== 'undefined')
|
|
10245
|
+
return Buffer.from(buffer).toString('base64');
|
|
10246
|
+
const CHUNK_SIZE = 0x8000; // 32 KB, well below the argument-count limit
|
|
10247
|
+
let binary = '';
|
|
10248
|
+
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
|
10249
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
|
|
10250
|
+
}
|
|
10251
|
+
return btoa(binary);
|
|
10252
|
+
}
|
|
10253
|
+
|
|
10209
10254
|
async function getRasterTiles(job, sourceName) {
|
|
10210
10255
|
const { width, height } = job.renderer;
|
|
10211
10256
|
const { zoom, center } = job.view;
|
|
@@ -10219,10 +10264,7 @@ async function getRasterTiles(job, sourceName) {
|
|
|
10219
10264
|
const tile = await getTile(sourceUrl, zoomLevel, x, y);
|
|
10220
10265
|
if (!tile)
|
|
10221
10266
|
return null;
|
|
10222
|
-
const
|
|
10223
|
-
? Buffer.from(tile.buffer).toString('base64')
|
|
10224
|
-
: btoa(String.fromCharCode(...new Uint8Array(tile.buffer)));
|
|
10225
|
-
const dataUri = `data:${tile.contentType};base64,${base64}`;
|
|
10267
|
+
const dataUri = `data:${tile.contentType};base64,${arrayBufferToBase64(tile.buffer)}`;
|
|
10226
10268
|
return {
|
|
10227
10269
|
x: offsetX,
|
|
10228
10270
|
y: offsetY,
|
|
@@ -10268,10 +10310,7 @@ async function loadSpriteAtlas(style) {
|
|
|
10268
10310
|
const { jsonResponse, imageResponse } = spritePair;
|
|
10269
10311
|
const json = (await jsonResponse.json());
|
|
10270
10312
|
const imageBuffer = await imageResponse.arrayBuffer();
|
|
10271
|
-
const
|
|
10272
|
-
? Buffer.from(imageBuffer).toString('base64')
|
|
10273
|
-
: btoa(String.fromCharCode(...new Uint8Array(imageBuffer)));
|
|
10274
|
-
const sheetDataUri = `data:image/png;base64,${base64}`;
|
|
10313
|
+
const sheetDataUri = `data:image/png;base64,${arrayBufferToBase64(imageBuffer)}`;
|
|
10275
10314
|
// Estimate sheet dimensions from sprite entries
|
|
10276
10315
|
let sheetWidth = 0;
|
|
10277
10316
|
let sheetHeight = 0;
|
|
@@ -10725,7 +10764,12 @@ const ALLOWED_TAGS = new Set(['a', 'b', 'i', 'em', 'strong', 'span']);
|
|
|
10725
10764
|
function sanitizeHTML(html) {
|
|
10726
10765
|
const parser = new DOMParser();
|
|
10727
10766
|
const doc = parser.parseFromString(html, 'text/html');
|
|
10728
|
-
|
|
10767
|
+
// Serialize the sanitized tree back to HTML so allowed tags and safe links
|
|
10768
|
+
// survive. The result is later assigned via innerHTML; every text node is
|
|
10769
|
+
// re-escaped by the serializer, so it is safe to concatenate and inject.
|
|
10770
|
+
const container = document.createElement('div');
|
|
10771
|
+
container.append(sanitizeNode(doc.body));
|
|
10772
|
+
return container.innerHTML;
|
|
10729
10773
|
}
|
|
10730
10774
|
function sanitizeNode(node) {
|
|
10731
10775
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
@@ -10764,10 +10808,7 @@ class SVGExportControl {
|
|
|
10764
10808
|
renderGeneration = 0;
|
|
10765
10809
|
options;
|
|
10766
10810
|
constructor(options) {
|
|
10767
|
-
this.options = {
|
|
10768
|
-
defaultWidth: options?.defaultWidth ?? 1024,
|
|
10769
|
-
defaultHeight: options?.defaultHeight ?? 1024,
|
|
10770
|
-
};
|
|
10811
|
+
this.options = { ...options };
|
|
10771
10812
|
}
|
|
10772
10813
|
onAdd(map) {
|
|
10773
10814
|
this.map = map;
|
|
@@ -10797,6 +10838,12 @@ class SVGExportControl {
|
|
|
10797
10838
|
if (this.panel || !this.map)
|
|
10798
10839
|
return;
|
|
10799
10840
|
const mapContainer = this.map.getContainer();
|
|
10841
|
+
// Seed the size inputs from the current map viewport so the default export
|
|
10842
|
+
// matches what the user sees on screen. Explicit constructor options win;
|
|
10843
|
+
// the 1024 fallback covers environments that report no layout size
|
|
10844
|
+
// (clientWidth 0). Inputs stay independent — the user can change either.
|
|
10845
|
+
const defaultWidth = this.options.defaultWidth ?? (mapContainer.clientWidth || 1024);
|
|
10846
|
+
const defaultHeight = this.options.defaultHeight ?? (mapContainer.clientHeight || 1024);
|
|
10800
10847
|
this.panel = document.createElement('div');
|
|
10801
10848
|
this.panel.className = 'svg-export-panel';
|
|
10802
10849
|
this.panel.innerHTML = `
|
|
@@ -10811,8 +10858,8 @@ class SVGExportControl {
|
|
|
10811
10858
|
</div>
|
|
10812
10859
|
<div class="panel-inputs">
|
|
10813
10860
|
<div class="grid">
|
|
10814
|
-
<label>Width<input type="number" class="input-width" value="${String(
|
|
10815
|
-
<label>Height<input type="number" class="input-height" value="${String(
|
|
10861
|
+
<label>Width<input type="number" class="input-width" value="${String(defaultWidth)}" min="1" max="8192"></label>
|
|
10862
|
+
<label>Height<input type="number" class="input-height" value="${String(defaultHeight)}" min="1" max="8192"></label>
|
|
10816
10863
|
</div>
|
|
10817
10864
|
<label class="label-checkbox"><input type="checkbox" class="input-labels"> Include labels and icons (buggy)</label>
|
|
10818
10865
|
</div>
|
|
@@ -10978,4 +11025,4 @@ class SVGExportControl {
|
|
|
10978
11025
|
}
|
|
10979
11026
|
|
|
10980
11027
|
export { SVGExportControl, renderToSVG };
|
|
10981
|
-
//# sourceMappingURL=maplibre.js.map
|
|
11028
|
+
//# sourceMappingURL=maplibre-svg-export.js.map
|