yWorks / svg2pdf.js

A javascript-only SVG to PDF conversion utility that runs in the browser. Brought to you by yWorks - the diagramming experts
MIT License
643 stars 96 forks source link

Setting width and height as floats throws t. #259

Closed Fufs closed 1 year ago

Fufs commented 1 year ago

Describe the bug When adding the svg to the doc with width and height options (might also affect x and y) being a float, I get an error t.toFixed is not a function. Works fine with ints or no options set.

What version are you using (exact version of svg2pdf.js and jspdf)?

To Reproduce Code:

function splitDimension(dimension) {
    return dimension.match(/-?\d+(\.\d*)?|[A-Za-z]+/g);
}

function convertUnitsFromValue(num, fromUnits, toUnits) {
    let numInMM = NaN;

    // Convert from fromUnits to mm
    if(fromUnits == "mm") numInMM = num;
    if(fromUnits == "cm") numInMM = num*10;
    if(fromUnits == "Q")  numInMM = num*0.25;
    if(fromUnits == "in") numInMM = num*25.4;
    if(fromUnits == "pc") numInMM = num*4.2333;
    if(fromUnits == "pt") numInMM = num*0.3528;
    if(fromUnits == "px") numInMM = num*0.2646;

    // convert from mm to toUnits
    if(toUnits == "mm") return numInMM;
    if(toUnits == "cm") return numInMM/10;
    if(toUnits == "Q")  return numInMM/0.25;
    if(toUnits == "in") return numInMM/25.4;
    if(toUnits == "pc") return numInMM/4.2333;
    if(toUnits == "pt") return numInMM/0.3528;
    if(toUnits == "px") return numInMM/0.2646;

    // Return NaN if an error occurs
    return numInMM;
}

function convertUnitsFromDimension(dimension, toUnits) {
    let dimArray = splitDimension(dimension);

    return dimArray.length === 2 ? convertUnitsFromValue(dimArray[0], dimArray[1], toUnits) : NaN;
}

async function generatePDF(svg) {
    const format = [parseFloat(convertUnitsFromDimension(svg.width.baseVal.valueAsString, "mm")).toFixed(2), parseFloat(convertUnitsFromDimension(svg.height.baseVal.valueAsString, "mm")).toFixed(2)]
    const doc = new jspdf.jsPDF({unit: "mm", format: format, orientation: format[0] > format[1] ? "l" : "p"});

    await doc.svg(svg, {x: 0, y: 0}); // Doesn't throw Error
    // await doc.svg(svg, {x: 0, y: 0, width: format[0], height: format[1]}); // Throws Error

    return await doc.output('datauristring');
}

SVG I was testing with:

<svg width="514.48mm" height="496.07mm" viewBox="0 0 183.08810424804688 176.53631591796875" version="1.1" id="svg305" xml:space="preserve" inkscape:version="1.2 (dc2aedaf03, 2022-05-15)" sodipodi:docname="vishroom_color.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
    <g transform="matrix(-1,0,0,1,203.29302978515625,-7.15194845199585)">...</g>
</svg>

Log:

jspdf.js:494 Uncaught (in promise) TypeError: t.toFixed is not a function
    at M.y.roundToPrecision.y.__private__.roundToPrecision (jspdf.js:494:19)
    at M.y.hpf.y.__private__.hpf (jspdf.js:504:14)
    at M.y.__private__.rect.y.rect (jspdf.js:4635:9)
    at e.<anonymous> (svg.ts:47:10)
    at tslib.es6.js:100:23
    at Object.next (tslib.es6.js:81:53)
    at tslib.es6.js:74:71
    at new Promise (<anonymous>)
    at s (tslib.es6.js:70:12)
    at e.render (svg.ts:15:16)
M.y.roundToPrecision.y.__private__.roundToPrecision @ jspdf.js:494
M.y.hpf.y.__private__.hpf @ jspdf.js:504
M.y.__private__.rect.y.rect @ jspdf.js:4635
(anonimowa) @ svg.ts:47
(anonimowa) @ tslib.es6.js:100
(anonimowa) @ tslib.es6.js:81
(anonimowa) @ tslib.es6.js:74
s @ tslib.es6.js:70
e.render @ svg.ts:15
(anonimowa) @ svg2pdf.ts:47
(anonimowa) @ tslib.es6.js:100
(anonimowa) @ tslib.es6.js:81
s @ tslib.es6.js:71

Expected behavior No error thrown.

Desktop (please complete the following information):

yGuy commented 1 year ago

That's because you are passing the number as a string (.toFixed() returns a string) - pass it as a Number and it should work. The API will not automatically try to parse and convert strings into numbers. Ideally it would provide a clear error message. But typescript should have also warned you about this mistake.

Fufs commented 1 year ago

Yup, that was exactly the issue. I haven't even realized that .toFixed() returns a string. Just parsing it as a float works beautifully, thanks!