2

问题

如何正确匹配 PHP (5.2.17) 中 GD 库的字距调整与 SVG (1.1)?

问题

PNG 和 SVG 文件文本不相同。字距调整似乎已关闭。

情况

我有一个 Web 应用程序,它从一些文本和位置数据创建 SVG 文件和图像文件 (PNG)。

目前,这两种实现都“工作”,就像所有东西都在创建中一样。SVG 文件正常工作,PNG 正常工作。我遇到的问题是文本没有对齐。字距调整似乎已关闭。

PNG 将成为我们的基准(我们试图复制的内容)。

我想要的是

我希望“line”中的“l”与第二行的最后一个“L”对齐。 错误的可视化

我试过的

  • 我确保我对 SVG 和 PNG 文件使用相同的字体。
  • 我用 SVG 对字体进行了 base64 编码,以确保它是相同的字体
  • 我牺牲了一只山羊。

额外的

  • PHP 将提升到 5.3,在我尝试过的所有 PHP 5.3 服务器上,我有相同的输出。
  • SVG 版本也可以更改。
  • SVG 可以使用分组和视图框。我只是还没有牢牢掌握它们。

演示

http://resurrectionwebdesign.com/demo/demo_text.php

来源
好的,这里有很多来源。我试图尽可能地简化它;然而,剩下的东西是必要的(至少为了让每个人都可以使用它)。

<?php
   // Prepare Background Image
   $image_width  =  300;
   $image_height =  200;
   $bg           = imagecreatetruecolor($image_width, $image_height);

   // Make it transparent
   imagesavealpha($bg, true);
   $transparent_color = imagecolorallocatealpha($bg, 0, 0, 0, 127);
   imagefill($bg, 0, 0, imagecolorallocatealpha($bg, 0, 0, 0, 127));

   // Prepare black for the color to use
   $black = imagecolorallocate($bg, 0x00, 0x00, 0x00);

   // Prepare two lines of text.
   $line1['text']     = "Multiple words in a line.";
   $line1['height']   = 22;
   $line1['width']    = 233;
   $line1['pt']       = 17;
   $line1['font']     = "ARIAL.TTF";
   $line1['font_fam'] = 'Arial';
   $line1['x']        = 24;
   $line1['y']        = 94;
   $line1['angle']    = 0;

   $line2['text']     = "Last up last L";
   $line2['height']   = 25;
   $line2['width']    = 160;
   $line2['pt']       = 20.25;
   $line2['font']     = "ARIAL.TTF";
   $line2['font_fam'] = 'Arial';
   $line2['x']        = 68;
   $line2['y']        = 118;

   imagettftext( $bg, 
                 $line1['pt'], 
                 $line1['angle'], 
                 $line1['x'],
                 $line1['y'],
                 $line1['color'],
                 $line1['font'],
                 $line1['text']);

   imagettftext( $bg, 
                 $line2['pt'], 
                 $line2['angle'], 
                 $line2['x'],
                 $line2['y'],
                 $line2['color'],
                 $line2['font'],
                 $line2['text']);

   // Save the PNG file.
   $im = imagepng($bg, 'text_align.png');
   imagedestroy($bg);

   // Prepare SVG
   $svg = <<< EOT
       <svg xmlns="http://www.w3.org/2000/svg"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:ev="http://www.w3.org/2001/xml-events" 
         width="{$image_width}px" 
         height="{$image_height}px"
         baseProfile="full"
         version="1.1">
EOT;

   // Line 1
   $svg .= "\r\n\t<text x='{$line1['x']}' y='{$line1['y']}'";
   $svg .= "font-size='{$line1['pt']}pt'  font-family='{$line1['font_fam']}'";
   $svg .= "fill='rgb(0,0,0)'>{$line1['text']}</text>";
   // Line 2
   $svg .= "\r\n\t<text x='{$line2['x']}' y='{$line2['y']}'";
   $svg .= "font-size='{$line2['pt']}pt'  font-family='{$line2['font_fam']}'";
   $svg .= "fill='rgb(0,0,0)'>{$line2['text']}</text>";

   // Close SVG
   $svg .= "\r\n</svg>";

   $file = fopen('text_align.svg', 'w');
   fwrite($file, $svg);
   fclose($file);   

   if(isset($_GET['download']) && !empty($_GET['download']))
   {
      if($_GET['download'] == 'SVG')
      {
         $file   = 'text_align.svg';
         $header = "Content-type: image/svg+xml";
      }
      elseif($_GET['download'] == 'IMAGE')
      {
         $file   = 'text_align.png';
         $header = "Content-type: image/png";
      }
      if(isset($header))
      {
         header($header);
         header('Content-Disposition: attachment; filename="'.$file.'"');
         echo file_get_contents($file);
      }
   }
   else
   {
?>   
<!doctype html>
<html>
   <body>
      Download Image: <a href="demo_text.php?download=IMAGE">Text Align PNG</a>
      <br /><img src='text_align.png' /><br />
      Download SVG: <a href="demo_text.php?download=SVG">Text Align SVG</a>
      <div style="border: 1px dotted blue;"><?echo $svg ?></div>
   </body>
</html>
<?
   }
