jspdf-invoice-template icon indicating copy to clipboard operation
jspdf-invoice-template copied to clipboard

Added Support for Languages and Symbols via Custom Fonts

Open benjamin-stern opened this issue 10 months ago • 3 comments

Currently the project only supports generating an invoice with the default fonts as outline in various issues (and I myself have experienced with currency symbols) there is no way to interact with the jsPDF document prior to the process being run and adding the fonts and setting it as the default after the fact does not help as the document has already been generated.

As documented in the readme this adds the following support:

You can use the onJsPDFDocCreation property arrow function to hook into configuring the jsPDF functionality as soon as it's initialized.

Allowing the support of multiple languages and currencies for your invoice as outlined in jsPDF documentation:

Use of Unicode Characters / UTF-8: The 14 standard fonts in PDF are limited to the ASCII-codepage. If you want to use UTF-8 you have to integrate a custom font, which provides the needed glyphs.

Example Usage:

jsPDFInvoiceTemplate({
  //https://github.com/edisonneza/jspdf-invoice-template/issues/20#issuecomment-1859975854
  onJsPDFDocCreation: (doc: jsPDF) => {
      //var font = "...";
      doc.addFileToVFS('LiberationSans-Regular-normal.ttf', font);
      doc.addFont('LiberationSans-Regular-normal.ttf', 'LiberationSans-Regular', 'normal');
      doc.setFont('LiberationSans-Regular');
      
  },
});

Related issues that this resolves: #20 #35

benjamin-stern avatar Apr 14 '24 08:04 benjamin-stern

@edisonneza when you get a chance can you please review and merge, if looks alright to you. 🙏 Should help those waiting for a fix myself included.

benjamin-stern avatar Apr 14 '24 10:04 benjamin-stern

Hi @benjamin-stern, first of all thanks for the contribution. Really appreciate it. I will merge it asap, but firstly i wanted to make some tests. Did the tests on your side work properly? With what fonts did u make the tests? Can u provide some screenshots of the fonts tested?

Thank you!

edisonneza avatar Apr 16 '24 13:04 edisonneza

Hey @edisonneza,

first of all thanks for the contribution. Really appreciate it.

The thanks is mutual, you created this repository and maintained it until now.

I will merge it asap, but firstly i wanted to make some tests.

🙏

Did the tests on your side work properly?

Yes as outlined above (as well as in the modified version of the readme), I was able to get it to work correctly for my instance which was currencies.

But I also tested the text outlined in the issues: #20 #35

Currencies: $₪₠€₨₹£₱
Arabic: نعم ، هذا يعمل يا أخي
Japanese: こんにちは, 簡単なテスト

With what fonts did u make the tests?

I don't have many fonts that I needed to test, so I just used one for the currencies and different ones to make sure the tests passed:

  • Arial Unicode Font.ttf
  • NotoSansArabic-Regular.ttf
  • NotoSansCJKjp-VF.ttf
  • NotoSans-Regular.ttf
  • NotoSerifLiving-Regular.ttf
  • LiberationSans-Regular-normal.ttf

Sources: https://github.com/notofonts/notofonts.github.io https://github.com/notofonts/noto-cjk/blob/main/Sans/Variable/TTF/NotoSansCJKjp-VF.ttf https://github.com/taylor/fonts

Can u provide some screenshots of the fonts tested?

Default (no specific font) image

Japanese, Issue #20 (NotoSansCJKjp-VF.ttf) image

Arabic, Issue #35 (NotoSansArabic-Regular.ttf) image

Currencies, My Issue (unifont-15.1.05.ttf) image

Serif (NotoSerifLiving-Regular.ttf) image

Most everything (Arial Unicode Font.ttf) image

Example

Fonts can be converted using the jsPDF form: https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html

Or you can use a stub I used to test the fonts and invoices, using TypeScript for testing as it matches my production environment:

