草庐IT

php - 用于在 php 中转换为图像的开箱即用的文本

coder 2023-12-30 原文

我正在尝试将文本转换为图像。我已经这样做了,但在某些情况下,文本会超出图像框

单词“The”的“e”被切掉了。我曾尝试减小字体大小或增加图像宽度,但在某些情况下,这种情况会再次发生在另一个文本上。这是代码:

    $new_line_position = 61;        
    $angle = 0;        
    $left = 20;
    $top = 45;
    $image_width = 1210;
    $image_line_height = 45;                

    $content_input = wordwrap($content_input,    $new_line_position, "\n", true);  

    $lineas = preg_split('/\\n/', $content_input);
    $lines_breaks = count($lineas); 
    $image_height = $image_line_height * $lines_breaks;
    $im = imagecreatetruecolor($image_width, $image_height);

    // Create some colors
    $white = imagecolorallocate($im, 255, 255, 255);        
    $black = imagecolorallocate($im, 0, 0, 0);
    imagefilledrectangle($im, 0, 0, $image_width, $image_height, $white);   

   $font_ttf =  public_path().'/fonts/'.$ttf_font;                     


    foreach($lineas as $linea){
        imagettftext($im, $font_size, $angle, $left, $top, $black, $font_ttf, $linea);        
        $top = $top + $image_line_height;
    }

    // Add the text                        
    imagepng($im);                
    imagedestroy($im);

谢谢。

最佳答案

问题是每个字符的宽度可能略有不同,例如 Wi。由于您无法按每行的字母数拆分字符串,因此您需要一种更准确的方法。

主要技巧是使用

imagettfbbox

它给出了使用 TrueType 字体的文本的边界框,由此,您可以获得文本将使用的实际宽度。

这是在 http://php.net/manual/en/function.wordwrap.php 找到的像素完美分割函数 使用它代替 wordwrap 并传递额外的值,如图像宽度、字体大小和字体路径

<?php

/**
 * Wraps a string to a given number of pixels.
 * 
 * This function operates in a similar fashion as PHP's native wordwrap function; however,
 * it calculates wrapping based on font and point-size, rather than character count. This
 * can generate more even wrapping for sentences with a consider number of thin characters.
 * 
 * @static $mult;
 * @param string $text - Input string.
 * @param float $width - Width, in pixels, of the text's wrapping area.
 * @param float $size - Size of the font, expressed in pixels.
 * @param string $font - Path to the typeface to measure the text with.
 * @return string The original string with line-breaks manually inserted at detected wrapping points.
 */
function pixel_word_wrap($text, $width, $size, $font)
{

    #    Passed a blank value? Bail early.
    if (!$text)
        return $text;

    #    Check if imagettfbbox is expecting font-size to be declared in points or pixels.
    static $mult;
    $mult = $mult ?: version_compare(GD_VERSION, '2.0', '>=') ? .75 : 1;

    #    Text already fits the designated space without wrapping.
    $box = imagettfbbox($size * $mult, 0, $font, $text);
    if ($box[2] - $box[0] / $mult < $width)
        return $text;

    #    Start measuring each line of our input and inject line-breaks when overflow's detected.
    $output = '';
    $length = 0;

    $words      = preg_split('/\b(?=\S)|(?=\s)/', $text);
    $word_count = count($words);
    for ($i = 0; $i < $word_count; ++$i) {

        #    Newline
        if (PHP_EOL === $words[$i])
            $length = 0;

        #    Strip any leading tabs.
        if (!$length)
            $words[$i] = preg_replace('/^\t+/', '', $words[$i]);

        $box = imagettfbbox($size * $mult, 0, $font, $words[$i]);
        $m   = $box[2] - $box[0] / $mult;

        #    This is one honkin' long word, so try to hyphenate it.
        if (($diff = $width - $m) <= 0) {
            $diff = abs($diff);

            #    Figure out which end of the word to start measuring from. Saves a few extra cycles in an already heavy-duty function.
            if ($diff - $width <= 0)
                for ($s = strlen($words[$i]); $s; --$s) {
                    $box = imagettfbbox($size * $mult, 0, $font, substr($words[$i], 0, $s) . '-');
                    if ($width > ($box[2] - $box[0] / $mult) + $size) {
                        $breakpoint = $s;
                        break;
                    }
                }

            else {
                $word_length = strlen($words[$i]);
                for ($s = 0; $s < $word_length; ++$s) {
                    $box = imagettfbbox($size * $mult, 0, $font, substr($words[$i], 0, $s + 1) . '-');
                    if ($width < ($box[2] - $box[0] / $mult) + $size) {
                        $breakpoint = $s;
                        break;
                    }
                }
            }

            if ($breakpoint) {
                $w_l = substr($words[$i], 0, $s + 1) . '-';
                $w_r = substr($words[$i], $s + 1);

                $words[$i] = $w_l;
                array_splice($words, $i + 1, 0, $w_r);
                ++$word_count;
                $box = imagettfbbox($size * $mult, 0, $font, $w_l);
                $m   = $box[2] - $box[0] / $mult;
            }
        }

        #    If there's no more room on the current line to fit the next word, start a new line.
        if ($length > 0 && $length + $m >= $width) {
            $output .= PHP_EOL;
            $length = 0;

            #    If the current word is just a space, don't bother. Skip (saves a weird-looking gap in the text).
            if (' ' === $words[$i])
                continue;
        }

        #    Write another word and increase the total length of the current line.
        $output .= $words[$i];
        $length += $m;
    }

    return $output;
}
;

