Nulab Appsチームのフロントエンド開発を担当している本橋です。現在、Nulab Appsチームではリデザイン・プロジェクトを進めており、その一環でNulabアカウントのデフォルトアイコンを変更する案が持ち上がっています。
そこで、本記事では、デフォルトアイコンを文字付きアイコンに変更するために便利なデモサービスの作り方をご紹介します。
これは何?
- 丸い背景をバックにして文字が中央に配置されたアイコンを簡単に作れます。
- Web API を使ってプログラムから利用できます。
- Web アプリもあります。
背景
Nulab Apps チームではリデザイン・プロジェクトを進めており、その一環で Nulab アカウントのデフォルトアイコンを変更する案が持ち上がっています。
UI デザイナーの方から提案されたのは、丸い背景をバックにアカウント名のイニシャルが中央に配置されているようなアイコンでした。
このようなアイコン画像は SVG ならばあまり苦労せず作成できるのですが、Nulab の他プロダクトとの兼ね合いもあり、CSS や SVG ではなく PNG で作るのがよろしいということになりました。
しかし限られた時間でそれをやるのは難しく、適当なライブラリも見つからなかったため見送りとなってしまいました。
が、技術的なトピックとしては面白くチャレンジしがいのあるものでしたので、個人プロジェクトとして取り組むことにしました。今回はこのプロジェクトの初期段階~動くものを用意するまでをご紹介します。
アーキテクチャ
どのように画像を生成するか
さっそく仕組みづくりに取り掛かります。まず考えるのは「どのように画像を生成するか?」というところです。ここさえ押さえれば実現はそこまで難しくないだろうと考えていたため、最初にこの部分の検証を行うことにしました。考えられるのは以下の2パターンです。
- PNG を直接生成する
- SVG を PNG に変換する
前述の通り SVG なら簡単にできることがわかっていたため、後者を採用することにしました。
前者のほうがパフォーマンス的には優れそうな気がしますが、おそらくこの方法だと文字のレイアウト等でかなり泥臭いロジックを組むことになると考えられたため見送りました。
ライブラリ探し
さて、方針も決まったことなので SVG を PNG に変換するライブラリを探さなければなりません。最初に考えたのは ImageMagick を使うことです。
How to convert a SVG to a PNG with Image Magick?
ですが SVG の描画があまり上手くなく、レイアウトやフォントが崩れてしまったりと今回の要件を満たすことはできなさそうでした。
他の方法を探したところ、ヘッドレスの Web ブラウザで SVG を描画し、PNG に変換するライブラリがいくつかあるようです。その中でも Puppeteer を使って Chromium を操作する以下のライブラリ (Node.js 製) を使うことにしました。
このライブラリを使うと以下のように簡単に SVG から PNG への変換を行うことができます。
const { convert } = require('convert-svg-to-png'); const png = await convert(svg); // バイナリ (Buffer 型) const b64 = png.toString('base64'); // base64
提供方法
Nulab Apps のサーバーサイドは Java で作られています (ただしリデザイン・プロジェクトでは Kotlin を採用)。そのため、Node.js 製の上記ライブラリをそのまま利用することは出来ません。
また、サーバーサイドで Puppeteer (Chromium) を動かすことになるため、通常の Web アプリとは別のプラットフォームで動作したほうがいいだろうと考えました。そこで上記ライブラリをラップした Web API を用意し、マイクロサービス的に機能を使ってもらうことにしました。
実装
あとは実装するだけです。Web API のインターフェースは Express で用意し、リクエストパラメーターに応じた SVG/PNG 画像を返します。
// server.js // PNG を生成するエンドポイント // GET だとリクエストボディからパラメータを受け取れないため POST app.post('/api/png', async (req, res, next) => { const png = await generatePng(/* ... */); // generatePng は上記ライブラリをラップして PNG を返す関数 const b64 = 'data:/image/png;base64,' + png.toString('base64'); res.header({ 'content-type': 'text/plain' }); res.send(b64); });
デモ用 (という体の開発時確認用) の Web アプリも用意します。
// demo.js const fetchPng = async (letter, opt) => { const uri = `/api/png?l=${letter}`; const res = await fetch(uri, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ opt }) }); return res.text(); };
また、デプロイ用の Dockerfile を用意します。様々なフォントを使えるようにするため、コンテナ内にフォントをインストールします。日本語フォントもここでインストールしておきます。
# Dockerfile RUN apk update \ && apk add --no-cache curl fontconfig msttcorefonts-installer \ # # Install Japanese Font && curl -O https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip \ && mkdir -p /usr/share/fonts/NotoSansCJKjp \ && unzip NotoSansCJKjp-hinted.zip -d /usr/share/fonts/NotoSansCJKjp/ \ && rm NotoSansCJKjp-hinted.zip \ # # Install Arial, Courier New, Vardana, etc... # https://pkgs.alpinelinux.org/package/v3.8/community/x86/msttcorefonts-installer && update-ms-fonts \ # && fc-cache -vf \ && apk del --purge curl fontconfig msttcorefonts-installer
ひとまず完成
そして出来たものが以下になります。
- https://github.com/mmktomato/letter-icon-generator
- https://hub.docker.com/r/mmktomato/letter-icon-generator/
Web API の使い方は README を読んでいただければいいので、デモ用の Web アプリを少し紹介します。
Docker でコンテナを起動して /demo.html
をブラウザで開きます。
$ docker run -dp 1337:1337 mmktomato/letter-icon-generator:latest # Docker for (Mac|Windows) の場合は Docker Machine の IP アドレス # http://localhost:1337/demo.html
「GO」ボタンをクリックすると SVG と PNG を生成して表示します。日本語にも対応しているので是非試してみてください。(PNG の方は初回の生成に時間がかかります。)
最後に
仕組みの考察から実装までをざっと振り返ってみました。まだこれはプロダクトに採用されていませんが (というか採用されるかどうかもわかりませんが)、引き続き改善して行きたいと思います。