1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

サロゲートペアでWindows10でサポートしない絵文字を表示する

Last updated at Posted at 2024-10-11

はじめに

普段はMac環境で開発をしているため、Windows環境について考えることがあまりない。しかし最近、Windows 10で特定の絵文字が正しく表示されない問題があることに気づいた。
色々調べて、ユニコードに「サロゲートペア」という概念があり、このサロゲートペアを用いることで問題を解決できたため、記事として残してみた。

サロゲートペアとは

サロゲートペア(Surrogate Pair)は、直訳するとSurrogate(代理)のペアである。

何が代理?

これはUTF-16エンコーディングで使用される概念で、1つの文字を表現するために2つの16ビットコードユニットを使用する方法である。

例えば以下の地球の絵文字は一文字に見えるが、実際は長さ2の文字になっている。

const emoji = "🌍";
console.log(emoji.length); // 出力: 2

なぜ必要?

Unicodeは世界中のすべての文字を表現するために作られたけど、当初は16ビット(65,536文字)で十分だと考えられていた。

しかし、絵文字のような新しい文字が追加されるにつれて16ビットでは数が足りなくなって、サロゲートペアが導入されたことになる。

JavaScriptでのサロゲートペア

JavaScriptでは文字列をUTF-16でエンコードする。

例えば以下のような使い方ができる。

// サロゲートペアかどうかを確認する関数
function isSurrogatePair(str, index) {
  const firstCode = str.charCodeAt(index);
  if (firstCode > 0xD7FF && firstCode < 0xDC00) {
    const secondCode = str.charCodeAt(index + 1);
    return secondCode > 0xDBFF && secondCode < 0xE000;
  }
  return false;
}

// サロゲートペアかどうかを確認する関数
//(Intl.Segmenterを使用を利用するとマジックナンバーを使わずに実装できる)
const isSurrogatePairIntl = str => [...new Intl.Segmenter('ja', {granularity: 'grapheme'}).segment(str)].length !== str.length;

// コードポイントをサロゲートペアに変換
function toSurrogatePair(codePoint) {
  codePoint -= 0x10000;
  const highSurrogate = (codePoint >> 10) | 0xD800;
  const lowSurrogate = (codePoint & 0x3FF) | 0xDC00;
  return String.fromCharCode(highSurrogate, lowSurrogate);
}

Windows 10での問題

Windows 10でサポートされていない絵文字を表示しようとすると、「□」(tofuと呼ばれる)(かわいい)のような四角い文字が出力される。かわいいけど、絵文字の代わりに出されると文字化けになるため、せめて絵文字を見せていきたい。

代替文字出力のコード

ということでサロゲートペアを利用して、Unicodeを対応している場合はそのまま表示し、対応しない場合は代替の文字を表示するロジックを組んでみた。

// 絵文字がサポートされているか確認
function isEmojiSupported(emoji) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  ctx.font = '1px sans-serif';
  
  const emojiWidth = ctx.measureText(emoji).width;
  const fallbackWidth = ctx.measureText('').width;
  
  return emojiWidth !== fallbackWidth;
}

// 絵文字の代替マッピング(コードポイントを使用)
const emojiReplacements = {
  0x1FAE5: 0x1F636, // 🫥 (点線の顔) -> 😶 (無表情の顔)
  0x1FAE0: 0x1F643, // 🫠 (溶ける顔) -> 🙃 (逆さまの顔)
};

// 絵文字の表示
function renderEmoji(str) {
  let result = '';
  for (let i = 0; i < str.length; i++) {
    if (isSurrogatePair(str, i)) {
      let codePoint = str.codePointAt(i);
      let emoji = String.fromCodePoint(codePoint);
      if (!isEmojiSupported(emoji) && emojiReplacements[codePoint]) {
        result += toSurrogatePair(emojiReplacements[codePoint]);
      } else {
        result += emoji;
      }
      i++; // サロゲートペアの2番目の部分をスキップ
    } else {
      result += str[i];
    }
  }
  return result;
}

おわりに

もうWindows 11が出ているとは言え、WindowsでUnicode12までしか対応しないのはおかしいと思う。
個人アプリだと特に手元にWindowsの実証環境がないので、定期的に対応してほしい。

1
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?