?>

下面是工作代码示例: 我修改了这个函数 pixel_word_wrap 一点点。另外,修改了代码中的一些计算。现在正在为我提供正确计算边距的完美图像。我对代码不太满意,注意到有一个 $adjustment 变量,当您使用更大的字体大小时,它应该更大。我认为这归因于 imagettfbbox 功能的不完善。但这是一种适用于大多数字体大小的实用方法。

<?php

$angle = 0;
$left_margin = 20;
$top_margin = 20;
$image_width = 1210;
$image_line_height = 42;
$font_size = 32;
$top = $font_size + $top_margin;

$font_ttf = './OpenSans-Regular.ttf';

$text = 'After reading Mr. Gatti`s interview I finally know what bothers me so much about his #elenaFerrante`s unamsking. The whole thing is about him, not the author, not the books, just himself and his delusion of dealing with some sort of unnamed corruption';$adjustment=  $font_size *2; //

$adjustment=  $font_size *2; // I think because imagettfbbox is buggy adding extra adjustment value for text width calculations,

function pixel_word_wrap($text, $width, $size, $font) {

  #    Passed a blank value? Bail early.
  if (!$text) {
    return $text;
  }


  $mult = 1;
  #    Text already fits the designated space without wrapping.
  $box = imagettfbbox($size * $mult, 0, $font, $text);

  $g = $box[2] - $box[0] / $mult < $width;

  if ($g) {
    return $text;
  }

  #    Start measuring each line of our input and inject line-breaks when overflow's detected.
  $output = '';
  $length = 0;

  $words = preg_split('/\b(?=\S)|(?=\s)/', $text);
  $word_count = count($words);
  for ($i = 0; $i < $word_count; ++$i) {

    #    Newline
    if (PHP_EOL === $words[$i]) {
      $length = 0;
    }

    #    Strip any leading tabs.
    if (!$length) {
      $words[$i] = preg_replace('/^\t+/', '', $words[$i]);
    }

    $box = imagettfbbox($size * $mult, 0, $font, $words[$i]);
    $m = $box[2] - $box[0] / $mult;

    #    This is one honkin' long word, so try to hyphenate it.
    if (($diff = $width - $m) <= 0) {
      $diff = abs($diff);

      #    Figure out which end of the word to start measuring from. Saves a few extra cycles in an already heavy-duty function.
      if ($diff - $width <= 0) {
        for ($s = strlen($words[$i]); $s; --$s) {
          $box = imagettfbbox($size * $mult, 0, $font,
            substr($words[$i], 0, $s) . '-');
          if ($width > ($box[2] - $box[0] / $mult) + $size) {
            $breakpoint = $s;
            break;
          }
        }
      }

      else {
        $word_length = strlen($words[$i]);
        for ($s = 0; $s < $word_length; ++$s) {
          $box = imagettfbbox($size * $mult, 0, $font,
            substr($words[$i], 0, $s + 1) . '-');
          if ($width < ($box[2] - $box[0] / $mult) + $size) {
            $breakpoint = $s;
            break;
          }
        }
      }

      if ($breakpoint) {
        $w_l = substr($words[$i], 0, $s + 1) . '-';
        $w_r = substr($words[$i], $s + 1);

        $words[$i] = $w_l;
        array_splice($words, $i + 1, 0, $w_r);
        ++$word_count;
        $box = imagettfbbox($size * $mult, 0, $font, $w_l);
        $m = $box[2] - $box[0] / $mult;
      }
    }

    #    If there's no more room on the current line to fit the next word, start a new line.
    if ($length > 0 && $length + $m >= $width) {
      $output .= PHP_EOL;
      $length = 0;

      #    If the current word is just a space, don't bother. Skip (saves a weird-looking gap in the text).
      if (' ' === $words[$i]) {
        continue;
      }
    }

    #    Write another word and increase the total length of the current line.
    $output .= $words[$i];
    $length += $m;
  }

  return $output;
}


