【问题标题】:GD2 Fonts locking on Windows/ApacheGD2 字体锁定在 Windows/Apache 上
【发布时间】:2011-09-12 16:00:00
【问题描述】:

我有一个使用 GD2 创建图像的 PHP 脚本。它使用 TrueTypeFont 文件在图像中生成带有imagettftext(和imagettfbbox)的文本。 这个脚本可以在 Windows 和 Linux 机器上运行,所以我决定将一个 TTF 文件从 Windows/Fonts 目录复制到源代码中,否则我不知道在哪里寻找它。 我对这个解决方案一点也不满意,但我不知道有更好的解决方案。

但真正的问题是,在 Windows/Apache 上,字体文件在使用一次后就会被锁定。解锁它的唯一方法是重新启动 Apache。锁定是个问题,因为我无法在需要时删除文件,如果您使用的是版本系统,这尤其令人讨厌。

所以我的问题有 3 个解决方案:

  • 有没有办法避免在 Windows/Apache 上锁定字体文件(在源代码/webroot 中)?
  • 或者有没有办法避免复制字体文件并使用本机可用的 TrueTypeFont? (尽可能独立于操作系统,可能的主机是 Windows 和 Linux - Mac,不是那么多)
  • 或者有没有办法避免使用 TrueTypeFont 并且仍然使用 PHP GD2 获得漂亮的(别名)文本?

--

GD Support  enabled
GD Version  bundled (2.0.34 compatible)
FreeType Support    enabled
FreeType Linkage    with freetype
FreeType Version    2.1.9
T1Lib Support   enabled
GIF Read Support    enabled
GIF Create Support  enabled
JPG Support     enabled
PNG Support     enabled
WBMP Support    enabled
XBM Support     enabled 

【问题讨论】:

  • 几年前我遇到了同样的问题,但不记得我是如何解决的。今晚我会检查我的存档硬盘,看看我是否能找到它的代码,但不记得它只是解决锁定问题还是防止它的实际解决方案。
  • 拜托了!我非常想奖励赏金而不是让它付诸东流^^
  • @Frits van Campen 恐怕我要让你失望了。在我之前的项目中没有解决方案,相反,我最终在 OS tmp 文件夹中创建了一个包含字体文件内容的副本。那时我已经对它进行了相当多的研究,除非此后发生任何变化,否则问题是 FreeType 库打开了一个字体处理程序并且它不会再次关闭它。有没有在bugs.php.net/bug.php?id=24450 中评论了这个错误链接。如果您不想使用我使用的临时文件解决方案,我建议您尝试使用其他 2 个选项。
  • 感谢您的评论。至少知道其他人花时间在上面但找不到解决方案会有所帮助(“没有解决方案”也是我问题的答案)。我将研究临时文件的解决方法。如果有人对其他 2 个选项有任何想法,请随时分享!我很想奖励这个赏金。
  • 嗯,是的,计划永远不会重启:D

标签: php apache fonts gd


【解决方案1】:

为什么不使用库定义的字体路径?

imagettftext() docs,可以使用库字体路径:

根据 PHP 使用的 GD 库版本,当 fontfile 不以前导 / 开头时,.ttf 将附加到文件名,并且库将尝试沿着 library- 搜索该文件名定义的字体路径

查看gd_info() page 以了解您的 windows php 版本正在使用哪个 ttf 库。然后检查相应的库文档 font-path 是什么。

使用字体路径中的 TTF 可能会出现问题。

