diff --git a/scripts/lib/setup_venv.py b/scripts/lib/setup_venv.py
index 2e8e2f8844..4bacba06e5 100644
--- a/scripts/lib/setup_venv.py
+++ b/scripts/lib/setup_venv.py
@@ -28,6 +28,7 @@ VENV_DEPENDENCIES = [
"jq", # No longer used in production (clean me up later)
"libsasl2-dev", # For building python-ldap from source
"libvips", # For thumbnailing
+ "libvips-tools",
]
COMMON_YUM_VENV_DEPENDENCIES = [
@@ -46,6 +47,7 @@ COMMON_YUM_VENV_DEPENDENCIES = [
"openssl-devel",
"jq",
"vips", # For thumbnailing
+ "vips-tools",
]
REDHAT_VENV_DEPENDENCIES = [
diff --git a/tools/setup/emoji/build_emoji b/tools/setup/emoji/build_emoji
index e42df4125b..3ef92d912c 100755
--- a/tools/setup/emoji/build_emoji
+++ b/tools/setup/emoji/build_emoji
@@ -4,6 +4,7 @@
# works.
import os
import shutil
+import subprocess
import sys
from collections.abc import Iterator, Sequence
from typing import Any
@@ -49,7 +50,7 @@ div.emoji,
span.emoji
{{
display: inline-block;
- background-image: url(~emoji-datasource-{emojiset}/img/{alt_name}/sheets-256/64.png);
+ background-image: url(../emoji/{emojiset}.webp);
background-size: {background_size};
background-repeat: no-repeat;
@@ -116,15 +117,16 @@ def main() -> None:
# /srv/zulip-emoji-cache/*/web gets copied to ZULIP_PATH/web/generated
# These must not be symlinked so webpack can resolve module references.
- TARGET_EMOJI_STYLES = os.path.join(ZULIP_PATH, "web", "generated", "emoji-styles")
- os.makedirs(TARGET_EMOJI_STYLES, exist_ok=True)
- to_remove = set(os.listdir(TARGET_EMOJI_STYLES))
- source_emoji_dump = os.path.join(emoji_cache_path, "web", "emoji-styles")
- for filename in os.listdir(source_emoji_dump):
- shutil.copy2(os.path.join(source_emoji_dump, filename), TARGET_EMOJI_STYLES)
- to_remove.discard(filename)
- for filename in to_remove:
- os.remove(os.path.join(TARGET_EMOJI_STYLES, filename))
+ for subdir in ("emoji", "emoji-styles"):
+ target_dir = os.path.join(ZULIP_PATH, "web", "generated", subdir)
+ os.makedirs(target_dir, exist_ok=True)
+ to_remove = set(os.listdir(target_dir))
+ source_emoji_dump = os.path.join(emoji_cache_path, "web", subdir)
+ for filename in os.listdir(source_emoji_dump):
+ shutil.copy2(os.path.join(source_emoji_dump, filename), target_dir)
+ to_remove.discard(filename)
+ for filename in to_remove:
+ os.remove(os.path.join(target_dir, filename))
def percent(f: float) -> str:
@@ -334,6 +336,26 @@ def setup_emoji_farms(cache_path: str, emoji_data: list[dict[str, Any]]) -> None
generate_sprite_css_files(cache_path, emoji_data, emojiset, alt_name, fallback_emoji_data)
+ print(f"Converting {emojiset} sheet to webp...")
+ TARGET_EMOJI_SHEETS = os.path.join(cache_path, "web", "emoji")
+ os.makedirs(TARGET_EMOJI_SHEETS, exist_ok=True)
+
+ sheet_src = os.path.join(
+ NODE_MODULES_PATH,
+ f"emoji-datasource-{emojiset}",
+ "img",
+ alt_name,
+ "sheets-256",
+ "64.png",
+ )
+ sheet_dst = os.path.join(TARGET_EMOJI_SHEETS, f"{emojiset}.webp")
+ # From libwebp: [Q is] between 0 and 100. For lossy, 0 gives
+ # the smallest size and 100 the largest. For lossless, this
+ # parameter is the amount of effort put into the
+ # compression: 0 is the fastest but gives larger files
+ # compared to the slowest, but best, 100.
+ subprocess.check_call(["vips", "copy", sheet_src, f"{sheet_dst}[lossless=true,Q=100]"])
+
# Set up standard emoji sets.
for emojiset in ["google", "twitter"]:
setup_emoji_farm(emojiset, emoji_data)
diff --git a/web/.gitignore b/web/.gitignore
index f3873c8bf6..3ea477d029 100644
--- a/web/.gitignore
+++ b/web/.gitignore
@@ -1,4 +1,5 @@
# From emoji
+/generated/emoji
/generated/emoji-styles
# From passing pygments data to the frontend
/generated/pygments_data.json
diff --git a/web/src/assets.d.ts b/web/src/assets.d.ts
index 53b34255fe..9a7c16f75e 100644
--- a/web/src/assets.d.ts
+++ b/web/src/assets.d.ts
@@ -13,6 +13,11 @@ declare module "*.png" {
export default url;
}
+declare module "*.webp" {
+ const url: string;
+ export default url;
+}
+
// Declare the style loader for CSS files. This is used in the
// `import` statements in the `emojisets.ts` file.
declare module "!style-loader?*" {
diff --git a/web/src/emojisets.ts b/web/src/emojisets.ts
index 6abe738c68..c5760c2668 100644
--- a/web/src/emojisets.ts
+++ b/web/src/emojisets.ts
@@ -1,8 +1,7 @@
-import google_sheet from "emoji-datasource-google/img/google/sheets-256/64.png";
-import google_blob_sheet from "emoji-datasource-google-blob/img/google/sheets-256/64.png";
-import twitter_sheet from "emoji-datasource-twitter/img/twitter/sheets-256/64.png";
-
import octopus_url from "../../static/generated/emoji/images-google-64/1f419.png";
+import google_blob_sheet from "../generated/emoji/google-blob.webp";
+import google_sheet from "../generated/emoji/google.webp";
+import twitter_sheet from "../generated/emoji/twitter.webp";
import * as blueslip from "./blueslip";
import {user_settings} from "./user_settings";
diff --git a/web/tests/copy_and_paste.test.js b/web/tests/copy_and_paste.test.js
index ed6e0d6a94..a8263a1b8b 100644
--- a/web/tests/copy_and_paste.test.js
+++ b/web/tests/copy_and_paste.test.js
@@ -147,7 +147,7 @@ run_test("paste_handler_converter", () => {
// Emojis
input =
- 'emojis: :smile: :family_man_woman_girl:';
+ 'emojis: :smile: :family_man_woman_girl:';
assert.equal(
copy_and_paste.paste_handler_converter(input),
"emojis: :smile: :family_man_woman_girl:",
diff --git a/web/webpack.config.ts b/web/webpack.config.ts
index 3e7b8e261f..9c8a79526e 100644
--- a/web/webpack.config.ts
+++ b/web/webpack.config.ts
@@ -192,7 +192,7 @@ const config = (
},
// load fonts and files
{
- test: /\.(eot|jpg|svg|ttf|otf|png|woff2?)$/,
+ test: /\.(eot|jpg|svg|ttf|otf|png|webp|woff2?)$/,
type: "asset/resource",
},
],