Skip to content

Commit 9866921

Browse files
committed
Build with resvg-js use
1 parent bc5303c commit 9866921

File tree

4 files changed

+98
-15
lines changed

4 files changed

+98
-15
lines changed

build/jsroot.js

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const version_id = 'dev',
1414

1515
/** @summary version date
1616
* @desc Release date in format day/month/year like '14/04/2022' */
17-
version_date = '30/01/2026',
17+
version_date = '2/02/2026',
1818

1919
/** @summary version id and date
2020
* @desc Produced by concatenation of {@link version_id} and {@link version_date}
@@ -257,6 +257,8 @@ settings = {
257257
Render3DBatch: constants$1.Render3D.Default,
258258
/** @summary Way to embed 3D drawing in SVG, see {@link constants.Embed3D} for possible values */
259259
Embed3D: constants$1.Embed3D.Default,
260+
/** @summary Use `resvg-js` backend for converting SVGs in node.js */
261+
UseResvgJs: true,
260262
/** @summary Default canvas width */
261263
CanvasWidth: 1200,
262264
/** @summary Default canvas height */
@@ -965,11 +967,12 @@ function findFunction(name) {
965967

966968
/** @summary Method to create http request, without promise can be used only in browser environment
967969
* @private */
968-
function createHttpRequest(url, kind, user_accept_callback, user_reject_callback, use_promise) {
970+
function createHttpRequest(url, kind, user_accept_callback, user_reject_callback, use_promise, tmout) {
969971
function configureXhr(xhr) {
970972
xhr.http_callback = isFunc(user_accept_callback) ? user_accept_callback.bind(xhr) : () => {};
971973
xhr.error_callback = isFunc(user_reject_callback) ? user_reject_callback.bind(xhr) : function(err) {
972-
console.warn(err.message);
974+
if (err?.message)
975+
console.warn(err.message);
973976
this.http_callback(null);
974977
}.bind(xhr);
975978

@@ -1079,6 +1082,15 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback
10791082
xhr.responseType = 'arraybuffer';
10801083
}
10811084

1085+
if (tmout && Number.isFinite(tmout)) {
1086+
xhr.timeout = tmout;
1087+
xhr.ontimeout = function() {
1088+
this.did_abort = true;
1089+
this.abort();
1090+
this.error_callback(Error(`Request ${url} timeout`));
1091+
};
1092+
}
1093+
10821094
return xhr;
10831095
}
10841096

@@ -9601,7 +9613,8 @@ async function svgToImage(svg, image_format, args) {
96019613
return internals.makePDF ? internals.makePDF(svg, args) : null;
96029614

96039615
// required with df104.py/df105.py example with RCanvas or any special symbols in TLatex
9604-
const doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
9616+
const doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
9617+
is_rgba = image_format === 'rgba';
96059618

96069619
if (isNodeJs()) {
96079620
svg = encodeURIComponent(doctype + svg);
@@ -9612,6 +9625,31 @@ async function svgToImage(svg, image_format, args) {
96129625

96139626
const img_src = 'data:image/svg+xml;base64,' + btoa_func(decodeURIComponent(svg));
96149627

9628+
// Use the newer and stabler `resvg-js` backend for converting SVG to PNG
9629+
if (settings.UseResvgJs) {
9630+
return Promise.resolve().then(function () { return index; }).then(({ Resvg }) => {
9631+
const rawSvg = decodeURIComponent(svg), // raw SVG XML
9632+
resvg = new Resvg(rawSvg), // Initialize Resvg and create the PNG buffer
9633+
renderData = resvg.render(),
9634+
pngBuffer = renderData.asPng();
9635+
9636+
// Return raw RGBA pixels if caller requested it
9637+
if (is_rgba) {
9638+
return {
9639+
width: renderData.width,
9640+
height: renderData.height,
9641+
data: renderData.pixels
9642+
};
9643+
}
9644+
9645+
if (args?.as_buffer)
9646+
return pngBuffer;
9647+
9648+
return 'data:image/png;base64,' + pngBuffer.toString('base64');
9649+
});
9650+
}
9651+
9652+
// Fallback to `node-canvas`
96159653
return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(async handle => {
96169654
return handle.default.loadImage(img_src).then(img => {
96179655
const canvas = handle.default.createCanvas(img.width, img.height);
@@ -9621,7 +9659,7 @@ async function svgToImage(svg, image_format, args) {
96219659
if (args?.as_buffer)
96229660
return canvas.toBuffer('image/' + image_format);
96239661

9624-
return image_format ? canvas.toDataURL('image/' + image_format) : canvas;
9662+
return image_format && !is_rgba ? canvas.toDataURL('image/' + image_format) : canvas;
96259663
});
96269664
});
96279665
}
@@ -9643,7 +9681,7 @@ async function svgToImage(svg, image_format, args) {
96439681
if (args?.as_buffer && image_format)
96449682
canvas.toBlob(blob => blob.arrayBuffer().then(resolveFunc), 'image/' + image_format);
96459683
else
9646-
resolveFunc(image_format ? canvas.toDataURL('image/' + image_format) : canvas);
9684+
resolveFunc(image_format && !is_rgba ? canvas.toDataURL('image/' + image_format) : canvas);
96479685
};
96489686
image.onerror = function(arg) {
96499687
URL.revokeObjectURL(img_src);
@@ -91245,18 +91283,26 @@ class TPavePainter extends ObjectPainter {
9124591283
tm = pad?.fTopMargin ?? gStyle.fPadTopMargin,
9124691284
bm = pad?.fBottomMargin ?? gStyle.fPadBottomMargin;
9124791285

91248-
return svgToImage(svg_code).then(canvas => {
91249-
if (!canvas)
91286+
return svgToImage(svg_code, 'rgba').then(image => {
91287+
if (!image)
91288+
return false;
91289+
91290+
let arr = image.data;
91291+
const width = image.width, height = image.height;
91292+
91293+
if (!arr && isFunc(image.getContext) && image.getContext('2d'))
91294+
arr = image.getContext('2d').getImageData(0, 0, width, height).data;
91295+
91296+
if (!arr)
9125091297
return false;
9125191298

9125291299
let nX = 100, nY = 100;
91253-
const context = canvas.getContext('2d'),
91254-
arr = context.getImageData(0, 0, canvas.width, canvas.height).data,
91255-
boxW = Math.floor(canvas.width / nX), boxH = Math.floor(canvas.height / nY),
91300+
const boxW = Math.floor(width / nX),
91301+
boxH = Math.floor(height / nY),
9125691302
raster = new Array(nX * nY);
9125791303

91258-
if (arr.length !== canvas.width * canvas.height * 4) {
91259-
console.log(`Image size missmatch in TLegend autoplace ${arr.length} expected ${canvas.width * canvas.height * 4}`);
91304+
if (arr.length !== width * height * 4) {
91305+
console.log(`Image size missmatch in TLegend autoplace ${arr.length} expected ${width * height * 4}`);
9126091306
nX = nY = 0;
9126191307
}
9126291308

@@ -91268,7 +91314,7 @@ class TPavePainter extends ObjectPainter {
9126891314

9126991315
for (let x = px1; (x < px2) && !filled; ++x) {
9127091316
for (let y = py1; y < py2; ++y) {
91271-
const indx = (y * canvas.width + x) * 4;
91317+
const indx = (y * width + x) * 4;
9127291318
if (arr[indx] || arr[indx + 1] || arr[indx + 2] || arr[indx + 3]) {
9127391319
filled = 1;
9127491320
break;
@@ -168594,6 +168640,34 @@ __proto__: null,
168594168640
default: _rollup_plugin_ignore_empty_module_placeholder
168595168641
});
168596168642

168643+
const { render: _render, renderAsync: _renderAsync, Resvg: _Resvg } = require('./js-binding.js');
168644+
168645+
module.exports.render = function render(svg, options) {
168646+
if (options) {
168647+
return _render(svg, JSON.stringify(options))
168648+
}
168649+
return _render(svg)
168650+
};
168651+
168652+
module.exports.renderAsync = function renderAsync(svg, options, signal) {
168653+
if (options) {
168654+
return _renderAsync(svg, JSON.stringify(options), signal)
168655+
}
168656+
return _renderAsync(svg, null, signal)
168657+
};
168658+
168659+
module.exports.Resvg = class Resvg extends _Resvg {
168660+
constructor(svg, options) {
168661+
super(svg, JSON.stringify(options));
168662+
}
168663+
};
168664+
168665+
// module.exports.Resvg = _Resvg
168666+
168667+
var index = /*#__PURE__*/Object.freeze({
168668+
__proto__: null
168669+
});
168670+
168597168671
/** @summary Draw TEllipse
168598168672
* @private */
168599168673
function drawEllipse() {

changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
1. Implements `TTree` branches filtering via context menu #364
99
1. Let define alternative draw function #378
1010
1. Implement padsN draw option for `THStack` and `TMultiGraph`
11+
1. Use `resvg-js` backend for PNG support in node.js #391, thanks to https://github.com/OmarMesqq
1112
1. Remove support for deprectaed `TH1K` class
1213
1. Fix - paint frame border mode/size from TCanvas
1314
1. Fix - correctly process `TLeafB` arrays in tree draw #384

modules/core.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const version_id = 'dev',
66

77
/** @summary version date
88
* @desc Release date in format day/month/year like '14/04/2022' */
9-
version_date = '30/01/2026',
9+
version_date = '2/02/2026',
1010

1111
/** @summary version id and date
1212
* @desc Produced by concatenation of {@link version_id} and {@link version_date}

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)