【讨论】:

  • 感谢您的回复。这只是包含字体文件的一种非常晦涩的方式。是否有特定于操作系统的默认值?有没有办法检查存在哪些字体文件?我希望此脚本始终有效,即使在我对环境没有太多控制权的主机上也是如此。它目前当然是这样做的。
  • 这有点像用霰弹枪杀死蚊子,不是吗?而且我不希望依赖于 imagemagick,因为它远不如 gd2 常见。我希望只有一种“正确”的方式来处理 gd2 中的字体。
  • 好吧,我不想听起来粗鲁。请参阅您与 GD2 一起使用的库的手册以处理字体以了解有关字体路径的更多信息。
  • ^ 这就是我从手册中得到的。这对我一点帮助都没有。我不想要一种花哨的方式来包含我的字体文件。我想防止锁定字体文件。如果您知道一种方法,请提供代码示例。我愿意为此付出高昂的代价。这对我来说是一个微不足道的功能,所以我没有时间真正去检查任何环境排列的解决方案,但它对我来说很重要,足以奖励可观的赏金。我不是要求某人解决问题,我想要遇到这个问题并解决它的人的经验。
  • 您实际使用的是哪个 TTF 库?
【解决方案2】:

我的解决方案是综合建议。我检查了有关拆分赏金的 StackOverflow 政策,但这是不可能的。赏金必须授予单个综合答案。我想将我的赏金分配给所有在这里回复/评论的人,如果有其他方式请与我联系。

我对 .gdf 字体不太满意,我真的找不到任何好看的字体。它确实为我指明了imagestring 的方向,它可以在不需要任何字体文件(gdf 或 ttf)的情况下绘制文本 - 请参阅 php 文档。 “默认”字体只是一些等宽字体,不是很漂亮,但可以很好地作为后备。

为了避免 .ttf 锁定,我将尝试找到 OS 字体文件夹并从那里加载字体。这已经在 my Windows 开发机器上进行了测试。如果找不到 .ttf 文件,它将使用来自 imagestring 的本机字体回退。

我选择了一种面向对象的方法来抽象出文本是如何写入图像的。

abstract class TextWriter {

    public static function getInstance() {
        $osName = php_uname( 's' );

        if (strtoupper(substr($osName, 0, 3)) === 'WIN') {
            $path = 'C:/Windows/Fonts/';
        } else if (strtoupper(substr($osName, 0, 5)) === 'LINUX') {
            $path = '/usr/share/fonts/truetype/';
        } else if (strtoupper(substr($osName, 0, 7)) === 'FREEBSD') {
            $path = '/usr/local/lib/X11/fonts/TrueType';
        }
        if (is_dir($path) && is_file($path . "/arial.ttf")) {
            return new TTFTextWriter($path . "/arial.ttf");
        }
        return new NativeTextWriter();
    }

    abstract public function get_dimenions($string);
    abstract public function write_text($img_resource, $x, $y, $text, $color);

}

class TTFTextWriter extends TextWriter {

    private $ttf_file;
    private $fontsize = 10;

    public function __construct($ttf_file) {
        $this->ttf_file = $ttf_file;
    }

    public function get_dimenions($text) {
        $dimenions = imagettfbbox($this->fontsize, 0, $this->ttf_file, $text);
        return array($dimenions[2], abs($dimenions[5] - $dimenions[3]));
    }

    public function write_text($img_resource, $x, $y, $text, $color) {
        imagettftext($img_resource, $this->fontsize, 0, $x, $y, $color, $this->ttf_file, $text);
    }

}

class NativeTextWriter extends TextWriter {

    private $fontsize = 3;  // number between 1-5 see manual for imagestring
    private $text_width = 7;  // corresponds to $fontsize 3
    private $text_height = 15;  // corresponds to $fontsize 3

    public function get_dimenions($text) {
        return array(strlen($text) * $this->text_width, $this->text_height);
    }

    public function write_text($img_resource, $x, $y, $text, $color) {
        imagestring($img_resource, $this->fontsize, $x, $y - $this->text_height, $text, $color);
    }
}

像这样使用:

$writer = TextWriter::getInstance();

$dimenions = $writer->get_dimenions($text);
$width = $dimenions[0];
$height = $dimenions[1];
$im = imagecreatetruecolor($width, $height);
$black = imagecolorallocate($im, 1, 1, 1);

$writer->write_text($im, 0, 0, $text, $black);
header('Content-Type: image/gif');

