119 lines
3.8 KiB
Python
119 lines
3.8 KiB
Python
|
"""Copy Noto Color Emoji PNGs into an extracted Signal ASAR archive.
|
|||
|
|
|||
|
Signal loads small Apple emoji PNGs directly from
|
|||
|
`node_modules/emoji-datasource-apple/img/apple/64`, and downloads and
|
|||
|
caches large Apple emoji WebP files in `.proto` bundles on the fly. The
|
|||
|
latter are not a copyright concern for the Nixpkgs cache, but would
|
|||
|
result in inconsistent presentation between small and large emoji.
|
|||
|
|
|||
|
We skip the complexity and buy some additional privacy by replacing the
|
|||
|
`emoji://jumbo?emoji=` URL prefix with a `file://` path to the copied
|
|||
|
PNGs inside the ASAR archive, and linking the `node_modules` PNG paths
|
|||
|
directly to them.
|
|||
|
"""
|
|||
|
|
|||
|
import json
|
|||
|
import shutil
|
|||
|
import sys
|
|||
|
from pathlib import Path
|
|||
|
|
|||
|
|
|||
|
def signal_name_to_emoji(signal_emoji_name: str) -> str:
|
|||
|
r"""Return the emoji corresponding to a Signal emoji name.
|
|||
|
|
|||
|
Signal emoji names are concatenations of UTF‐16 code units,
|
|||
|
represented in lowercase big‐endian hex padded to four digits.
|
|||
|
|
|||
|
>>> signal_name_to_emoji("d83dde36200dd83cdf2bfe0f")
|
|||
|
'😶🌫️'
|
|||
|
>>> b"\xd8\x3d\xde\x36\x20\x0d\xd8\x3c\xdf\x2b\xfe\x0f".decode("utf-16-be")
|
|||
|
'😶🌫️'
|
|||
|
"""
|
|||
|
hex_bytes = zip(signal_emoji_name[::2], signal_emoji_name[1::2])
|
|||
|
emoji_utf_16_be = bytes(
|
|||
|
int("".join(hex_pair), 16) for hex_pair in hex_bytes
|
|||
|
)
|
|||
|
return emoji_utf_16_be.decode("utf-16-be")
|
|||
|
|
|||
|
|
|||
|
def emoji_to_noto_name(emoji: str) -> str:
|
|||
|
r"""Return the Noto emoji name of an emoji.
|
|||
|
|
|||
|
Noto emoji names are underscore‐separated Unicode scalar values,
|
|||
|
represented in lowercase big‐endian hex padded to at least four
|
|||
|
digits. Any U+FE0F variant selectors are omitted.
|
|||
|
|
|||
|
>>> emoji_to_noto_name("😶🌫️")
|
|||
|
'1f636_200d_1f32b'
|
|||
|
>>> emoji_to_noto_name("\U0001f636\u200d\U0001f32b\ufe0f")
|
|||
|
'1f636_200d_1f32b'
|
|||
|
"""
|
|||
|
return "_".join(
|
|||
|
f"{ord(scalar_value):04x}"
|
|||
|
for scalar_value in emoji
|
|||
|
if scalar_value != "\ufe0f"
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
def emoji_to_emoji_data_name(emoji: str) -> str:
|
|||
|
r"""Return the npm emoji-data emoji name of an emoji.
|
|||
|
|
|||
|
emoji-data emoji names are hyphen‐minus‐separated Unicode scalar
|
|||
|
values, represented in lowercase big‐endian hex padded to at least
|
|||
|
four digits.
|
|||
|
|
|||
|
>>> emoji_to_emoji_data_name("😶🌫️")
|
|||
|
'1f636-200d-1f32b-fe0f'
|
|||
|
>>> emoji_to_emoji_data_name("\U0001f636\u200d\U0001f32b\ufe0f")
|
|||
|
'1f636-200d-1f32b-fe0f'
|
|||
|
"""
|
|||
|
return "-".join(f"{ord(scalar_value):04x}" for scalar_value in emoji)
|
|||
|
|
|||
|
|
|||
|
def _main() -> None:
|
|||
|
noto_png_path, asar_root = (Path(arg) for arg in sys.argv[1:])
|
|||
|
asar_root = asar_root.absolute()
|
|||
|
|
|||
|
out_path = asar_root / "images" / "nixpkgs-emoji"
|
|||
|
out_path.mkdir(parents=True)
|
|||
|
|
|||
|
emoji_data_out_path = (
|
|||
|
asar_root
|
|||
|
/ "node_modules"
|
|||
|
/ "emoji-datasource-apple"
|
|||
|
/ "img"
|
|||
|
/ "apple"
|
|||
|
/ "64"
|
|||
|
)
|
|||
|
emoji_data_out_path.mkdir(parents=True)
|
|||
|
|
|||
|
jumbomoji_json_path = asar_root / "build" / "jumbomoji.json"
|
|||
|
with jumbomoji_json_path.open() as jumbomoji_json_file:
|
|||
|
jumbomoji_packs = json.load(jumbomoji_json_file)
|
|||
|
|
|||
|
for signal_emoji_names in jumbomoji_packs.values():
|
|||
|
for signal_emoji_name in signal_emoji_names:
|
|||
|
emoji = signal_name_to_emoji(signal_emoji_name)
|
|||
|
|
|||
|
try:
|
|||
|
shutil.copy(
|
|||
|
noto_png_path / f"emoji_u{emoji_to_noto_name(emoji)}.png",
|
|||
|
out_path / emoji,
|
|||
|
)
|
|||
|
except FileNotFoundError:
|
|||
|
print(
|
|||
|
f"Missing Noto emoji: {emoji} {signal_emoji_name}",
|
|||
|
file=sys.stderr,
|
|||
|
)
|
|||
|
continue
|
|||
|
|
|||
|
(
|
|||
|
emoji_data_out_path / f"{emoji_to_emoji_data_name(emoji)}.png"
|
|||
|
).symlink_to(out_path / emoji)
|
|||
|
|
|||
|
print(out_path.relative_to(asar_root))
|
|||
|
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
_main()
|