Friday, August 24, 2018

Properly render text with a given font in Python and accurately detect its boundaries

This might strike as something very simple, and I too thought it'd be, but it apparently isn't. I must've spent a week trying to make this work, but I for the love of me can't manage to do so.

What I need

I need to render any given string (only containing standard characters) with any given font (handwritten-like) in Python. The font must be loaded from a TTF file. I also need to be able to accurately detect its borders (get the exact start and end position of the text, vertically and horizontally), preferably before drawing it. Lastly, it'd really make my life easier if the output is an array which I can then keep processing, and not an image file written to disc.

What I've tried

Imagemagick bindings (namely Wand): Couldn't figure out how to get the text metrics before setting the image size and rendering the text on it.

Pango via Pycairo bindings: nearly inexistent documentation, couldn't figure out how to load a TrueType font from a file.

PIL (Pillow): The most promising option. I've managed to accurately calculate the height for any text (which surprisingly is not the height getsize returns), but the width seems buggy for some fonts. Not only that, but those fonts with buggy width also get rendered incorrectly. Even when making the image large enough, they get cut off.

Here are some examples, with the text "Puzzling":

Font: Lovers Quarrel

Result:

Lovers Quarrel Render

Font: Miss Fajardose

Result:

Miss Fajardose Render

This is the code I'm using to generate the images:

from PIL import Image, ImageDraw, ImageFont
import cv2
import numpy as np
import glob
import os

font_size = 75
font_paths = sorted(glob.glob('./fonts/*.ttf'))
text = "Puzzling"
background_color = 180
text_color = 50
color_variance = 60
cv2.namedWindow('display', 0)

for font_path in font_paths:

    font = ImageFont.truetype(font_path, font_size)
    text_width, text_height = font.getsize(text)

    ascent, descent = font.getmetrics()
    (width, baseline), (offset_x, offset_y) = font.font.getsize(text)

    # +100 added to see that text gets cut off
    PIL_image = Image.new('RGB', (text_width-offset_x+100, text_height-offset_y), color=0x888888)
    draw = ImageDraw.Draw(PIL_image)
    draw.text((-offset_x, -offset_y), text, font=font, fill=0)

    cv2.imshow('display', np.array(PIL_image))
    k = cv2.waitKey()
    if chr(k & 255) == 'q':
        break

Some questions

Are the fonts the problem? I've been told by some colleagues that might be it, but I don't think so, since they get rendered correctly by the Imagemagick via command line.

Is my code the problem? Am I doing something wrong which is causing the text to get cut off?

Lastly, is it a bug in PIL? In that case, which library do you recommend I use to solve my problem? Should I give Pango and Wand another try?

Solved

pyvips seems to do this correctly. I tried this:

$ mkdir ~/.fonts
$ cp LoversQuarrel-Regular.ttf ~/.fonts

Then in Python:

$ python
Python 2.7.14 (default, Sep 23 2017, 22:06:14) 
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> image = pyvips.Image.text("Puzzling", dpi=300, font="Lovers Quarrel")
>>> image.width, image.height
(107, 58)
>>> image.write_to_file("x.png")

To make:

textx sample

The pyvips docs have a quick intro to the options:

https://jcupitt.github.io/pyvips/vimage.html#pyvips.Image.text

Or the C library docs have a lot more detail:

http://jcupitt.github.io/libvips/API/current/libvips-create.html#vips-text

It makes a one-band 8-bit image of the antialiased text which you can use for further processing, pass to NumPy or PIL, etc etc. There's a section in the intro on how to convert libvips images into arrays:

https://jcupitt.github.io/pyvips/intro.html#numpy-and-pil


Monday, August 20, 2018

Flask app load/stress testing errors

I have a simple Flask application that has about 7 end points (GET). No POST operation is used by any end point. When a specific GET request is received I return a JSON file back after a little bit of computation. I am using locust to load test it. I notice that if I use 100 users in locust and a hatch rate of 20 initially it runs smoothly for a bit and then I start seeing these errors from Flask (below). Is there something I can do to fix such an issue?


Exception happened during processing of request from ('127.0.0.1', 65360)
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 596, in process_request_thread
File  "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 331, in finish_request
File   "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 652, in __init__
File "/Library/Python/2.7/site-packages/werkzeug/serving.py", line 217, in handle
File   "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/BaseHTTPServer.py", line 340, in handle
File "/Library/Python/2.7/site-packages/werkzeug/serving.py", line 252, in handle_one_request
File "/Library/Python/2.7/site-packages/werkzeug/serving.py", line 200, in run_wsgi
ImportError: No module named debug.tbtools

