有没有办法在保持其 CSS 属性的同时将 SVG 元素转换为 PDF?
Is there a way to convert an SVG element to a PDF while maintaining its CSS properties?
我有一个使用 d3 库生成并使用 css 设置样式的 svg 图形。我想将此图转换为 PDF,同时保持其 CSS 属性。
我尝试了各种不同的方法,但 none 似乎捕捉到了带有样式属性的 svg。目前,我正在使用 html2canvas 和 jsPDF 创建 canvas 并将其保存为 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 标签中。
您可能需要考虑 server-side 解决方案。 cairosvg (https://cairosvg.org/) 听起来应该能够处理它。它确实支持 css svg 样式。虽然我试图避免 server-side 解决方案,但通常使用服务器端点来处理图像格式、转换和打印。这还有一个优点,即它不需要太多浏览器,因此您不应该 运行 陷入浏览器兼容性或内存使用问题。
您需要在 SVG 容器上添加 CSS 样式:
SVG to Canvas with 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/
//
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');
我有一个使用 d3 库生成并使用 css 设置样式的 svg 图形。我想将此图转换为 PDF,同时保持其 CSS 属性。
我尝试了各种不同的方法,但 none 似乎捕捉到了带有样式属性的 svg。目前,我正在使用 html2canvas 和 jsPDF 创建 canvas 并将其保存为 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 标签中。
您可能需要考虑 server-side 解决方案。 cairosvg (https://cairosvg.org/) 听起来应该能够处理它。它确实支持 css svg 样式。虽然我试图避免 server-side 解决方案,但通常使用服务器端点来处理图像格式、转换和打印。这还有一个优点,即它不需要太多浏览器,因此您不应该 运行 陷入浏览器兼容性或内存使用问题。
您需要在 SVG 容器上添加 CSS 样式:
SVG to Canvas with 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/
//
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');