Author: Sailing and Xia Ge

Project background: Rust and WebAssembly were used to create a quick poster generation tool for Rust China Conf, online experience.

In addition, 👉 to deploy the TensorFlow AI reasoning function with Serverless, you can obtain Rust enamel cups, Serverless masks and stickers. Please join us.


Enjoy! 🦀

When holding various technical conferences, the organizer will make invitation posters with personal names for the participants to show the importance of the participants, but also hope to achieve the maximum range of dissemination.

The general workflow of an invitation poster is:

  • Design to determine the poster style;
  • Operation provides design names;
  • Design according to the name of one output poster;
  • The operator handed out posters to participants one by one;

If we only had double-digit participants, we would have no problem working with this workflow. However, when we were working on a 300-person Rust China Conf, this workflow was too much work and required more human support. In addition, even though developers in the Rust community were not able to attend the conference, they still wanted to forward the Rust Conference poster and Call for the Rust China Conf.

In this case, counting people’s names, asking design volunteers to export the posters one by one and send them to the designated people was almost impossible for the Rust China Conf organizing committee, which was in single digits.

Is not to change the parameters? 🤔

So what do you do? In Rust China Conf, the first thing that comes to mind is to use Rust. Of course, Rust’s collection of WebAssembly lends itself well to computationally intensive functions such as image processing.

When our startup (Second State) was founded, we firmly believed that Rust and Wasm as serverless was the future of cloud computing. So it’s natural to think of the Watermark demo we wrote in Rust. Why not turn the watermark into a name/nickname so that developers can type in their own name on the web and then generate a dedicated invitation poster. The advantage of using Serverless is that you only have to pay for the resources used and the costs Cover.

Using the example given by Second State Functions, the solution is

  • Find a common Chinese and English font, without any copyright issues
  • Recalculate the watermark position on the poster and center it
  • Color and size of the watermark
#[wasm_bindgen] pub fn watermark (img_buf: &[u8]) -> Vec<u8> {let mut img = image::load_from_memory(img_buf).unwrap(); let (w,h) = img.dimensions(); Let scale = scale {x: w as f32/10.0, y: h as f32/10.0,}; Let font = Vec::from(include_bytes! ("DejaVuSans.ttf") as &[u8]); let font = Font::try_from_vec(font).unwrap(); // Draw watermark drawing::draw_text_mut(&mut img, image::Rgba([255U8, 255U8, 255U8, 255U8]), 0+(h/10),h/2, scale, &font, "Hello Second State"); Let mut buf = vec! []; img.write_to(&mut buf, image::ImageOutputFormat::Png).unwrap(); return buf; }Copy the code

let font = Vec::from(include_bytes! (“DejaVuSans.ttf”) as &[u8]); Replace the font here with PingFang bold.ttf.

PingFang bold.ttf is in Both English and Chinese and is a perfect choice for Rust China Conf’s main KV font.

Rgba([255U8, 255U8, 255U8, 255U8]), 0+(H /10), H /2, here adjust the RGB value and position of the color, RGB value is the color of the watermark. White is selected according to the background of the poster. The next step is to adjust the font position by adjusting 0+(h/10),h/2.

After several debugging, we finally found a basically middle position. There are a few minor flaws, but they look good and ready to use. The effect is shown below:

Change parameters is also to skill 🧐

But soon there was another problem, the length of the Chinese and English are not the same. If the name is Second State Functions, the English characters are too long and the text is not centered, or even outside the safe area of the image.

😭 😭 😭

Simply adjusting position parameters does not solve this problem. Especially now is a diversified environment, we have a variety of nicknames, it is impossible to judge the size of the input text, also can not judge whether the input is Chinese, or mixed Chinese and English.

We need to write a little more Rust to make sure that the input text, long and short, in Both English and Chinese, is centered so that we can generate custom posters for anyone.

The previous code shows you how to add text to an image and output a new image. Then we know the size of the text to the picture, can realize the text center display.

So how do you get this size?

The idea is that once you type the text, the function will first draw it on a plain white image at the base size, and then automatically resize the text based on how it looks in the image to keep the text always centered and at the right size.

const MAX_WIDTH : u32 = 349; const MAX_HEIGHT : u32 = 80; Const MAX_FONT_SIZE: f32 = 125.0; const FAR_LEFT : u32 = 200; const FAR_TOP : u32 = 590;Copy the code

In lib.rs, the width and height of the input text, the base size, and the position of the upper left corner of the text are determined, which can be interpreted as a security zone.

After we enter the Second State Functions, the write_to_crop function of the watermark function in lib.rs will first set the watermark size to MAX_FONT_SIZE. Draw it on crop. PNG. PNG is the pure white image we mentioned above.

pub fn watermark (watermark_text: &str) -> Vec<u8> {
    let width = write_to_crop(watermark_text);

    let mut left = FAR_LEFT;
    let mut top = FAR_TOP;
    let mut font_size = MAX_FONT_SIZE;
Copy the code

Next, the structure object defined in Imagecrop.rs calculates the width occupied by black text on a white background, which is the baseline width for the watermark to the image.

If the position of the Second State Functions is clearly too wide, adjust the size,

The watermark function in lib.rs calculates the location and font size to center the text based on the baseline width calculated in imagecrop and the maximum size and relative position of text allowed on the poster.

if width < MAX_WIDTH { left = FAR_LEFT + (MAX_WIDTH - width) / 2; } else { font_size = (MAX_WIDTH as f32) / (width as f32) * MAX_FONT_SIZE; Top = FAR_TOP + ((1.0 - (MAX_WIDTH as f32)/(width as f32)) * (MAX_HEIGHT as f32)) as u32; }Copy the code

Once you figure out the size and where to center the text, the rest of the work is exactly the same as adding text to the image mentioned earlier.

Once the Rust program is complete, you can compile it into a WASM file and deploy it to Second State Functions for external use. This step implements the service through Ajax. This way, anyone who gets a link can type in their name and create a custom personal Call poster.

Generate your own posters, free your hands 👐

Start by forking the Github repo, and you can make changes at will.

Fonts are in TTF format. Otf fonts didn’t work when I tested them. If anyone knows why, please point it out. Change font in lib.rs.

Template. PNG is a template poster that you can replace with your own. If your poster is the same width as ours and the name is in the same position, you don’t need to adjust any parameters.

If your poster does not match our template, you will need to modify the following parameters in lib.rs:

Const MAX_WIDTH: u32 = 349; Const MAX_HEIGHT: u32 = 80; // Max size const MAX_FONT_SIZE: f32 = 125.0; Const FAR_LEFT: u32 = 200; // The offset of the text safety area relative to the upper left corner of the image const FAR_LEFT: u32 = 200; const FAR_TOP : u32 = 590;Copy the code

After changing the parameters, test the best location by following the Readme deployment function on Github.

For testing purposes, we added the main.rs file to the SRC folder.

Finally use Ajax deployment into a web page, so that you can easily generate the poster application to share with relevant people to use, free hands!

$.ajax({ url: "https://rpc.ssvm.secondstate.io:8081/api/run/149/watermark/bytes", type: "post", data : $('#input')[0].files[0], contentType: "application/octet-stream", processData: false, xhrFields:{ responseType: 'blob' }, success: function (data) { const img_url = URL.createObjectURL(data); $('#wm_img').prop('src', img_url); }});Copy the code

This might also be called Low Code Rust. Happy Coding! 🦀

The Reference 🤝

  • Mixing Text and Binary Data in Call Arguments
  • The source code
  • The WebAssembly VIRTUAL machine SSVM that executes Rust functions