Sunday, August 19, 2018

Named Pipe CreateFile() returns INVALID_HANDLE_VALUE, and GetLastError() returns ERROR_PIPE_BUSY

I have written a class to handle named pipe connections, and if I create an instance, close it, and then try to create another instance the call to CreateFile() returns INVALID_HANDLE_VALUE, and GetLastError() returns ERROR_PIPE_BUSY. What's going on here? What can I do to insure the call to Connect() succeeds?

PipeAsync A, B;

A.Connect("\\\\.\\pipe\\test",5000);
A.Close();

cout << GetLastError(); // some random value
B.Connect("\\\\.\\pipe\\test",5000);
cout << GetLastError(); // 231 (ERROR_PIPE_BUSY)
B.Close(); 

Here are my implementations of Connect() and Close()

BOOL PipeAsync::Connect(LPCSTR pszPipeName, DWORD dwTimeout)
{
    this->pszPipeName = pszPipeName;
    this->fExisting = TRUE;
    DWORD dwMode = this->fMessageMode ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE;

    hPipe = CreateFile(
        this->pszPipeName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL);

    if( INVALID_HANDLE_VALUE == hPipe )
        return FALSE; /* set break point here ; breaks here on second call to Connect() */

    if( GetLastError() == ERROR_PIPE_BUSY )
        if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
            return FALSE; /* set break point here */

    if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) )
        return FALSE; /* set break point here */

    return TRUE;

}

VOID PipeAsync::Close()
{

    if( fExisting )
        DisconnectNamedPipe( hPipe );

    CloseHandle( hPipe );
}





EDIT: I forgot to tell you how I concluded this... I set break points indicated in the comments. When run, it stops on the first break point.

EDIT: This is my updated code

