#javascript #html #canvas #fonts #ligature
#javascript #HTML #холст #шрифты #лигатура
Вопрос:
Я пытаюсь собрать инструмент, который проверяет, отображается ли данный символ шрифтом указанного стиля или системным шрифтом по умолчанию. Моя конечная цель — иметь возможность проверять, по крайней мере, в современных (читай: IE8 ) браузерах, поддерживается ли лигатура в данном шрифте.
У меня есть два холста, отображающие одну и ту же лигатуру (в данном случае st). Я превращаю эти холсты в данные и сравниваю их, чтобы увидеть, совпадают ли символы.
Arial (как и большинство шрифтов) не имеет лигатуры st, поэтому он возвращается к шрифту с засечками по умолчанию. Вот где это становится странным: хотя они отображают один и тот же шрифт, два холста не имеют одинаковых данных.
Почему? Потому что их позиции на холсте не совсем одинаковы. Я предполагаю, что это как-то связано с разной относительной высотой шрифтов (один немного выше другого, хотя он варьируется от шрифта к шрифту). Разница, по-видимому, составляет один или два пикселя, и она варьируется от шрифта к шрифту.
Как можно решить эту проблему? Моя единственная текущая идея — найти способ измерить высоту шрифта и соответствующим образом отрегулировать его положение, но, к сожалению, я понятия не имею, как это сделать. Существуют ли другие подходы, которые я мог бы предпринять, чтобы сделать два изображения идентичными?
Вы можете увидеть код ниже. Оба холста успешно инициализированы и добавлены к телу элемента, поэтому я могу видеть, что происходит визуально (хотя в реальном скрипте, над которым я работаю, это необязательно). Я отбросил инициализацию и контекст, так как все они работают просто отлично.
function checkLig() {
lig = 'fb06' // this is the unicode for the st ligature
canvas0.width = 250;
canvas0.height = 50;
context0.fillStyle = 'rgb(0, 0, 0)';
context0.textBaseline = 'top';
context0.font = 'normal normal normal 40px Arial';
context0.fillText(String.fromCharCode(parseInt(lig, 16)), 0, 0);
var print0 = context0.getImageData(0, 0, 720, 50).data;
canvas1.width = 250;
canvas1.height = 50;
context1.fillStyle = 'rgb(0, 0, 0)';
context1.textBaseline = 'top';
context1.font = 'normal normal normal 40px serif';
context1.fillText(String.fromCharCode(parseInt(lig, 16)), 0, 0);
var print1 = context1.getImageData(0, 0, 720, 50).data;
var i = 0, same = true, len = 720 * 50 * 4;
while (i < len amp;amp; same === true) {
if (print0[i] !== print1[i]) {
same = false;
}
else {
i ;
}
}
return same;
}
Комментарии:
1. Если кому-то интересно, все еще (очень незавершенные ) результаты этого можно найти здесь .
Ответ №1:
Итак, я правильно понимаю вопрос, проблема в том, что один холст указывает Arial (но возвращается к Serif), а другой — Serif, и когда вы выполняете сопоставление пикселей, и это не совпадение, потому что один из них имеет небольшое смещение?
Одно из предложений состоит в том, чтобы захватить ссылочный пиксель из каждого холста и сравнить позиции двух ссылочных пикселей, чтобы получить смещение. А затем учитывайте это смещение в вашем цикле сравнения.
Например, в этом случае вы могли бы получить свой эталонный пиксель, начав проверять пиксели с верхнего левого угла одного холста и сканировать вниз в этом столбце, а когда вы дойдете до низа, вернитесь к началу следующего столбца и сканируйте вниз.
Как только вы нажмете на пиксель, который не совпадает с цветом вашего фона, запишите позицию и используйте ее в качестве эталонного пикселя. Это должно быть краем вашего шрифта. Проделайте то же самое со следующим холстом, а затем сравните позиции двух опорных пикселей, чтобы получить смещение. Учитывайте это смещение в цикле сравнения.
Надеюсь, у меня правильное представление о проблеме, и я надеюсь, что это поможет!
Комментарии:
1. Вы правильно поняли вопрос, и это хорошая идея… Я поиграю с этим. Я обеспокоен тем, что это может привести к проблемам с общим случаем (когда шрифты не возвращаются), но я не думаю, что это должно быть проблемой в целом. Настоящий трюк будет заключаться в том, чтобы выяснить, как перемещаться по холсту таким образом.
2. Если getImageData возвращает одномерный массив, сканирование по горизонтали приведет к тому же результату. Используя один и тот же метод сканирования на обоих холстах, вы должны нажать «land» в одном и том же месте. В общем случае, когда шрифты разные, я думаю, что предварительное сканирование не повлияет на результат. Единственное соображение — это дополнительный удар по производительности.
3. Правильно. Тогда это должно быть простой математикой, чтобы сделать смещение от этой точки. И это достаточно маленький фрагмент кода, поэтому проблемы с производительностью незначительны. Спасибо!