signal-desktop: replace unlicensed Apple emoji (#337161)
This commit is contained in:
commit
c32bd049ad
@ -0,0 +1,118 @@
|
||||
"""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()
|
@ -1,8 +1,13 @@
|
||||
{ stdenv
|
||||
, lib
|
||||
, callPackage
|
||||
, fetchurl
|
||||
, autoPatchelfHook
|
||||
, noto-fonts-color-emoji
|
||||
, dpkg
|
||||
, asar
|
||||
, rsync
|
||||
, python3
|
||||
, wrapGAppsHook3
|
||||
, makeWrapper
|
||||
, nixosTests
|
||||
@ -57,6 +62,27 @@
|
||||
let
|
||||
inherit (stdenv) targetPlatform;
|
||||
ARCH = if targetPlatform.isAarch64 then "arm64" else "x64";
|
||||
|
||||
# Noto Color Emoji PNG files for emoji replacement; see below.
|
||||
noto-fonts-color-emoji-png = noto-fonts-color-emoji.overrideAttrs (prevAttrs: {
|
||||
pname = "noto-fonts-color-emoji-png";
|
||||
|
||||
# The build produces 136×128 PNGs by default for arcane font
|
||||
# reasons, but we want square PNGs.
|
||||
buildFlags = prevAttrs.buildFlags or [ ] ++ [ "BODY_DIMENSIONS=128x128" ];
|
||||
|
||||
makeTargets = [ "compressed" ];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/share
|
||||
mv build/compressed_pngs $out/share/noto-fonts-color-emoji-png
|
||||
python3 add_aliases.py --srcdir=$out/share/noto-fonts-color-emoji-png
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
});
|
||||
in
|
||||
stdenv.mkDerivation rec {
|
||||
inherit pname version;
|
||||
@ -71,11 +97,36 @@ stdenv.mkDerivation rec {
|
||||
|
||||
src = fetchurl {
|
||||
inherit url hash;
|
||||
recursiveHash = true;
|
||||
downloadToTemp = true;
|
||||
nativeBuildInputs = [ dpkg asar ];
|
||||
# Signal ships the Apple emoji set without a licence via an npm
|
||||
# package and upstream does not seem terribly interested in fixing
|
||||
# this; see:
|
||||
#
|
||||
# * <https://github.com/signalapp/Signal-Android/issues/5862>
|
||||
# * <https://whispersystems.discoursehosting.net/t/signal-is-likely-violating-apple-license-terms-by-using-apple-emoji-in-the-sticker-creator-and-android-and-desktop-apps/52883>
|
||||
#
|
||||
# We work around this by replacing it with the Noto Color Emoji
|
||||
# set, which is available under a FOSS licence and more likely to
|
||||
# be used on a NixOS machine anyway. The Apple emoji are removed
|
||||
# during `fetchurl` to ensure that the build doesn’t cache the
|
||||
# unlicensed emoji files, but the rest of the work is done in the
|
||||
# main derivation.
|
||||
postFetch = ''
|
||||
dpkg-deb -x $downloadedFile $out
|
||||
asar extract "$out/opt/${dir}/resources/app.asar" $out/asar-contents
|
||||
rm -r \
|
||||
"$out/opt/${dir}/resources/app.asar"{,.unpacked} \
|
||||
$out/asar-contents/node_modules/emoji-datasource-apple
|
||||
'';
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
rsync
|
||||
asar
|
||||
python3
|
||||
autoPatchelfHook
|
||||
dpkg
|
||||
(wrapGAppsHook3.override { inherit makeWrapper; })
|
||||
];
|
||||
|
||||
@ -127,11 +178,13 @@ stdenv.mkDerivation rec {
|
||||
wayland
|
||||
];
|
||||
|
||||
unpackPhase = "dpkg-deb -x $src .";
|
||||
|
||||
dontBuild = true;
|
||||
dontConfigure = true;
|
||||
|
||||
unpackPhase = ''
|
||||
rsync -a --chmod=+w $src/ .
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
@ -147,6 +200,30 @@ stdenv.mkDerivation rec {
|
||||
# Create required symlinks:
|
||||
ln -s libGLESv2.so "$out/lib/${dir}/libGLESv2.so.2"
|
||||
|
||||
# Copy the Noto Color Emoji PNGs into the ASAR contents. See `src`
|
||||
# for the motivation, and the script for the technical details.
|
||||
emojiPrefix=$(
|
||||
python3 ${./copy-noto-emoji.py} \
|
||||
${noto-fonts-color-emoji-png}/share/noto-fonts-color-emoji-png \
|
||||
asar-contents
|
||||
)
|
||||
|
||||
# Replace the URL used for fetching large versions of emoji with
|
||||
# the local path to our copied PNGs.
|
||||
substituteInPlace asar-contents/preload.bundle.js \
|
||||
--replace-fail \
|
||||
'emoji://jumbo?emoji=' \
|
||||
"file://$out/lib/${lib.escapeURL dir}/resources/app.asar/$emojiPrefix/"
|
||||
|
||||
# `asar(1)` copies files from the corresponding `.unpacked`
|
||||
# directory when extracting, and will put them back in the modified
|
||||
# archive if you don’t specify them again when repacking. Signal
|
||||
# leaves their native `.node` libraries unpacked, so we match that.
|
||||
asar pack \
|
||||
--unpack '*.node' \
|
||||
asar-contents \
|
||||
"$out/lib/${dir}/resources/app.asar"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
@ -180,8 +257,21 @@ stdenv.mkDerivation rec {
|
||||
'';
|
||||
homepage = "https://signal.org/";
|
||||
changelog = "https://github.com/signalapp/Signal-Desktop/releases/tag/v${version}";
|
||||
license = lib.licenses.agpl3Only;
|
||||
maintainers = with lib.maintainers; [ eclairevoyant mic92 equirosa urandom bkchr teutat3s ];
|
||||
license = [
|
||||
lib.licenses.agpl3Only
|
||||
|
||||
# Various npm packages
|
||||
lib.licenses.free
|
||||
];
|
||||
maintainers = with lib.maintainers; [
|
||||
eclairevoyant
|
||||
mic92
|
||||
equirosa
|
||||
urandom
|
||||
bkchr
|
||||
teutat3s
|
||||
emily
|
||||
];
|
||||
mainProgram = pname;
|
||||
platforms = [ "x86_64-linux" "aarch64-linux" ];
|
||||
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
|
||||
|
@ -0,0 +1,15 @@
|
||||
[tool.mypy]
|
||||
files = ["*.py"]
|
||||
strict = true
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 80
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["ALL"]
|
||||
ignore = ["COM812", "D203", "D213", "ISC001", "T201"]
|
||||
allowed-confusables = ["‐"]
|
||||
|
||||
[tool.ruff.format]
|
||||
docstring-code-format = true
|
||||
docstring-code-line-length = "dynamic"
|
@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec {
|
||||
dir = "Signal";
|
||||
version = "7.19.0";
|
||||
url = "https://github.com/0mniteck/Signal-Desktop-Mobian/raw/${version}/builds/release/signal-desktop_${version}_arm64.deb";
|
||||
hash = "sha256-L5Wj1ofMR+QJezd4V6pAhkINLF6y9EB5VNFAIOZE5PU=";
|
||||
hash = "sha256-wyXVZUuY1TDGAVq7Gx9r/cuBuoMmSk9KQttTJlIN+k8=";
|
||||
}
|
||||
|
@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec {
|
||||
dir = "Signal Beta";
|
||||
version = "7.19.0-beta.1";
|
||||
url = "https://updates.signal.org/desktop/apt/pool/s/signal-desktop-beta/signal-desktop-beta_${version}_amd64.deb";
|
||||
hash = "sha256-kD08xke+HYhwAZG7jmU1ILo013556vNcvAFc/+9BTjg=";
|
||||
hash = "sha256-dIZvzJ45c5kL+2HEaKrtbck5Zz572pQAj3YTenzz6Zs=";
|
||||
}
|
||||
|
@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec {
|
||||
dir = "Signal";
|
||||
version = "7.21.0";
|
||||
url = "https://updates.signal.org/desktop/apt/pool/s/signal-desktop/signal-desktop_${version}_amd64.deb";
|
||||
hash = "sha256-mjf27BISkvN9Xsi36EXtiSkvaPEc4j/Cwjlh4gkfdsA=";
|
||||
hash = "sha256-c4INjHMqTH2B71aUJtzgLSFZSe/KFo1OW/wv7rApSxA=";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user