Есть ли способ конвертировать SVG-элемент в PDF при сохранении его свойств CSS? - PullRequest
0 голосов
/ 09 июля 2019

У меня есть график SVG, который создается с использованием библиотеки d3 и стилизован с использованием css.Я хотел бы преобразовать этот график в 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 голосов
/ 12 июля 2019

Вам необходимо добавить стили CSS в контейнер SVG:

SVG в Canvas с d3.js

    function getGraphImage(container) {
        addCssStyles(container);
        var svgChart = d3.select("#" + container + " svg");
        svgChart.attr('width', svgChart.node().clientWidth);
        svgChart.attr('height', svgChart.node().clientHeight);

        //Get svg markup as string
        var svg = document.getElementById(container).innerHTML;

        if (svg)
            svg = svg.replace(/\r?\n|\r/g, '').trim();

        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');

        context.clearRect(0, 0, canvas.width, canvas.height);
        canvg(canvas, svg);

        return canvas.toDataURL('image/png');
    }

    function addCssStyles(container) {
        // get styles from all required stylesheets
        // http://www.coffeegnome.net/converting-svg-to-png-with-canvg/
        // https://stackoverflow.com/questions/11567668/svg-to-canvas-with-d3-js
        var style = "\n";
        var requiredSheets = ['nv.d3.css']; // list of required CSS
        for (var i = 0; i < document.styleSheets.length; i++) {
            var sheet = document.styleSheets[i];
            if (sheet.href) {
                var sheetName = sheet.href.split('/').pop();
                if (requiredSheets.indexOf(sheetName) !== -1) {
                    var rules = sheet.rules;
                    if (rules) {
                        for (var j = 0; j < rules.length; j++) {
                            style += (rules[j].cssText + '\n');
                        }
                    }
                }
            }
        }

        d3.select("#" + container + " svg")
                .insert('defs', ":first-child");
        d3.select("#" + container + " svg defs")
                .append('style')
                .attr('type', 'text/css')
                .html(style);
    }

, затем вы просто добавляете изображение, используяaddImage ():

    var doc = new jsPDF('p', 'mm', 'letter');
    doc.addImage(getGraphImage(containerID), 'PNG', x, y, w, h);
    doc.output('dataurlnewwindow');
0 голосов
/ 09 июля 2019

Возможно, вы захотите рассмотреть решение на стороне сервера.cairosvg (https://cairosvg.org/) звучит так, как будто он должен справиться с этим. Он поддерживает CSS-стилизацию svg. Хотя я пытаюсь избежать серверных решений, обычно есть серверные конечные точки для обработки форматирования изображений, преобразованияи печать. Это также имеет то преимущество, что он не требует большого количества браузера, поэтому вы не должны сталкиваться с проблемами совместимости браузера или использования памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...