【问题标题】:How do I deal with characters unsupported by the font file when using imagettftext()?使用 imagettftext() 时如何处理字体文件不支持的字符?
【发布时间】:2014-04-11 20:55:41
【问题描述】:

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

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

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

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

我用过这个:

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

输出是这样的:

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

如果不写一个很大的str_replace() 块,似乎不可能将ɦ 音译为hɲn。喜欢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

【问题讨论】:

  • 我编辑了标题和标签,如果您不喜欢,请随时更改。
  • 您是否 100% 确定传入的数据 UTF-8?因为如果不是,iconv() 就无法为您音译字符。您必须确保在第一个参数中指定正确的编码。 (也就是说,我不确定ɦ首先是否应该应该音译为h,所以也许这是预期的结果。)
  • 我确定 UTF8。即使没有 iconv,最好知道 Verdana 是否可以打印它。所以如果字体不合适,我可以打印一个虚拟词。

标签: php utf-8 fonts gd imagettftext


【解决方案1】:

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

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

  • PHP >= 5.4:使用新的内置 Transliterator
  • PHP his excellent port

我现在手头没有 v5.4,所以我无法确定 Transliterator,但 Kemal Dağ 的 JTransliteration 端口性能相当不错:

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

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

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

最后,如果你想检查一个给定的字体是否支持给定的字符,它会变得有点麻烦。 This library 会有很大帮助。它需要 >= 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 支持这些字符(因为没有实际需要,因为它们不被任何真正的人类语言使用)。

【讨论】:

    【解决方案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');
    

    结果:

    【讨论】:

    • 我还检查了dejavu。似乎 dejavu 支持这些奇怪的字符。并且大多数用户的名字都可以用 Dejavu 显示。但我不想失去 Verdana ......当我检查字符串时,我总是看到它是 UTF8 编码的。我正在考虑一种复合解决方案,将 Verdana 用于安全字符并将 Verdana 用于这些 Verdana 字符。
    • 您可能不得不选择继续使用不支持 unicode 的字体并为您的代码增加一些复杂性和限制,或者决定找到一种让您的代码保持简单的字体。在这里您可以找到有关 unicode 和字体的更多信息:unicode.org/faq/basic_q.html 在这里您可以找到 unicode 的字体和资源列表:unicode.org/resources/fonts.html 以下链接是关于在 verdana 中输入和输出 unicode 字符的链接:fileformat.info/info/unicode/font/verdana/list.htm fileformat.info/info/unicode/font/verdana/missing.htm
    【解决方案3】:

    您是否设置了正确的语言环境?对于 iconv 可能是必要的 - http://cz1.php.net/manual/en/function.iconv.php#74101

    【讨论】:

    • 当前语言环境是英语或德语。我无法预测 ɦɪɲɣƙƨєʌɾ 存在于哪个语言环境中。所以我认为在这种情况下是不可能使用 iconv 的。
    【解决方案4】:

    您描述的问题有多个可能失败的地方,对于您首先要了解如何以最佳方式解决此问题的正确决定很重要。

    因为很多事情都可能出错,所以你需要尽早失败,以防输入不符合预期。所以首先,您必须验证字符串的编码是否正确,以便与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 编码的,所以它应该已经通过了。但是,以防万一您在编码方面犯了一些错误并且它无效(很容易发生),此检查可防止您查找错误的位置。

    正如您在问题中的输出已经显示的那样,您很可能在编码方面犯了错误,因为否则至少会正确显示支持的字符,但是不仅会遗漏一些字符,甚至是 显示不同的个字符。错误编码的明显标志:

    所以不要跳过这一步来实际验证所需的字符串编码。

    这对于接下来的事情尤其重要:

    您需要确保字体支持该字符串中的字母。 Verdana 字体支持 794 个 Unicode 字符 (full list)。如果您要查找的字符不是其中的一部分,imagettftext() 函数将无法显示它们,因为字体缺少它们。在这种情况下,您需要选择支持您正在寻找的 Unicode 字符的字体。 Wikipedia 上提供了具有不同字体的概览表:

    有关正确字体选择的更多指导可以在 Stackoverflow 上找到:

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

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

    【讨论】:

    • 我重新检查了mb_detect_encodingpreg_match。我看到所有奇怪的名字都是 UTF8 编码的。但似乎 Verdana 不支持它们。但是 Dejavu 支持这些奇怪的字符。 (en.wikipedia.org/wiki/DejaVu_fonts) 但我不知道 Verdana 是否可以支持任何新文本。
    猜你喜欢
    • 1970-01-01
    • 2013-01-08
    • 2023-03-07
    • 2012-06-16
    • 1970-01-01
    • 2015-11-09
    • 1970-01-01
    • 2023-03-10
    • 2013-09-11
    相关资源
    最近更新 更多