imagegif($im);
imagedestroy($im);

【讨论】:

  • :) 没什么大不了的:只要赏金被浪费,被分成一半并给予最高投票,失去 250 到一无所有...:p 安慰是所有答案都加起来哈哈。也许可以建议接受多个答案并分割赏金。毕竟在编程中,可能有不止一种方法可以完成指定的工作,而没有两种方法优于另一种方法:p
【解决方案3】:

首先,如果这不起作用,我深表歉意。因为我只能在 WAMP 上测试它们。这个错误(根据在线研究)不影响的地方。

1)确保imagedestroy($im);

2) 获取任一平台的字体文件夹

<?php
function fontFolder() {
    $osName = php_uname( 's' );

    if (strtoupper(substr($osName, 0, 3)) === 'WIN') {
        return '/Windows/Fonts/';
    }

    if (strtoupper(substr($osName, 0, 5)) === 'LINUX') {
        return '/usr/share/fonts/truetype/';
    }

    if (strtoupper(substr($osName, 0, 7)) === 'FREEBSD') {
        //This is not tested
        return '/usr/share/fonts/truetype/';
    }
}

echo fontFolder();
?>

*请注意,此操作系统列表并不完整,您可能需要根据需要添加/修改它。

3) [不推荐] Persudo-“缓存”字体文件:并在服务器重置第一次运行后让缓存自行清除。这样,虽然字体被“锁定”,但只有缓存副本被锁定。不是您正在“玩”的实际工作文件。因此,它不会影响您的工作周期。当系统重新启动时,这些文件最终会被删除,并且它们被“清除为删除”。

编辑:请注意,在 linux 设置中,您始终可以将该文件夹指向 tmp 文件夹,应该会有所相同。

<?php
/**
Recursive delete, with a 'file/folder igonore option'
[$ignoreArra] : An array or a single string, to ignore delete (folder or file)
[$ignoreRootFolder] : Ignores the starting root folder
**/
function recursiveDelete($str, $ignoreArray = null, $ignoreRootFolder = false){
    if($str == '' || $str == '/')   {   //Prevent accidental 'worse case scenerios'
        return false;   
    }

    //Ensures it working as an array
    if($ignoreArray == null) {
        $ignoreArray = array(); //new Array
    }
    if(!is_array( $ignoreArray ) ) {
        $ignoreArray = array( $ignoreArray );
    }

    if(is_file($str)){
        if(in_array( $str, $ignoreArray ) ) {
            return false;
        } //else
        return @unlink($str);
    }
    elseif(is_dir($str)){
        $scan = glob(rtrim($str,'/').'/*');
        $chk = true;
        foreach($scan as $index=>$path) {
            $buf = recursiveDelete($path, $ignoreArray);
            if( $buf == false ) {
                $chk = false;
            }
        }

        if( in_array( $str, $ignoreArray ) || $chk == false || $ignoreRootFolder ) {
            return false;
        } else {
            return @rmdir($str);
        }
    }   else    {
        return false;
    }
}

define('fontCacheFolder', './font_cache/');
function fontCache($fontFolder, $fontFile) {
    $cachedFile = fontCacheFolder.$fontFile;
    recursiveDelete( fontCacheFolder , $cachedFile , true);
    if( is_file( $cachedFile ) ) {
        return $cachedFile;
    }
    copy( $fontFolder.$fontFile, $cachedFile);
    return $cachedFile;
}

echo fontCache('./', 'arial.ttf');
?>

4) 更新: 只需将所有字体放在一个文件夹中,并且只删除在实际/最终部署服务器之前不需要的内容。 =) 只需保留字体文件夹即可。

共享服务器结构示例。

www/root
   +----- fonts
   +----- app A
   +----- site B

因此对于嵌套在根 www 文件夹中的任何网站/应用程序,只需使用即可访问共享字体文件夹

'../fonts/fontName.ttf'

因此,每当您对应用程序/网站进行更改和更新时,都可以省去字体冲突的麻烦。

