文本没有在SVG中排队

时间:2012-10-24 19:43:34

标签: php svg gd kerning

问题

如何将PHP(5.2.17)中GD库的字距调整与SVG(1.1)进行适当匹配?

问题

PNG和SVG文件文本不相同。科宁似乎已经关闭了。

情况

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

目前,两个实现都在“工作”,就像创建所有内容一样。 SVG文件正常工作,PNG正常工作。我遇到的问题是文本没有排列。字距似乎是关闭的。

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

我想要什么

我想要“行”中的“l”,与第二行的最后一个“L”对齐。 Visualization of what is wrong

我尝试过什么

  • 我确保我对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虚拟机,测试虚拟机上的映像以及虚拟机中浏览器的图像。

更新2

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

更新3

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

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

以下是代码,如果有人感兴趣的话:

/**
* 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元素的作用:

enter image description here

所以,正如你所看到的,第二行“Line up last L”看起来非常完美,就排队而言,没有textLength。然而,第一行“一行中的多个单词”看起来很糟糕。一切都是好的。但是,使用textLength,它似​​乎是很多更接近。

更新7

我想我解决了。而不仅仅是做“textLength”并将其设置为所提供的长度,我将其调整为2px,并且排列得相当好。微小的差异可能仅仅是因为服务器上的像素宽度与SVG放置的粒度有关。我不知道我能做些什么来改变它。我确定有,但我会回答我自己的问题并将其标记为已解决。

1 个答案:

答案 0 :(得分:1)

OP,这是我,OP。我尽可能地解决了我的问题,就是这样。

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

以前

$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的帮助。非常感谢。

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