In the application of yii2, we use imagine library to generate shared graph.

Keywords: QRCode PHP CentOS

This requirement is now particularly common, such as generating small program sharing graphs, generating circle of friends sharing graphs and so on, generally text + two-dimensional code + background template. Today we use imagine to accomplish this task and use it as an interview module for the website.

The layout of my shared map is as follows

The title, date and two-dimensional code of the title above need to be replaced, and other parts can be done in the background map.

Preparation stage

In order for this to happen, we need to prepare something.

  • imagine Gallery
  • Two-Dimensional Code Generation Library
  • A nice font
  • A poster (processed by PS)

imagine

imagine There are three kinds of image processing libraries (GD, Imagick and Gmagick). GD is the oldest image library, and its natural processing ability is not as good as the other two. This time I use Imagick as the underlying support. How to install the Imagick extension in PHP can be referred to. https://www.cnblogs.com/aini521521/p/8398770.html

We know that composer can be used for imagine installation, so let's install it first.

composer require imagine/imagine

After installation, you can find it in vendor/imagine/imagine of yii2. If not, please check the composer environment of the environment (try not to use mirrors, otherwise you may not be able to get the latest version).

Because the PHP environment required by imagine is 5.3+, imagine is generally okay as long as your yii2 runs.

QR code

In this sharing map, I plan to put a two-dimensional code, which contains the URL of the interview question. When sharing with friends or groups, you can access it by long-press two-dimensional code. In order to reflect only one topic for each picture, the requirements of subscription to interview questions are put on the target page, and the sharing map only does one thing.

There are mature libraries for generating two-dimensional codes in yii2—— qrcode-library It can generate two-dimensional codes of different sizes, contents and styles. It is highly recommended.

The installation of qrcode-library is also very simple, and it is still composer.

composer require 2amigos/qrcode-library

Once installed, we should find it in the vendor/2amigos/qrcode-library folder.

Find a font

In order to make the style look good, I decided to find a font and write it on the sharing map. There are too many websites downloaded by fonts on the Internet. I chose to download Microsoft Yahei and get familiar with the fonts regardless of their size.

Write title

Through the above are ready to complete, let's think about the generation logic of the shared graph, in fact, the watermarking, text watermarking, image watermarking, that's it. The size of my background template is 500*750. Look at the picture below.

Of course, we need to write and fill in the picture later. The white rectangular area in the picture I plan to prevent the content of the title, because the length of the title of the interview question is usually not long, and the height of the three lines I reserved is enough.

Now let's assume that the title is [Please use PHP to circle out this Monday to Sunday]. We need to open the background image and then do some hand and foot work to start the actual coding.

use Imagine\Imagick\Imagine;

public function actionShare($id){
    $imagine = new Imagine();
    $image = $imagine->open(Yii::getAlias("@webroot")."/images/task-bg.jpg");
    //VarDumper::dump($image,10,true);
}

You can see that it's opened correctly by dump.

Both imagine and image objects are output normally.

The next major task is to write the title.

Start writing for the first time

Writing a title is equivalent to adding a text watermarking to an image. The problems involved are as follows.

  • What is the content of the text?
  • What font to use
  • FontSize
  • Font color
  • Which coordinate to start with

Note: Imagee and Cartesian coordinate systems are different, see details. File

First of all, the font library, although Microsoft Yahei is the standard of win series, but there is not necessarily a server. I use centos system, need to download and specify. In the previous part, we have downloaded the font. Now I put it under the fonts folder of yii2 application, so that it can be accessed through the following code.

Yii::getAlias('@app')."/fonts/yahei.ttf";

Unless your page needs this font, it is not recommended to place the font in the web directory, which can effectively prevent other people from accessing it through the browser.

Next, let's make a preliminary implementation, followed by the above code.

use Imagine\Imagick\Imagine;
use Imagine\Image\Palette\RGB;
use Imagine\Image\Point;

