python:使用windows api使用ttf字体渲染文本

发布于 2024-11-02 12:28:22 字数 152 浏览 1 评论 0原文

一个完整的例子是什么,从什么都没有到最终在内存中得到一个位图,打开一个特定的 .ttf 文件并使用该字体,使用本机 Windows API 渲染一些文本?我目前正在努力使用 Windows API,所以这是我和 stackoverflow 的其他人之间的一场竞赛。

What would be a full example, going from nothing to ending up with a bitmap in memory, of opening a particular .ttf file and rendering some text using that font, using the native Windows API? I'm currently slogging through the windows API, so it's a race between me and the rest of stackoverflow.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

星光不落少年眉 2024-11-09 12:28:22

完成渲染字体(需要 PyWin32):

import ctypes
import struct
import win32con
import win32gui
import win32ui

from PIL import Image


def RGB(r, g, b):    
    return r | (g << 8) | (b << 16)

def native_bmp_to_pil(hdc, bitmap_handle, width, height):
    bmpheader = struct.pack("LHHHH", struct.calcsize("LHHHH"),
                            width, height, 1, 24) #w,h, planes=1, bitcount)
    c_bmpheader = ctypes.c_buffer(bmpheader)

    #3 bytes per pixel, pad lines to 4 bytes    
    c_bits = ctypes.c_buffer(" " * (height * ((width*3 + 3) & -4)))

    res = ctypes.windll.gdi32.GetDIBits(
        hdc, bitmap_handle, 0, height,
        c_bits, c_bmpheader,
        win32con.DIB_RGB_COLORS)
    if not res:
        raise IOError("native_bmp_to_pil failed: GetDIBits")

    im = Image.frombuffer(
        "RGB", (width, height), c_bits,
        "raw", "BGR", (width*3 + 3) & -4, -1)
    return im    


class Win32Font:
    def __init__(self, name, height, weight=win32con.FW_NORMAL,
                 italic=False, underline=False):
        self.font = win32ui.CreateFont({
            'name': name, 'height': height,
            'weight': weight, 'italic': italic, 'underline': underline})

        #create a compatible DC we can use to draw:
        self.desktopHwnd = win32gui.GetDesktopWindow()
        self.desktopDC = win32gui.GetWindowDC(self.desktopHwnd)
        self.mfcDC = win32ui.CreateDCFromHandle(self.desktopDC)         
        self.drawDC = self.mfcDC.CreateCompatibleDC()

        #initialize it
        self.drawDC.SelectObject(self.font)

    def renderText(self, text):
        """render text to a PIL image using the windows API."""
        self.drawDC.SetTextColor(RGB(255,0,0))

        #create the compatible bitmap:
        w,h = self.drawDC.GetTextExtent(text)
        
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(self.mfcDC, w, h)        
        self.drawDC.SelectObject(saveBitMap)

        #draw it
        self.drawDC.DrawText(text, (0, 0, w, h), win32con.DT_LEFT)

        #convert to PIL image
        im = native_bmp_to_pil(self.drawDC.GetSafeHdc(), saveBitMap.GetHandle(), w, h)

        #clean-up
        win32gui.DeleteObject(saveBitMap.GetHandle())

        return im        

    def __del__(self):
        self.mfcDC.DeleteDC()
        self.drawDC.DeleteDC()
        win32gui.ReleaseDC(self.desktopHwnd, self.desktopDC)
        win32gui.DeleteObject(self.font.GetSafeHandle())

    def __del__(self):
        win32gui.DeleteObject(self.font.GetSafeHandle())

用法:

>>> f = Win32Font("Arial", 15)
>>> im = f.renderText("this is just a test")
>>> im.save("c:/hope.png")

结果:

GLORY

太棒了!!!

要渲染特定的 .ttf 文件,我需要进行更多研究。

更新:更新以计算 bmp 大小:

woot

Done and done for rendering a font (requires PyWin32):

import ctypes
import struct
import win32con
import win32gui
import win32ui

from PIL import Image


