Windows/Apache 上的 GD2 字体锁定

发布于 2024-11-14 22:11:19 字数 960 浏览 6 评论 0原文

我有一个使用 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 

I have a PHP script that uses GD2 to create an image. It uses a TrueTypeFont file to generate text in the image with imagettftext (and imagettfbbox).
This script can be run on both Windows and Linux machines so I decided to copy a TTF file from the Windows/Fonts directory into the source-code, else I wouldn't know where to look for it.
I'm not at all happy with this solution but I don't know of a better one.

The real problem though is that on Windows/Apache the font file gets locked after it's been used once. The only way to unlock it is to restart Apache. The locking is a problem because I can't delete the file when I want to, which is especially annoying if you're using a version system.

So my problem has 3 solutions:

  • Is there a way to avoid locking of font files (in source code/webroot) on Windows/Apache?
  • Or is there a way to avoid copying the font file and use a native available TrueTypeFont? (OS independent if at all possible, likely hosts are Windows and Linux - Mac, not so much)
  • Or is there a way to avoid using a TrueTypeFont and still get pretty (aliased) text with 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 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

心安伴我暖 2024-11-21 22:11:19

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

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

b - 安装 imagick 而不是 GD

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

以下是我当时使用的一些链接:
wamp 上的 imagick< br>
howtoforge
drupal
libgd
boutell

I also faced such an issue with GD but on a Debian distro. I found two solutions that might lead to wamp solutions as well :

a- install the bundle graphic library from dotdeb.org libgd i/o native php GD library and recompile the sources the with-freetype option,

or

b- install imagick instead of GD

I applied solution "a" successfully on a debian distro.

Here are some links I used at that time :
imagick on wamp
howtoforge
drupal
libgd
boutell

小梨窩很甜 2024-11-21 22:11:19

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

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

为了避免 .ttf 锁定,我将尝试找到操作系统字体文件夹并从那里加载字体。这已经在我的 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);

My solution is a combination of suggestions. I checked StackOverflow policy on splitting bounties and it's not possible. The bounty must be awarded to a single comprehensive answer. I would like to split my bounty to all the people who replied / commented here, if there is an alternative way please contact me.

I didn't have much luck with the .gdf fonts, I can't really find any good looking fonts. It did point me in the direction of imagestring which can draw text without the need of any font file (gdf or ttf) - see the php doc. The 'default' font is just some monospace font which isn't very pretty but it serves nicely as a fallback.

To avoid the .ttf locking I will attempt to find the OS font folder and load the font from there. This has been tested on my Windows development machine. If the .ttf file can't be found it will use the native font fallback from imagestring.

I've chosen an Object-Oriented approach to make an abstraction on how the text is written to the image.

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);
    }
}

Use like:

$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);
并安 2024-11-21 22:11:19

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

imagettftext() 文档 中,您可以使用库字体路径:

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

请参阅 gd_info() 页面了解哪个 ttf 库您正在使用的 Windows php 版本。然后检查相应的库文档字体路径是什么。

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

Why not using the library-defined font path?

From the imagettftext() docs, you can use the library font path:

Depending on which version of the GD library PHP is using, when fontfile does not begin with a leading / then .ttf will be appended to the filename and the library will attempt to search for that filename along a library-defined font path.

See the gd_info() page to find out which ttf library your windows php version is using. Then check the according library documentation what the font-path is.

Using TTFs from the fontpath might have the problem.

不爱素颜 2024-11-21 22:11:19

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

就我而言,这可能有效,因为我实际上只是在寻找一种方法来生成一些带有文本的非常简单的图像。它避免了我的问题,但当然没有解决根本问题。

I do know a workaround that is a bit laborious. A similar solution as I suggested: imagettftext-and-the-euro-sign.

In my case this could work as I'm really just looking for a way to generate some very simple images with text. It averts my problem but of course doesn't fix the underlying issue.

ゝ杯具 2024-11-21 22:11:19

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

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

在此处输入图像描述

$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);

Using GD fonts (.gdf) will solve the issue with the files getting locked. You can move/rename/delete them as you wish even after using them.

They aren't quite as pretty as .ttf fonts, but with a bit of tinkering with gaussian blur you can get them to look almost identical to their ttf counterparts in terms of anti aliasing. Sample text with arial:

enter image description here

$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);
浊酒尽余欢 2024-11-21 22:11:19

首先,如果这不起作用,我深表歉意。因为我只能在 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'

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

Firstly, my apologies if this does not work. For i could only test them on WAMP. Where this bug (according to online research) does not affect.

1) Make sure to imagedestroy($im);

2) To get the font folders for either platforms

<?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();
?>

*Note this list of OS is not fully extensive, you may need to add / modify it for your needs.

3) [NOT RECOMMENDED] Persudo-"Cache" the font files : And leave the cache to clear on its own after the first run on server reset. That way, while the fonts gets 'locked' it is only the cache copy that gets locked. Not the actual working files you are 'playing' with. Hence it does not affect your work cycle. The files does get deleted eventually, when the system is rebooted, and they are 'cleared to delete'.

EDIT: Note that you can always point the folder to a tmp folder in a linux setup, should work somewhat the same.

<?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) UPDATED: Just leave all your fonts in one folder, and only do deletes of what is not needed before the actual / final deployment of your server. =) Just leave the font folder alone.

Example shared server structure.

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

Hence for any website / application nested in the root www folder, to access the shared font folder just use

'../fonts/fontName.ttf'

Hence, whenever you make changes and updates to the app / site, you save the trouble of conflicts in fonts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文