properly render text with a given font in python and accurately detect its boundaries

  • Last Update :
  • Techknowledgy :

pyvips seems to do this correctly. I tried this:

$ python3
Python 3.7 .3(
   default, Apr 3 2019, 05: 39: 12)[GCC 8.3 .0] on linux
Type "help", "copyright", "credits"
or "license"
for more information. >>>
   import pyvips >>>
   x = pyvips.Image.text("Puzzling", dpi = 300, font = "Miss Fajardose", fontfile = "/home/john/pics/MissFajardose-Regular.ttf") >>>

Here's some code I created that works for me and is PIL-based. I found using getsize_multiline worked pretty well (and also drew the text using the ImageDraw.Draw multiline_text function).

from PIL
import Image, ImageFont, ImageDraw, ImageColor

def text_to_image(
      text: str,
      font_filepath: str,
      font_size: int,
      color: (int, int, int), #color is in RGB font_align = "center"):

   font = ImageFont.truetype(font_filepath, size = font_size)
box = font.getsize_multiline(text)
img ="RGBA", (box[0], box[1]))
draw = ImageDraw.Draw(img)
draw_point = (0, 0)
draw.multiline_text(draw_point, text, font = font, fill = color, align = font_align)
return img

Suggestion : 2

September 17, 2018 at 9:56 pm,September 17, 2018 at 7:47 pm,September 17, 2018 at 7:34 pm,September 17, 2018 at 7:22 pm

To check your Ubuntu version you can use the lsb_release command:

$ lsb_release - a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04 .1 LTS
Release: 18.04
Codename: bionic

For Ubuntu 18.04 users, Tesseract 4 is part of the main apt-get repository, making it super easy to install Tesseract via the following command:

$ sudo apt install tesseract - ocr

Just add the alex-p/tesseract-ocr PPA repository to your system, update your package definitions, and then install Tesseract:

$ sudo add - apt - repository ppa: alex - p / tesseract - ocr
$ sudo apt - get update
$ sudo apt install tesseract - ocr

If you already have Tesseract installed on your Mac (if you followed my previous Tesseract install tutorial, for example), you’ll first want to unlink the original install:

$ brew unlink tesseract

Once you have Tesseract installed on your machine you should execute the following command to verify your Tesseract version:

$ tesseract - v
tesseract 4.0 .0 - beta .3
leptonica - 1.76 .0
libjpeg 9 c: libpng 1.6 .34: libtiff 4.0 .9: zlib 1.2 .11
Found AVX512BW
Found AVX512F
Found AVX2
Found AVX
Found SSE

Suggestion : 3

The textwrap module provides some convenience functions, as well as TextWrapper, the class that does all the work. If you’re just wrapping or filling one or two text strings, the convenience functions should be good enough; otherwise, you should use an instance of TextWrapper for efficiency.,TextWrapper also provides some public methods, analogous to the module-level convenience functions:,wrap(), fill() and shorten() work by creating a TextWrapper instance and calling a single method on it. That instance is not reused, so for applications that process many text strings using wrap() and/or fill(), it may be more efficient to create your own TextWrapper object.,Since the sentence detection algorithm relies on string.lowercase for the definition of “lowercase letter”, and a convention of using two spaces after a period to separate sentences on the same line, it is specific to English-language texts.

"\n".join(wrap(text, ...))
>>> textwrap.shorten("Hello  world!", width = 12)
'Hello world!' >>>
textwrap.shorten("Hello  world!", width = 11)
'Hello [...]' >>>
textwrap.shorten("Hello world", width = 10, placeholder = "...")
def test():
   # end first line with\ to avoid the empty line!
   s = ''
print(repr(s)) # prints '    hello\n      world\n    '
print(repr(dedent(s))) # prints 'hello\n  world\n'
>>> s = 'hello\n\n \nworld' >>>
   indent(s, '  ')
'  hello\n\n \n  world'
>>> print(indent(s, '+ ', lambda line: True)) +
   hello +
wrapper = TextWrapper(initial_indent = "* ")

Suggestion : 4

Last modified: Apr 5, 2022, by MDN contributors

/* Keyword values */
text - rendering: auto;
text - rendering: optimizeSpeed;
text - rendering: optimizeLegibility;
text - rendering: geometricPrecision;

/* Global values */
text - rendering: inherit;
text - rendering: initial;
text - rendering: revert;
text - rendering: revert - layer;
text - rendering: unset;
text - rendering = auto | optimizeSpeed | optimizeLegibility | geometricPrecision
<p class="small">LYoWAT - ff fi fl ffl</p>
<p class="big">LYoWAT - ff fi fl ffl</p>
.small {
   font: 19.9 px "Constantia",
   "Times New Roman",
.big {
   font: 20 px "Constantia",
   "Times New Roman",
<p class="speed">LYoWAT - ff fi fl ffl</p>
<p class="legibility">LYoWAT - ff fi fl ffl</p>
p {
   font: 1.5 em "Constantia",
   "Times New Roman",

.speed {
   text - rendering: optimizeSpeed;
.legibility {
   text - rendering: optimizeLegibility;

Suggestion : 5

A FreeType face hosts a collection of glyphs. We can set one of those glyphs as the active glyph by calling FT_Load_Char. Here we choose to load the character glyph 'X': , We combine both the position and texture coordinate data into one vec4. The vertex shader multiplies the coordinates with a projection matrix and forwards the texture coordinates to the fragment shader: , FreeType loads these TrueType fonts and, for each glyph, generates a bitmap image and calculates several metrics. We can extract these bitmap images for generating textures and position each character glyph appropriately using the loaded metrics. , Each of these FreeType functions returns a non-zero integer whenever an error occurred.

Then include the appropriate headers:

#include <ft2build.h>
   #include FT_FREETYPE_H

To load a font, all we have to do is initialize the FreeType library and load the font as a face as FreeType likes to call it. Here we load the arial.ttf TrueType font file that was copied from the Windows/Fonts directory:

FT_Library ft;
if (FT_Init_FreeType( & ft)) {
   std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
   return -1;

FT_Face face;
if (FT_New_Face(ft, "fonts/arial.ttf", 0, & face)) {
   std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;
   return -1;

Once we've loaded the face, we should define the pixel font size we'd like to extract from this face:

FT_Set_Pixel_Sizes(face, 0, 48);

We could load a character glyph, retrieve its metrics, and generate a texture each time we want to render a character to the screen, but it would be inefficient to do this each frame. We'd rather store the generated data somewhere in the application and query it whenever we want to render a character. We'll define a convenient struct that we'll store in a map:

struct Character {
    unsigned int TextureID;  // ID handle of the glyph texture
    glm::ivec2   Size;       // Size of glyph
    glm::ivec2   Bearing;    // Offset from baseline to left/top of glyph
    unsigned int Advance;    // Offset to advance to next glyph

std::map<char, Character> Characters;

For this chapter we'll keep things simple by restricting ourselves to the first 128 characters of the ASCII character set. For each character, we generate a texture and store its relevant data into a Character struct that we add to the Characters map. This way, all data required to render each character is stored for later use.

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction
for (unsigned char c = 0; c < 128; c++)
    // load character glyph 
    if (FT_Load_Char(face, c, FT_LOAD_RENDER))
        std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
    // generate texture
    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    // set texture options
    // now store character for later use
    Character character = {
        glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
        glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
    Characters.insert(std::pair<char, Character>(c, character));