Txt2img - simple image generation from your terminal without AI

This is my next Zig project, and it’s certainly larger than my last couple. It’s txt2img, a simple CLI tool that lets you generate a customizable image with text on it from the command line. The reason I wrote this is that I often need just a simple PNG with text for a logo, artwork, etc., as a blind developer, and I really don’t want to shell out to an AI for that when some basic code can do me just fine.

This was a great opertunity to learn about Zig’s C interop, it works wonderfully! Although, I would absolutely welcome suggestions on how to ditch the tiny bit of C I had to write to make the STB libraries I used fully link into Zig.

Usage: txt2img "text to draw" [<options>]

Options:
    -o, --output name of the file to write to. Defaults to output.png
    -s, --size <width>x<height> specifies the size, in pixels, of the generated image. Defaults to 512x512
    -bg <color> specifies the background color of the image, see below for details on valid colors. Defaults to white
    -fg <color> specifies the foreground color of the image, see below for details on valid colors. Defaults to black
    -p, --pos <X,Y> specifies the starting position of the text in the image. Defaults to 16,32

Notes:
    Colors accept names (white, black, red, etc.), #RRGGBB, or #RRGGBBAA

I certainly welcome any feedback, but especially around the C linkage and how the images look, I’m fully blind and the only validation I’ve gotten that this works is OCR and having an AI describe a few generated pictures.

7 Likes

Linking the implementation part of c single header libraries via a c-source file that defines the #define STB_IMAGE_WRITE_IMPLEMENTATION is the correct approach, that way the c-Include only runs on the header-part and not on the implementation part, doing this is good, because c-Include doesn’t translate every c code without errors. When it only translates the header-/declaration-part it is more likely to work correctly.

The text is readable, but relatively small, so it might be useful to have an option to change the size of the font.
Because it seems to be a pixel perfect font, maybe you could increase font size in integer increments?

When I passed a very long line of text, it silently got cut off once it no longer was within the 512x512 image region, similarly when I send it very many lines it would go past the bottom of the image.

I think it may help you catch errors, if you make sure that this results in an error message.

1 Like

Yup, this all makes perfect sense. Thanks a ton! They’re all things I didn’t consider, but they make perfect sense and I’ll spend some time hacking on it :slight_smile:

Thanks a ton for the feedback!

1 Like

main.zig:145:43: 0x7ff7704b0921 in drawText (txt2img_zcu.obj) @memcpy(text_buf[0..cli.text.len], cli.text);
Error for me when using on windows with zig 15.2

wrote txt2img.exe “text to draw”

1 Like

Just pushed a fix, thanks!

1 Like

Nice! You might want to look at Zignal for this.

You could replace all the c and stb stuff with the functionality it provides:

  • Native image support with Rgb and Rgba
  • Loading and saving PNG and JPEG
  • Basic font support: default 8x8, and custom BDF, PCF
  • Bonus: you could display a preview of the image in the terminal thanks to zignal’s Kitty image graphics protocol and sixel support.

Basically your program would be a thin wrapper around zignal. Here’s an example of a logo generated with zignal:

Code: zignal/examples/src/make_logo.zig at master · bfactory-ai/zignal · GitHub

If you’re interested and run into any issues, let me know.
Docs: Zig Documentation

3 Likes

Oooh, this definitely looks interesting! I won’t have a ton of time to explore it until this weekend, but thanks a ton for pointing me at it, this looks terrific! Wrapping STB was great for learning how @cImport works, but having it all be Zig would be great.

1 Like

I’ve pushed a commit that converts this tool to use Zignal, massively simplifying it! A huge thanks to @adria for pointing me at it.

1 Like

I just checked out, nice diff

 build.zig                       |    7 +-
 build.zig.zon                   |    7 +-
 lib/stb/stb_easy_font.h         |  305 -------
 lib/stb/stb_easy_font_wrapper.c |    7 -
 lib/stb/stb_easy_font_wrapper.h |    7 -
 lib/stb/stb_image_write.h       | 1724 ---------------------------------------
 src/main.zig                    |  159 +---
 7 files changed, 35 insertions(+), 2181 deletions(-)
 delete mode 100644 lib/stb/stb_easy_font.h
 delete mode 100644 lib/stb/stb_easy_font_wrapper.c
 delete mode 100644 lib/stb/stb_easy_font_wrapper.h
 delete mode 100644 lib/stb/stb_image_write.h

I’ve also submitted a PR with

  • a flag to preview the image in the terminal
  • an option to scale the text
  • some simplifications

Feel free to pick and choose what you like :slight_smile:

1 Like

Ah, I just added custom font support, too!

1 Like

Wow, you’re awesome, thanks loads! I’ll review this right before going to bed. Let’s see how Codeberg’s PR reviewing screen works with screen readers, if it’s better than the mess GitHub cranked out.

I just checked out, nice diff
build.zig | 7 ±
build.zig.zon | 7 ±
lib/stb/stb_easy_font.h | 305 -------
lib/stb/stb_easy_font_wrapper.c | 7 -
lib/stb/stb_easy_font_wrapper.h | 7 -
lib/stb/stb_image_write.h | 1724 ---------------------------------------
src/main.zig | 159 ±–
7 files changed, 35 insertions(+), 2181 deletions(-)
delete mode 100644 lib/stb/stb_easy_font.h
delete mode 100644 lib/stb/stb_easy_font_wrapper.c
delete mode 100644 lib/stb/stb_easy_font_wrapper.h
delete mode 100644 lib/stb/stb_image_write.h

Also nice diff from your side, even with your new features the diff was +42 -42 :slight_smile: nifty!

that was not intended at all, haha

1 Like