メモログ

塵が積もって山とならないメモのログ

ngBindHtmlとサロゲートペア

AngularJSでngBindHtmlを使用すると、ngSanitize(angular-sanitize.js)のencodeEntitiesに行き着く(はず)。そこでは下記のような処理が行われています。

1
2
3
4
5
6
7
8
9
function encodeEntities(value) {
return value.
replace(/&/g, '&').
replace(NON_ALPHANUMERIC_REGEXP, function(value){
return '&#' + value.charCodeAt(0) + ';';
}).
replace(//g, '>');
}

NON_ALPHANUMERIC_REGEXPは、/([^\#-~| |!])/g;でマッチした値。文字列はこの処理を通る感じになる。charCodeAtの処理はcharCodeAt - MDNを参照。

Note that charCodeAt will always return a value that is less than 65,536. This is because the higher code points are represented by a pair of (lower valued) “surrogate” pseudo-characters which are used to comprise the real character.

charCodeAtはつねに65,536以下の値が返ってくるようになっていて、それ以上のコードポイントの場合は、サロゲートペアの状態になってくる。サロゲートペアについては、サロゲートペア入門の「サロゲートペアとは?」を参照。

なので、ngBindHtmlで絵文字のように65,356以上のコードポイントを持ってそうな文字列を通すと、サロゲートペアをひとつずつ実体参照の状態にしてしまい、うまく表示できない。

ngBindHtmlでそのような文字列を表示するためには、なんとかしないといけない。それでとりあえずeller86/surrogate-pair.jsを使ってsurrogate pairを処理するように対応。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function handleSurrogatePair(content){
if (sp.findSurrogatePair(content)) {
content = $sce.getTrustedHtml(content);

var match = content.match(/\&\#5[5-6][0-9]{3};\&\#5[6-7][0-9]{3};/g),
i = 0,
len = match.length,
str;

for (; i < len; i++) {
str = match[i].match(/[0-9]+/g);
if (sp.checkHighSurrogate(str[0])) {
content = content.replace(match[i], '&#' + (((str[0] - 0xD800) * 0x400) + (str[1] - 0xDC00) + 0x10000) + ';');
}
}
return content
} else {
return $sce.trustAsHtml($sce.getTrustedHtml(content));
}
}

$sceはStrict Contextual Escaping services to AngularJSを参照。対処療法に過ぎないので、あんまりいい感じではないけど、一応表示できるようになる。

そもそも、ngSanitizeでsurrogate pairを適切に処理するようにpull requestを作るべきなんだろうなあと思いつつ、そこまでいたってない。そして基本的には、ngBindHtmlを使わずにngBindだけでうまく設計する方がいい。ngSanitizeの処理量半端ない。

というメモ。走り書き。

ngSanitize cant escape all unicode characters ? Issue #5088 ? angular/angular.jsに関連issueがあった。

fix(ngSanitize): encode surrogate pair properly by memolog ? Pull Request #6911 ? angular/angular.jsでPull Requestを送って、1.3.xではサロゲートペアに対する処理が追加されました。ので、絵文字表示できない問題は起きない。