Fundamentals 8 min read

Generating Chinese "Fu" Characters with Python and PIL

This tutorial demonstrates how to use Python libraries such as requests and Pillow to scrape individual Chinese characters, resize them, compose them onto a background image, and output a customized "Fu" couplet image, complete with full source code and usage instructions.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Generating Chinese "Fu" Characters with Python and PIL

The article introduces a Python-based method for automatically generating Chinese "Fu" characters, inspired by the popularity of the Alipay "Collect Five Blessings" activity, and encourages readers to create their own images using code.

Step 1: Import required libraries

import io<br/>from PIL import Image<br/>import requests

Step 2: Define a function to fetch a single character image

def get_word(ch, quality):<br/>    fp = io.BytesIO(requests.post(url='http://xufive.sdysit.com/tk', data={'ch':ch}).content)<br/>    im = Image.open(fp)<br/>    w, h = im.size<br/>    if quality == 'M':<br/>        w, h = int(w*0.75), int(0.75*h)<br/>    elif quality == 'L':<br/>        w, h = int(w*0.5), int(0.5*h)<br/>    return im.resize((w,h))

Step 3: Define a helper to obtain the background image

def get_bg(quality):<br/>    return get_word('bg', quality)

Step 4: Create the main function that assembles the couplet

def write_couplets(text, HorV='V', quality='L', out_file=None):<br/>    usize = {'H':(640,23), 'M':(480,18), 'L':(320,12)}<br/>    bg_im = get_bg(quality)<br/>    text_list = [list(item) for item in text.split()]<br/>    rows = len(text_list)<br/>    cols = max([len(item) for item in text_list])<br/>    if HorV == 'V':<br/>        ow, oh = 40+rows*usize[quality][0]+(rows-1)*10, 40+cols*usize[quality][0]<br/>    else:<br/>        ow, oh = 40+cols*usize[quality][0], 40+rows*usize[quality][0]+(rows-1)*10<br/>    out_im = Image.new('RGBA', (ow, oh), '#f0f0f0')<br/>    for row in range(rows):<br/>        if HorV == 'V':<br/>            row_im = Image.new('RGBA', (usize[quality][0], cols*usize[quality][0]), 'white')<br/>            offset = (ow-(usize[quality][0]+10)*(row+1)-10, 20)<br/>        else:<br/>            row_im = Image.new('RGBA', (cols*usize[quality][0], usize[quality][0]), 'white')<br/>            offset = (20, 20+(usize[quality][0]+10)*row)<br/>        for col, ch in enumerate(text_list[row]):<br/>            if HorV == 'V':<br/>                pos = (0, col*usize[quality][0])<br/>            else:<br/>                pos = (col*usize[quality][0], 0)<br/>            ch_im = get_word(ch, quality)<br/>            row_im.paste(bg_im, pos)<br/>            row_im.paste(ch_im, (pos[0]+usize[quality][1], pos[1]+usize[quality][1]), mask=ch_im)<br/>        out_im.paste(row_im, offset)<br/>    if out_file:<br/>        out_im.convert('RGB').save(out_file)<br/>    out_im.show()

Step 5: Full script example

import io<br/>from PIL import Image<br/>import requests<br/>def get_word(ch, quality):<br/>    fp = io.BytesIO(requests.post(url='http://xufive.sdysit.com/tk', data={'ch':ch}).content)<br/>    im = Image.open(fp)<br/>    w, h = im.size<br/>    if quality == 'M':<br/>        w, h = int(w*0.75), int(0.75*h)<br/>    elif quality == 'L':<br/>        w, h = int(w*0.5), int(0.5*h)<br/>    return im.resize((w,h))<br/>def get_bg(quality):<br/>    return get_word('bg', quality)<br/>def write_couplets(text, HorV='V', quality='L', out_file=None):<br/>    usize = {'H':(640,23), 'M':(480,18), 'L':(320,12)}<br/>    bg_im = get_bg(quality)<br/>    text_list = [list(item) for item in text.split()]<br/>    rows = len(text_list)<br/>    cols = max([len(item) for item in text_list])<br/>    if HorV == 'V':<br/>        ow, oh = 40+rows*usize[quality][0]+(rows-1)*10, 40+cols*usize[quality][0]<br/>    else:<br/>        ow, oh = 40+cols*usize[quality][0], 40+rows*usize[quality][0]+(rows-1)*10<br/>    out_im = Image.new('RGBA', (ow, oh), '#f0f0f0')<br/>    for row in range(rows):<br/>        if HorV == 'V':<br/>            row_im = Image.new('RGBA', (usize[quality][0], cols*usize[quality][0]), 'white')<br/>            offset = (ow-(usize[quality][0]+10)*(row+1)-10, 20)<br/>        else:<br/>            row_im = Image.new('RGBA', (cols*usize[quality][0], usize[quality][0]), 'white')<br/>            offset = (20, 20+(usize[quality][0]+10)*row)<br/>        for col, ch in enumerate(text_list[row]):<br/>            if HorV == 'V':<br/>                pos = (0, col*usize[quality][0])<br/>            else:<br/>                pos = (col*usize[quality][0], 0)<br/>            ch_im = get_word(ch, quality)<br/>            row_im.paste(bg_im, pos)<br/>            row_im.paste(ch_im, (pos[0]+usize[quality][1], pos[1]+usize[quality][1]), mask=ch_im)<br/>        out_im.paste(row_im, offset)<br/>    if out_file:<br/>        out_im.convert('RGB').save(out_file)<br/>    out_im.show()<br/>text = '福'<br/>write_couplets(text, HorV='V', quality='M', out_file='福.jpg')

The article concludes with encouragement for continuous learning, a reminder that difficulties are normal, and an invitation to join a Python learning community for further discussion and resource sharing.

Tutorialimage-processingPILChinese charactersweb-scraping
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.