public function actionShare($id){
    $imagine = new Imagine();
    $image = $imagine->open(Yii::getAlias("@webroot")."/images/task-bg.jpg");
    
    $palette = new RGB();
    $color = $palette->color("000000");

    $point = new Point(0,0);

    $font = $imagine->font(Yii::getAlias('@app')."/fonts/yahei.ttf",12,$color);

    $image->draw()->text("Please use PHP Cycle Monday to Sunday",$font,$point);
    $image->show('jpg'); 
}

Before explaining the above code, let's look at the results.

We got the results we wanted, and then we talked about logic.

If a text watermarking is added to an image, it belongs to Rendering function To call the text method of draw, let's first look at the declaration of this method.

text(string $string, AbstractFont $font, PointInterface $position, int $angle = 0, int $width = null)

This is the $image - > draw () - > text (); part of your code just now.

It has several important parameters, such as text content, font, starting coordinates, content radian, etc.

So what we do together is to prepare parameter values, content, font, coordinates for this function.

use Imagine\Image\Palette\RGB;
use Imagine\Image\Point;

These classes are prepared for the final call to the text method.

For example, we've heard RGB classes define color classes

$palette = new RGB();
$color = $palette->color("000000");

For example, we need font method to get font object

$font = $imagine->font(Yii::getAlias('@app')."/fonts/yahei.ttf",12,$color);

For example, we use Point to define coordinate objects.

$point = new Point(0,0);

Okay, although we have realized the title writing up to now, but the location and size are not ideal, and then optimize.

  • font size
  • Coordinate position

It used to be 12. Now we set it to 24 and change the coordinates from [0, 0] to [44, 230].

$point = new Point(44,230);
$font = $imagine->font(Yii::getAlias('@app')."/fonts/yahei.ttf",24,$color);

Look at the effect

I'm glad that the size and position have been adjusted so far, but a new problem has arisen. My title content has exceeded one line. The other parts have not been changed but have been cut off. What should I do???

Processing of Content Beyond Boundary Problem

How to deal with this problem? In imagine 1.0.0 +, an automatic Line-Changing function has been provided. You just need to upgrade the version. The usage is very simple, as follows.

$font = $imagine->font(Yii::getAlias('@app')."/fonts/yahei.ttf",24,$color);
$text = $font->wrapText("Please use PHP Cycle Monday to Sunday",400);

The second parameter of the wrapText method represents the maximum length of the text, beyond which the wrapText is a newline. Then the wrapText is passed to image - > draw () - > text (); that's all.

However, this article is not applicable, wrapText support for Chinese is not good, so we need to think of other ways, however, we still need to look at the implementation principle of wrapText.

// vendor/imagine/imagine/src/Image/FontInterface.php:59
public function wrapText($string, $maxWidth, $angle = 0){
    $words = explode(' ', $string);
    foreach ($words as $word) {
        if ($currentLine === null) {
            $currentLine = $word;
        } else {
            $testLine = $currentLine . ' ' . $word;
            $testbox = $this->box($testLine, $angle);
            if ($testbox->getWidth() <= $maxWidth) {
                $currentLine = $testLine;
            } else {
                $lines[] = $currentLine;
                $currentLine = $word;
            }
        }
    }
    .....
    return implode("\n", $lines);    
}

You see that wrapText is implemented by dividing words into spaces, so this algorithm is only applicable to English. Finally, it calculates the width and achieves the final effect by newline characters.

** What about Chinese? It doesn't matter. From the official wrapText method, we can transform the method of son, but each word is no longer a blank. I have made the following modifications.

$lines = array();
$maxWidth = 420;
$currentLine = null;
$text = "Please use PHP Cycle Monday to Sunday";
for($i = 0;$i < mb_strlen($text,'UTF-8');$i++){
    $word = mb_substr($text,$i,1,'UTF-8');
    if ($currentLine === null) {
        $currentLine = $word;
    } else {
        $testLine = $currentLine.$word;
        $testbox = $font->box($testLine, 0);
        if ($testbox->getWidth() <= $maxWidth) {
            $currentLine = $testLine;
        } else {
            $lines[] = $currentLine;
            $currentLine = $word;
        }
    }
}
if ($currentLine !== null) {
    $lines[] = $currentLine;
}

$text = implode("\n", $lines);

