Search code examples
gowinapifontstruetype

How do I draw characters without corresponding cmap through the system API?


A font file can allow the character to have glyf data, but it does not necessarily have to have a cmap mapping. (no codepoint)

Therefore, I want to know if there is a system API that can render character in this situation.

I know that I can get all the point set data of this contours and the flags (onCurve,...) information from the glyf raw data, so that I can draw the glyph myself.

However, I am seeking a solution using a system API (preferably Windows, but solutions for other platforms are also welcome).

If it has a code point, I can directly use TextOutW to draw the character, but if there is no code point and I only know the glyphIndex, is there a way to achieve that?

ExtTextOut combined with ETO_GLYPH_INDEX seems to be able to do it, but I'm not very sure.


Solution

  • Indeed, using ExtTextOut can accomplish it. The options should be set to ETO_GLYPH_INDEX (0x10). The content that lpString points to should be filled according to the glyph index.

    ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, nil, []uint16{49, 50, 51}) // glyphIndex: 49, 50, 51
    ExtTextOut(hdc, 0, 0, 0, nil, utf16.Encode([]rune("Hello world 世界")) // draw string
    

    Example

    func main() {
        lf := w32.LogFont{
            Height:         -64,
            Width:          0,
            Escapement:     0,
            Orientation:    0,
            Weight:         400,
            Italic:         0,
            Underline:      0,
            StrikeOut:      0,
            CharSet:        w32.DEFAULT_CHARSET,
            OutPrecision:   w32.OUT_TT_PRECIS,
            ClipPrecision:  w32.CLIP_DEFAULT_PRECIS,
            Quality:        w32.ANTIALIASED_QUALITY,
            PitchAndFamily: w32.FF_DONTCARE,
        }
        hFont := gdiDll.CreateFont(
            lf.Height,
            lf.Width,
            lf.Escapement,
            lf.Orientation,
            lf.Weight,
            uint32(lf.Italic),
            uint32(lf.Underline),
            uint32(lf.StrikeOut),
            uint32(lf.CharSet),
            uint32(lf.OutPrecision),
            uint32(lf.ClipPrecision),
            uint32(lf.Quality),
            uint32(lf.PitchAndFamily),
            "Arial",
        )
        defer gdiDll.DeleteObject(w32.HGDIOBJ(hFont))
    
        hdc := userDll.GetDC(0)
        defer userDll.ReleaseDC(0, hdc)
    
        hMemDC := gdiDll.CreateCompatibleDC(hdc)
        defer gdiDll.DeleteObject(w32.HGDIOBJ(hMemDC))
    
        const width = 1000
        const height = 500
        hBitmap := gdiDll.CreateCompatibleBitmap(hdc, width, height)
        hObjOld := gdiDll.SelectObject(hMemDC, w32.HGDIOBJ(hBitmap))
        defer gdiDll.SelectObject(hMemDC, hObjOld)
        oldFont := gdiDll.SelectObject(hMemDC, w32.HGDIOBJ(hFont))
        defer gdiDll.SelectObject(hMemDC, oldFont)
    
        gdiDll.SetTextColor(hMemDC, w32.RGB(255, 0, 0))
        gdiDll.ExtTextOut(hMemDC, 20, 20, w32.ETO_GLYPH_INDEX, nil, []uint16{49, 50, 51}) // GlyphIndex 49, 50, 51
        gdiDll.ExtTextOut(hMemDC, 500, 300, 0, nil, utf16.Encode([]rune("Hello world 世界")))
    
        _ = userDll.OpenClipboard(0)
        _ = userDll.EmptyClipboard()
        _, _ = userDll.SetClipboardData(w32.CF_BITMAP, w32.HANDLE(hBitmap))
        _ = userDll.CloseClipboard()
    }