def RGB(r, g, b):    
    return r | (g << 8) | (b << 16)

def native_bmp_to_pil(hdc, bitmap_handle, width, height):
    bmpheader = struct.pack("LHHHH", struct.calcsize("LHHHH"),
                            width, height, 1, 24) #w,h, planes=1, bitcount)
    c_bmpheader = ctypes.c_buffer(bmpheader)

    #3 bytes per pixel, pad lines to 4 bytes    
    c_bits = ctypes.c_buffer(" " * (height * ((width*3 + 3) & -4)))

    res = ctypes.windll.gdi32.GetDIBits(
        hdc, bitmap_handle, 0, height,
        c_bits, c_bmpheader,
        win32con.DIB_RGB_COLORS)
    if not res:
        raise IOError("native_bmp_to_pil failed: GetDIBits")

    im = Image.frombuffer(
        "RGB", (width, height), c_bits,
        "raw", "BGR", (width*3 + 3) & -4, -1)
    return im    


class Win32Font:
    def __init__(self, name, height, weight=win32con.FW_NORMAL,
                 italic=False, underline=False):
        self.font = win32ui.CreateFont({
            'name': name, 'height': height,
            'weight': weight, 'italic': italic, 'underline': underline})

        #create a compatible DC we can use to draw:
        self.desktopHwnd = win32gui.GetDesktopWindow()
        self.desktopDC = win32gui.GetWindowDC(self.desktopHwnd)
        self.mfcDC = win32ui.CreateDCFromHandle(self.desktopDC)         
        self.drawDC = self.mfcDC.CreateCompatibleDC()

        #initialize it
        self.drawDC.SelectObject(self.font)

    def renderText(self, text):
        """render text to a PIL image using the windows API."""
        self.drawDC.SetTextColor(RGB(255,0,0))

        #create the compatible bitmap:
        w,h = self.drawDC.GetTextExtent(text)
        
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(self.mfcDC, w, h)        
        self.drawDC.SelectObject(saveBitMap)

        #draw it
        self.drawDC.DrawText(text, (0, 0, w, h), win32con.DT_LEFT)

        #convert to PIL image
        im = native_bmp_to_pil(self.drawDC.GetSafeHdc(), saveBitMap.GetHandle(), w, h)

        #clean-up
        win32gui.DeleteObject(saveBitMap.GetHandle())

        return im        

    def __del__(self):
        self.mfcDC.DeleteDC()
        self.drawDC.DeleteDC()
        win32gui.ReleaseDC(self.desktopHwnd, self.desktopDC)
        win32gui.DeleteObject(self.font.GetSafeHandle())

    def __del__(self):
        win32gui.DeleteObject(self.font.GetSafeHandle())

usage:

>>> f = Win32Font("Arial", 15)
>>> im = f.renderText("this is just a test")
>>> im.save("c:/hope.png")

result:

GLORY

brilliant!!!

To render a particular .ttf file I'll need to dig around more.

UPDATE: Updated to calculate the bmp size:

woot

过潦 2024-11-09 12:28:22

这是已接受答案的 Python 3 的更新版本(完全归功于@Claudiu)。我以“Segoe UI”为例来展示我们如何完美匹配原生Windows用户界面:

import ctypes, struct, win32con, win32gui, win32ui, PIL.Image

def native_bmp_to_pil(hdc, bitmap_handle, width, height):
    bmpheader = struct.pack("LHHHH", struct.calcsize("LHHHH"), width, height, 1, 24)
    c_bmpheader = ctypes.c_buffer(bmpheader)
    c_bits = ctypes.c_buffer(b" " * (height * ((width*3 + 3) & -4)))
    res = ctypes.windll.gdi32.GetDIBits(hdc, bitmap_handle, 0, height, c_bits, c_bmpheader, win32con.DIB_RGB_COLORS)
    if not res:
        raise IOError("native_bmp_to_pil failed: GetDIBits")
    im = PIL.Image.frombuffer("RGB", (width, height), c_bits, "raw", "BGR", (width*3 + 3) & -4, -1)
    return im    

