svg2pdf.js
svg2pdf.js copied to clipboard
The exported PDF is not rendering custom fonts on some of the PDF text objects.
Describe the bug I'm using this library and fabricJS to make and exportable version of the canvas and have a pdf from there, I was able to add custom fonts and the source of the pdf generator is a svg export, but some of the pdf objects (once is generated) have missing font declarations, and i used some text styling to and it doesn't appear to be applied (underlining and striking) too.
P.S: the PDF file size is exponentially bigger than the SVG representation, any hints about this.
What version are you using (exact version of svg2pdf.js and jspdf)? svg2pdf.js -> 2.2.1 jspdf-> 2.5.1
To Reproduce
//add jspdf font files as follow, the font is an object holding all the font information //some of the code is removed but all should work
const [fontName] = font.file_name.split('.'); const fontFetchResponse = await Axios.get(font.file_url, { responseType: 'arraybuffer' }); pdf.addFileToVFS(font.file_name, Buffer.from(fontFetchResponse.data, 'binary').toString('base64')); pdf.addFont(font.file_name, fontName, 'normal'); pdf.setFont(fontName);
//then
const pdf = new jsPDF({ unit: 'in', putOnlyUsedFonts: true }); const imageInformation = canvas.toSVG({ suppressPreamble: true }); //add a page pdf.addPage([pageWidth, pageHeight], orientation); //adding the svg to pdf await pdf.svg(imageInformation, { x: 0, y: 0, width: pageWidth, height: pageHeight });
Expected behavior have the pdf objects with their respective font declaration, and styling
Desktop (please complete the following information):
- OS: macOs
- Browser: chromium
- Version: 113.0.5630.0
Additional context Added fonts, svg file and exported PDF on this file
Please reduce your SVG to just the text where the fonts are not applied.
Here it is, there seems to be the same structure as the one that has the font applied, any ideas what could be happening with text styles too?
so I checked how we could implement the underlining and the line-through text decoration properties on the pdf, and we need to draw a line with the same color as the tspan style declaration but in order to do that, we need to get the text object width and height to add this to the x and y props and then draw from there, so I if you can guide me on how to get these props (style declaration, text rendered height and width) after we set the text and were specifically we need to set it on svg2pdf I will gladly do the work and testing needed @HackbrettXXX
Thank you for reducing the SVG. Regarding the font issue: could also attach a PDF of the reduced SVG, please?
Regarding the underline and strike-through: a PR would be very much appreciated. The best location to put the code is probably somewhere in textchunk: https://github.com/yWorks/svg2pdf.js/blob/master/src/textchunk.ts#L72. The text position and width is already known there. One issue you might run into is that every tspan might get its own line and we might need to consolidate the lines for inline tspans. There is already a similar open issue: #226
I checked the possible font issues and they seems to be related to the font weight declaration, in the svg these are custom fonts that are added as 'normal' font weights, so there's no match on both the use and font declaration, I will start working on the changes required to add the underlining.
I'm having a related problem, I'm also using fabric.js and I need to generate a PDF from the SVG generated by canvas, the SVG is being generated and displayed correctly, I use some custom fonts, I managed to add the custom fonts and it's working If the text element has a tspan inside, but when I use a standard PDF font like Helvetica and Courier it doesn't work
svg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="637" height="1012" viewBox="301.5 50 637 1012" xml:space="preserve">
<desc>Created with Fabric.js 5.3.0</desc>
<defs>
</defs>
<g transform="matrix(1 0 0 1 620.5 556.5)" id="stage">
<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" x="-318.5" y="-506" rx="0" ry="0" width="637" height="1012"/>
</g>
<g transform="matrix(4.41 0 0 4.41 512.63 234.05)" style="" id="itext-001c8nc3i">
<text xml:space="preserve" font-family="Helvetica" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(254,0,0); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-41.13" y="6.28">Helvetica</tspan></text>
</g>
<g transform="matrix(4.29 0 0 4.29 443.87 125.01)" style="" id="itext-001th0bi9">
<text xml:space="preserve" font-family="Calibri" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-25.75" y="6.28">Calibri</tspan></text>
</g>
<g transform="matrix(4.34 0 0 4.34 730.56 125)" style="" id="itext-001s4e88y">
<text xml:space="preserve" font-family="Roboto" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,216,0); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-32.05" y="6.28">Roboto</tspan></text>
</g>
<g transform="matrix(3.43 0 0 3.43 533.1 334.93)" style="" id="itext-001od0816">
<text xml:space="preserve" font-family="Gotham Condensed" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,188); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-56.73" y="6.28">Gotham Condensed</tspan></text>
</g>
<g transform="matrix(4.18 0 0 4.18 596.4 444.54)" style="" id="itext-000kx8wdn">
<text xml:space="preserve" font-family="Brush Script MT" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(250,116,0); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-59.88" y="6.28">Brush Script MT</tspan></text>
</g>
<g transform="matrix(3.66 0 0 3.66 817.59 236.17)" style="" id="itext-000eqyaha">
<text xml:space="preserve" font-family="Courier" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(207,0,254); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-30" y="6.28">Texto</tspan></text>
</g>
</svg>
<script src="assets/js/chart.js"></script>
<script src="assets/js/fabric/5.3.0/fabric.js"></script>
<script src="assets/js/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="assets/js/svg2pdf.js/2.2.3/svg2pdf.umd.min.js"></script>
<script>
const { jsPDF } = window.jspdf
async function svgToPdf(svgElement, x, y, width, height, fileName) {
await loadFont('assets/fonts/calibri/calibri.ttf', 'Calibri', 'normal', 'normal', '400');
await loadFont('assets/fonts/roboto/roboto_regular.ttf', 'Roboto', 'normal', '400');
await loadFont('assets/fonts/gotham/gotham_condensed_medium.ttf', 'Gotham Condensed', 'normal', '400');
await loadFont('assets/fonts/brush_script/brush_script_mt.ttf', 'Brush Script MT', 'normal', '400');
await loadFont('assets/fonts/arial/arial.ttf', 'Arial', 'normal', '400');
await loadFont('assets/fonts/arial/arial_black.ttf', 'Arial Black', 'normal', '400');
//orientation, unit, format
const doc = new jsPDF('p', 'mm', [width, height]);
await doc
.svg(svgElement, {
x: x,
y: y,
width: width,
height: height
});
doc.setFont('Helvetica', 'normal');
doc.text("Texto Helvetica", 100, 100);
// save the created pdf
doc.save(fileName)
}
// [src] file 'calibri-normal.ttf', [name] 'calibri', [style] 'normal'
async function loadFont(src, name, style, weight) {
const dataBase64 = await urlContentToDataUri(src);
const filename = src.split('\\').pop().split('/').pop();
console.log(filename);
console.log(name);
var callAddFont = function () {
this.addFileToVFS(filename, dataBase64);
this.addFont(filename, name, style, weight);
};
jsPDF.API.events.push(['addFonts', callAddFont]);
}
function urlContentToDataUri(url) {
return fetch(url)
.then(response => response.blob())
.then(blob => new Promise(callback => {
const reader = new FileReader();
reader.onload = function () { callback(this.result.substr(this.result.indexOf(',') + 1)) };
reader.readAsDataURL(blob);
}));
}
</script>
If I add the helvetica.ttf file as a custom font then it works
I will add my findings here, you must add all the fonts used on the canvas to the PDF to be safe, even the safe fonts