@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.
@@ -634,19 +634,28 @@
634
634
  },
635
635
  "property-type": "data-constant"
636
636
  } },
637
- "layout_fill-extrusion": { "visibility": {
638
- "type": "enum",
639
- "values": {
640
- "visible": {},
641
- "none": {}
642
- },
643
- "default": "visible",
644
- "expression": {
645
- "interpolated": false,
646
- "parameters": ["global-state"]
637
+ "layout_fill-extrusion": {
638
+ "visibility": {
639
+ "type": "enum",
640
+ "values": {
641
+ "visible": {},
642
+ "none": {}
643
+ },
644
+ "default": "visible",
645
+ "expression": {
646
+ "interpolated": false,
647
+ "parameters": ["global-state"]
648
+ },
649
+ "property-type": "data-constant"
647
650
  },
648
- "property-type": "data-constant"
649
- } },
651
+ "fill-extrusion-rounded-corner-distance": {
652
+ "type": "number",
653
+ "default": 0,
654
+ "minimum": 0,
655
+ "units": "meters",
656
+ "property-type": "constant"
657
+ }
658
+ },
650
659
  layout_line: {
651
660
  "line-cap": {
652
661
  "type": "enum",
@@ -4684,7 +4693,11 @@
4684
4693
  }
4685
4694
  case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
4686
4695
  case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
4687
- case "projectionDefinition": return this.args[0].evaluate(ctx);
4696
+ case "projectionDefinition": {
4697
+ const input = this.args[0].evaluate(ctx);
4698
+ if (ProjectionDefinition.parse(input)) return input;
4699
+ throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
4700
+ }
4688
4701
  default: return valueToString(this.args[0].evaluate(ctx));
4689
4702
  }
4690
4703
  }
@@ -4787,7 +4800,11 @@
4787
4800
  const expected = this.expectedType;
4788
4801
  const actual = parsed.type;
4789
4802
  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");