【讨论】:

  • 1) 我检查了,不过应该没关系,因为 PHP 应该在关闭时清理所有资源。
  • 2) 是的,我很害怕。这是最好的解决方案,因为我不必复制任何字体文件。我可能会这样做:/
  • 3) 我不确定你的意思。我也许可以将字体文件缓存在服务器 tmp 目录中。我无法将它缓存在 webroot 附近的任何地方,因为我无法随时清除它。
  • 4) 我不明白你的意思?我目前只有一种字体,而且总是需要。
  • @Frits van Campen:拥有一个集中字体文件夹的意义在于,您永远不需要清理它。有关详细信息,请参阅 4) 处的编辑...
【解决方案4】:

使用 GD 字体 (.gdf) 将解决文件被锁定的问题。即使在使用它们之后,您也可以随意移动/重命名/删除它们。

它们不像 .ttf 字体那么漂亮,但是通过对高斯模糊进行一些修补,您可以让它们在抗锯齿方面看起来几乎与它们的 ttf 对应物相同。带有 arial 的示例文本:

$im = imagecreatetruecolor(400, 200);

$bg = imagecolorallocate($im, 255, 255, 255);

imagefill ( $im , 0 , 0 ,$bg );
$textcolor = imagecolorallocate($im, 0, 0, 0);
imageantialias ( $im ,true );

$font = imageloadfont('arial-reg-20.gdf');

imagestring($im, $font, 10, 10, 'Hello world!', $textcolor);

imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR,10);
imagestring($im, $font, 10, 10, 'Hello world!', $textcolor);
imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR,1);
// Output the image
header('Content-type: image/png');

imagepng($im);
imagedestroy($im);

【讨论】:

  • 如何查找或创建 .gdf 文件?它并不像我希望的那样简单:(
  • 已经有很多可用的字体,例如搜索 .gdf 字体或 arial.gdf 会给你很多结果。同样,有很多不同的工具可用于将 ttf 文件转换为 gdf。
  • 是的,但 .gdf 文件可能存在大端与小端编码问题。我不知道这将在什么样的机器上运行。
  • @Frits van Campen 您在问题中提到您只需要在 Windows/Linux(而不是 Mac)上使用它,因此认为这不是问题,因为它们都在 little-endian 下。
  • 我明白了,那会更容易加载 :) 我会在星期一试试这个。
【解决方案5】:

我在 GD 上也遇到过这样的问题,但在 Debian 发行版上。我发现了两种可能导致 wamp 解决方案的解决方案:

a- 从 dotdeb.org libgd i/o 本机 php GD 库安装捆绑图形库并重新编译源代码 with-freetype 选项,

b- 安装 imagick 而不是 GD

我在 debian 发行版上成功应用了解决方案“a”。

这是我当时使用的一些链接:
imagick on wamp
howtoforge
drupal
libgd
boutell

【讨论】:

  • 感谢您的回答。遗憾的是,重新编译 PHP 对我来说不是一个选项,并且在我的环境中 with-freetype 已经启用,所以我怀疑它是否能解决我的具体问题。 ImageMagick 对我来说也不是一个选项,因为大多数主机都没有启用它。我正在寻找一个非常通用的解决方案来解决我认为有点错误的问题。
  • 您确认字体锁定也发生在 Debian 机器上吗?
  • @Frits van Campen:我很久以前就有这个问题了。我不记得这些 GD 函数的使用是否因为 GD 库本身和/或字体锁定而出现问题。但我记得我还必须在重新编译捆绑的 GD 库的基础上导入额外的 TTF 字体。祝你好运,因为我还记得这个 @%$.. 问题让我头疼的问题。
【解决方案6】:

我确实知道一个有点费力的解决方法。我建议的类似解决方案:imagettftext-and-the-euro-sign

在我的情况下,这可能会起作用,因为我真的只是在寻找一种方法来生成一些非常简单的带有文本的图像。它避免了我的问题,但当然不能解决根本问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多