yWorks/svg2pdf.js

The exported PDF is not rendering custom fonts on some of the PDF text objects.

sauriio opened this issue · 8 comments

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

export_svg.zip

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?
canvas

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

pdf

modelo.pdf

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>

cracha_modelo

image

 <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

image

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