Giter VIP home page Giter VIP logo

imagetext-py's Introduction

hero banner

A blazing fast text drawing library

imagetext-py is python bindings for imagetext

pypi Downloads PythonVersions discord


if you found this library useful, consider leaving a star ⭐

About

imagetext makes use of rusttype for font parsing and tiny-skia for drawing. It has a simple API that allows you to draw text with ease.

Currently imagetext-py does beat out Pillow for most of the cases I've tested, but I will setup some benchmarks soon.

Features

  • Multi-line text
  • Text wrapping
  • Text alignment
  • Font fallbacks
  • Text stroke
  • Gradient fills
  • Emojis! (almost every platform supported, including discord)
  • Global Font Database with css-like font querying

Note: emojis are fetched and cached from the internet during runtime, so you will need an internet connection to use them. The ability to use local emoji images will be added soon.

Installation

pip install imagetext-py

Example Usage

from PIL import Image
from imagetext_py import *

# supports fonts with fallbacks
FontDB.LoadFromDir(".")
font = FontDB.Query("coolvetica japanese")

# create a canvas to draw on
cv = Canvas(512, 512, (255, 255, 255, 255))

# paints are used to fill and stroke text
black = Paint.Color((0, 0, 0, 255))
rainbow = Paint.Rainbow((0.0,0.0), (256.0,256.0))

# if a font doesn't have a glyph for a character, it will use the fallbacks
text = "hello my 😓 n🐢ame i☕s 会のすべ aての構成員 nathan and i drink soup boop coop, the quick brown fox jumps over the lazy dog"

draw_text_wrapped(canvas=cv,              # the canvas to draw on
                  text=text,
                  x=256, y=256,           # the position of the text
                  ax=0.5, ay=0.5,         # the anchor of the text
                  size=67,                # the size of the text
                  width=500,              # the width of the text
                  font=font,
                  fill=black,
                  align=TextAlign.Center,
                  stroke=2.0,             # the stroke width (optional)
                  stroke_color=rainbow,
                  draw_emojis=True)   # the stroke color (optional)

# you can convert the canvas to a PIL image
im: Image.Image = cv.to_image()
im.save("test.png")

# or you can just get the raw bytes
dimensions, bytes = cv.to_bytes()

# you can also save directly to a file
cv.save("test.png")

produces this image:

test.png

Pillow and FontDB Usage

from PIL import Image
from imagetext_py import *

FontDB.SetDefaultEmojiOptions(EmojiOptions(parse_discord_emojis=True))
FontDB.LoadFromDir(".")

font = FontDB.Query("coolvetica japanese")

with Image.new("RGBA", (512, 512), "white") as im:
    with Writer(im) as w:
        w.draw_text_wrapped(
            text="hello from python 😓 lol, <:blobpain:739614945045643447> " \
                 "ほまみ <:chad:682819256173461522><:bigbrain:744344773229543495> " \
                 "emojis workin",
            x=256, y=256,
            ax=0.5, ay=0.5,
            width=500,
            size=90,
            font=font,
            fill=Paint.Color((0, 0, 0, 255)),
            align=TextAlign.Center,
            stroke=2.0,
            stroke_color=Paint.Rainbow((0.0,0.0), (256.0,256.0)),
            draw_emojis=True
        )
    im.save("test.png")

produces this image:

test.png

imagetext-py's People

Contributors

diceroll123 avatar nathanielfernandes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

imagetext-py's Issues

Need help for "Usage"

Hi. it is nice package.
However, cannot get over the first line that font query.

  1. In case designate custom font file folder myself,
    how to load a font with it?

for example,
FontDB.LoadFromDir("c:\\custom_vari_font_folder\\") font = FontDB.Query( what should I put the name here? file name? or what? )

  1. When setup single font as below, how to map a fallback font? since there is no automatic fallback font mapping.
    FontDB.LoadFromPath('test_font', "c:\\custom_vari_font_folder\\test.ttf") font : Font = FontDB.Query("test_font")

  2. The 'LoadFromPath' takes first parameter as a name. Is it support any space included name? Seems does not.
    How to use/assign space included name with it?
    FontDB.LoadFromPath('test font', "c:\\custom_vari_font_folder\\test.ttf") font : Font = FontDB.Query("test font") <<< ERROR

CI caching

currently the CI steps are not cached

odd behaviour with python3.12

Hello and greetings,

Trying to install imagetext-py in a python3.12 virtual environment results in pip installing old version 1.0.2 and I couldn't quite figure out why it happened (latest version on pypi is v2.1.3) so I decided to open the issue. Screenshot:

Screenshot_20231021-012217_Pixel Launcher

Thank you for this package!

I'm super impressed by your good work here and very grateful for it 👍