async function fetchFileAsBinaryString(url): Promise<string> {
    try {
        const response = await fetch(url);
        const blob = await response.blob();

        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = () => {
                const arrayBuffer = reader.result;
                const binaryString = arrayBufferToBinaryString(arrayBuffer);
                resolve(binaryString);
            };
            reader.onerror = reject;
            reader.readAsArrayBuffer(blob);
        });
    } catch (error) {
        console.error('Error fetching file:', error);
        throw error;
    }
}

function arrayBufferToBinaryString(arrayBuffer) {
    const uint8Array = new Uint8Array(arrayBuffer);
    return uint8Array.reduce((binaryString, byte) => binaryString + String.fromCharCode(byte), '');
}

var fontFileName = "unifont-15.1.05";
var fetchRequest = await fetchFileAsBinaryString(`/${fontFileName}.ttf`);
var propsTest = {
    "outputType": OutputType.Save,
    onJsPDFDocCreation: (doc: jsPDF) => {
        doc.addFileToVFS(`${fontFileName}.ttf`, fetchRequest);
        doc.addFont(`${fontFileName}.ttf`, `${fontFileName}`, 'normal');
        doc.setFont(`${fontFileName}`);
    },
    "returnJsPDFDocObject": true,
    "fileName": "Generic Company Receipt",
    "orientationLandscape": false,
    "compress": true,
    "logo": {
        "src": "data:image/png;base64,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",
        "type": "PNG",
        "width": 15,
        "height": 15,
        "margin": {
            "top": 0,
            "left": 0
        }
    },
    "business": {
        "name": "Generic Company Ltd",
        "address": "1121 Tower Rd, Beverly Hills, CA, USA",
        "email": "[email protected]",
        "website": "https://www.example.com"
    },
    "contact": {
        "label": "Issued to:",
        "name": "Customer Name",
        "address": null,
        "otherInfo": "---"
    },
    "invoice": {
        "label": "Receipt #",
        "num": 1234567890,
        "invDate": "Payment Date:4/17/2024",
        "headerBorder": false,
        "tableBodyBorder": false,
        "header": [
            {
                "title": "#",
                "style": {
                    "width": 10
                }
            },
            {
                "title": "Title",
                "style": {
                    "width": 80
                }
            },
            {
                "title": "Price"
            },
            {
                "title": "Total"
            }
        ],
        "table": [
            [
                1,
                "Watermark Removal",
                "$33.82",
                "$33.82"
            ],
            [
                2,
                "Currencies",
                "$₪₠€₨₹£₱",
                "$33.82"
            ],
            [
                3,
                "Arabic",
                "نعم ، هذا يعمل يا أخي",
                "$33.82"
            ],
            [
                4,
                "Japanese",
                "こんにちは, 簡単なテスト",
                "$33.82"
            ]
        ]
    },
    "footer": {},
    "pageEnable": false,
    "pageLabel": "Page "
};
const _pdfObject = jsPDFInvoiceTemplate(propsTest);

benjamin-stern avatar Apr 17 '24 12:04 benjamin-stern

Hey @edisonneza,

Any progress on evaluating and merging the PR? Having functionality like this (or similar) merged would be greatly appreciated allowing a broader range of functionality and versatility.

benjamin-stern avatar May 12 '24 09:05 benjamin-stern

Hey @benjamin-stern, really i've been too busy in some other projects.

I'll try to merge it and publish within 2 days.

Thanks!

edisonneza avatar May 12 '24 22:05 edisonneza

hi @benjamin-stern just merged and published the new version.

if u have to generate any invoice with it let me know if it is working properly after the publish.

Thanks!

edisonneza avatar May 13 '24 22:05 edisonneza

Hey @edisonneza, Sorry for the delay, just a heads up it (as requested) the new build 1.4.4 is working as expected.

benjamin-stern avatar May 23 '24 05:05 benjamin-stern

Wonderful, thanks for your feedback!

edisonneza avatar May 23 '24 15:05 edisonneza