%% %% Copyright (C) 2020, 2021 by Xiangdong Zeng %% %% This work may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, either %% version 1.3c of this license or (at your option) any later %% version. The latest version of this license is in: %% %% http://www.latex-project.org/lppl.txt %% %% and version 1.3 or later is part of all distributions of %% LaTeX version 2005/12/01 or later. %% %% This work has the LPPL maintenance status `maintained'. %% %% The Current Maintainer of this work is Xiangdong Zeng. %% \NeedsTeXFormat{LaTeX2e}[2020/02/02] \ProvidesExplPackage{emoji}{2021/07/17}{0.2.2}{Emoji support in (Lua)LaTeX} \msg_new:nnn { emoji } { require-luatex } { The~'emoji'~package~requires~LuaTeX. } \sys_if_engine_luatex:F { \msg_critical:nn { emoji } { require-luatex } } \RequirePackage { fontspec } % Set emoji font. When used in preamble, it will be delayed until the % `\AtBeginDocument` hook. % #1: name % #2: option \NewDocumentCommand \setemojifont { m o } { \__emoji_if_preamble:TF { % The actual `\emoji_font:` command will be defined in the % `\AtBeginDocument` hook, but it will precede our checking. % So we need to "pretend" that it's defined. \cs_set_eq:NN \emoji_font: \prg_do_nothing: \AtBeginDocument } { \use:n } { \IfValueTF {#2} { \emoji_set_font:nn {#1} {#2} } { \emoji_set_font:n {#1} } } } % Check if in the preamble. \prg_set_conditional:Npnn \__emoji_if_preamble: { TF } { \cs_if_eq:NNTF \@onlypreamble \@notprerr { \prg_return_false: } { \prg_return_true: } } % Define `\emoji_font:` when used. % #1: name % #2: option \cs_new_protected:Npn \emoji_set_font:nn #1#2 { \setfontface \emoji_font: {#1} [ Renderer = HarfBuzz, #2 ] } \cs_new_protected:Npn \emoji_set_font:n #1 { \emoji_set_font:nn {#1} {} } % Detect available emoji fonts from OS or TEXMF tree. Currently support: % - Apple Color Emoji % - Segoe UI Emoji % - Noto Color Emoji % Remarks: % - `\file_if_exist:nTF` is much faster than `\fontspec_font_if_exist:nTF`. % - We use environment variable `$WINDIR` for path on Windows. \cs_new_protected:Npn \__emoji_detect_font: { \file_if_exist:nTF { /System/Library/Fonts/Apple~Color~Emoji.ttc } { \emoji_set_font:n { Apple~Color~Emoji } } { \file_if_exist:nTF { \c_dollar_str WINDIR/Fonts/seguiemj.ttf } { \emoji_set_font:n { Segoe~UI~Emoji } } { \fontspec_font_if_exist:nTF { Noto~Color~Emoji } { \emoji_set_font:n { Noto~Color~Emoji } } { \msg_warning:nn { emoji } { no-emoji-font } } } } } \msg_new:nnn { emoji } { no-emoji-font } { It~seems~that~you~have~not~declare~an~emoji~font. \\ You~should~use~"\setemojifont"~to~set~a~font. } % The main command for use emoji. % #1: name \NewDocumentCommand \emoji { m } { % TODO: options \emoji_if_name_exist:nTF {#1} { \emoji_print:n {#1} } { \msg_error:nnn { emoji } { emoji-not-exist } {#1} } } \msg_new:nnn { emoji } { emoji-not-exist } { The~emoji~name~"#1"~can't~be~found. \\ Please~check~your~spelling~or~try~another~one. } % Check if an emoji name exists or not. % #1: name \prg_new_protected_conditional:Npnn \emoji_if_name_exist:n #1 { T, F, TF } { \tl_if_exist:cTF { c__emoji_ #1 _tl } { \prg_return_true: } { \prg_return_false: } } % In a group, change to the emoji font then use corresponding tl constant. \cs_new_protected:Npn \emoji_print:n #1 { \group_begin: \exp_args:Nv \__emoji_ltj_set_range:n { c__emoji_ #1 _tl } \emoji_font: \tl_use:c { c__emoji_ #1 _tl } \group_end: } \cs_new_eq:NN \__emoji_ltj_set_range:n \use_none:n % Define new emoji. They are stored in tl constants internally. % For special characters (#), we first change their catcode to 12 (other), % then define the tl constant. % This function is mainly used in `emoji-table.def`, but will be changed to % print the table in the documentation. % #1: Code points % #2: Name % #3: Aliases % #4: Description % #5: Version \cs_new_protected:Npn \__emoji_def:nnnnn { \group_begin: \char_set_catcode_other:N \# \__emoji_def_aux:nnnnn } \cs_new_protected:Npn \__emoji_def_aux:nnnnn #1#2#3#4#5 { \clist_map_inline:nn { #2, #3 } { \tl_const:cn { c__emoji_ ##1 _tl } {#1} } \group_end: } % These two commands will be used in the documentation. Set to be empty % commands as placeholders. \cs_set:Npn \__emoji_group:n #1 {} \cs_set:Npn \__emoji_subgroup:n #1 {} % Input the emoji definition file. % This file is generated by a Python script from Unicode and GitHub data. \file_input:n { emoji-table.def } \cs_new_protected:Npn \__emoji_if_package_loaded:nT #1#2 { \@ifpackageloaded {#1} {#2} {} } \AtBeginDocument { % Compatibility with hyperref. % `\emoji{...}` will be turned to a normal character sequence. \__emoji_if_package_loaded:nT { hyperref } { \pdfstringdefDisableCommands { \cs_set_nopar:Npn \emoji #1 { \tl_use:c { c__emoji_ #1 _tl } } } } % Compatibility with luatexja. % Emoji should be "ALchar" so that the correct font can be used. \__emoji_if_package_loaded:nT { luatexja } { \cs_set_protected:Npn \__emoji_ltj_set_range:n #1 { \tl_set:Nx \l_tmpa_tl { \tl_map_function:nN {#1} \__emoji_encode_from_char:n } % Range 4 means "characters usually not in Japanese fonts". % `\ltjdefcharrange` does not accespt empty value, so we need to % remove extra comma. \exp_args:Nnx \ltjdefcharrange {4} { \tl_tail:N \l_tmpa_tl } } % 0-7F are always treated as an ALchar and can't be customized. \cs_new:Npn \__emoji_encode_from_char:n #1 { \int_compare:nNnF {`#1} < {"80} {,`#1} } } % If the user doesn't set emoji font explicitly, then detect and set default % fonts automatically. \cs_if_exist:NF \emoji_font: { \__emoji_detect_font: } } \endinput