Arabic font not showing correctly in p5.js

Viewed 54

I want to make some Arabic-inspired designs but im having issues with how the text appears, written from left to right and disconnected.

let bg;
let str = "السلام عليكم";
var Diwani;

function preload() {
  Diwani = loadFont('DiwaniFont.ttf');
}

function setup() {
  bg = loadImage('pattern.png');
  createCanvas(400, 400);
}

function draw() {
  background(bg);
  textSize(40);
  textAlign(CENTER);
  stroke(255, 255, 255);
  strokeWeight(5);
  textFont(Diwani);
  text(str, 200, 200);
}

output

It's definitely not an issue with the font I am using. I tried others yet I still get the same result.

Is there a fix?

1 Answers

Apparently, p5.js has problems rendering right-to-left text direction correctly.

Worth noting: text direction seems to be correct, when using a system default font.

Workaround 1: preload font in HTML/CSS

The following example loads the google font "Noto Naskh Arabic" via css @font-face and HTML usage.
Replace the font url with your locally available 'DiwaniFont.ttf' to test the desired result.

let bg;
let str = "السلام عليكم";

function setup() {
  //bg = loadImage('pattern.png');
  createCanvas(400, 400);
}

function draw() {
  //background(bg);
  textFont('Diwani');
  textSize(40);
  textAlign(CENTER);
  stroke(255, 255, 255);
  strokeWeight(5);
  text(str, 200, 200);
}
@font-face {
  font-family: 'Diwani';
  font-weight: 400;
  font-stretch: normal;
  src: url('https://fonts.gstatic.com/s/notonaskharabic/v17/RrQ5bpV-9Dd1b1OAGA6M9PkyDuVBePeKNaxcsss0Y7bwvc5Urqc3.ttf') format('truetype');
  font-style: normal;
}

body {
  font-family: 'Diwani';
  font-size: 40px;
}

canvas {
  width: 50%;
  border: 1px solid red;
}

.hidden {
  visibility: hidden;
  position: absolute;
  overflow: hidden;
  width: 0;
  height: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
<p>السلام عليكم</p>
<p class="hidden" aria-hidden="true">السلام عليكم</p>

So we're actually registering/loading the font via @font-face rule

@font-face {
  font-family: 'Diwani';
  font-weight: 400;
  font-stretch: normal;
  src: url('https://fonts.gstatic.com/s/notonaskharabic/v17/RrQ5bpV-9Dd1b1OAGA6M9PkyDuVBePeKNaxcsss0Y7bwvc5Urqc3.ttf') format('truetype');
  font-style: normal;
}

Now we don't need to preload the font using loadFont() method.
Instead we can set the font-family by its name textFont('Diwani').

We need to ensure, the font is loaded:
Adding an invisible <p> element using the font family (Diwani) will force the browser to load the font file.

Example 2: render only canvas

let bg;
let str = "السلام عليكم";

function setup() {
  //bg = loadImage('pattern.png');
  createCanvas(400, 400);
}

function draw() {
  //background(bg);
  textFont('Diwani');
  textSize(40);
  textAlign(CENTER);
  stroke(255, 255, 255);
  strokeWeight(5);
  text(str, 200, 200);
}
@font-face {
  font-family: 'Diwani';
  font-weight: 400;
  font-stretch: normal;
  src: url('https://fonts.gstatic.com/s/notonaskharabic/v17/RrQ5bpV-9Dd1b1OAGA6M9PkyDuVBePeKNaxcsss0Y7bwvc5Urqc3.ttf') format('truetype');
  font-style: normal;
}

body {
  font-family: 'Diwani';
  font-size: 40px;
}


.hidden {
  visibility: hidden;
  position: absolute;
  overflow: hidden;
  width: 0;
  height: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
<p class="hidden" aria-hidden="true">السلام عليكم</p>

Workaround 2: preload font via javaScript FontFace() constructor

let str = "السلام عليكم";
    let fontUrl =
      "https://fonts.gstatic.com/s/notonaskharabic/v17/RrQ5bpV-9Dd1b1OAGA6M9PkyDuVBePeKNaxcsss0Y7bwvc5Urqc3.ttf";
    let fontFamily = "Diwani";
    let fontOptions = {
      weight: 400,
      style: "normal"
    };


    let fontsReady = new Promise(function(){
      loadFonts(fontFamily, fontUrl, fontOptions);
    });

    async function loadFonts(fontFamily, fontUrl, fontOptions) {
      const font = new FontFace(fontFamily, `url(${fontUrl})`);
      font.weight = fontOptions.weight ? fontOptions.weight : 400;
      font.style = fontOptions.style ? fontOptions.style : "normal";
      // wait for font to be loaded
      await font.load();
      fontsLoaded = true;
      document.fonts.add(font);
      // apply font styles to body
      document.body.setAttribute(
        "style",
        `font-family:${fontFamily}; font-weight:${fontOptions.weight}; font-style:${fontOptions.style}`
      );
    }

    async function setup() {
      createCanvas(400, 400);
      //draw text if fonts are ready
      await fontsReady;
    }

    function draw() {
        textFont('Diwani');
        textSize(40);
        textAlign(CENTER);
        stroke(255, 255, 255);
        strokeWeight(5);
        text(str, 200, 200);
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>

Same concept as the aforementioned html/css approach.
But we're loading the font files by an async function loadFonts(fontFamily, fontUrl, fontOptions).
Advantage: we dont't need to add any css rules or hidden html elements.

Hope it helps!
However you should report this issue on github.

Related