【问题标题】:python: use windows api to render text using a ttf fontpython:使用 windows api 使用 ttf 字体呈现文本
【发布时间】:2011-08-10 16:32:12
【问题描述】:

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

【问题讨论】:

  • 您有使用本机 Windows API 吗?如果没有,你可以使用PIL。
  • @Harpyon:是的,看我的otherquestions

标签: python windows winapi fonts rendering


【解决方案1】:

这里是 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")

【讨论】:

    【解决方案2】:

    渲染字体已完成(需要 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")
    

    结果:

    太棒了!!!

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

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

    【讨论】:

    • 你不想渲染一个 ttf 文件
    • @David:你能读懂我的心思!?!开个玩笑,什么意思?
    • 字体通常由多个 ttf 文件组成。通常你想渲染一个字体,例如宋体。查看构成 arial 的不同 ttf 文件。
    • 您应该只安装字体并继续使用它。这是你应该做的。
    • @David:很好,但是我如何从字体文件中获取字体?
    猜你喜欢
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 2013-11-15
    • 2011-04-14
    • 2013-11-17
    • 1970-01-01
    • 2015-12-23
    • 2010-09-09
    相关资源
    最近更新 更多