Надо считать не ширину и высоту, а минимальные и максимальные координаты элементов.
По ним рассчитать необходимую ширину, высоту и смещение viewBox
Ну и учесть, что сам svg может располагаться в произвольном месте на странице
<head>
<style>
svg {
border: 3px solid black;
margin: 40px;
}
</style>
</head>
<body>
<svg id="svg">
<rect width="200" height="300" x="40" y="50" fill="red" />
<circle cx="160" cy="400" r="130" fill="blue" />
<rect width="200" height="100" x="250" y="90" fill="green" />
</svg>
<script>
let svg = document.getElementById("svg");
let t = 1000000, l = 1000000, b = -1000000, r = -1000000;
// Вычисляем минимальные и максимальные координаты
for (let i = 0; i < svg.children.length; i++) {
let rec = svg.children[i].getBoundingClientRect();
t = Math.min(t, rec.top);
l = Math.min(l, rec.left);
b = Math.max(b, rec.bottom);
r = Math.max(r, rec.right);
}
let w = r - l;
let h = b - t;
// Вычисляем смещение в viewBox, учитывая смещение svg и его границу
let recs = svg.getBoundingClientRect();
t -= recs.top + svg.clientTop;
l -= recs.left + svg.clientLeft;
svg.setAttribute("width", w);
svg.setAttribute("viewBox", `${l} ${t} ${w} ${h}`);
</script>