class Win32Font:
    def __init__(self, name, height, weight=win32con.FW_NORMAL, italic=False, underline=False):
        self.font = win32ui.CreateFont({'name': name, 'height': height, 'weight': weight, 'italic': italic, 'underline': underline})
        self.desktopHwnd = win32gui.GetDesktopWindow()
        self.desktopDC = win32gui.GetWindowDC(self.desktopHwnd)
        self.mfcDC = win32ui.CreateDCFromHandle(self.desktopDC)
        self.drawDC = self.mfcDC.CreateCompatibleDC()
        self.drawDC.SelectObject(self.font)

    def renderText(self, text):
        self.drawDC.SetTextColor(0)
        w,h = self.drawDC.GetTextExtent(text)
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(self.mfcDC, w, h)        
        self.drawDC.SelectObject(saveBitMap)
        self.drawDC.DrawText(text, (0, 0, w, h), win32con.DT_LEFT)
        im = native_bmp_to_pil(self.drawDC.GetSafeHdc(), saveBitMap.GetHandle(), w, h)
        win32gui.DeleteObject(saveBitMap.GetHandle())
        return im

    def __del__(self):
        self.mfcDC.DeleteDC()
        self.drawDC.DeleteDC()
        win32gui.ReleaseDC(self.desktopHwnd, self.desktopDC)
        win32gui.DeleteObject(self.font.GetSafeHandle())

f = Win32Font("Segoe UI", 15)
im = f.renderText("Hello World")
im.save("test.png")

Here is updated version for Python 3 of the accepted answer (full credit to @Claudiu). I'm using "Segoe UI" as an example to show how we can perfectly match the native Windows user interface:

import ctypes, struct, win32con, win32gui, win32ui, PIL.Image

def native_bmp_to_pil(hdc, bitmap_handle, width, height):
    bmpheader = struct.pack("LHHHH", struct.calcsize("LHHHH"), width, height, 1, 24)
    c_bmpheader = ctypes.c_buffer(bmpheader)
    c_bits = ctypes.c_buffer(b" " * (height * ((width*3 + 3) & -4)))
    res = ctypes.windll.gdi32.GetDIBits(hdc, bitmap_handle, 0, height, c_bits, c_bmpheader, win32con.DIB_RGB_COLORS)
    if not res:
        raise IOError("native_bmp_to_pil failed: GetDIBits")
    im = PIL.Image.frombuffer("RGB", (width, height), c_bits, "raw", "BGR", (width*3 + 3) & -4, -1)
    return im    

class Win32Font:
    def __init__(self, name, height, weight=win32con.FW_NORMAL, italic=False, underline=False):
        self.font = win32ui.CreateFont({'name': name, 'height': height, 'weight': weight, 'italic': italic, 'underline': underline})
        self.desktopHwnd = win32gui.GetDesktopWindow()
        self.desktopDC = win32gui.GetWindowDC(self.desktopHwnd)
        self.mfcDC = win32ui.CreateDCFromHandle(self.desktopDC)
        self.drawDC = self.mfcDC.CreateCompatibleDC()
        self.drawDC.SelectObject(self.font)

    def renderText(self, text):
        self.drawDC.SetTextColor(0)
        w,h = self.drawDC.GetTextExtent(text)
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(self.mfcDC, w, h)        
        self.drawDC.SelectObject(saveBitMap)
        self.drawDC.DrawText(text, (0, 0, w, h), win32con.DT_LEFT)
        im = native_bmp_to_pil(self.drawDC.GetSafeHdc(), saveBitMap.GetHandle(), w, h)
        win32gui.DeleteObject(saveBitMap.GetHandle())
        return im

    def __del__(self):
        self.mfcDC.DeleteDC()
        self.drawDC.DeleteDC()
        win32gui.ReleaseDC(self.desktopHwnd, self.desktopDC)
        win32gui.DeleteObject(self.font.GetSafeHandle())

f = Win32Font("Segoe UI", 15)
im = f.renderText("Hello World")
im.save("test.png")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文