Search code examples
three.jsarabicpersian

Support for Farsi/Arabic texts in Three.js


I need to show some text in Persian/Arabic language. I loaded a font including characters and I used TextGeometry to create a text on the scene:

var loader = new THREE.FontLoader();
loader.load('B Zar_Regular.js', function (font) {
    var textGeo = new THREE.TextGeometry('سلام!', {
        font: font,
        size: 1,
        height: 0.05,
        curveSegments: 12,
    });
    var material = new THREE.MeshNormalMaterial();
    var textMesh = new THREE.Mesh(textGeo, material);
    textMesh.position.x = 15;
    scene.add(textMesh);
});

I was expecting to see سلام! but the output was:

three.js arabic text problem

letters are separated and order of characters is reversed mistakenly. After all it seems threejs do not support rtl languages. Am I right or I missed something? Is there any workaround as a quick solution? Thanks.


Solution

  • First of all I found very useful blog post here by Chris Loer. He has written a plugin (mapbox-gl-rtl-text.js) that solves the problem.

    Usage example:

    var rtlText = require('mapbox-gl-rtl-text');
    var arabicString = "سلام";
    var shapedArabicText = rtlText.applyArabicShaping(arabicString);
    var readyForDisplay = rtlText.processBidirectionalText(shapedArabicText, []);
    


    PS: When I used this plugin with common Farsi fonts some of letters was not shown so I add some extra code to fix it.

    RtlTextHelper.farsify("سلام");
    

    Usage example:

    private createTextMesh(font, text) {
            var shapedText = RtlTextHelper.farsify(text);
            var fontSize = 0.3;
            var textHieght = 0.2;
            var material = new THREE.MeshBasicMaterial({
                color: this.colors.label.normal,
                side: THREE.DoubleSide
            });
            var textGeo = new THREE.TextGeometry(shapedText, {
                font: font,
                size: fontSize,
                height: 0.05,
                curveSegments: 12
            });
            var textMesh = new THREE.Mesh(textGeo, material);
            textGeo.computeBoundingBox();
            var box = new THREE.Box3().setFromObject(textMesh);
            var textLength = box.max.x;
            return {
                mesh: textMesh,
                hieght: textHieght,
                font: {
                    size: fontSize
                },
                length: textLength
            };
        }