if( INVALID_HANDLE_VALUE == hPipe )
    if( GetLastError() == ERROR_PIPE_BUSY )
    {
        if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
            return FALSE; /* break-point: breaks here on second call */
    }
    else
        return FALSE; /* break-point /*

Now, WaitNamedPipe() is returning false on the second call to Connect() and GetLastError() is returning 2, or ERROR_FILE_NOT_FOUND ?

Solved

From Named Pipe Client:

If the pipe exists but all of its instances are busy, CreateFile returns INVALID_HANDLE_VALUE and the GetLastError function returns ERROR_PIPE_BUSY. When this happens, the named pipe client uses the WaitNamedPipe function to wait for an instance of the named pipe to become available.

The link has example code on coping with ERROR_PIPE_BUSY.

EDIT:

Small compilable example that demonstrates accepting and connecting on a named pipe:

const char* const PIPE_NAME = "\\\\.\\pipe\\test";
const int MAX_CONNECTIONS   = 10;

void client_main()
{
    DWORD last_error;
    unsigned int elapsed_seconds       = 0;
    const unsigned int timeout_seconds = 5;

    HANDLE handle = CreateFile(PIPE_NAME,
                               GENERIC_READ | GENERIC_WRITE,
                               0,
                               0,
                               OPEN_EXISTING,
                               FILE_ATTRIBUTE_NORMAL,
                               0);

    while (INVALID_HANDLE_VALUE == handle &&
           elapsed_seconds < timeout_seconds)
    {
        last_error = GetLastError();

        if (last_error != ERROR_PIPE_BUSY)
        {
            break;
        }

        Sleep(1 * 1000);
        elapsed_seconds++;

        handle = CreateFile(PIPE_NAME,
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            0,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            0);
    }

    if (INVALID_HANDLE_VALUE == handle)
    {
        std::cerr << "Failed to connect to pipe " << PIPE_NAME <<
            ": last_error=" << last_error << "\n";
    }
    else
    {
        std::cout << "Connected to pipe " << PIPE_NAME << "\n";
        CloseHandle(handle);
    }
}

HANDLE _get_server_handle()
{
    // Error handling omitted for security descriptor creation.
    SECURITY_DESCRIPTOR sd;
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, TRUE, static_cast(0), FALSE);

    SECURITY_ATTRIBUTES sa;
    sa.nLength              = sizeof(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle       = FALSE;

    // Create a bi-directional message pipe.
    HANDLE handle = CreateNamedPipe(PIPE_NAME,
                                    PIPE_ACCESS_DUPLEX,
                                    PIPE_TYPE_MESSAGE       |
                                      PIPE_READMODE_MESSAGE |
                                      PIPE_NOWAIT,
                                    PIPE_UNLIMITED_INSTANCES,
                                    4096,
                                    4096,
                                    0,
                                    &sa);

    if (INVALID_HANDLE_VALUE == handle)
    {
        std::cerr << "Failed to create named pipe handle: last_error=" <<
            GetLastError() << "\n";
    }

    return handle;
}

void server_main()
{
    HANDLE handle = _get_server_handle();

    if (INVALID_HANDLE_VALUE != handle)
    {
        int count = 0;
        while (count < MAX_CONNECTIONS)
        {
            BOOL result = ConnectNamedPipe(handle, 0);

            const DWORD last_error = GetLastError();

            if (ERROR_NO_DATA == last_error)
            {
                count++;
                std::cout << "A client connected and disconnected: count=" <<
                    count << "\n";
                CloseHandle(handle);
                handle = _get_server_handle();
            }
            else if (ERROR_PIPE_CONNECTED == last_error)
            {
                count++;
                std::cout << "A client connected before call to " <<
                    "ConnectNamedPipe(): count=" << count << "\n";
                CloseHandle(handle);
                handle = _get_server_handle();
            }
            else if (ERROR_PIPE_LISTENING != last_error)
            {
                std::cerr << "Failed to wait for connection: last_error=" <<
                    GetLastError() << "\n";
                CloseHandle(handle);
                break;
            }
            Sleep(100);
        }
    }
}

int main(int a_argc, char** a_argv)
{
    if (2 == a_argc)
    {
        if (std::string("client") == *(a_argv + 1))
        {
            for (int i = 0; i < MAX_CONNECTIONS; i++)
            {
                client_main();
            }
        }
        else if (std::string("server") == *(a_argv + 1))
        {
            server_main();
        }
    }
    return 0;
}

Execute server-side first:

pipetest.exe server

Then execute client-side:

pipetest.exe client

I could not tell what the problem was from the posted code. Hopefully this small example will assist you in finding the issue.


Saturday, August 18, 2018

How Do I Find Good Graphic Designers for my Web Application Projects?

While this isn't strictly programming related, it's something I've run into as a web developer on several occasions, and I imagine others have run into as well. For that reason, I hope this question can remain open.

As web developers, we know how to make our applications work. But clients/customers/visitors aren't necessarily happy with a system that just works, they want a site with some graphic personality. Something that "looks pretty".

Whether it's "trendy", "professional", "cool", or "grungy", there's always a feeling that a web site needs to give it's audience, in conjunction with the functionality of the site itself.

Unfortunately, a Google search for "web designer", returns a list of everybody in the world who owns a copy of MS FrontPage and/or PhotoShop.

So the questions are:

Where do you turn when you need a (talented) designer on your project?

What criteria do you use to determine if a designer candidate has skills, or is qualified?

Solved

37Signals recently launched Haystack to address exactly this problem.


Designer freelance sites.

These are sites where you post a project, and the designers each post their proposals.

You choose the one you are happy with.

Believe it or, you will get lots of quality offerings - almost too many.

Nothing else even comes close.

  1. 99 Designs
  2. Crowdspring

(The latter is my favorite)

Also, some good points here: http://blog.siteroller.net/tips-when-looking-for-a-web-developer


  1. Freelancing/marketplace websites. You can make a post for a job you need done, sometimes however you really need to go out and look at the design offers that graphic designers post, it might take a bit of work to find a really good one, they won't always come to you. Sites such as:



  2. I usually look at their portfolio and make sure they have nice graphical skills, as well as decent communication skills as indicated by the content they have on their website.


I think the best approach is to find a website design that looks good to you and maybe a few of your end users. Find out who designed it and then ask them if they would be available for a side-project or full time employment. If they don't have the time, I am sure they would have friends to suggest to you. As always, be sure to see the portfolio of any graphic design before hiring them and perhaps ask them some questions about their design for your specific project. I wouldn't suggest templates because they often force you to build your website function around the design instead of the other way around.


Not sure about the quality of designers there, but there should be some overlap on Rent a Coder


I hate to advertise for myself on here, but I can't help but suggest you visit my website, and look under the portfolio tab, the url is in my profile.


You can search for templates instead of designers (to pick one example, Get Template): a template is like a designer distilled (and much cheaper). Having done that you could also contact the designers of some templates that you like.


You could also look on Odesk.com. They seem to have some nice professionals with lots of feedback.