Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
sauriio opened this issue Mar 14, 2023 · 8 comments

Comments

@sauriio
Copy link

sauriio commented Mar 14, 2023

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

@HackbrettXXX
Copy link
Member

Please reduce your SVG to just the text where the fonts are not applied.

@sauriio
Copy link
Author

sauriio commented Mar 15, 2023

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

@sauriio
Copy link
Author

sauriio commented Mar 17, 2023

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

@HackbrettXXX
Copy link
Member

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

@sauriio
Copy link
Author

sauriio commented Mar 20, 2023

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.

@insinfo
Copy link

insinfo commented Jan 9, 2024

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=https://github.com/yWorks/svg2pdf.js/issues/"assets/js/fabric/5.3.0/fabric.js"> <script src=https://github.com/yWorks/svg2pdf.js/issues/"assets/js/jspdf/2.5.1/jspdf.umd.min.js"> <script src=https://github.com/yWorks/svg2pdf.js/issues/"assets/js/svg2pdf.js/2.2.3/svg2pdf.umd.min.js"> <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>">
 <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>

@insinfo
Copy link

insinfo commented Jan 9, 2024

If I add the helvetica.ttf file as a custom font then it works

image

@sauriio
Copy link
Author

sauriio commented Jan 9, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants