# Android PdfDocumentでEmojiCompatの絵文字をテキストとして描画する
Android の PdfDocument で、通常文字と絵文字が混在する文字列を描画する方法をまとめます。
# 目的
- 単純絵文字を黒一色で表示します。
- ZWJ 絵文字を分離させずに表示します。
- 通常文字と絵文字が混在しても描画位置を維持します。
BitmapやPathを使わず、PDF 上のテキスト選択とコピーを維持します。
# 方針
EmojiCompat.process() で文字列を処理し、付与された EmojiSpan を PDF の Canvas に直接描画します。
EmojiSpan.draw() に渡す Paint へ PorterDuffColorFilter を設定すると、カラー glyph を黒一色で描画できます。EmojiSpan を取得できない場合だけ、白黒絵文字フォントの Canvas.drawText() を補助的に使用します。
# 最小サンプル
このコードは、EmojiCompat の初期化完了後に呼び出します。androidx.emoji2:emoji2 と androidx.emoji2:emoji2-bundled が必要です。
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.pdf.PdfDocument;
import android.text.Spanned;
import androidx.emoji2.text.EmojiCompat;
import androidx.emoji2.text.EmojiSpan;
import java.io.IOException;
import java.io.OutputStream;
public final class EmojiTextPdf {
public static void write(OutputStream output) throws IOException {
if (EmojiCompat.get().getLoadState() != EmojiCompat.LOAD_STATE_SUCCEEDED) {
throw new IllegalStateException("EmojiCompat の初期化完了後に実行してください");
}
PdfDocument document = new PdfDocument();
PdfDocument.Page page = document.startPage(
new PdfDocument.PageInfo.Builder(500, 200, 1).create());
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(32);
drawText(page.getCanvas(), "前🙂中😶🌫️後", 32, 100, paint);
document.finishPage(page);
document.writeTo(output);
document.close();
}
private static void drawText(Canvas canvas, String text, float x, int baseline, Paint paint) {
Spanned spanned = (Spanned) EmojiCompat.get().process(text);
int index = 0;
while (index < spanned.length()) {
EmojiSpan[] spans = spanned.getSpans(index, index + 1, EmojiSpan.class);
if (spans.length == 0 || spanned.getSpanStart(spans[0]) != index) {
int next = spanned.nextSpanTransition(index, spanned.length(), EmojiSpan.class);
String plain = spanned.subSequence(index, next).toString();
canvas.drawText(plain, x, baseline, paint);
x += paint.measureText(plain);
index = next;
continue;
}
EmojiSpan span = spans[0];
int end = spanned.getSpanEnd(span);
Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
int width = span.getSize(paint, spanned, index, end, metrics);
Paint emojiPaint = new Paint(paint);
emojiPaint.setColorFilter(new PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN));
span.draw(canvas, spanned, index, end, x, baseline + metrics.ascent,
baseline, baseline + metrics.descent, emojiPaint);
x += width;
index = end;
}
}
}
# 実装時の注意点
- PDF 出力は、EmojiCompat の
LOAD_STATE_SUCCEEDEDを確認してから開始します。 - 中央寄せや右寄せでは、描画と同じ分岐で幅を計測します。
EmojiSpan.draw()の座標は baseline を基準に計算します。- PDF ビューアで、表示だけでなく選択とコピーも確認します。
この方法では Bitmap 化を避けられるため、見た目だけでなくテキストとしての扱いも維持できます。