The idea is to first get the length of the entire string N, then traverse from 0 to N-1 to get each word (Chinese and English), then put these words into a test line, and get the width of the test line through the method of $font - > box. If the width exceeds our maximum width, then reset the test line, time after time, and finally the lines array is each line, and they do not exceed it. Boundary maxWidth.

Finally, use the newline character to patch the lines array back into the string, ok, to see the effect.

Better row spacing

We just solved the overflow problem, but now the spacing of each line is too small, which greatly affects the experience. In this section, we will make a suitable line spacing, but you know when we draw the text into the picture, it is impossible to set the line margin.

All this needs to be studied from the coordinates.

So I plan to cancel the code above to reassemble the lines array into strings, save each line, and then specify the specific Y coordinates of each line.

$height = $font->box($model->title)->getHeight();// Get the height of the word. $model - > title is the output
$lines = array();
$currentLine = null;
for($i = 0;$i < mb_strlen($model->title,'UTF-8');$i++){
    $word = mb_substr($model->title,$i,1,'UTF-8');
    if ($currentLine === null) {
        $currentLine = $word;
    } else {
        $testLine = $currentLine.$word;
        $testbox = $font->box($testLine, 0);
        if ($testbox->getWidth() <= 420) {
            $currentLine = $testLine;
        } else {
            $lines[] = $currentLine;
            $currentLine = $word;
        }
    }
}
if ($currentLine !== null) {
    $lines[] = $currentLine;
}

// Get the lines array
foreach($lines as $key=>$value){
    $point = new Point(40,($key == 0 ? 230 : (230 + ($height + 10)*$key)));
    $image->draw()->text($value,$font,$point,0);
}

$image->show('jpg');

I left 10 px between rows to see the effect.

Writing time (coordinate automatic calculation)

It's not complicated to write time by the above method, but I decided to change the way of thinking. We don't want to specify coordinates directly, but by calculating. This method is very suitable for the situation where some words are in the middle.

Written content is very simple 2018-09-28, is a date.

Continue to expand the above code, mainly calculating X coordinates. Y coordinates 400.

use Imagine\Image\Point\Center;

$dateText = date('Y-m-d',$model->publish_date);
$dateFont = $imagine->font(Yii::getAlias('@app')."/fonts/yahei.ttf",12,$color);
$dateBox = $dateFont->box($dateText);
$dateCenterPosition = new Center($dateBox);
$image->draw()->text($dateText,$dateFont,new Point(($image->getSize()->getWidth()/2 - $dateCenterPosition->getX()),420));

The reason for this is to familiarize you with the Center class. We can put a font box in the Center and get the coordinates of the middle point.

Write two-dimensional code

It's getting closer and closer to success. Next we write two-dimensional code, which is actually two steps.

  • Generating two-dimensional codes
  • Write two-dimensional code to share graph

Write two-dimensional code

Use qrcode-library It's very simple to generate two-dimensional codes. We'd better paste the codes first.

use Da\QrCode\QrCode;

$qrCode = (new QrCode(Yii::$app->urlManager->createAbsoluteUrl(['/task/detail','id'=>$id])))->setSize(180)->setMargin(10);
$path = Yii::getAlias('@webroot').'/uploads/tmp/'.Yii::$app->security->generateRandomString().'.jpg';
$qrCode->writeFile($path);

Reasonably, it's a little flawed. The two-dimensional code generated by QrCode is relatively simple, but only supports the generation of Uri, server files and streams, but it can't return resources. So we must save it and use imagine to read it, otherwise we can use the read method of imagine directly.

All in all, the code above generates two-dimensional code by passing in the URL address for the QrCode object, and uses writeFile to store it on the server.

## Write two-dimensional code to share graph

The paste method of imagine library is needed to write two-dimensional codes into shared graphs, which is also the method of image watermarking.

$water = $imagine->open($path);
$image->paste($water,new Point(150,520));

Read a file through the open method and return it to the image object, paste it onto the $image image image.

Finally, we see the desired effect.

Summary

There are too many details to share, all of which need to be optimized. I hope this article can serve as a reference for you. If you have a good library, you are welcome to leave a message.

Posted by brewmiser on Fri, 17 May 2019 20:10:56 -0700