4790
- else if ("projectionDefinition" === expected.kind && ["string", "array"].includes(actual.kind) || [
4803
+ else if ("projectionDefinition" === expected.kind && [
4804
+ "string",
4805
+ "array",
4806
+ "value"
4807
+ ].includes(actual.kind) || [
4791
4808
  "color",
4792
4809
  "formatted",
4793
4810
  "resolvedImage"
@@ -10212,6 +10229,34 @@
10212
10229
  }
10213
10230
  }
10214
10231
 
10232
+ /**
10233
+ * Base64-encode an ArrayBuffer (standard alphabet, with padding).
10234
+ *
10235
+ * Three paths, all producing identical output:
10236
+ * 1. `Uint8Array.prototype.toBase64` — the native method (Node 22+, modern
10237
+ * browsers). Fastest and simplest; used when present.
10238
+ * 2. Node's `Buffer` — for Node versions without the native method.
10239
+ * 3. Chunked `btoa(String.fromCharCode(...))` — the browser fallback. It must
10240
+ * chunk: spreading a whole tile/sprite PNG at once passes one argument per
10241
+ * byte, overflowing the engine's argument-count limit (Safari ~64K, V8
10242
+ * higher but finite) and throwing `RangeError: Maximum call stack size
10243
+ * exceeded` for buffers above ~64 KB. 32 KB slices stay well under the limit.
10244
+ */
10245
+ function arrayBufferToBase64(buffer) {
10246
+ const bytes = new Uint8Array(buffer);
10247
+ const toBase64 = bytes.toBase64;
10248
+ if (typeof toBase64 === 'function')
10249
+ return toBase64.call(bytes);
10250
+ if (typeof Buffer !== 'undefined')
10251
+ return Buffer.from(buffer).toString('base64');
10252
+ const CHUNK_SIZE = 0x8000; // 32 KB, well below the argument-count limit
10253
+ let binary = '';
10254
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
10255
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
10256
+ }
10257
+ return btoa(binary);
10258
+ }
10259
+
10215
10260
  async function getRasterTiles(job, sourceName) {
10216
10261
  const { width, height } = job.renderer;
10217
10262
  const { zoom, center } = job.view;
@@ -10225,10 +10270,7 @@
10225
10270
  const tile = await getTile(sourceUrl, zoomLevel, x, y);
10226
10271
  if (!tile)
10227
10272
  return null;
10228
- const base64 = typeof Buffer !== 'undefined'
10229
- ? Buffer.from(tile.buffer).toString('base64')
10230
- : btoa(String.fromCharCode(...new Uint8Array(tile.buffer)));
10231
- const dataUri = `data:${tile.contentType};base64,${base64}`;
10273
+ const dataUri = `data:${tile.contentType};base64,${arrayBufferToBase64(tile.buffer)}`;
10232
10274
  return {
10233
10275
  x: offsetX,
10234
10276
  y: offsetY,
@@ -10274,10 +10316,7 @@
10274
10316
  const { jsonResponse, imageResponse } = spritePair;
10275
10317
  const json = (await jsonResponse.json());
10276
10318
  const imageBuffer = await imageResponse.arrayBuffer();
10277
- const base64 = typeof Buffer !== 'undefined'
10278
- ? Buffer.from(imageBuffer).toString('base64')
10279
- : btoa(String.fromCharCode(...new Uint8Array(imageBuffer)));
10280
- const sheetDataUri = `data:image/png;base64,${base64}`;
10319
+ const sheetDataUri = `data:image/png;base64,${arrayBufferToBase64(imageBuffer)}`;
10281
10320
  // Estimate sheet dimensions from sprite entries
10282
10321
  let sheetWidth = 0;
10283
10322
  let sheetHeight = 0;
@@ -10731,7 +10770,12 @@
10731
10770
  function sanitizeHTML(html) {
10732
10771
  const parser = new DOMParser();
10733
10772
  const doc = parser.parseFromString(html, 'text/html');
10734
- return sanitizeNode(doc.body).textContent ?? '';
10773
+ // Serialize the sanitized tree back to HTML so allowed tags and safe links
10774
+ // survive. The result is later assigned via innerHTML; every text node is
10775
+ // re-escaped by the serializer, so it is safe to concatenate and inject.
10776
+ const container = document.createElement('div');
10777
+ container.append(sanitizeNode(doc.body));
10778
+ return container.innerHTML;
10735
10779
  }
10736
10780
  function sanitizeNode(node) {
10737
10781
  if (node.nodeType === Node.TEXT_NODE) {
@@ -10770,10 +10814,7 @@
10770
10814
  renderGeneration = 0;
10771
10815
  options;
10772
10816
  constructor(options) {
10773
- this.options = {
10774
- defaultWidth: options?.defaultWidth ?? 1024,
10775
- defaultHeight: options?.defaultHeight ?? 1024,
10776
- };
10817
+ this.options = { ...options };
10777
10818
  }
10778
10819
  onAdd(map) {
10779
10820
  this.map = map;
@@ -10803,6 +10844,12 @@
10803
10844
  if (this.panel || !this.map)
10804
10845
  return;
10805
10846
  const mapContainer = this.map.getContainer();
10847
+ // Seed the size inputs from the current map viewport so the default export
10848
+ // matches what the user sees on screen. Explicit constructor options win;
10849
+ // the 1024 fallback covers environments that report no layout size
10850
+ // (clientWidth 0). Inputs stay independent — the user can change either.
10851
+ const defaultWidth = this.options.defaultWidth ?? (mapContainer.clientWidth || 1024);
10852
+ const defaultHeight = this.options.defaultHeight ?? (mapContainer.clientHeight || 1024);
10806
10853
  this.panel = document.createElement('div');
10807
10854
  this.panel.className = 'svg-export-panel';
10808
10855
  this.panel.innerHTML = `
@@ -10817,8 +10864,8 @@
10817
10864
  </div>
10818
10865
  <div class="panel-inputs">
10819
10866
  <div class="grid">
10820
- <label>Width<input type="number" class="input-width" value="${String(this.options.defaultWidth)}" min="1" max="8192"></label>
10821
- <label>Height<input type="number" class="input-height" value="${String(this.options.defaultHeight)}" min="1" max="8192"></label>
10867
+ <label>Width<input type="number" class="input-width" value="${String(defaultWidth)}" min="1" max="8192"></label>
10868
+ <label>Height<input type="number" class="input-height" value="${String(defaultHeight)}" min="1" max="8192"></label>
10822
10869
  </div>
10823
10870
  <label class="label-checkbox"><input type="checkbox" class="input-labels"> Include labels and icons (buggy)</label>
10824
10871
  </div>
@@ -10987,4 +11034,4 @@
10987
11034
  exports.renderToSVG = renderToSVG;
10988
11035
 
10989
11036
  }));
10990
- //# sourceMappingURL=maplibre.umd.js.map
11037
+ //# sourceMappingURL=maplibre-svg-export.umd.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versatiles/svg-renderer",
3
- "version": "0.8.0",
3
+ "version": "1.1.0",
4
4
  "description": "Renders vector maps as SVG.",
5
5
  "keywords": [
6
6
  "svg",
@@ -18,8 +18,8 @@
18
18
  "main": "dist/index.cjs",
19
19
  "module": "dist/index.js",
20
20
  "types": "dist/index.d.ts",
21
- "unpkg": "dist/maplibre.umd.js",
22
- "jsdelivr": "dist/maplibre.umd.js",
21
+ "unpkg": "dist/maplibre-svg-export.umd.js",
22
+ "jsdelivr": "dist/maplibre-svg-export.umd.js",
23
23
  "exports": {
24
24
  ".": {
25
25
  "import": {
@@ -32,19 +32,16 @@
32
32
  }
33
33
  },
34
34
  "./maplibre": {
35
- "script": "./dist/maplibre.umd.js",
35
+ "script": "./dist/maplibre-svg-export.umd.js",
36
36
  "import": {
37
- "types": "./dist/maplibre.d.ts",
38
- "default": "./dist/maplibre.js"
39
- },
40
- "require": {
41
- "types": "./dist/maplibre.d.ts",
42
- "default": "./dist/maplibre.cjs"
37
+ "types": "./dist/maplibre-svg-export.d.ts",
38
+ "default": "./dist/maplibre-svg-export.js"
43
39
  }
44
40
  }
45
41
  },
46
42
  "files": [
47
- "dist"
43
+ "dist",
44
+ "!dist/**/*.map"
48
45
  ],
49
46
  "scripts": {
50
47
  "build": "rm -rf dist && rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript && rm -rf dist/types",
@@ -86,32 +83,32 @@
86
83
  "devDependencies": {
87
84
  "@eslint/js": "^10.0.1",
88
85
  "@mapbox/vector-tile": "^3.0.0",
89
- "@maplibre/maplibre-gl-style-spec": "^26.1.0",
86
+ "@maplibre/maplibre-gl-style-spec": "^26.2.1",
90
87
  "@rollup/plugin-node-resolve": "^16.0.3",
91
88
  "@rollup/plugin-typescript": "^12.3.0",
92
- "@turf/union": "^7.3.5",
93
- "@types/node": "^26.1.1",
89
+ "@types/node": "^26.2.0",
94
90
  "@types/pbf": "^3.0.5",
95
91
  "@types/pngjs": "^6.0.5",
96
- "@versatiles/release-tool": "^2.8.1",
97
- "@versatiles/style": "^5.13.0",
92
+ "@versatiles/release-tool": "^2.9.0",
93
+ "@versatiles/style": "^5.13.1",
98
94
  "@vitest/coverage-v8": "^4.1.10",
99
- "eslint": "^10.7.0",
95
+ "eslint": "^10.8.1",
100
96
  "geojson": "^0.5.0",
101
- "jsdom": "^29.1.1",
102
- "maplibre-gl": "^5.24.0",
103
- "npm-check-updates": "^22.2.9",
97
+ "jsdom": "^30.0.1",
98
+ "maplibre-gl": "^6.3.0",
99
+ "npm-check-updates": "^23.0.2",
104
100
  "pbf": "^5.1.2",
105
101
  "pixelmatch": "^7.2.0",
106
- "playwright": "^1.61.1",
102
+ "playwright": "^1.62.1",
107
103
  "pngjs": "^7.0.0",
108
- "prettier": "^3.9.5",
109
- "rollup": "^4.62.2",
110
- "rollup-plugin-dts": "^6.4.1",
111
- "rollup-plugin-visualizer": "^7.0.1",
112
- "tsx": "^4.23.1",
113
- "typescript": "<7",
114
- "typescript-eslint": "^8.64.0",
104
+ "prettier": "^3.9.6",
105
+ "rollup": "^4.62.4",
106
+ "rollup-plugin-dts": "^6.5.1",
107
+ "rollup-plugin-visualizer": "^7.1.1",
108
+ "tslib": "^2.8.1",
109
+ "tsx": "^4.23.12",
110
+ "typescript": "^6.0.3",
111
+ "typescript-eslint": "^8.67.0",
115
112
  "vitest": "^4.1.10"
116
113
  },
117
114
  "allowScripts": {