質問
PHP(5.2.17)のGDライブラリからのカーニングをSVG(1.1)と適切に一致させるにはどうすればよいですか?
問題
PNGとSVGファイルのテキストは同一ではありません。カーニングはオフのようです。
状況
いくつかのテキストと位置データからSVGファイルと画像ファイル(PNG)を作成するWebアプリケーションがあります。
現在、すべてが作成されている限り、両方の実装が「機能」します。SVGファイルは適切に機能し、PNGは適切に機能します。私が抱えている問題は、テキストが整列していないことです。カーニングがオフになっているようです。
PNGがベースライン(複製しようとしているもの)になります。
私が欲しいもの
「行」の「l」を2行目の最後の「L」に合わせたい。
私が試したこと
- SVGファイルとPNGファイルの両方に同じフォントを使用していることを確認しました。
- 同じフォントであることを確認するために、base64でSVGを使用してフォントをエンコードしました
- 私は山羊を犠牲にしました。
追加
- PHPは5.3にブーストされます。私が試したすべてのPHP5.3サーバーで、同じ出力が得られます。
- SVGバージョンも変更できます。
- SVGはグループ化とviewBoxを使用できます。私はまだそれらをしっかりと把握していません。
デモ
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
(文だけでなく)各文字の間隔を明示的に設定しようとしましたが、それでもサイコロはありません。これはすべてをさらに相殺するようでした。そして醜く見えた。この機能はうまく機能し、画像には役立ちますが(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要素が実行していることがわかりました。
したがって、ご覧のとおり、2行目の「最後のLを並べる」は、textLengthを使用せずに、並べる限り、ほぼ完璧に見えます。ただし、最初の行「1行に複数の単語」はひどいように見えます。すべてが途方に暮れています。ただし、textLengthを使用すると、はるかに適合しているように見えます。
アップデート7
私はそれを解決したと思います。「textLength」を実行して指定された長さに設定する代わりに、2px調整すると、かなりうまく整列します。わずかな違いは、サーバー上のピクセル幅とSVG配置の粒度が原因である可能性があります。それを変えるために私にできることが本当に他にあるかどうかはわかりません。確かにありますが、私は自分の質問に答えて、それを解決済みとしてマークします。