cpfair / quran-tajweed

Tajweed annotation for the Qur'an
125 stars 49 forks source link

Javascript usage, resultat differs from usual versions #6

Open korbav opened 4 years ago

korbav commented 4 years ago

Thanks for this phenomenal work.

I'm using Javascript to parse the rules and apply CSS classes to colorize the verses. Nevertheless, it seems I miss something since the result I get (except, of course, that the colours rules are obviously not the same everywhere), is quite different from whatever I can find anywhere else.

This is what I get (using the original othmani json file available on this repository) :

2020-07-06_15h18_29

And this is what one PDF version I found outputs :

2020-07-06_15h19_05

The code I'm using to parse the rules is the following :

const createTajweedClasses = (classesArray, rules, index) => {
  const ruleStart = parseInt(rules[index].start);
  const ruleEnd = parseInt(rules[index].end);
  const ruleType = rules[index].type;
  const limit = ruleEnd - 1;
  for (let i = ruleStart; i <= limit; i += 1) {
    classesArray[i] = styles[ruleType];
  }
};
...

const classesArray = [];
tajweedRules.forEach((rule, index) => createTajweedClasses(classesArray, tajweedRules, index));

// It will fill classesArray with [ { Char Position } => Class Name ] elements

Then I have a CSS set of rules to colorize everything :

.idghaam_shafawi {
  color: rgba(190, 0, 0, 1);
}

.idghaam_mutajaanisain {
  color: rgba(190, 0, 0, 1);
}

.idghaam_mutaqaaribain {
  color: rgba(190, 0, 0, 1);
}

.idghaam_ghunnah {
  color: rgba(190, 0, 0, 1);
}

.idghaam_no_ghunnah {
  color: rgba(150, 60, 60, 1);
}

.ghunnah {
  color: rgba(0, 180, 0, 1);
}

.qalqalah {
  color: rgba(8, 80, 170, 1);
}

.iqlab {
  color: rgba(140, 50, 170, 1);
}

.ikhfa {
  color: rgba(50, 180, 160, 1);
}

.ikhfa_shafawi {
  color: rgba(50, 180, 160, 1);
}

/* Ikhfa mim saakin */
.madd_6 {
  color: rgba(200, 0, 0, 1);
}

.madd_246 {
  color: rgba(255, 180, 60, 1);
}

.madd_2 {
  color: rgba(50, 180, 160, 1);
}

.madd_muttasil {
  color: rgba(255, 0, 0, 1);
}

.madd_munfasil {
  color: rgba(0, 221, 147, 1);
}

.hamzat_wasl {
  color: rgba(150, 252, 0, 1);
}

.lam_shamsiyyah {
  color: rgba(175, 0, 221, 1);
}

.silent {
  color: rgb(180, 180, 180);
}

And for each verse, I split all the characters this way to apply the rules (simplified version of the code) :

ayah.split('').map((char, index) => applyClassOnChar(classesArray[index]);

Which will output a list of `span` elements with the appropriate class

It would be great if you could give your thoughts about what is being wrong! @cpfair @Ysajid

bekzattt commented 4 years ago

Hi @korbav ! I had same issue in my app, you can read here

It's happening because your version of quranic text doesn't contain pause marks and juz signs, while json from this repo was generated on quranic text with these marks.

I recommend whenever you see such pause mark, just do positionsShift++

then you can highlight range from (ruleStart+positionsShift) to (ruleEnd+positionsShift)

Hope it helps, if you have more questions, please just ask :)

korbav commented 4 years ago

Hi @bekzattt ,

Thanks a lot for your help, I enhanced a bit the parsing so that it outputs less DOM elements, and I get something much closer to the expected result, but still, not quite the same.

I had noticed that we must use the Othmani script including pause and sajdah signs :

2020-07-08_11h27_21

So I think I'm using the correct version.

On the following picture, I compare, a version of the tajweed found on the web, and the output of the script Im running with this repo, we can see a lot of differences, I don't know how important they are but, definitely, it's noticeable :

Sans titre-1

For information, the last version of my Javascript code to output this result (running for each ayah) is now this one :

const textChunks = [];
tajweedRules.forEach((rule, index) => {
if (index === 0) {
  textChunks.push(text.substr(0, rule.start));
}

textChunks.push(React.createElement('span', {
  className: styles[rule.type],
}, text.substr(rule.start, rule.end - rule.start)));

if (index !== tajweedRules.length - 1 && (tajweedRules[index + 1].start) > (rule.end + 1)) {
  const length = tajweedRules[index + 1].start - rule.end;
  textChunks.push(text.substr(rule.end, length));
}

if (index === tajweedRules.length - 1 && rule.end < text.length - 1) {
  textChunks.push(text.substr(rule.end));
}
});

