有没有办法在保持CSS属性的同时将SVG元素转换为PDF?

时间:2019-07-09 12:34:15

标签: javascript html pdf-generation jspdf html2canvas

我有一个使用d3库生成并使用css样式化的svg图。我想将此图转换为PDF,同时保持其CSS属性。

我尝试了各种不同的方法,但是似乎没有一种方法可以捕获带有样式属性的svg。目前,我正在使用html2canvas和jsPDF来创建画布并将其另存为PDF。 我已经包含了样式图的最小可重现示例的代码+当前生成PDF所需的代码。

<script src="https://d3js.org/d3.v5.js"></script>

<!-- jQuery CDN -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript">
</script>

<!-- html2canvas CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js" type="text/javascript">

<!-- jsPDF CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js"
    integrity="sha384-NaWTHo/8YCBYJ59830LTz/P4aQZK1sS0SneOgAvhsIl3zBu8r9RevNg5lHCHAuQ/" crossorigin="anonymous">
</script>

<script>
    function genPDF() {
        // gets the quality based on the value that is input in the field
        if (quality = document.getElementById("quality").value) {
            // window.devicePixelRatio = 2; 
            html2canvas(document.getElementById("svg1"), {
                // scale based on quality value input
                scale: quality,
                // logging for debugging
                logging: true,
                letterRendering: 1,
                // allows for cross origin images
                allowTaint: true,
                useCORS: true
            }).then(function (canvas) {
                // creating a canvas with page data 
                var img = canvas.toDataURL('image/png');
                // creating a portrait a4 page
                var doc = new jsPDF('l', 'mm', 'a4');
                // setting the width of the page to auto
                const imgProps = doc.getImageProperties(img);
                const pdfWidth = doc.internal.pageSize.getWidth();
                const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
                // adding the image canvas to the pdf and saving
                doc.addImage(img, 'PNG', 2, 2, pdfWidth, pdfHeight);
                doc.save('generatedPDF.pdf');
            });
        } else {
            // throw if no scale value is entered
            throw "Please enter a scale value!";
        }
    }
</script>
<style>

    rect {
        fill: #868e96;
        opacity: 0.3;
    }
</style>

<body>
<form>
    Enter scale quality:<br>
    <input type="text" id="quality" name="quality" placeholder="1 - 5">
</form>
<button onclick="genPDF()">Generate PDF</button>
    <svg id="svg1" width="1000" height="700">
        <g id="elementsContainer">

            <rect x="25" y="25" width="240" height="240" />
            <rect x="275" y="25" width="240" height="240" />
            <rect x="25" y="275" width="240" height="240" />
            <rect x="275" y="275" width="240" height="240" />
   </g>
</svg>
</body>

我刚得到一个黑色的无样式图。

编辑: 我尝试添加一个超时功能,该功能似乎仍然无法解决问题:

function genPDF() {
        // gets the quality based on the value that is input in the field
        if (quality = document.getElementById("quality").value) {
            // window.devicePixelRatio = 2; 
            html2canvas(document.getElementById("apply"), {
                // scale based on quality value input
                scale: quality,
                // logging for debugging
                logging: true,
                letterRendering: 1,
                // allows for cross origin images
                allowTaint: true,
                useCORS: true
            }).then(function (canvas) {
                setTimeout(function () {
                    var img = canvas.toDataURL('image/png');
                    var doc = new jsPDF('l', 'mm', 'a4');
                    const imgProps = doc.getImageProperties(img);
                    const pdfWidth = doc.internal.pageSize.getWidth();
                    const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
                    doc.addImage(img, 'PNG', 2, 2, pdfWidth, pdfHeight);
                    doc.save('generatedPDF.pdf');
                }, 3000);
            });
        } else {
            // throw if no scale value is entered
            throw "Please enter a scale value!";
        }
    }

编辑:我发现最好的解决方案是将内联样式属性简单地添加到SVG标签中。

2 个答案:

答案 0 :(得分:0)

您可能要考虑服务器端解决方案。 cairosvg(https://cairosvg.org/)听起来应该能够处理它。它确实支持SVG的CSS样式。虽然我尝试避免使用服务器端解决方案,但通常都有服务器端点来处理图像格式,转换和打印。这样做的好处还在于它不需要太多的浏览器,因此您不应该遇到浏览器兼容性或内存使用问题。

答案 1 :(得分:0)

您需要在SVG容器上添加CSS样式:

enter image description here

const listBlobs = async (continuationToken) => {
    return new Promise((resolve, reject) => {
        blobService.listBlobsSegmented(containerName, continuationToken, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data)
            }
        });
    });
};

const listAll = async () => {
    first = await listBlobs(null);
    all = [].concat(first.entries)
    var continuationToken = first.continuationToken;
    while(continuationToken != null) {
        next = await listBlobs(continuationToken);
        all = all.concat(next.entries)
        continuationToken = next.continuationToken
    }
    return Promise.resolve(all);
};

(async() => {
    blobs = await listAll();
    blobs.map((result, index) => {console.log(index, result.name)})
})();

然后您只需使用addImage()添加图像:

-?[0-9]*([1-9][0-9]*(.[0-9]*)?|.[0-9]*[1-9][0-9]*)