?>

更新/进展

更新 1

我不确定它是否与服务器上生成的图像以及客户端上呈现的文本有关。我目前正在运行 Windows 构建,但如果必须,我将抛出一个 CentOS VM,并在 VM 上测试映像以及从 VM 中的浏览器查看映像的样子。

更新 2

我已经尝试过以我知道的各种方式嵌入字体。到目前为止,结果都是一样的。我会考虑尝试转换,看看是否有效。

更新 3

好的!一些进步!我发现 SVG WG 委员会的人建议从 SVG 文档中删除 doctype。我做到了,现在我得到了一些不同的结果。我不确定它们有多好,但我现在肯定看到了不同的东西。所以这是一个优点。

更新 4
我尝试明确设置每个字符之间的间距(而不仅仅是句子),但仍然没有骰子。这似乎更抵消了一切。而且看起来更丑。尽管该功能运行良好并且对图像很有用(可能分析验证码?)

这是代码,如果有人感兴趣:

/**
* Function: Returns the space between letters, and the width of letters.
*           The first index is set to 0, as that is where it starts
*           Since spaces don't register (no pixels), strlen may not work.
* $image to analyze vertical gaps (from imagecreatetruecolor)
*/
function get_letter_dimensions($image)
{
   $bg           = imagecolorallocatealpha($image, 0, 0, 0, 127);
   $height       = imagesy($image) - 1; // was causing issues without
   $width        = imagesx($image);
   $bottom       = 0;
   $right        = 0;
   $letter_space = array(0=>0); // This holds the pixels between the letters
   $letter_width = array(0=>0); // This holds the width of the letters
   $y            = 0;
   $spacing      = 0;
   $lettering    = 0;
   $data         = array();

   for ($x = 0 ; $x < $width ; $x++) 
   {
      while((imagecolorat($image, $x, $y) == $bg) && ($y < ($height))) $y++;

      if($y == $height) 
      {
         if($lettering) $letter_width[] = $lettering;
         $lettering = 0;
         $spacing++;
      }
      else
      {
         if($spacing) $letter_space[] = $spacing;
         $spacing = 0;
         $lettering++;
      }
      $y = 0;
   }

   foreach($letter_space as $k=>$val) $data[$k] = $val + $letter_width[$k];

   return $data;      
}

更新 5

名为“textLength”的强大属性对我有所帮助!我还没有让它完全工作,但这绝对是一个显着的改进。我仍然遇到一些缩放问题。我不确定它是否可以接受,但是,它非常接近。

更新 6

这是我发现 textLength 元素所做的事情:

在此处输入图像描述

所以,正如你所看到的,第二行“Line up last L”看起来非常完美,就排队而言,没有 textLength。但是,第一行“一行中的多个单词”看起来很糟糕。一切都已经过去了。然而,与 textLength 相比,它似乎合适。

更新 7

我想我解决了。我不只是执行“textLength”并将其设置为提供的长度,而是将其调整了 2px,它排列得很好。微小的差异可能仅仅是因为服务器上的像素宽度与 SVG 放置的粒度。我不知道我是否真的可以做更多的事情来改变它。我确定有,但我将回答我自己的问题并将其标记为已解决。

4

1 回答 1

1

OP,又是我,OP。我刚刚解决了我的问题,尽我所能,就是这样。

我修改了textSVG 部分的元素生成。

之前

$svg .= "fill='rgb(0,0,0)'>{$line2['text']}</text>";

修改

$line2['width'] = $line2['width'] + 2;
$svg .= "fill='#000' textLength='{$line2['width']}px'>$line2['text']}</text>";

这将是足够的。谢谢你,@feeela,你的帮助。非常感谢。

对于任何未来读到这个问题的人,我希望我的精神错乱也对你有益。

于 2012-10-25T17:47:48.530 回答