This package is exactly what I was looking for, I can't believe Pillow doesn't do all of these simple things by default. Thankfully I found this, you deserve more stars! I don't know if you maintain this anymore, but best wishes :)

Best way to wrap Japanese and Thai text?

Hello,
This library is amazing. It is so fast. Using it I was able to check large sets of data by writing to the same canvas to test draw_text_wrapped function. Unfortunately, as the title implies, I found one small gap: text wrapping for Japanese isn't perfect. When I try to write out Japanese strings which contain 、 glyphs (and possibly others), they do not wrap and end up exceeding beyond the bounds of the canvas. I don't know if there is a better library in Python which can handle this. I couldn't find one and the built-in textwrap function doesn't work great on Thai language.

Do you know what the best way to implement Kinsoku Shori support? https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages#Line_breaking_rules_in_Japanese_text_(Kinsoku_Shori)
Using something like https://github.com/google/budou won't work for my application, since I want to distribute it to work with minimal setup/configuration (no web credentials). I don't know if there is a better regex-based approach which would allow me to pre-split my strings/insert spaces/hyphens.

Getting height of wrapped text

Thanks for your work on this library! Super useful.

How would I get the height of the text drawn by draw_text_wrapped()? Not the height of the image the text is drawn in, which is already given, but the height of just the text after it's been wrapped?

Or even more better, how can I know what the height of the text will be before I draw it? I want to predetermine how big the containing image based on the text I want to draw in it.

Font Fallbacks Stopped Working in 2.1.0

Hi, @nathanielfernandes. I appreciate your fixes and dedication to this project!

Today I wanted to try text_wrap to compare the WordStyle.Word against WordStyle.Character output, but I encountered a problem (not an error):
Font fallbacks stopped working; the missing unicode glyph character was printed out. I reverted to 2.0.1, along with my code (text_wrap to word_wrap) and characters printed out correctly.

Here's a sample of my code:

from pathlib import Path
import os
root_folder = Path(os.getcwd()).absolute()
primary_font = str(Path(root_folder / "fonts" / "JetBrainsMono-Regular.ttf"))
fallback_fonts = [str(Path(Path(os.getcwd()).absolute() / "fonts" /"fallback_fonts" / f)) for f in os.listdir(Path(os.getcwd()).absolute() / "fonts" / "fallback_fonts" ) if os.path.isfile(os.path.join(Path(os.getcwd()).absolute() / "fonts" / "fallback_fonts", f))]
f = Font(primary_font, fallbacks=fallback_fonts)

I tried moving the fonts to the root directory and specified their name, but it didn't matter.

I really admire your work on this project. I have an idea for you thought. I learned a lot from Pytlicek (Tomas Pytel) when I contributed to https://github.com/Pytlicek/sheet2dict, the key thing I learned was implementing test cases. I think it would really help to build a battery of tests before deploying.

I have two ideas about potential test cases you could use:

  1. Generate a baseline of images from the previous version, or store the hashes of images for expected file outputs (You could also do a bit-by-bit comparison as well, but I think a hash would probably be fine.). Then, as part of the build process, test that the output matches what is expected. You may wish to use some 'simple' images, without things like the rainbow stroke. It might be easier to recognize less complex images.
  2. Use an OCR library to compare the OCR against the input text.

I'm not a great coder, I just piece together a lot of libraries and wish I had the time right now to do a PR to contribute. I don't know how Tomas set up his Github projects to perform the automated testing, but it would really benefit you long term (you have a great personal website!)

RTL Language Rendering Problem

Description

The library is currently unable to properly render text in RTL (Right-to-Left) languages such as Arabic. When attempting to add text in Arabic, for example, the text is rendered in LTR (Left-to-Right) direction, which results in incorrect display and readability issues.

Steps to Reproduce:

from imagetext_py import *
from PIL import Image

FontDB.LoadFromDir("/path/to/fonts")
# using this font: https://fonts.google.com/noto/specimen/Noto+Kufi+Arabic
font = FontDB.Query("NotoKufiArabic-VariableFont_wght")
cv = Canvas(512, 512, (255, 255, 255, 255))
black = Paint.Color((0, 0, 0, 255))

text = "السلام عليكم"

draw_text_wrapped(canvas=cv, text=text, x=256, y=256, ax=0.5, ay=0.5, size=67, width=500, fill=black, align=TextAlign.Center, stroke=2.0, stroke_color=black, font=font)

im = cv.to_image()
im.show()

Expected Behavior:

The Arabic text should be rendered correctly in RTL direction, maintaining the correct reading order and alignment, as shown in the following picture:

image

Actual Behavior:

The Arabic text is rendered in LTR direction, causing the text to appear incorrectly and affecting readability.

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.