8

我需要用中文文本注释图像,我现在正在使用 Imagick 库。

中文文本的一个例子是

这是中文

使用的中文字体文件是这个

文件原名为华文黑体.ttf

它也可以在 Mac OSX 的 /Library/Font 下找到

我已将其重命名为英文 STHeiTi.ttf,以便更轻松地在 php 代码中调用该文件。

特别Imagick::annotateImage功能

我也在使用“如何在 PHP 中使用 Imagick 绘制包装文本?”中的答案。.

我之所以使用它是因为它对于英文文本是成功的,并且应用程序需要同时注释英文和中文,但不能同时进行。

问题是当我使用中文文本运行 annotateImage 时,我得到的注释看起来像罍

此处包含的代码

4

3 回答 3

7

问题是您正在向 imagemagick 提供“线拆分器”(wordWrapAnnotation)的输出,您正在向该输出utf8_decode输入文本。如果您正在处理中文文本,这肯定是错误的。utf8_decode只能处理可以转换为 ISO-8859-1(ASCII 最常见的 8 位扩展)的 UTF-8 文本。

现在,我希望你的文本是UTF-8编码的。如果不是,您可以像这样转换它:

$text = mb_convert_encoding($text, 'UTF-8', 'BIG-5');

或者像这样

$text = mb_convert_encoding($text, 'UTF-8', 'GB18030'); // only PHP >= 5.4.0

(在您的代码$text中是$text1$text2)。

然后(至少)有两件事要在您的代码中修复:

  1. 将文本“按原样”(不带utf8_decode)传递给wordWrapAnnotation,
  2. 根据规范setTextEncoding将from的参数更改"utf-8""UTF-8"

我希望您代码中的所有变量都在其中的某些缺失部分中进行了初始化。有了上面的两个更改(第二个可能不是必需的,但你永远不知道......),并且缺少的部分就位,我认为你的代码没有理由不工作,除非你的 TTF 文件损坏或Imagick库坏了(基于imagemagickImagick的库是一个很棒的库,所以我认为最后一种可能性不大)。

编辑:

根据您的要求,我更新我的答案

a)mb_internal_encoding('utf-8')正如您在回答中所说,设置对于解决方案非常重要,并且

b) 我对更好的分行器的建议,它对西方语言和中文都可以接受,这可能是使用韩语语标(日文汉字和韩文汉字)的其他语言的一个很好的起点:

function wordWrapAnnotation(&$image, &$draw, $text, $maxWidth)
{
   $regex = '/( |(?=\p{Han})(?<!\p{Pi})(?<!\p{Ps})|(?=\p{Pi})|(?=\p{Ps}))/u';
   $cleanText = trim(preg_replace('/[\s\v]+/', ' ', $text));
   $strArr = preg_split($regex, $cleanText, -1, PREG_SPLIT_DELIM_CAPTURE |
                                                PREG_SPLIT_NO_EMPTY);
   $linesArr = array();
   $lineHeight = 0;
   $goodLine = '';
   $spacePending = false;
   foreach ($strArr as $str) {
      if ($str == ' ') {
         $spacePending = true;
      } else {
         if ($spacePending) {
            $spacePending = false;
            $line = $goodLine.' '.$str;
         } else {
            $line = $goodLine.$str;
         }
         $metrics = $image->queryFontMetrics($draw, $line);
         if ($metrics['textWidth'] > $maxWidth) {
            if ($goodLine != '') {
               $linesArr[] = $goodLine;
            }
            $goodLine = $str;
         } else {
            $goodLine = $line;
         }
         if ($metrics['textHeight'] > $lineHeight) {
            $lineHeight = $metrics['textHeight'];
         }
      }
   }
   if ($goodLine != '') {
      $linesArr[] = $goodLine;
   }
   return array($linesArr, $lineHeight);
}

换句话说:首先通过用单个空格替换所有运行的空格(包括换行符)来清理输入,但前导和尾随空格除外,后者已被删除。然后在空格处拆分,或者就在没有“前导”字符(如左括号或左引号)的汉字符之前,或者就在“前导”字符之前。行被组合是为了不以超过$maxWidth像素的水平水平渲染,除非分割规则无法做到这一点(在这种情况下,最终渲染可能会溢出)。在溢出情况下强制拆分的修改并不困难。请注意,例如,中文标点在 Unicode 中不归类为汉,因此,除了“前​​导”标点之外,

于 2012-06-26T12:38:52.317 回答
3

恐怕您将不得不选择可以支持中文代码点的TTF。这有很多来源,这里有两个:

http://www.wazu.jp/gallery/Fonts_ChineseTraditional.html

http://wildboar.net/multilingual/asian/chinese/language/fonts/unicode/non-microsoft/non-microsoft.html

于 2012-06-22T13:38:28.480 回答
3

这里的完整解决方案:

https://gist.github.com/2971092/232adc3ebfc4b45f0e6e8bb5934308d9051450a4

关键思想:

必须在表单和处理页面上设置html字符集和内部编码

header('Content-Type: text/html; charset=utf-8');
mb_internal_encoding('utf-8');

这些行必须位于 php 文件的顶部行。

使用该函数判断文本是否为中文并使用正确的字体文件

function isThisChineseText($text) {
    return preg_match("/\p{Han}+/u", $text);
}

有关更多详细信息,请查看https://stackoverflow.com/a/11219301/80353

在 ImagickDraw 对象中正确设置 TextEncoding

$draw = new ImagickDraw();

// set utf 8 format
$draw->setTextEncoding('UTF-8');

注意大写的 UTF。Walter Tross在他的回答中向我指出了这一点: https ://stackoverflow.com/a/11207521/80353

使用 preg_match_all 爆破英文单词、中文单词和空格

// separate the text by chinese characters or words or spaces
preg_match_all('/([\w]+)|(.)/u', $text, $matches);
$words = $matches[0];

受此答案启发https://stackoverflow.com/a/4113903/80353

也适用于英文文本

于 2012-06-27T04:28:17.737 回答