2023年2月3日 星期五

golang 中文字在圖片上的位置

最近在研究要如何知道繪製文字時,文字的位置到底為何處

以下的 code 是在 (50, 50) 的位置中繪製一個 綠色的區塊,並在同一位置繪製文字——Hello, golang image

package main

import (
	"image"
	"image/color"

	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
)

func main() {
	img := image.NewRGBA(image.Rect(0, 0, 300, 100))

	textX, textY := 50, 50

	fontFace := basicfont.Face7x13

	// 在 (50, 50) 繪製一個 150 ✕ 20 的長方形
	for y := textY; y < textY+20; y++ {
		for x := textX; x < textX+150; x++ {
			img.Set(x, y, color.NRGBA{
				R: 0,
				G: 0xAF,
				B: 0,
				A: 0x99,
			})
		}
	}

	// 繪製文字
	d := &font.Drawer{
		Dst:  img,
		Src:  image.NewUniform(color.RGBA{0x4b, 0x11, 0x80, 0xff}),
		Face: fontFace,
		Dot:  fixed.P(textX, textY),
	}

	d.DrawString("Hello, golang image")

	// 輸出圖片的 code (下略)
    
}

會得到以下圖片

可以很明顯地看出來,文字並不是繪製於 (50, 50) 這個位置

研究一下後,發現 Font.Face 有 GlyphBounds 這個 func,可以取得繪製指定文字的位置

如下:

fontFace := basicfont.Face7x13
bound, _, _ := fontFace.GlyphBounds(rune('A'))

// 這樣就可藉由 bound.Min 和 bound.Max 來取得繪製出來的文字的左上、右下的點了
// (bound.Min.X, bound.Min.Y) 為在 (0, 0) 繪製文字 "A" 時,A 的左上頂點

詳細可參閱 Apple 的這張圖

根據這張圖,假設 Q 的頂點距離 Baseline 為 12px,那 bound.Min.Y (實際上 bound.Min.Y / X 是特殊類型,還要經過 Round() 之類的 func 來轉換成 pixel,但這邊為了敘述方便所以省略)就會是 -12;

同理,若 Q 的尾巴最底端離 Baseline 有 3px,那 bound.Max.Y 會是 3。

根據這結論,如果我要讓字串的繪製於 (50, 50),位移應該要是 (50 - bound.Min.X, 50 -bound.Min.Y)

所以 code 應為:

package main

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"image"
	"image/color"

	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
)

func main() {
	img := image.NewRGBA(image.Rect(0, 0, 300, 100))

	textX, textY := 50, 50

	fontFace := basicfont.Face7x13
	{
		for y := textY; y < textY+20; y++ {
			for x := textX; x < textX+150; x++ {
				img.Set(x, y, color.NRGBA{
					R: 0,
					G: 0xAF,
					B: 0,
					A: 0x99,
				})
			}
		}
	}

	bound, _, _ := fontFace.GlyphBounds(rune('H'))
	d := &font.Drawer{
		Dst:  img,
		Src:  image.NewUniform(color.RGBA{0x4b, 0x11, 0x80, 0xff}),
		Face: fontFace,
		Dot:  fixed.P(textX-bound.Min.X.Round(), textY-bound.Min.Y.Round()),
	}

	d.DrawString("Hello, golang image")

	// 輸出圖片的 code (下略)
}

輸出結果:

沒有留言:

張貼留言