$out = pixel_word_wrap($text, $image_width -$left_margin-$adjustment,
  $font_size, $font_ttf);


$lineas = preg_split('/\\n/', $out);
$lines_breaks = count($lineas);
$image_height = $image_line_height * $lines_breaks;
$im = imagecreatetruecolor($image_width, $image_height + $top);

// Create some colors
$white = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0, 0, 0);
imagefilledrectangle($im, 0, 0, $image_width, $image_height + $top, $white);


foreach ($lineas as $linea) {
  imagettftext($im, $font_size, $angle, $left_margin, $top, $black, $font_ttf,
    $linea);
  $top = $top + $image_line_height;
}


header('Content-Type: image/png');
imagepng($im);

举个例子

您也可以使用等宽字体。等宽字体是一种字体,其字母和字符各自占据相同数量的水平空间。

关于php - 用于在 php 中转换为图像的开箱即用的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39901581/

有关php - 用于在 php 中转换为图像的开箱即用的文本的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

  4. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  5. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  6. ruby - inverse_of 是否适用于 has_many? - 2

    当我使用has_one时,它​​工作得很好,但在has_many上却不行。在这里您可以看到object_id不同,因为它运行了另一个SQL来再次获取它。ruby-1.9.2-p290:001>e=Employee.create(name:'rafael',active:false)ruby-1.9.2-p290:002>b=Badge.create(number:1,employee:e)ruby-1.9.2-p290:003>a=Address.create(street:"123MarketSt",city:"SanDiego",employee:e)ruby-1.9.2-p290

  7. ruby-on-rails - 在 Ruby (on Rails) 中使用 imgur API 获取图像 - 2

    我正在尝试使用Ruby2.0.0和Rails4.0.0提供的API从imgur中提取图像。我已尝试按照Ruby2.0.0文档中列出的各种方式构建http请求,但均无济于事。代码如下:require'net/http'require'net/https'defimgurheaders={"Authorization"=>"Client-ID"+my_client_id}path="/3/gallery/image/#{img_id}.json"uri=URI("https://api.imgur.com"+path)request,data=Net::HTTP::Get.new(path

  8. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  9. ruby - 是否有将图像文件转换为 ASCII 艺术的命令行程序或库? - 2

    有这样的事吗?我想在Ruby程序中使用它。 最佳答案 试试这个http://csl.sublevel3.org/jp2a/此外,Imagemagick可能还有一些东西 关于ruby-是否有将图像文件转换为ASCII艺术的命令行程序或库?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6510445/

  10. ruby - "undefined method"用于 rails 模型 - 2

    我正在使用带有Rails的Devise,我想添加一个方法“getAllComments”,所以我这样写:classUser在我的Controller中:defdashboard@user=current_user@comments=@user.getAllComments();end当我访问我的url时,我得到了undefinedmethod`getAllComments'for#我做错了什么?谢谢 最佳答案 因为getAllComments是一个类方法,而您正试图将其作为实例方法访问。您要么需要访问它:User.getAllCom

随机推荐