⚙️

【Go】幅広い入力を𝙈𝙮 𝙣𝙚𝙬 𝙜𝙚𝙖𝙧...にするtransform.Transformer実装

2022/05/02に公開

先日、こちらの記事を拝見して「おもしろい!」と感じました。

https://zenn.dev/ikanag0/articles/3af1882762a5cf

Twitterなんかで「𝙈𝙮 𝙣𝙚𝙬 𝙜𝙚𝙖𝙧...」という文字を見かけるたびに、これみんなどうやってタイプしてるんだろう?と気になっていたので、なるほど、こうしたツールやWebサイトを利用するのね、と得心しました。

上記記事ではCLIをリリースして下さっています。もっと幅広く使えるツールも作ってみたいな、ということで目をつけたのがGoのtransform.Transformerの実装です。以下ではその詳細を述べていきます。

transform.Transformer?

Goの持ち味の1つが、io.Reader/io.Writerによる入出力の抽象化&ストリーム化です。それらのインタフェースを活用することで、幅広い入出力に効率良く処理をかませることができます。

その活用例の一つとして、ShiftJISでエンコーディングされたファイルの読み込みなどがあります。下記の記事でも解説されていますが、GoはUTF-8で文字列を扱うため、ShiftJISでエンコーディングされたコードを読み込むには、transform.NewReader(io.Reader, japanese.ShiftJIS.NewDecoder())によってio.Readerを作成し、これを所望のストリーム処理に繋げます。

https://zenn.dev/mattn/articles/fd545a14b0ffdf

transform.NewReaderの第2引数はtransform.Transformerインタフェースであり、次のメソッドを持つ構造体であればオリジナルの変換処理を実装することができます。

type Transformer interface {
	Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
	Reset()
}

https://pkg.go.dev/golang.org/x/text@v0.3.7/transform#Transformer

Transform関数が肝で、srcから読み取ったバイト列に所望の変換を行い、dstに書き込みます。入力のsrcが何度かに分けられる場合を想定しマルチバイト文字の際は文字途中での分割に対する配慮が必要ですし、書き込みたいバイト列に対しdstの長さが不足する際は余剰分を留保しておく必要があるなど、なかなか実装が面倒な子ではあります。しかしこれを実装することで様々な入出力の間に文字列の変換を入れることができるのは、大きな魅力ではないでしょうか。

ということで、Unicodeで表現できる範囲内で書体を変えるようなtransform.Transformerの実装を作ってみます。

今回の実装

作成したパッケージは下記です。

https://github.com/tenkoh/go-textstyle

a-z, A-Z, 0-9の文字を所望の書体に変換します。これらの文字は1バイト文字なので、その分だけtransform.Transformerの実装は簡単でした。

現状は次の書体に対応しています。

  • Bold
  • Italic
  • BoldItalic
  • Script
  • BoldScript
  • Fraktur
  • BoldFraktur
  • DoubleStruck
  • SansSerif
  • SansSerifBold
  • SansSerifItalic
  • SansSerifBoldItalic
  • Monospce

例えば文字列を変換して標準出力に出す場合は次のように使います。

package main

import (
	"io"
	"os"
	"strings"

	"github.com/tenkoh/go-textstyle"
	"golang.org/x/text/transform"
)

func main() {
	s := "Hello, Gophers"
	r := transform.NewReader(strings.NewReader(s), textstyle.Bold())
	io.Copy(os.Stdout, r)
	//Output: 𝐇𝐞𝐥𝐥𝐨, 𝐆𝐨𝐩𝐡𝐞𝐫𝐬
}

ここで使用している以外にも様々な入出力を選択できます。例えば出力先をhttp.RequestなんかにしつつTwitter APIを噛ませれば、所望の書体に変換した上でTwitterに投稿するようなアプリケーションが簡単に構築できますね。

上記コード中では基本的にコードポイントのオフセットを与えるだけで変換する簡単な処理を採用しています。いろいろな書体のオフセット量を算出してコードに書き写すのが手間だったので、go generateによるコード生成も試しています。いろいろな経験ができて勉強になりました。

おわりに

みんなで楽しく𝙈𝙮 𝙣𝙚𝙬 𝙜𝙚𝙖𝙧...しましょう!愉快な活用事例があればぜひコメントで教えてください。大変喜びます。

GitHubで編集を提案

Discussion