使用imagettftext()时,如何处理字体文件不支持的字符?

时间:2014-03-08 16:02:17

标签: php utf-8 fonts gd imagettftext

我在PHP GD库创建的图像中使用了Verdana字体。

imagettftext($image, $fontSize, 0, 70, $y, $color, $font, $username );

大多数情况下,imagettftext非常适合字符串 但是我的一些用户在他们的名字中使用了奇怪的字符/符号 所以,当我尝试将他们的名字打印到图像时。例如:
enter image description here

此用户使用ɦɪɲɣƙƨєʌɾ 符号。所以Verdana无法打印出来。

我用过这个:

$username=iconv('UTF-8', 'ASCII//TRANSLIT', $username);

输出是这样的:
enter image description here

(当前语言环境在英语和德语之间发生变化。因此,当前的语言环境可能无法处理这些字符:ɦɪɲɣƙƨєʌɾ

似乎无法在不编写非常大的ɦ块的情况下将h音译为ɲnstr_replace()。与this一样。

  • 所以我想知道是否可以检查字体(Verdana)是否可以显示这些符号。如果其中一个字符无法在字符串中显示,那么我可以将空字符串传递给imagettftext方法。我可以检查字体内支持的字符吗?或者创建一个包含Verdana支持的符号的字符映射,并检查我的字符串是否包含不支持的符号? (我认为由于this question

  • ,这是不可能的
  • 或许是另一种解决方案,是否可以在imagettftext()中使用多种字体? 例如,首先尝试Verdana,如果Verdana没有涵盖那些符号使用Arial sans serif等。

  • 或其他任何解决方案?

编辑:
似乎Verdana在我的文本中不支持这些unicode字符 Verdana支持角色:http://www.fileformat.info/info/unicode/font/verdana/grid.htm
Verdana不支持的字符:http://www.fileformat.info/info/unicode/font/verdana/missing.htm

4 个答案:

答案 0 :(得分:4)

我的第一选择是切换到支持您希望能够处理的全部字符的字体。但是不要指望单个字体会实现million-or-so possible characters in UTF-8

现在,如果您想采用(懒惰)音译路线,我会参考this answer from Kemal Dağ

我现在手头没有v5.4,所以我不知道Transliterator,但是KemalDağ的JTransliteration端口表现相当不错:

<?php
    require 'transliteration/JTransliteration.php';

    $input = 'ɦɪɲɣƙƨєʌɾ';
    echo JTransliteration::transliterate($input); // output: hIngk2ie^r

    $input = 'Хეλлఒ Wओრলद';
    echo JTransliteration::transliterate($input);

最后,如果你想检查给定的字体是否支持给定的字符,它会变得更加毛茸茸。 This library会有很大帮助。它需要&gt; = 5.3(使用命名空间):

<?php
    $fontFile = 'arial.ttf';
    $charToCheck = 'ɣ';

    require_once 'php-font-lib-master/src/FontLib/Autoloader.php';

    use FontLib\Font;
    use FontLib\TrueType\Collection;


    $font = Font::load($fontFile);
    if ($font instanceof Collection) {
        $font = $font->getFont(0);
    }
    $subtable = null;
    foreach($font->getData("cmap", "subtables") as $_subtable) {
        if ($_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
            $subtable = $_subtable;
            break;
        }
    }

    if (isset($subtable["glyphIndexArray"][ord_utf8($charToCheck)])) {
        $supported = 'Supported';
    } else {
        $supported = 'Not Supported';
    }

    echo "$charToCheck is $supported by font $fontFile";


    function ord_utf8($c) {
        $b0 = ord($c[0]);
        if ( $b0 < 0x10 ) {
            return $b0;
        }
        $b1 = ord($c[1]);
        if ( $b0 < 0xE0 ) {
            return (($b0 & 0x1F) << 6) + ($b1 & 0x3F);
        }
        return (($b0 & 0x0F) << 12) + (($b1 & 0x3F) << 6) + (ord($c[2]) & 0x3F);
    }

来自font_info.php和R. Hill的ord_utf8()

的无耻掠夺代码

P.S。字符串“ɦɪɲɣƙƨєʌɾ”由国际音标字符组成。我不确定任何 locale 是否支持这些字符(因为它没有实际需要,因为它们不被任何真正的人类语言使用)。

答案 1 :(得分:2)

只要您使用的是UTF-8,UTF-8 True Type字体就没有理由显示这些字母(东亚字母的免责声明!)

这是我的简单示例,使用真正的字体:

// utf-8 text
$text   = 'ɦɪɲɣƙƨєʌɾ';

// if text read from a file (for example)
// and the default locale (for most of western countries)
// is ISO-8859-1, you can simly convert it to
// utf-8 using:

//$text = utf8_encode($text);

$png    = imagecreatefrompng('/tmp/sample.png');
$color  = imagecolorallocate($png, 0, 0, 0);

// True type font that support UTF-8!!!!
$font   = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf';

imagettftext($png, 50, 0, 50, 50, $color, $font, $text);
imagepng($png, '/tmp/test.png');

结果:

enter image description here

答案 2 :(得分:0)

您是否设置了正确的区域设置?对于iconv可以是必要的 - http://cz1.php.net/manual/en/function.iconv.php#74101

答案 3 :(得分:0)

您描述的问题有多个地方可能会失败,哪些对于您首先要做出正确决定如何最好地解决这个问题非常重要。

因为很多事情都可能出错,所以如果输入不符合预期,您需要提前失败。首先,必须验证字符串是否采用正确的编码方式,以便在调用该函数之前与imagettftext() 一起使用:

if (!preg_match('//u', $username)) {
    throw new Exception(
        sprintf(
            "Username string %s can not be used with imagettftext()"
            , var_export($username, true)
        )
    );
}

不这样做不会让你获得正确的结果。然后,如果此检查失败,则传递此解决方案的方法是确保字符串是UTF-8编码的。这或多或少是一个健全性检查,因为你说字符串已经是UTF-8编码,所以它应该已经通过。但是,如果您在编码时出现了一些错误并且无效(可能很容易发生),则此检查会阻止您查找错误的位置。

由于您已经在问题中显示的输出已经显示,您很可能在编码时出错了,因为否则支持的字符至少会正确显示,但不仅会遗漏一些字符,而且#&# 39;甚至显示不同的字符。编码错误的明确标志:

  

enter image description here

因此,请勿跳过此步骤以实际验证字符串所需的编码。

这对下一件事尤为重要:

您需要确保fontface支持该字符串中的字母。 Verdana字体支持794个Unicode字符(full list)。如果您要查找的字符不属于它,则imagettftext()函数无法显示它们,因为字体缺少它们。在这种情况下,您需要选择支持您正在寻找的Unicode字符的字体。维基百科提供了包含不同字体的概述表:

有关正确字体选择的更多指导,请参阅Stackoverflow:

如果您在字符串变量中使用正确的编码,并且对该字符串中编码的所有Unicode字符使用具有字形的字体,imagettftext确实可以满足您的需求。

正如我在开头写的那样,有很多地方可能会出错:如果你通过编码检查字体支持所有字符,那么这里还有一个失败的地方:该字符串是UTF-8编码的,但它不包含您认为的字符。