From what you said @bekzattt it will also need to handle the pause marks but for now, at least on the verses I'm showing in the picture, there's no pause marks so it can' be the problem.

I notice also that madd2 rules are never visible (even though the script will output span class="madd2" for the concerned caracter.

korbav commented 4 years ago

Hi again @bekzattt,

Would you mind sharing your algorithm? I'm still struggling with being aligned to existing official tajweed Qurans.

From your advice about sleeping signs (waqfs), I'm looking for them with the following regular expression :

const waqfs = /[\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06e9]/g;

Then for each tajweed rule, I'm offsetting the start and stop of the rule from the number of waqfs counted before the current rule occurs.

But I'm still observing misalignments, with Muslim Pro tajweed as a reference for example :

Comparison :

Mine : 2020-08-08_15h31_25

Muslim Pro one : tempFileForShare_20200808-153218

As you can see :

Again, am I missing something? is there any scientific tajweed we can rely on? might there be any mistakes with the tajweed rules generated by this project?

bekzattt commented 4 years ago

@korbav sorry for delay, I will try to get back to you tomorrow

bekzattt commented 4 years ago

Hi @korbav

  1. Missing ikhfa in Muslim pro might be their bug, better to compare with recitequran.com.

  2. With idghaam_no_ghunnah, maybe you can solve problem with by taking -1 from start position. Right now rule is trying to color only tanween without letter T, if you add -1, this might also include coloring including تٍ . It might be web issue, since coloring only tanween without letter T worked fine for my app (https://apps.apple.com/kz/app/quran-kz/id1120620764, it's in Kazakh)

  3. Tajweed implementation in https://globalquran.com looks good, you can check it as example since it's also using text rendered in browser, only tip is to change default font to Sheherezade, otherwise it's a little bit hard to read :)

bekzattt commented 4 years ago

Also regarding pause marks, don't forget to add +2 to positionsShift, 1 for pause mark + 1 for extra space after pause mark.

korbav commented 4 years ago

Hi @korbav

1. Missing ikhfa in Muslim pro might be their bug, better to compare with recitequran.com.

2. With idghaam_no_ghunnah, maybe you can solve problem with by taking -1 from start position. Right now rule is trying to color only tanween without letter T, if you add -1, this might also include coloring including تٍ . It might be web issue, since coloring only tanween without letter T worked fine for my app (https://apps.apple.com/kz/app/quran-kz/id1120620764, it's in Kazakh)

3. Tajweed implementation in https://globalquran.com looks good, you can check it as example since it's also using text rendered in browser, only tip is to change default font to Sheherezade, otherwise it's a little bit hard to read :)

Hi @bekzattt,

This is what I'm running right now to adjust all the original rules coming from the project :

const waqfs = /[\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06e9]/g;
const text = 'خَتَمَ ٱللَّهُ عَلَىٰ قُلُوبِهِمْ وَعَلَىٰ سَمْعِهِمْ ۖ وَعَلَىٰٓ أَبْصَٰرِهِمْ غِشَٰوَةٌ ۖ وَلَهُمْ عَذَابٌ عَظِيمٌ' // text is set for each ayah;
const adjustedTajweedRules = [];
originalTajweedRules.forEach((originalRule, index) => {
    const rule = {
        start: originalRule.start,
        end: originalRule.end,
        type: originalRule.type,
    };
    // We compute the number of sleeping signs that occurred before the start of the current rule
    const silentMarksBeforeTheCurrentRule = index === 0 ? 0 : _.size(text.substr(0, rule.start).match(waqfs));

    // We compute the number of sleeping signs that are present inside the current rule
    const silentMarksInsideTheCurrentRule = _.size(text.substr(rule.start + (2 * silentMarksBeforeTheCurrentRule), rule.end - rule.start).match(waqfs));

    // We add an offset to the rule.start based on the sleeping signs occurring before the current rule
    rule.start += (2 * silentMarksBeforeTheCurrentRule);

    // We add an offset to the rule.end based on the sleeping signs occurring before the current rule & also inside the current rule
    rule.end += (2 * silentMarksBeforeTheCurrentRule) + (2 * silentMarksInsideTheCurrentRule);
    adjustedTajweedRules.push(rule);
});

Thanks!